Featuring Scala.js "in browser testing" by ScalaTest 3.x
A Scala hardcore action game where you possess and play as a Hero 😄.
The focus is on showcasing the use of several Functional Programming technologies and therefore it's heavily violating on purpose the Principle of Least Power Strategic Scala Style.
Please rate this repository.
This "Simple HTML5 Canvas Game" is a Scala.js project which targets a browser capable displaying HTML5, especially the <canvas>
element.
Stored on GitHub.com, due to sbt the code is also remote tested on Travis-CI. Also possible on an other continuous integration service.
This quite super minimalistic and simple game is heavily über engineered. It's certainly not the game that counts but the technology around this, it features:
- HTML5 Canvas controlled by Scala.js
- Headless canvas Selenium 2 "in browser testing" with the recently released ScalaTest 3.x
- ScalaTest 3.x featuring "async" testing styles.
- Test running on Continuous Integration service. Travis CI
- Scala 2.12 compiler.
- Exhaustive use of a variety of Scala features, e.g.:
Traits
,case
Class
es and SingletonObject
sFuture
s sane way to dramatically reduce latency in web requests- Generic[T] programming (shameless, even in the frenzied Ough).
- Algebraic Data Types
- Pattern matching
- Lazy evaluation
- Implicit Conversions
- Explicitly Typed Self References
- Reactive design, event driven instead of continuous polling.
- Eliminating a power-draining continuously redrawn of the canvas saves cpu time and (mobile) power.
- Tackling Cross-origin resource sharing CORS enabled images.
- Scala generated HTML.
- CSS rendered Corner Ribbon (as
fastOptJS
version). - Scala 2.12 fresh Scaladoc look..
Scala.js compile-to-Javascript language is by its compile phase ahead of runtime errors in production. It prevents you of nasty runtime errors because everything must be ok in the compile phase, specially the types of the functions and variables.
In the original tutorial in Javascript: How to make a simple HTML5 Canvas game, a continuous redraw of the canvas was made, which is a simple solution, but resource costly.
Play the live demo. Scaladoc you will find here. Installation instructions here
By the initial call from SimpleCanvas.main
to Game.play
its (private) gameLoop
will periodic started given its framesPerSec
frequency.
There the status of eventually pressed arrow keys will be tested and per GameState.keyEffect
converted to a move of the Hero
.
In an instance of GameState
the position of the CanvasComponent
s are immutable recorded. When a change has to be made a new instance will be
generated with only the changed variables adjusted and leaving the rest unchanged by copying the object.
With the changes in this CanvasState
a render method of Page
is only called if the instance is found changed.
The render method repaints the canvas completely. Successively the background, monster and hero will be painted, so the last image is at the foreground.
The images found are the respectively instances of CanvasComponent
subclasses Playground
, Monster
and Hero
.
They are asynchronously loaded once at startup by means of the use of Future
s.
In spite of the fact that the application is technically one-tier in an MVC design pattern perspective, everything runs in the browser, the following parts can be identified:
Part | Class | Auxiliary |
---|---|---|
Model | GameState | Position |
View | Page | CanvasComponents (Playground, Monster and Hero) |
Controller | Game | GameState |
Communication from Game to Page is done by calling with a modified GameState to Page.
Unit testing is done with ScalaTest 3.x which is completely detached from the JVM system and Java runtime. Although running sbt, the test code will be executed in a browser. This is enabled by a Selenium environment interface direct running to Firefox and via Chrome Driver to a Google Chrome browser.
The necessary resources are downloaded from a external server because the test environment lacks a server for this this task.
The test tasks can be invoked by chrome:test
for the Google Chrome browser and firefox:test
, separated configs constructed in InbrowserTesting.scala
.
As proposed by this article.
Unfortunately at Travis-CI it's not possible to run Google Chrome, so firefox:test
is the only option.
Also unfortunately chrome:test
fails on test 10
, so if executed locally test 10
must be comment out.
Test Class file | Coverage | Remarks |
---|---|---|
CanvasComponentSuite |
canvasComponent | Playground , Monster and Hero are concrete classes of CanvasComponent |
GameStateSuite |
GameState |
|
GameSuite |
Game |
|
PageSuite |
Page |
This excercises a Hero instance against border limitations.
E.g. GameState equality.
Test the effect of the arrow keys on the Hero moves.
This is the most interesting unit test, it features:
- Asynchronous non-blocking testing
- Canvas testing
ScalaTest 3.x supports asynchronous non-blocking testing by returning a Future[Assertion]
type. With this the assertion is
postponed as long if the Future
is not completed.
Because of the difference of processing between various render engines in browsers, is it hard to test the content of a canvas. One pixel difference becomes immediately a negative test result. However, a couple of techniques can be used by hashing the canvas to a hash value. The techniques are:
- Exact comparison, only possible if a complete image is rendered in the same sized canvas. In this case no processing (cropping, resizing) is required and therefor not tainted. Only the pixels of original source are used which gives the same result, even in different browsers. Actual it's a property of the source.
- A different hash value per different browser. In this case there are multiple hash values valid, one per browser.
- Tainted canvas. E.g. text on a canvas gives sometimes a slightly different result in pixels by e.g. rounding errors. The only test we can do is to test if the canvas has changed.
- Clone the Github project to a new directory. This is the project directory which become the working directory of current folder.
- Naturally, at least a Java SE Runtime Environment (JRE) is installed on your platform and has a path to it enables execution.
- (Optional) Test this by submitting a
java -version
command in a Command Line Interface (CLI, terminal). The output should look like this:
java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)
- Make sure sbt is runnable from almost any work directory, use eventually one of the platform depended installers:
- Installing sbt on Mac or
- Installing sbt on Windows or
- Installing sbt on Linux or
- Manual installation (not recommended)
- (Optional ) To test if sbt is effective submit the
sbt sbtVersion
command. The response could look like as this:
[info] Set current project to fransdev (in build file:/C:/Users/FransDev/)
[info] 0.13.13
Remember shells (CLI's) are not reactive. To pick up the new environment variables the CLI must be closed and restarted.
- Run sbt in one of the next modes in a CLI in the working directory or current folder, a compilation will be started and a local web server will be spinned up using:
- Inline mode on the command line:
sbt fastOptJS
or - Interactive mode, start first the sbt by hitting in the CLI
sbt
followed byfastOptJS
on the sbt prompt, or - Triggered execution by a
~
before the command, so~fastOptJS
. This command will execute and wait after the target code is in time behind the source code (Auto build). chrome:test
will run the ScalaTest test scripts in the test directory in a Google Chrome browser.firefox:test
will start the ScalaTest test scripts in the test directory in a FireFox browser. (preferred)
- Inline mode on the command line:
- sbt will give a notice that the server is listening by the message:
Bound to localhost/127.0.0.1:12345
(Ignore the dead letter notifications with the enter key.) - Open this application in a browser on this given URL
When running this way a tool "workbench" also will be running in the browser, noticeable by opening the console of the browser.
Licensed under the EUPL-1.1
Language files blank comment code
-------------------------------------------------------------------------------
Scala 6 96 113 261
game.js
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
JavaScript 1 21 16 93
Scala.js minimal project
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
JavaScript 1 26 1 572
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
JavaScript 2 795 1 15423
HTML 2 13 25 51
CSS 1 14 0 49
-------------------------------------------------------------------------------
SUM: 5 822 26 15523
-------------------------------------------------------------------------------