From 7e9745ce3f5c66fd2d508065ab7eabebec1a7ab3 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 21 Oct 2016 12:42:32 -0400 Subject: [PATCH 01/10] Add basic test coverage for clock module. --- test/unit/io_clock.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/unit/io_clock.js diff --git a/test/unit/io_clock.js b/test/unit/io_clock.js new file mode 100644 index 000000000..cec77839e --- /dev/null +++ b/test/unit/io_clock.js @@ -0,0 +1,34 @@ +var test = require('tap').test; +var Clock = require('../../src/io/clock'); +var Runtime = require('../../src/engine/runtime'); + +test('spec', function (t) { + var rt = new Runtime(); + var c = new Clock(rt); + + t.type(Clock, 'function'); + t.type(c, 'object'); + t.type(c.projectTimer, 'function'); + t.type(c.pause, 'function'); + t.type(c.resume, 'function'); + t.type(c.resetProjectTimer, 'function'); + t.end(); +}); + +test('cycle', function (t) { + var rt = new Runtime(); + var c = new Clock(rt); + + t.strictEqual(c.projectTimer(), 0); + setTimeout(function () { + c.resetProjectTimer(); + setTimeout(function () { + t.ok(c.projectTimer() > 0); + c.pause(); + t.ok(c.projectTimer() > 0); + c.resume(); + t.ok(c.projectTimer() > 0); + t.end(); + }, 100); + }, 100); +}); From faf8a898a0b9983065a44e09d47540166149a379 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Fri, 21 Oct 2016 18:31:07 -0400 Subject: [PATCH 02/10] Add minimal test coverage and docs for keyboard module. --- src/io/keyboard.js | 17 ++++++++++ test/unit/io_keyboard.js | 73 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 test/unit/io_keyboard.js diff --git a/src/io/keyboard.js b/src/io/keyboard.js index ea423fd40..e72f689f2 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -18,6 +18,7 @@ function Keyboard (runtime) { * Convert a Scratch key name to a DOM keyCode. * @param {Any} keyName Scratch key argument. * @return {number} Key code corresponding to a DOM event. + * @private */ Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { if (typeof keyName == 'number') { @@ -37,6 +38,12 @@ Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { return keyString.toUpperCase().charCodeAt(0); }; +/** + * Convert a DOM keyCode into a Scratch key name. + * @param {number} Key code from DOM event. + * @return {Any} Scratch key argument. + * @private + */ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { if (keyCode >= 48 && keyCode <= 90) { // Standard letter. @@ -52,6 +59,11 @@ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { return null; }; +/** + * Keyboard DOM event handler. + * @param {object} DOM event object. + * @return {void} + */ Keyboard.prototype.postData = function (data) { if (data.keyCode) { var index = this._keysPressed.indexOf(data.keyCode); @@ -74,6 +86,11 @@ Keyboard.prototype.postData = function (data) { } }; +/** + * Get key down state for a specified Scratch key name. + * @param {Any} Scratch key argument. + * @return {boolean} + */ Keyboard.prototype.getKeyIsDown = function (key) { if (key == 'any') { return this._keysPressed.length > 0; diff --git a/test/unit/io_keyboard.js b/test/unit/io_keyboard.js new file mode 100644 index 000000000..ad3a65c6d --- /dev/null +++ b/test/unit/io_keyboard.js @@ -0,0 +1,73 @@ +var test = require('tap').test; +var Keyboard = require('../../src/io/keyboard'); +var Runtime = require('../../src/engine/runtime'); + +test('spec', function (t) { + var rt = new Runtime(); + var k = new Keyboard(rt); + + t.type(k, 'object'); + t.type(k.postData, 'function'); + t.type(k.getKeyIsDown, 'function'); + t.end(); +}); + +test('space', function (t) { + var rt = new Runtime(); + var k = new Keyboard(rt); + + k.postData({ + keyCode: 32, + isDown: true + }); + t.strictDeepEquals(k._keysPressed, [32]); + t.strictEquals(k.getKeyIsDown('space'), true); + t.strictEquals(k.getKeyIsDown('any'), true); + t.end(); +}); + +test('letter', function (t) { + var rt = new Runtime(); + var k = new Keyboard(rt); + + k.postData({ + keyCode: 65, + isDown: true + }); + t.strictDeepEquals(k._keysPressed, [65]); + t.strictEquals(k.getKeyIsDown('a'), true); + t.strictEquals(k.getKeyIsDown('any'), true); + t.end(); +}); + +test('number', function (t) { + var rt = new Runtime(); + var k = new Keyboard(rt); + + k.postData({ + keyCode: 49, + isDown: true + }); + t.strictDeepEquals(k._keysPressed, [49]); + t.strictEquals(k.getKeyIsDown(49), true); + t.strictEquals(k.getKeyIsDown('any'), true); + t.end(); +}); + +test('keyup', function (t) { + var rt = new Runtime(); + var k = new Keyboard(rt); + + k.postData({ + keyCode: 37, + isDown: true + }); + k.postData({ + keyCode: 37, + isDown: false + }); + t.strictDeepEquals(k._keysPressed, []); + t.strictEquals(k.getKeyIsDown(37), false); + t.strictEquals(k.getKeyIsDown('any'), false); + t.end(); +}); From 2de58050dd1f07563537044c0fd7d493fc72c205 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 11:40:07 -0400 Subject: [PATCH 03/10] Conform JSdoc for keyboard module to format used elsewhere --- src/io/keyboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/keyboard.js b/src/io/keyboard.js index e72f689f2..6b1d28d93 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -61,7 +61,7 @@ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { /** * Keyboard DOM event handler. - * @param {object} DOM event object. + * @param {object} DOM event. * @return {void} */ Keyboard.prototype.postData = function (data) { From 336bc65e2d37d14417d33e438d332548de9c5eb3 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 11:56:52 -0400 Subject: [PATCH 04/10] Add minimal test coverage and docs for mouse module --- src/io/mouse.js | 53 +++++++++++++++++++++++++++++++------------ test/unit/io_mouse.js | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 test/unit/io_mouse.js diff --git a/src/io/mouse.js b/src/io/mouse.js index 6b9860ee7..368c84701 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -12,21 +12,12 @@ function Mouse (runtime) { this.runtime = runtime; } -Mouse.prototype.postData = function(data) { - if (data.x) { - this._x = data.x - data.canvasWidth / 2; - } - if (data.y) { - this._y = data.y - data.canvasHeight / 2; - } - if (typeof data.isDown !== 'undefined') { - this._isDown = data.isDown; - if (this._isDown) { - this._activateClickHats(data.x, data.y); - } - } -}; - +/** + * Activate "event_whenthisspriteclicked" hats if needed. + * @param {int} X position to be sent to the renderer. + * @param {int} Y position to be sent to the renderer. + * @return {void} + */ Mouse.prototype._activateClickHats = function (x, y) { if (this.runtime.renderer) { var drawableID = this.runtime.renderer.pick(x, y); @@ -42,14 +33,46 @@ Mouse.prototype._activateClickHats = function (x, y) { } }; +/** + * Mouse DOM event handler. + * @param {object} DOM event. + * @return {void} + */ +Mouse.prototype.postData = function(data) { + if (data.x) { + this._x = data.x - data.canvasWidth / 2; + } + if (data.y) { + this._y = data.y - data.canvasHeight / 2; + } + if (typeof data.isDown !== 'undefined') { + this._isDown = data.isDown; + if (this._isDown) { + this._activateClickHats(data.x, data.y); + } + } +}; + +/** + * Get the X position of the mouse. + * @return {float} Clamped X position of the mouse cursor. + */ Mouse.prototype.getX = function () { return MathUtil.clamp(this._x, -240, 240); }; +/** + * Get the Y position of the mouse. + * @return {float} Clamped Y position of the mouse cursor. + */ Mouse.prototype.getY = function () { return MathUtil.clamp(-this._y, -180, 180); }; +/** + * Get the down state of the mouse. + * @return {boolean} Is the mouse down? + */ Mouse.prototype.getIsDown = function () { return this._isDown; }; diff --git a/test/unit/io_mouse.js b/test/unit/io_mouse.js new file mode 100644 index 000000000..ae4f3fe63 --- /dev/null +++ b/test/unit/io_mouse.js @@ -0,0 +1,49 @@ +var test = require('tap').test; +var Mouse = require('../../src/io/mouse'); +var Runtime = require('../../src/engine/runtime'); + +test('spec', function (t) { + var rt = new Runtime(); + var m = new Mouse(rt); + + t.type(m, 'object'); + t.type(m.postData, 'function'); + t.type(m.getX, 'function'); + t.type(m.getY, 'function'); + t.type(m.getIsDown, 'function'); + t.end(); +}); + +test('mouseUp', function (t) { + var rt = new Runtime(); + var m = new Mouse(rt); + + m.postData({ + x: 1, + y: 10, + isDown: false, + canvasWidth: 480, + canvasHeight: 360 + }); + t.strictEquals(m.getX(), -239); + t.strictEquals(m.getY(), 170); + t.strictEquals(m.getIsDown(), false); + t.end(); +}); + +test('mouseDown', function (t) { + var rt = new Runtime(); + var m = new Mouse(rt); + + m.postData({ + x: 10, + y: 100, + isDown: true, + canvasWidth: 480, + canvasHeight: 360 + }); + t.strictEquals(m.getX(), -230); + t.strictEquals(m.getY(), 80); + t.strictEquals(m.getIsDown(), true); + t.end(); +}); From 49d7ad85643c29273db70dbf1aa03d93dd7d7c00 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 11:58:38 -0400 Subject: [PATCH 05/10] Add coverage/* to eslintignore --- .eslintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintignore b/.eslintignore index ac273ca10..564a29d1e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,3 +4,4 @@ node_modules/* playground/* vm.js vm.min.js +coverage/* From 8b87b36ed37c5a945693b8d6fff2837d40675ff4 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 12:03:43 -0400 Subject: [PATCH 06/10] Resolve race condition with the clock tests --- test/unit/io_clock.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/io_clock.js b/test/unit/io_clock.js index cec77839e..6e172c362 100644 --- a/test/unit/io_clock.js +++ b/test/unit/io_clock.js @@ -19,7 +19,7 @@ test('cycle', function (t) { var rt = new Runtime(); var c = new Clock(rt); - t.strictEqual(c.projectTimer(), 0); + t.ok(c.projectTimer() <= 0.001); setTimeout(function () { c.resetProjectTimer(); setTimeout(function () { From 279a9560afff5413355ce7570224cc6a9e01e36a Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 12:04:06 -0400 Subject: [PATCH 07/10] Resolve JSDoc lint errors --- src/io/keyboard.js | 8 ++++---- src/io/mouse.js | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/io/keyboard.js b/src/io/keyboard.js index 6b1d28d93..c85ce3a92 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -40,7 +40,7 @@ Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { /** * Convert a DOM keyCode into a Scratch key name. - * @param {number} Key code from DOM event. + * @param {number} keyCode Key code from DOM event. * @return {Any} Scratch key argument. * @private */ @@ -61,7 +61,7 @@ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { /** * Keyboard DOM event handler. - * @param {object} DOM event. + * @param {object} data DOM event. * @return {void} */ Keyboard.prototype.postData = function (data) { @@ -88,8 +88,8 @@ Keyboard.prototype.postData = function (data) { /** * Get key down state for a specified Scratch key name. - * @param {Any} Scratch key argument. - * @return {boolean} + * @param {Any} key Scratch key argument. + * @return {boolean} Is the specified key down? */ Keyboard.prototype.getKeyIsDown = function (key) { if (key == 'any') { diff --git a/src/io/mouse.js b/src/io/mouse.js index 368c84701..824206962 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -14,9 +14,10 @@ function Mouse (runtime) { /** * Activate "event_whenthisspriteclicked" hats if needed. - * @param {int} X position to be sent to the renderer. - * @param {int} Y position to be sent to the renderer. + * @param {int} x X position to be sent to the renderer. + * @param {int} y Y position to be sent to the renderer. * @return {void} + * @private */ Mouse.prototype._activateClickHats = function (x, y) { if (this.runtime.renderer) { @@ -35,7 +36,7 @@ Mouse.prototype._activateClickHats = function (x, y) { /** * Mouse DOM event handler. - * @param {object} DOM event. + * @param {object} data DOM event. * @return {void} */ Mouse.prototype.postData = function(data) { From 50b896e935e412f43d16757cee4ec5124aa10b75 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 12:04:22 -0400 Subject: [PATCH 08/10] Unify integration and unit tests to resolve issue with coverage reporting --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9d6bd11d4..0f528e374 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,8 @@ "prepublish": "npm run build", "prepublish-watch": "npm run watch", "start": "./node_modules/.bin/webpack-dev-server", - "tap-integration": "./node_modules/.bin/tap ./test/integration/*.js", - "tap-unit": "./node_modules/.bin/tap ./test/unit/*.js", - "test": "npm run lint && npm run tap-unit && npm run tap-integration", + "tap": "./node_modules/.bin/tap ./test/{unit,integration}/*.js", + "test": "npm run lint && npm run tap", "watch": "./node_modules/.bin/webpack --progress --colors --watch", "version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"" }, From 5b73ffa66cf3d76b72bf672d8cfc717ce839e399 Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 14:38:24 -0400 Subject: [PATCH 09/10] Add minimal test coverage for control blocks --- test/unit/blocks_control.js | 130 ++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/unit/blocks_control.js diff --git a/test/unit/blocks_control.js b/test/unit/blocks_control.js new file mode 100644 index 000000000..2dc6049df --- /dev/null +++ b/test/unit/blocks_control.js @@ -0,0 +1,130 @@ +var test = require('tap').test; +var Control = require('../../src/blocks/scratch3_control'); +var Runtime = require('../../src/engine/runtime'); + +test('getPrimitives', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + t.type(c.getPrimitives(), 'object'); + t.end(); +}); + +test('repeat', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + + // Test harness (mocks `util`) + var i = 0; + var repeat = 10; + var util = { + stackFrame: Object.create(null), + startBranch: function () { + i++; + c.repeat({TIMES: repeat}, util); + } + }; + + // Execute test + c.repeat({TIMES: 10}, util); + t.strictEqual(util.stackFrame.loopCounter, -1); + t.strictEqual(i, repeat); + t.end(); +}); + +test('repeatUntil', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + + // Test harness (mocks `util`) + var i = 0; + var repeat = 10; + var util = { + stackFrame: Object.create(null), + startBranch: function () { + i++; + c.repeatUntil({CONDITION: (i === repeat)}, util); + } + }; + + // Execute test + c.repeatUntil({CONDITION: (i === repeat)}, util); + t.strictEqual(i, repeat); + t.end(); +}); + +test('forever', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + + // Test harness (mocks `util`) + var i = 0; + var util = { + startBranch: function (branchNum, isLoop) { + i++; + t.strictEqual(branchNum, 1); + t.strictEqual(isLoop, true); + } + }; + + // Execute test + c.forever(null, util); + t.strictEqual(i, 1); + t.end(); +}); + +test('if / ifElse', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + + // Test harness (mocks `util`) + var i = 0; + var util = { + startBranch: function (branchNum) { + i += branchNum; + } + }; + + // Execute test + c.if({CONDITION: true}, util); + t.strictEqual(i, 1); + c.if({CONDITION: false}, util); + t.strictEqual(i, 1); + c.ifElse({CONDITION: true}, util); + t.strictEqual(i, 2); + c.ifElse({CONDITION: false}, util); + t.strictEqual(i, 4); + t.end(); +}); + +test('stop', function (t) { + var rt = new Runtime(); + var c = new Control(rt); + + // Test harness (mocks `util`) + var state = { + stopAll: 0, + stopOtherTargetThreads: 0, + stopThread: 0 + }; + var util = { + stopAll: function () { + state.stopAll++; + }, + stopOtherTargetThreads: function () { + state.stopOtherTargetThreads++; + }, + stopThread: function () { + state.stopThread++; + } + }; + + // Execute test + c.stop({STOP_OPTION: 'all'}, util); + c.stop({STOP_OPTION: 'other scripts in sprite'}, util); + c.stop({STOP_OPTION: 'other scripts in stage'}, util); + c.stop({STOP_OPTION: 'this script'}, util); + t.strictEqual(state.stopAll, 1); + t.strictEqual(state.stopOtherTargetThreads, 2); + t.strictEqual(state.stopThread, 1); + t.end(); +}); From 153df229185b9d415d7291eca4822ea059271a9f Mon Sep 17 00:00:00 2001 From: Andrew Sliwinski Date: Mon, 24 Oct 2016 15:16:06 -0400 Subject: [PATCH 10/10] Address feedback from PR review --- src/io/keyboard.js | 3 +-- src/io/mouse.js | 12 +++++------- test/unit/io_clock.js | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/io/keyboard.js b/src/io/keyboard.js index c85ce3a92..86a634374 100644 --- a/src/io/keyboard.js +++ b/src/io/keyboard.js @@ -61,8 +61,7 @@ Keyboard.prototype._keyCodeToScratchKey = function (keyCode) { /** * Keyboard DOM event handler. - * @param {object} data DOM event. - * @return {void} + * @param {object} data Data from DOM event. */ Keyboard.prototype.postData = function (data) { if (data.keyCode) { diff --git a/src/io/mouse.js b/src/io/mouse.js index 824206962..3ca389c06 100644 --- a/src/io/mouse.js +++ b/src/io/mouse.js @@ -14,9 +14,8 @@ function Mouse (runtime) { /** * Activate "event_whenthisspriteclicked" hats if needed. - * @param {int} x X position to be sent to the renderer. - * @param {int} y Y position to be sent to the renderer. - * @return {void} + * @param {number} x X position to be sent to the renderer. + * @param {number} y Y position to be sent to the renderer. * @private */ Mouse.prototype._activateClickHats = function (x, y) { @@ -36,8 +35,7 @@ Mouse.prototype._activateClickHats = function (x, y) { /** * Mouse DOM event handler. - * @param {object} data DOM event. - * @return {void} + * @param {object} data Data from DOM event. */ Mouse.prototype.postData = function(data) { if (data.x) { @@ -56,7 +54,7 @@ Mouse.prototype.postData = function(data) { /** * Get the X position of the mouse. - * @return {float} Clamped X position of the mouse cursor. + * @return {number} Clamped X position of the mouse cursor. */ Mouse.prototype.getX = function () { return MathUtil.clamp(this._x, -240, 240); @@ -64,7 +62,7 @@ Mouse.prototype.getX = function () { /** * Get the Y position of the mouse. - * @return {float} Clamped Y position of the mouse cursor. + * @return {number} Clamped Y position of the mouse cursor. */ Mouse.prototype.getY = function () { return MathUtil.clamp(-this._y, -180, 180); diff --git a/test/unit/io_clock.js b/test/unit/io_clock.js index 6e172c362..12989fcff 100644 --- a/test/unit/io_clock.js +++ b/test/unit/io_clock.js @@ -19,7 +19,7 @@ test('cycle', function (t) { var rt = new Runtime(); var c = new Clock(rt); - t.ok(c.projectTimer() <= 0.001); + t.ok(c.projectTimer() <= 0.1); setTimeout(function () { c.resetProjectTimer(); setTimeout(function () {