diff --git a/src/engine/blocks.js b/src/engine/blocks.js index 85726c7f3..66cc9b953 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -17,11 +17,14 @@ const getMonitorIdForBlockWithArgs = require('../util/get-monitor-id'); /** * Create a block container. + * @param {Runtime} runtime The runtime this block container operates within * @param {boolean} optNoGlow Optional flag to indicate that blocks in this container * should not request glows. This does not affect glows when clicking on a block to execute it. */ class Blocks { - constructor (optNoGlow) { + constructor (runtime, optNoGlow) { + this.runtime = runtime; + /** * All blocks in the workspace. * Keys are block IDs, values are metadata about the block. @@ -84,7 +87,6 @@ class Blocks { * @type {boolean} */ this.forceNoGlow = optNoGlow || false; - } /** @@ -276,7 +278,7 @@ class Blocks { } duplicate () { - const newBlocks = new Blocks(this.forceNoGlow); + const newBlocks = new Blocks(this.runtime, this.forceNoGlow); newBlocks._blocks = Clone.simple(this._blocks); newBlocks._scripts = Clone.simple(this._scripts); return newBlocks; @@ -288,23 +290,20 @@ class Blocks { * serves as a generic adapter between the blocks, variables, and the * runtime interface. * @param {object} e Blockly "block" or "variable" event - * @param {?Runtime} optRuntime Optional runtime to forward click events to. */ - blocklyListen (e, optRuntime) { + blocklyListen (e) { // Validate event if (typeof e !== 'object') return; if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' && typeof e.commentId !== 'string') { return; } - const stage = optRuntime.getTargetForStage(); - const editingTarget = optRuntime.getEditingTarget(); + const stage = this.runtime.getTargetForStage(); + const editingTarget = this.runtime.getEditingTarget(); // UI event: clicked scripts toggle in the runtime. if (e.element === 'stackclick') { - if (optRuntime) { - optRuntime.toggleScript(e.blockId, {stackClick: true}); - } + this.runtime.toggleScript(e.blockId, {stackClick: true}); return; } @@ -324,7 +323,7 @@ class Blocks { element: e.element, name: e.name, value: e.newValue - }, optRuntime); + }); break; case 'move': this.moveBlock({ @@ -337,19 +336,15 @@ class Blocks { }); break; case 'dragOutside': - if (optRuntime) { - optRuntime.emitBlockDragUpdate(e.isOutside); - } + this.runtime.emitBlockDragUpdate(e.isOutside); break; case 'endDrag': - if (optRuntime) { - optRuntime.emitBlockDragUpdate(false /* areBlocksOverGui */); + this.runtime.emitBlockDragUpdate(false /* areBlocksOverGui */); - // Drag blocks onto another sprite - if (e.isOutside) { - const newBlocks = adapter(e); - optRuntime.emitBlockEndDrag(newBlocks, e.blockId); - } + // Drag blocks onto another sprite + if (e.isOutside) { + const newBlocks = adapter(e); + this.runtime.emitBlockEndDrag(newBlocks, e.blockId); } break; case 'delete': @@ -360,8 +355,8 @@ class Blocks { return; } // Inform any runtime to forget about glows on this script. - if (optRuntime && this._blocks[e.blockId].topLevel) { - optRuntime.quietGlow(e.blockId); + if (this._blocks[e.blockId].topLevel) { + this.runtime.quietGlow(e.blockId); } this.deleteBlock(e.blockId); break; @@ -379,7 +374,7 @@ class Blocks { } } else { // Check for name conflicts in all of the targets - const allTargets = optRuntime.targets.filter(t => t.isOriginal); + const allTargets = this.runtime.targets.filter(t => t.isOriginal); for (const target of allTargets) { if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) { return; @@ -399,7 +394,7 @@ class Blocks { // This is a global variable stage.renameVariable(e.varId, e.newName); // Update all blocks on all targets that use the renamed variable - const targets = optRuntime.targets; + const targets = this.runtime.targets; for (let i = 0; i < targets.length; i++) { const currTarget = targets[i]; currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName); @@ -413,8 +408,8 @@ class Blocks { break; } case 'comment_create': - if (optRuntime && optRuntime.getEditingTarget()) { - const currTarget = optRuntime.getEditingTarget(); + if (this.runtime.getEditingTarget()) { + const currTarget = this.runtime.getEditingTarget(); currTarget.createComment(e.commentId, e.blockId, e.text, e.xy.x, e.xy.y, e.width, e.height, e.minimized); @@ -432,8 +427,8 @@ class Blocks { } break; case 'comment_change': - if (optRuntime && optRuntime.getEditingTarget()) { - const currTarget = optRuntime.getEditingTarget(); + if (this.runtime.getEditingTarget()) { + const currTarget = this.runtime.getEditingTarget(); if (!currTarget.comments.hasOwnProperty(e.commentId)) { log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); return; @@ -453,8 +448,8 @@ class Blocks { } break; case 'comment_move': - if (optRuntime && optRuntime.getEditingTarget()) { - const currTarget = optRuntime.getEditingTarget(); + if (this.runtime.getEditingTarget()) { + const currTarget = this.runtime.getEditingTarget(); if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) { log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); return; @@ -466,8 +461,8 @@ class Blocks { } break; case 'comment_delete': - if (optRuntime && optRuntime.getEditingTarget()) { - const currTarget = optRuntime.getEditingTarget(); + if (this.runtime.getEditingTarget()) { + const currTarget = this.runtime.getEditingTarget(); if (!currTarget.comments.hasOwnProperty(e.commentId)) { // If we're in this state, we have probably received // a delete event from a workspace that we switched from @@ -490,7 +485,7 @@ class Blocks { // forceNoGlow is set to true on containers that don't affect the project serialization, // e.g., the toolbox or monitor containers. - if (optRuntime && !this.forceNoGlow) optRuntime.emitProjectChanged(); + if (!this.forceNoGlow) this.runtime.emitProjectChanged(); } // --------------------------------------------------------------------- @@ -531,9 +526,8 @@ class Blocks { /** * Block management: change block field values * @param {!object} args Blockly change event to be processed - * @param {?Runtime} optRuntime Optional runtime to allow changeBlock to change VM state. */ - changeBlock (args, optRuntime) { + changeBlock (args) { // Validate if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return; let block = this._blocks[args.id]; @@ -555,7 +549,7 @@ class Blocks { if (args.name === 'VARIABLE' || args.name === 'LIST' || args.name === 'BROADCAST_OPTION') { // Get variable name using the id in args.value. - const variable = optRuntime.getEditingTarget().lookupVariableById(args.value); + const variable = this.runtime.getEditingTarget().lookupVariableById(args.value); if (variable) { block.fields[args.name].value = variable.name; block.fields[args.name].id = args.value; @@ -564,7 +558,8 @@ class Blocks { // Changing the value in a dropdown block.fields[args.name].value = args.value; - if (!optRuntime){ + if (!this.runtime){ + log.warn('Runtime is not optional, it should get passed in when the block container is created.'); break; } // The selected item in the sensing of block menu needs to change based on the @@ -576,12 +571,12 @@ class Blocks { } else { this._blocks[block.parent].fields.PROPERTY.value = 'x position'; } - optRuntime.requestBlocksUpdate(); + this.runtime.requestBlocksUpdate(); } const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block; if (flyoutBlock.isMonitored) { - optRuntime.requestUpdateMonitor(Map({ + this.runtime.requestUpdateMonitor(Map({ id: flyoutBlock.id, params: this._getBlockParams(flyoutBlock) })); @@ -592,7 +587,8 @@ class Blocks { block.mutation = mutationAdapter(args.value); break; case 'checkbox': { - if (!optRuntime) { + if (!this.runtime) { + log.warn('Runtime is not optional, it should get passed in when the block container is created.'); break; } @@ -609,11 +605,11 @@ class Blocks { // the checkbox because we're using the id of the block in the flyout as the base // check if a block with the new id already exists, otherwise create - let newBlock = optRuntime.monitorBlocks.getBlock(newId); + let newBlock = this.runtime.monitorBlocks.getBlock(newId); if (!newBlock) { newBlock = JSON.parse(JSON.stringify(block)); newBlock.id = newId; - optRuntime.monitorBlocks.createBlock(newBlock); + this.runtime.monitorBlocks.createBlock(newBlock); } block = newBlock; // Carry on through the rest of this code with newBlock @@ -625,32 +621,32 @@ class Blocks { // Variable blocks may be sprite specific depending on the owner of the variable let isSpriteLocalVariable = false; if (block.opcode === 'data_variable') { - isSpriteLocalVariable = !(optRuntime.getTargetForStage().variables[block.fields.VARIABLE.id]); + isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.VARIABLE.id]); } else if (block.opcode === 'data_listcontents') { - isSpriteLocalVariable = !(optRuntime.getTargetForStage().variables[block.fields.LIST.id]); + isSpriteLocalVariable = !(this.runtime.getTargetForStage().variables[block.fields.LIST.id]); } const isSpriteSpecific = isSpriteLocalVariable || - (optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) && - optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific); + (this.runtime.monitorBlockInfo.hasOwnProperty(block.opcode) && + this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific); if (isSpriteSpecific) { // If creating a new sprite specific monitor, the only possible target is // the current editing one b/c you cannot dynamically create monitors. // Also, do not change the targetId if it has already been assigned - block.targetId = block.targetId || optRuntime.getEditingTarget().id; + block.targetId = block.targetId || this.runtime.getEditingTarget().id; } else { block.targetId = null; } if (wasMonitored && !block.isMonitored) { - optRuntime.requestHideMonitor(block.id); + this.runtime.requestHideMonitor(block.id); } else if (!wasMonitored && block.isMonitored) { // Tries to show the monitor for specified block. If it doesn't exist, add the monitor. - if (!optRuntime.requestShowMonitor(block.id)) { - optRuntime.requestAddMonitor(MonitorRecord({ + if (!this.runtime.requestShowMonitor(block.id)) { + this.runtime.requestAddMonitor(MonitorRecord({ id: block.id, targetId: block.targetId, - spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null, + spriteName: block.targetId ? this.runtime.getTargetById(block.targetId).getName() : null, opcode: block.opcode, params: this._getBlockParams(block), // @todo(vm#565) for numerical values with decimals, some countries use comma diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 89beb4ace..e94667015 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -185,14 +185,14 @@ class Runtime extends EventEmitter { * These will execute on `_editingTarget.` * @type {!Blocks} */ - this.flyoutBlocks = new Blocks(true /* force no glow */); + this.flyoutBlocks = new Blocks(this, true /* force no glow */); /** * Storage container for monitor blocks. * These will execute on a target maybe * @type {!Blocks} */ - this.monitorBlocks = new Blocks(true /* force no glow */); + this.monitorBlocks = new Blocks(this, true /* force no glow */); /** * Currently known editing target for the VM. diff --git a/src/engine/target.js b/src/engine/target.js index e34748fe2..7d0b40d81 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -25,7 +25,7 @@ class Target extends EventEmitter { super(); if (!blocks) { - blocks = new Blocks(); + blocks = new Blocks(runtime); } /** diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 8cc49cd8a..e925929fd 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -413,7 +413,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip) } // Blocks container for this object. - const blocks = new Blocks(); + const blocks = new Blocks(runtime); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON. diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 5e029791e..6c4a9ba8d 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -834,7 +834,7 @@ const parseScratchObject = function (object, runtime, extensions, zip) { return Promise.resolve(null); } // Blocks container for this object. - const blocks = new Blocks(); + const blocks = new Blocks(runtime); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. const sprite = new Sprite(blocks, runtime); diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 482d1b0ba..6874f91bf 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -18,7 +18,7 @@ class Sprite { this.runtime = runtime; if (!blocks) { // Shared set of blocks for all clones. - blocks = new Blocks(); + blocks = new Blocks(runtime); } this.blocks = blocks; /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index a40d135e5..38eb79e9f 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -213,7 +213,7 @@ class VirtualMachine extends EventEmitter { const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); // Remove the target key, since it's a circular reference. const filteredThreadData = JSON.stringify(threadData, (key, value) => { - if (key === 'target') return; + if (key === 'target' || key === 'blockContainer') return; return value; }, 2); this.emit('playgroundData', { @@ -1108,7 +1108,7 @@ class VirtualMachine extends EventEmitter { */ blockListener (e) { if (this.editingTarget) { - this.editingTarget.blocks.blocklyListen(e, this.runtime); + this.editingTarget.blocks.blocklyListen(e); } } @@ -1117,7 +1117,7 @@ class VirtualMachine extends EventEmitter { * @param {!Blockly.Event} e Any Blockly event. */ flyoutBlockListener (e) { - this.runtime.flyoutBlocks.blocklyListen(e, this.runtime); + this.runtime.flyoutBlocks.blocklyListen(e); } /** @@ -1128,7 +1128,7 @@ class VirtualMachine extends EventEmitter { // Filter events by type, since monitor blocks only need to listen to these events. // Monitor blocks shouldn't be destroyed when flyout blocks are deleted. if (['create', 'change'].indexOf(e.type) !== -1) { - this.runtime.monitorBlocks.blocklyListen(e, this.runtime); + this.runtime.monitorBlocks.blocklyListen(e); } } @@ -1140,8 +1140,7 @@ class VirtualMachine extends EventEmitter { // Filter events by type, since blocks only needs to listen to these // var events. if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) { - this.runtime.getTargetForStage().blocks.blocklyListen(e, - this.runtime); + this.runtime.getTargetForStage().blocks.blocklyListen(e); } } diff --git a/test/integration/sb3-roundtrip.js b/test/integration/sb3-roundtrip.js index 5704e33b7..9dbc339d3 100644 --- a/test/integration/sb3-roundtrip.js +++ b/test/integration/sb3-roundtrip.js @@ -74,7 +74,7 @@ test('sb3-roundtrip', t => { const installThings = loadThings.then(results => { const [building, cat, squirrel, meow] = results; - const stageBlocks = new Blocks(); + const stageBlocks = new Blocks(runtime1); const stage = new Sprite(stageBlocks, runtime1); stage.name = 'Stage'; stage.costumes = [building]; @@ -82,7 +82,7 @@ test('sb3-roundtrip', t => { const stageClone = stage.createClone(); stageClone.isStage = true; - const spriteBlocks = new Blocks(); + const spriteBlocks = new Blocks(runtime1); const sprite = new Sprite(spriteBlocks, runtime1); sprite.name = 'Sprite'; sprite.costumes = [cat, squirrel]; diff --git a/test/integration/stack-click.js b/test/integration/stack-click.js index 4a1aa82a4..b2e2ec171 100644 --- a/test/integration/stack-click.js +++ b/test/integration/stack-click.js @@ -45,7 +45,7 @@ test('stack click activates the stack', t => { blockContainer.blocklyListen({ blockId: blockId, element: 'stackclick' - }, vm.runtime); + }); } } diff --git a/test/unit/blocks_event.js b/test/unit/blocks_event.js index 363eab66b..5024f6ce2 100644 --- a/test/unit/blocks_event.js +++ b/test/unit/blocks_event.js @@ -48,7 +48,7 @@ test('#760 - broadcastAndWait', t => { const rt = new Runtime(); const e = new Event(rt); - const b = new Blocks(); + const b = new Blocks(rt); b.createBlock(broadcastAndWaitBlock); b.createBlock(receiveMessageBlock); const tgt = new Target(rt, b); diff --git a/test/unit/blocks_sensing.js b/test/unit/blocks_sensing.js index a7d9b8ac5..423b46e8d 100644 --- a/test/unit/blocks_sensing.js +++ b/test/unit/blocks_sensing.js @@ -157,7 +157,7 @@ test('set drag mode', t => { const runtime = new Runtime(); runtime.requestTargetsUpdate = () => {}; // noop for testing const sensing = new Sensing(runtime); - const s = new Sprite(); + const s = new Sprite(null, runtime); const rt = new RenderedTarget(s, runtime); sensing.setDragMode({DRAG_MODE: 'not draggable'}, {target: rt}); @@ -232,7 +232,7 @@ test('loud? boolean', t => { test('get attribute of sprite variable', t => { const rt = new Runtime(); const sensing = new Sensing(rt); - const s = new Sprite(); + const s = new Sprite(null, rt); const target = new RenderedTarget(s, rt); const variable = { name: 'cars', @@ -249,7 +249,7 @@ test('get attribute of sprite variable', t => { test('get attribute of variable that does not exist', t => { const rt = new Runtime(); const sensing = new Sensing(rt); - const s = new Sprite(); + const s = new Sprite(null, rt); const target = new RenderedTarget(s, rt); rt.getTargetForStage = () => target; t.equal(sensing.getAttributeOf({PROPERTY: 'variableThatDoesNotExist'}), 0); diff --git a/test/unit/engine_blocks.js b/test/unit/engine_blocks.js index 5659b6878..3b3ecedc1 100644 --- a/test/unit/engine_blocks.js +++ b/test/unit/engine_blocks.js @@ -3,9 +3,10 @@ const Blocks = require('../../src/engine/blocks'); const Variable = require('../../src/engine/variable'); const adapter = require('../../src/engine/adapter'); const events = require('../fixtures/events.json'); +const Runtime = require('../../src/engine/runtime'); test('spec', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); t.type(Blocks, 'function'); t.type(b, 'object'); @@ -31,7 +32,7 @@ test('spec', t => { // Getter tests test('getBlock', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -48,7 +49,7 @@ test('getBlock', t => { }); test('getScripts', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); let scripts = b.getScripts(); t.type(scripts, 'object'); t.equals(scripts.length, 0); @@ -89,7 +90,7 @@ test('getScripts', t => { }); test('getNextBlock', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -123,7 +124,7 @@ test('getNextBlock', t => { }); test('getBranch', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); // Single branch b.createBlock({ id: 'foo', @@ -158,7 +159,7 @@ test('getBranch', t => { }); test('getBranch2', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); // Second branch b.createBlock({ id: 'foo', @@ -205,7 +206,7 @@ test('getBranch2', t => { }); test('getBranch with none', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -220,7 +221,7 @@ test('getBranch with none', t => { }); test('getOpcode', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); const block = { id: 'foo', opcode: 'TEST_BLOCK', @@ -240,7 +241,7 @@ test('getOpcode', t => { // Block events tests test('create', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -257,7 +258,7 @@ test('create', t => { }); test('move', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -297,7 +298,7 @@ test('move', t => { }); test('move into empty', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -324,7 +325,7 @@ test('move into empty', t => { }); test('move no obscure shadow', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -358,7 +359,7 @@ test('move no obscure shadow', t => { }); test('change', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -415,7 +416,7 @@ test('change', t => { }); test('delete', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -434,7 +435,7 @@ test('delete', t => { test('delete chain', t => { // Create a chain of connected blocks and delete the top one. // All of them should be deleted. - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -472,7 +473,7 @@ test('delete chain', t => { test('delete inputs', t => { // Create a block with two inputs, one of which has its own input. // Delete the block - all of them should be deleted. - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', opcode: 'TEST_BLOCK', @@ -543,7 +544,7 @@ test('delete inputs', t => { }); test('updateAssetName function updates name in sound field', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', fields: { @@ -560,7 +561,7 @@ test('updateAssetName function updates name in sound field', t => { }); test('updateAssetName function updates name in costume field', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', fields: { @@ -577,7 +578,7 @@ test('updateAssetName function updates name in costume field', t => { }); test('updateAssetName function updates name in backdrop field', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'foo', fields: { @@ -594,7 +595,7 @@ test('updateAssetName function updates name in backdrop field', t => { }); test('updateAssetName function updates name in all sprite fields', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'id1', fields: { @@ -677,7 +678,7 @@ test('updateAssetName function updates name in all sprite fields', t => { }); test('updateAssetName function updates name according to asset type', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'id1', fields: { @@ -706,7 +707,7 @@ test('updateAssetName function updates name according to asset type', t => { }); test('updateAssetName only updates given name', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'id1', fields: { @@ -734,7 +735,7 @@ test('updateAssetName only updates given name', t => { }); test('updateAssetName doesn\'t update name if name isn\'t being used', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'id1', fields: { @@ -751,7 +752,7 @@ test('updateAssetName doesn\'t update name if name isn\'t being used', t => { }); test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); b.createBlock({ id: 'originallySpriteClicked', opcode: 'event_whenthisspriteclicked' @@ -781,13 +782,13 @@ test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for }); test('getAllVariableAndListReferences returns an empty map references when variable blocks do not exist', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); t.equal(Object.keys(b.getAllVariableAndListReferences()).length, 0); t.end(); }); test('getAllVariableAndListReferences returns references when variable blocks exist', t => { - const b = new Blocks(); + const b = new Blocks(new Runtime()); let varListRefs = b.getAllVariableAndListReferences(); t.equal(Object.keys(varListRefs).length, 0); diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js index 622dc64b3..a383001fe 100644 --- a/test/unit/engine_sequencer.js +++ b/test/unit/engine_sequencer.js @@ -67,7 +67,7 @@ const generateBlockInput = function (id, next, inp) { }; const generateThread = function (runtime) { - const s = new Sprite(); + const s = new Sprite(null, runtime); const rt = new RenderedTarget(s, runtime); const th = new Thread(randomString()); diff --git a/test/unit/engine_target.js b/test/unit/engine_target.js index a656d5de7..b1fb6e457 100644 --- a/test/unit/engine_target.js +++ b/test/unit/engine_target.js @@ -6,7 +6,7 @@ const Runtime = require('../../src/engine/runtime'); const events = require('../fixtures/events.json'); test('spec', t => { - const target = new Target(); + const target = new Target(new Runtime()); t.type(Target, 'function'); t.type(target, 'object'); @@ -26,7 +26,7 @@ test('spec', t => { // Create Variable tests. test('createVariable', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); const variables = target.variables; @@ -43,7 +43,7 @@ test('createVariable', t => { // Create Same Variable twice. test('createVariable2', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); @@ -55,7 +55,7 @@ test('createVariable2', t => { // Create a list test('createListVariable creates a list', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo', 'bar', Variable.LIST_TYPE); const variables = target.variables; @@ -123,7 +123,7 @@ test('createVariable does not call cloud io device\'s requestCreateVariable if t }); test('createVariable throws when given invalid type', t => { - const target = new Target(); + const target = new Target(new Runtime()); t.throws( (() => target.createVariable('foo', 'bar', 'baz')), new Error('Invalid variable type: baz') @@ -134,7 +134,7 @@ test('createVariable throws when given invalid type', t => { // Rename Variable tests. test('renameVariable', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); target.renameVariable('foo', 'bar2'); @@ -151,7 +151,7 @@ test('renameVariable', t => { // Rename Variable that doesn't exist. test('renameVariable2', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.renameVariable('foo', 'bar2'); const variables = target.variables; @@ -163,7 +163,7 @@ test('renameVariable2', t => { // Rename Variable that with id that exists as another variable's name. // Expect no change. test('renameVariable3', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo1', 'foo', Variable.SCALAR_TYPE); target.renameVariable('foo', 'bar2'); @@ -233,7 +233,7 @@ test('renameVariable does not call cloud io device\'s requestRenameVariable func // Delete Variable tests. test('deleteVariable', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('foo', 'bar', Variable.SCALAR_TYPE); target.deleteVariable('foo'); @@ -245,7 +245,7 @@ test('deleteVariable', t => { // Delete Variable that doesn't exist. test('deleteVariable2', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.deleteVariable('foo'); const variables = target.variables; @@ -300,7 +300,7 @@ test('deleteVariable calls cloud io device\'s requestRenameVariable function', t }); test('duplicateVariable creates a new variable with a new ID by default', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('a var ID', 'foo', Variable.SCALAR_TYPE); t.equal(Object.keys(target.variables).length, 1); const originalVariable = target.variables['a var ID']; @@ -324,7 +324,7 @@ test('duplicateVariable creates a new variable with a new ID by default', t => { }); test('duplicateVariable creates new array reference for list variable.value', t => { - const target = new Target(); + const target = new Target(new Runtime()); const arr = [1, 2, 3]; target.createVariable('a var ID', 'arr', Variable.LIST_TYPE); const originalVariable = target.variables['a var ID']; @@ -337,7 +337,7 @@ test('duplicateVariable creates new array reference for list variable.value', t }); test('duplicateVariable creates a new variable with a original ID if specified', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('a var ID', 'foo', Variable.SCALAR_TYPE); t.equal(Object.keys(target.variables).length, 1); const originalVariable = target.variables['a var ID']; @@ -362,7 +362,7 @@ test('duplicateVariable creates a new variable with a original ID if specified', }); test('duplicateVariable returns null if variable with specified ID does not exist', t => { - const target = new Target(); + const target = new Target(new Runtime()); const variable = target.duplicateVariable('a var ID'); t.equal(variable, null); @@ -382,7 +382,7 @@ test('duplicateVariable returns null if variable with specified ID does not exis }); test('duplicateVariables duplicates all variables', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('var ID 1', 'var1', Variable.SCALAR_TYPE); target.createVariable('var ID 2', 'var2', Variable.SCALAR_TYPE); @@ -434,7 +434,7 @@ test('duplicateVariables duplicates all variables', t => { }); test('duplicateVariables re-IDs variables when a block container is provided', t => { - const target = new Target(); + const target = new Target(new Runtime()); target.createVariable('mock var id', 'a mock variable', Variable.SCALAR_TYPE); target.createVariable('another var id', 'var2', Variable.SCALAR_TYPE); @@ -489,7 +489,7 @@ test('duplicateVariables re-IDs variables when a block container is provided', t }); test('lookupOrCreateList creates a list if var with given id or var with given name does not exist', t => { - const target = new Target(); + const target = new Target(new Runtime()); const variables = target.variables; t.equal(Object.keys(variables).length, 0); @@ -502,7 +502,7 @@ test('lookupOrCreateList creates a list if var with given id or var with given n }); test('lookupOrCreateList returns list if one with given id exists', t => { - const target = new Target(); + const target = new Target(new Runtime()); const variables = target.variables; t.equal(Object.keys(variables).length, 0); @@ -518,7 +518,7 @@ test('lookupOrCreateList returns list if one with given id exists', t => { }); test('lookupOrCreateList succeeds in finding list if id is incorrect but name matches', t => { - const target = new Target(); + const target = new Target(new Runtime()); const variables = target.variables; t.equal(Object.keys(variables).length, 0); @@ -534,7 +534,7 @@ test('lookupOrCreateList succeeds in finding list if id is incorrect but name ma }); test('lookupBroadcastMsg returns the var with given id if exists', t => { - const target = new Target(); + const target = new Target(new Runtime()); const variables = target.variables; t.equal(Object.keys(variables).length, 0); @@ -550,7 +550,7 @@ test('lookupBroadcastMsg returns the var with given id if exists', t => { }); test('createComment adds a comment to the target', t => { - const target = new Target(); + const target = new Target(new Runtime()); const comments = target.comments; t.equal(Object.keys(comments).length, 0); @@ -572,7 +572,7 @@ test('createComment adds a comment to the target', t => { }); test('creating comment with id that already exists does not change existing comment', t => { - const target = new Target(); + const target = new Target(new Runtime()); const comments = target.comments; t.equal(Object.keys(comments).length, 0); @@ -599,7 +599,7 @@ test('creating comment with id that already exists does not change existing comm }); test('creating a comment with a blockId also updates the comment property on the block', t => { - const target = new Target(); + const target = new Target(new Runtime()); const comments = target.comments; // Create a mock block on the target target.blocks = { diff --git a/test/unit/engine_thread.js b/test/unit/engine_thread.js index 12ed08357..36206eb99 100644 --- a/test/unit/engine_thread.js +++ b/test/unit/engine_thread.js @@ -2,6 +2,7 @@ const test = require('tap').test; const Thread = require('../../src/engine/thread'); const RenderedTarget = require('../../src/sprites/rendered-target'); const Sprite = require('../../src/sprites/sprite'); +const Runtime = require('../../src/engine/runtime'); test('spec', t => { t.type(Thread, 'function'); @@ -120,8 +121,9 @@ test('PushGetParam', t => { test('goToNextBlock', t => { const th = new Thread('arbitraryString'); - const s = new Sprite(); - const rt = new RenderedTarget(s, null); + const r = new Runtime(); + const s = new Sprite(null, r); + const rt = new RenderedTarget(s, r); const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, @@ -175,8 +177,9 @@ test('goToNextBlock', t => { test('stopThisScript', t => { const th = new Thread('arbitraryString'); - const s = new Sprite(); - const rt = new RenderedTarget(s, null); + const r = new Runtime(); + const s = new Sprite(null, r); + const rt = new RenderedTarget(s, r); const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, @@ -227,8 +230,9 @@ test('stopThisScript', t => { test('isRecursiveCall', t => { const th = new Thread('arbitraryString'); - const s = new Sprite(); - const rt = new RenderedTarget(s, null); + const r = new Runtime(); + const s = new Sprite(null, r); + const rt = new RenderedTarget(s, r); const block1 = {fields: Object, id: 'arbitraryString', inputs: Object, diff --git a/test/unit/io_cloud.js b/test/unit/io_cloud.js index bf137ca90..deacc7c6c 100644 --- a/test/unit/io_cloud.js +++ b/test/unit/io_cloud.js @@ -45,7 +45,8 @@ test('setProvider sets the provider', t => { }); test('postData update message updates the variable', t => { - const stage = new Target(); + const runtime = new Runtime(); + const stage = new Target(runtime); const fooVar = new Variable( 'a fake var id', 'foo', @@ -56,7 +57,6 @@ test('postData update message updates the variable', t => { t.strictEquals(fooVar.value, 0); - const runtime = new Runtime(); const cloud = new Cloud(runtime); cloud.setStage(stage); cloud.postData({varUpdate: { diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index 6c92f1f0d..e768574d9 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -7,16 +7,17 @@ const FakeRenderer = require('../fixtures/fake-renderer'); test('clone effects', t => { // Create two clones and ensure they have different graphic effect objects. // Regression test for Github issue #224 - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); - const b = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); + const b = new RenderedTarget(spr, r); t.ok(a.effects !== b.effects); t.end(); }); test('setxy', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -27,8 +28,8 @@ test('setxy', t => { }); test('direction', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -38,8 +39,8 @@ test('direction', t => { }); test('setSay', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -49,8 +50,8 @@ test('setSay', t => { }); test('setVisible', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -59,8 +60,8 @@ test('setVisible', t => { }); test('setSize', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -70,8 +71,8 @@ test('setSize', t => { }); test('set and clear effects', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -88,8 +89,8 @@ test('set and clear effects', t => { test('setCostume', t => { const o = new Object(); - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); s.costumes = [o]; const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); @@ -105,8 +106,8 @@ test('deleteCostume', t => { const o4 = {id: 4}; const o5 = {id: 5}; - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); s.costumes = [o1, o2, o3]; const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); @@ -208,8 +209,8 @@ test('deleteSound', t => { const o2 = {id: 2}; const o3 = {id: 3}; - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); s.sounds = [o1, o2, o3]; const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); @@ -228,8 +229,8 @@ test('deleteSound', t => { }); test('setRotationStyle', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); a.renderer = renderer; @@ -238,8 +239,8 @@ test('setRotationStyle', t => { }); test('getBounds', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -251,8 +252,8 @@ test('getBounds', t => { }); test('isTouchingPoint', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -262,8 +263,8 @@ test('isTouchingPoint', t => { }); test('isTouchingEdge', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -275,8 +276,8 @@ test('isTouchingEdge', t => { }); test('isTouchingSprite', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -286,8 +287,8 @@ test('isTouchingSprite', t => { }); test('isTouchingColor', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -297,8 +298,8 @@ test('isTouchingColor', t => { }); test('colorIsTouchingColor', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -308,8 +309,8 @@ test('colorIsTouchingColor', t => { }); test('layers', t => { // TODO this tests fake functionality. Move layering tests into Render. - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); const o = new Object(); r.attachRenderer(renderer); @@ -332,8 +333,8 @@ test('layers', t => { // TODO this tests fake functionality. Move layering tests }); test('getLayerOrder returns result of renderer getDrawableOrder or null if renderer is not attached', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); // getLayerOrder should return null if there is no renderer attached to the runtime @@ -349,8 +350,8 @@ test('getLayerOrder returns result of renderer getDrawableOrder or null if rende }); test('keepInFence', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const renderer = new FakeRenderer(); r.attachRenderer(renderer); const a = new RenderedTarget(s, r); @@ -363,8 +364,8 @@ test('keepInFence', t => { }); test('#stopAll clears graphics effects', t => { - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); const a = new RenderedTarget(s, r); const effectName = 'brightness'; a.setEffect(effectName, 100); @@ -374,8 +375,9 @@ test('#stopAll clears graphics effects', t => { }); test('#getCostumes returns the costumes', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); a.sprite.costumes = [{id: 1}, {id: 2}, {id: 3}]; t.equals(a.getCostumes().length, 3); t.equals(a.getCostumes()[0].id, 1); @@ -385,8 +387,9 @@ test('#getCostumes returns the costumes', t => { }); test('#getSounds returns the sounds', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); const sounds = [1, 2, 3]; a.sprite.sounds = sounds; t.equals(a.getSounds(), sounds); @@ -394,8 +397,9 @@ test('#getSounds returns the sounds', t => { }); test('#toJSON returns the sounds and costumes', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); const sounds = [1, 2, 3]; a.sprite.sounds = sounds; a.sprite.costumes = [{id: 1}, {id: 2}, {id: 3}]; @@ -405,8 +409,9 @@ test('#toJSON returns the sounds and costumes', t => { }); test('#addSound does not duplicate names', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); a.sprite.sounds = [{name: 'first'}]; a.addSound({name: 'first'}); t.deepEqual(a.sprite.sounds, [{name: 'first'}, {name: 'first2'}]); @@ -414,8 +419,9 @@ test('#addSound does not duplicate names', t => { }); test('#addCostume does not duplicate names', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); a.addCostume({name: 'first'}); a.addCostume({name: 'first'}); t.equal(a.sprite.costumes.length, 2); @@ -425,8 +431,9 @@ test('#addCostume does not duplicate names', t => { }); test('#renameSound does not duplicate names', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); a.sprite.sounds = [{name: 'first'}, {name: 'second'}]; a.renameSound(0, 'first'); // Shouldn't increment the name, noop t.deepEqual(a.sprite.sounds, [{name: 'first'}, {name: 'second'}]); @@ -436,8 +443,9 @@ test('#renameSound does not duplicate names', t => { }); test('#renameCostume does not duplicate names', t => { - const spr = new Sprite(); - const a = new RenderedTarget(spr, null); + const r = new Runtime(); + const spr = new Sprite(null, r); + const a = new RenderedTarget(spr, r); a.sprite.costumes = [{name: 'first'}, {name: 'second'}]; a.renameCostume(0, 'first'); // Shouldn't increment the name, noop t.equal(a.sprite.costumes.length, 2); @@ -456,8 +464,8 @@ test('#reorderCostume', t => { const o3 = {id: 2}; const o4 = {id: 3}; const o5 = {id: 4}; - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); s.costumes = [o1, o2, o3, o4, o5]; const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); @@ -510,8 +518,8 @@ test('#reorderSound', t => { const o3 = {id: 2, name: 'name2'}; const o4 = {id: 3, name: 'name3'}; const o5 = {id: 4, name: 'name4'}; - const s = new Sprite(); const r = new Runtime(); + const s = new Sprite(null, r); s.sounds = [o1, o2, o3, o4, o5]; const a = new RenderedTarget(s, r); const renderer = new FakeRenderer(); diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 42ae52083..db7570462 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -14,9 +14,9 @@ const test = tap.test; test('deleteSound returns function after deleting or null if nothing was deleted', t => { const vm = new VirtualMachine(); - const sprite = new Sprite(); - sprite.sounds = [{id: 1}, {id: 2}, {id: 3}]; const rt = new Runtime(); + const sprite = new Sprite(null, rt); + sprite.sounds = [{id: 1}, {id: 2}, {id: 3}]; const target = new RenderedTarget(sprite, rt); vm.editingTarget = target; @@ -37,10 +37,10 @@ test('deleteSound returns function after deleting or null if nothing was deleted test('deleteCostume returns function after deleting or null if nothing was deleted', t => { const vm = new VirtualMachine(); - const sprite = new Sprite(); + const rt = new Runtime(); + const sprite = new Sprite(null, rt); sprite.costumes = [{id: 1}, {id: 2}, {id: 3}]; sprite.currentCostume = 0; - const rt = new Runtime(); const target = new RenderedTarget(sprite, rt); vm.editingTarget = target;