From c8ad1955d4fd3d0c896695f35a4c142158973ce9 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 5 Nov 2018 13:00:35 -0500 Subject: [PATCH] Load extensions before sharing blocks. This commit fixes the unit tests so the assertions are made after the promise resolves --- src/virtual-machine.js | 23 +++++- test/unit/virtual-machine.js | 141 ++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 74 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 1aaae9578..764242d31 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -1107,6 +1107,7 @@ class VirtualMachine extends EventEmitter { * @param {!string} targetId Id of target to add blocks to. * @param {?string} optFromTargetId Optional target id indicating that blocks are being * shared from that target. This is needed for resolving any potential variable conflicts. + * @return {!Promise} Promise that resolves when the extensions and blocks have been added. */ shareBlocksToTarget (blocks, targetId, optFromTargetId) { const copiedBlocks = JSON.parse(JSON.stringify(blocks)); @@ -1119,10 +1120,24 @@ class VirtualMachine extends EventEmitter { fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target); } - for (let i = 0; i < copiedBlocks.length; i++) { - target.blocks.createBlock(copiedBlocks[i]); - } - target.blocks.updateTargetSpecificBlocks(target.isStage); + // Create a unique set of extensionIds that are not yet loaded + const extensionIDs = new Set(copiedBlocks + .map(b => sb3.getExtensionIdForOpcode(b.opcode)) + .filter(id => !!id) // Remove ids that do not exist + .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions + ); + + // Create an array promises for extensions to load + const extensionPromises = Array.from(extensionIDs, + id => this.extensionManager.loadExtensionURL(id) + ); + + return Promise.all(extensionPromises).then(() => { + copiedBlocks.forEach(block => { + target.blocks.createBlock(block); + }); + target.blocks.updateTargetSpecificBlocks(target.isStage); + }); } /** diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 05926f521..b93ccc351 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -744,28 +744,29 @@ test('shareBlocksToTarget shares global variables without any name changes', t = t.type(stage.blocks.getBlock('a block'), 'undefined'); // Share the block to the stage - vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id); + vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id).then(() => { - // Verify that the block now exists on the target as well as the stage - t.type(target.blocks.getBlock('a block'), 'object'); - t.type(target.blocks.getBlock('a block').fields, 'object'); - t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); + // Verify that the block now exists on the target as well as the stage + t.type(target.blocks.getBlock('a block'), 'object'); + t.type(target.blocks.getBlock('a block').fields, 'object'); + t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); - t.type(stage.blocks.getBlock('a block'), 'object'); - t.type(stage.blocks.getBlock('a block').fields, 'object'); - t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); + t.type(stage.blocks.getBlock('a block'), 'object'); + t.type(stage.blocks.getBlock('a block').fields, 'object'); + t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); - // Verify that the variables haven't changed, the variable still exists on the - // stage, it should still have the same name and value, and there should be - // no variables on the target. - t.equal(Object.keys(target.variables).length, 0); - t.equal(Object.keys(stage.variables).length, 1); - t.equal(stage.variables['mock var id'].name, 'a mock variable'); - t.equal(vm.getVariableValue(stage.id, 'mock var id'), 10); + // Verify that the variables haven't changed, the variable still exists on the + // stage, it should still have the same name and value, and there should be + // no variables on the target. + t.equal(Object.keys(target.variables).length, 0); + t.equal(Object.keys(stage.variables).length, 1); + t.equal(stage.variables['mock var id'].name, 'a mock variable'); + t.equal(vm.getVariableValue(stage.id, 'mock var id'), 10); - t.end(); + t.end(); + }); }); test('shareBlocksToTarget shares a local variable to the stage, creating a global variable with a new name', t => { @@ -803,36 +804,36 @@ test('shareBlocksToTarget shares a local variable to the stage, creating a globa t.type(stage.blocks.getBlock('a block'), 'undefined'); // Share the block to the stage - vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id); + vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id).then(() => { + // Verify that the block still exists on the target and remains unchanged + t.type(target.blocks.getBlock('a block'), 'object'); + t.type(target.blocks.getBlock('a block').fields, 'object'); + t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); - // Verify that the block still exists on the target and remains unchanged - t.type(target.blocks.getBlock('a block'), 'object'); - t.type(target.blocks.getBlock('a block').fields, 'object'); - t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); + t.type(stage.blocks.getBlock('a block'), 'object'); + t.type(stage.blocks.getBlock('a block').fields, 'object'); + t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'StageVarFromLocal_mock var id'); - t.type(stage.blocks.getBlock('a block'), 'object'); - t.type(stage.blocks.getBlock('a block').fields, 'object'); - t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'StageVarFromLocal_mock var id'); + // Verify that a new global variable was created, the old one still exists on + // the target and still has the same name and value, and the new one has + // a new name and value 0. + t.equal(Object.keys(target.variables).length, 1); + t.equal(target.variables['mock var id'].name, 'a mock variable'); + t.equal(vm.getVariableValue(target.id, 'mock var id'), 10); - // Verify that a new global variable was created, the old one still exists on - // the target and still has the same name and value, and the new one has - // a new name and value 0. - t.equal(Object.keys(target.variables).length, 1); - t.equal(target.variables['mock var id'].name, 'a mock variable'); - t.equal(vm.getVariableValue(target.id, 'mock var id'), 10); + // Verify that a new variable was created on the stage, with a new name and new id + t.equal(Object.keys(stage.variables).length, 1); + t.type(stage.variables['mock var id'], 'undefined'); + const newGlobalVar = Object.values(stage.variables)[0]; + t.equal(newGlobalVar.name, 'Stage: a mock variable'); + const newId = newGlobalVar.id; + t.notEqual(newId, 'mock var id'); + t.equals(vm.getVariableValue(stage.id, newId), 0); - // Verify that a new variable was created on the stage, with a new name and new id - t.equal(Object.keys(stage.variables).length, 1); - t.type(stage.variables['mock var id'], 'undefined'); - const newGlobalVar = Object.values(stage.variables)[0]; - t.equal(newGlobalVar.name, 'Stage: a mock variable'); - const newId = newGlobalVar.id; - t.notEqual(newId, 'mock var id'); - t.equals(vm.getVariableValue(stage.id, newId), 0); - - t.end(); + t.end(); + }); }); test('shareBlocksToTarget chooses a fresh name for a new global variable checking for conflicts on all sprites', t => { @@ -877,36 +878,36 @@ test('shareBlocksToTarget chooses a fresh name for a new global variable checkin otherTarget.createVariable('a different var', 'Stage: a mock variable', Variable.SCALAR_TYPE); // Share the block to the stage - vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id); + vm.shareBlocksToTarget([target.blocks.getBlock('a block')], stage.id, target.id).then(() => { + // Verify that the block still exists on the target and remains unchanged + t.type(target.blocks.getBlock('a block'), 'object'); + t.type(target.blocks.getBlock('a block').fields, 'object'); + t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); - // Verify that the block still exists on the target and remains unchanged - t.type(target.blocks.getBlock('a block'), 'object'); - t.type(target.blocks.getBlock('a block').fields, 'object'); - t.type(target.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(target.blocks.getBlock('a block').fields.VARIABLE.id, 'mock var id'); + t.type(stage.blocks.getBlock('a block'), 'object'); + t.type(stage.blocks.getBlock('a block').fields, 'object'); + t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); + t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'StageVarFromLocal_mock var id'); - t.type(stage.blocks.getBlock('a block'), 'object'); - t.type(stage.blocks.getBlock('a block').fields, 'object'); - t.type(stage.blocks.getBlock('a block').fields.VARIABLE, 'object'); - t.equal(stage.blocks.getBlock('a block').fields.VARIABLE.id, 'StageVarFromLocal_mock var id'); + // Verify that a new global variable was created, the old one still exists on + // the target and still has the same name and value, and the new one has + // a new name and value 0. + t.equal(Object.keys(target.variables).length, 1); + t.equal(target.variables['mock var id'].name, 'a mock variable'); + t.equal(vm.getVariableValue(target.id, 'mock var id'), 10); - // Verify that a new global variable was created, the old one still exists on - // the target and still has the same name and value, and the new one has - // a new name and value 0. - t.equal(Object.keys(target.variables).length, 1); - t.equal(target.variables['mock var id'].name, 'a mock variable'); - t.equal(vm.getVariableValue(target.id, 'mock var id'), 10); + // Verify that a new variable was created on the stage, with a new name and new id + t.equal(Object.keys(stage.variables).length, 1); + t.type(stage.variables['mock var id'], 'undefined'); + const newGlobalVar = Object.values(stage.variables)[0]; + t.equal(newGlobalVar.name, 'Stage: a mock variable2'); + const newId = newGlobalVar.id; + t.notEqual(newId, 'mock var id'); + t.equals(vm.getVariableValue(stage.id, newId), 0); - // Verify that a new variable was created on the stage, with a new name and new id - t.equal(Object.keys(stage.variables).length, 1); - t.type(stage.variables['mock var id'], 'undefined'); - const newGlobalVar = Object.values(stage.variables)[0]; - t.equal(newGlobalVar.name, 'Stage: a mock variable2'); - const newId = newGlobalVar.id; - t.notEqual(newId, 'mock var id'); - t.equals(vm.getVariableValue(stage.id, newId), 0); - - t.end(); + t.end(); + }); }); test('Setting turbo mode emits events', t => {