mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-06-15 15:01:30 -04:00
Merge pull request #756 from cwillisf/autoload-extensions
Autoload extensions
This commit is contained in:
commit
ef961c5a4b
9 changed files with 314 additions and 62 deletions
src
test
|
@ -90,15 +90,16 @@ const flatten = function (blocks) {
|
|||
* or a list of blocks in an argument (e.g., move [pick random...]).
|
||||
* @param {Array.<object>} blockList SB2 JSON-format block list.
|
||||
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @return {Array.<object>} Scratch VM-format block list.
|
||||
*/
|
||||
const parseBlockList = function (blockList, getVariableId) {
|
||||
const parseBlockList = function (blockList, getVariableId, extensions) {
|
||||
const resultingList = [];
|
||||
let previousBlock = null; // For setting next.
|
||||
for (let i = 0; i < blockList.length; i++) {
|
||||
const block = blockList[i];
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const parsedBlock = parseBlock(block, getVariableId);
|
||||
const parsedBlock = parseBlock(block, getVariableId, extensions);
|
||||
if (typeof parsedBlock === 'undefined') continue;
|
||||
if (previousBlock) {
|
||||
parsedBlock.parent = previousBlock.id;
|
||||
|
@ -116,14 +117,15 @@ const parseBlockList = function (blockList, getVariableId) {
|
|||
* @param {!object} scripts Scripts object from SB2 JSON.
|
||||
* @param {!Blocks} blocks Blocks object to load parsed blocks into.
|
||||
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
*/
|
||||
const parseScripts = function (scripts, blocks, getVariableId) {
|
||||
const parseScripts = function (scripts, blocks, getVariableId, extensions) {
|
||||
for (let i = 0; i < scripts.length; i++) {
|
||||
const script = scripts[i];
|
||||
const scriptX = script[0];
|
||||
const scriptY = script[1];
|
||||
const blockList = script[2];
|
||||
const parsedBlockList = parseBlockList(blockList, getVariableId);
|
||||
const parsedBlockList = parseBlockList(blockList, getVariableId, extensions);
|
||||
if (parsedBlockList[0]) {
|
||||
// Adjust script coordinates to account for
|
||||
// larger block size in scratch-blocks.
|
||||
|
@ -167,12 +169,14 @@ const generateVariableIdGetter = (function () {
|
|||
|
||||
/**
|
||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||
* @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
|
||||
* @param {!Runtime} runtime Runtime object to load all structures into.
|
||||
* @param {boolean} topLevel Whether this is the top-level object (stage).
|
||||
* @return {?Promise} Promise that resolves to the loaded targets when ready.
|
||||
* TODO: parse the "info" section, especially "savedExtensions"
|
||||
* @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher.
|
||||
* @param {!Runtime} runtime - Runtime object to load all structures into.
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @param {boolean} topLevel - Whether this is the top-level object (stage).
|
||||
* @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects.
|
||||
*/
|
||||
const parseScratchObject = function (object, runtime, topLevel) {
|
||||
const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||
if (!object.hasOwnProperty('objName')) {
|
||||
// Watcher/monitor - skip this object until those are implemented in VM.
|
||||
// @todo
|
||||
|
@ -240,7 +244,7 @@ const parseScratchObject = function (object, runtime, topLevel) {
|
|||
|
||||
// If included, parse any and all scripts/blocks on the object.
|
||||
if (object.hasOwnProperty('scripts')) {
|
||||
parseScripts(object.scripts, blocks, getVariableId);
|
||||
parseScripts(object.scripts, blocks, getVariableId, extensions);
|
||||
}
|
||||
|
||||
if (object.hasOwnProperty('lists')) {
|
||||
|
@ -299,7 +303,7 @@ const parseScratchObject = function (object, runtime, topLevel) {
|
|||
const childrenPromises = [];
|
||||
if (object.children) {
|
||||
for (let m = 0; m < object.children.length; m++) {
|
||||
childrenPromises.push(parseScratchObject(object.children[m], runtime, false));
|
||||
childrenPromises.push(parseScratchObject(object.children[m], runtime, extensions, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,31 +328,42 @@ const parseScratchObject = function (object, runtime, topLevel) {
|
|||
* @param {!object} json SB2-format JSON to load.
|
||||
* @param {!Runtime} runtime Runtime object to load all structures into.
|
||||
* @param {boolean=} optForceSprite If set, treat as sprite (Sprite2).
|
||||
* @return {?Promise} Promise that resolves to the loaded targets when ready.
|
||||
* @return {Promise.<ImportedProject>} Promise that resolves to the loaded targets when ready.
|
||||
*/
|
||||
const sb2import = function (json, runtime, optForceSprite) {
|
||||
return parseScratchObject(
|
||||
json,
|
||||
runtime,
|
||||
!optForceSprite
|
||||
);
|
||||
const extensions = {
|
||||
extensionIDs: new Set(),
|
||||
extensionURLs: new Map()
|
||||
};
|
||||
return parseScratchObject(json, runtime, extensions, !optForceSprite)
|
||||
.then(targets => ({
|
||||
targets,
|
||||
extensions
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single SB2 JSON-formatted block and its children.
|
||||
* @param {!object} sb2block SB2 JSON-formatted block.
|
||||
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
||||
* @return {object} Scratch VM format block.
|
||||
* @param {Function} getVariableId function to retrieve a variable's ID based on name
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @return {object} Scratch VM format block, or null if unsupported object.
|
||||
*/
|
||||
const parseBlock = function (sb2block, getVariableId) {
|
||||
const parseBlock = function (sb2block, getVariableId, extensions) {
|
||||
// First item in block object is the old opcode (e.g., 'forward:').
|
||||
const oldOpcode = sb2block[0];
|
||||
// Convert the block using the specMap. See sb2specmap.js.
|
||||
if (!oldOpcode || !specMap[oldOpcode]) {
|
||||
log.warn('Couldn\'t find SB2 block: ', oldOpcode);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const blockMetadata = specMap[oldOpcode];
|
||||
// If the block is from an extension, record it.
|
||||
const dotIndex = blockMetadata.opcode.indexOf('.');
|
||||
if (dotIndex >= 0) {
|
||||
const extension = blockMetadata.opcode.substring(0, dotIndex);
|
||||
extensions.extensionIDs.add(extension);
|
||||
}
|
||||
// Block skeleton.
|
||||
const activeBlock = {
|
||||
id: uid(), // Generate a new block unique ID.
|
||||
|
@ -385,10 +400,10 @@ const parseBlock = function (sb2block, getVariableId) {
|
|||
let innerBlocks;
|
||||
if (typeof providedArg[0] === 'object' && providedArg[0]) {
|
||||
// Block list occupies the input.
|
||||
innerBlocks = parseBlockList(providedArg, getVariableId);
|
||||
innerBlocks = parseBlockList(providedArg, getVariableId, extensions);
|
||||
} else {
|
||||
// Single block occupies the input.
|
||||
innerBlocks = [parseBlock(providedArg, getVariableId)];
|
||||
innerBlocks = [parseBlock(providedArg, getVariableId, extensions)];
|
||||
}
|
||||
let previousBlock = null;
|
||||
for (let j = 0; j < innerBlocks.length; j++) {
|
||||
|
|
|
@ -21,6 +21,24 @@
|
|||
* properties. By hand, I matched the opcode name to the 3.0 opcode.
|
||||
* Finally, I filled in the expected arguments as below.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SB2SpecMap_blockInfo
|
||||
* @property {string} opcode - the Scratch 3.0 block opcode. Use 'extensionID.opcode' for extension opcodes.
|
||||
* @property {Array.<SB2SpecMap_argInfo>} argMap - metadata for this block's arguments.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SB2SpecMap_argInfo
|
||||
* @property {string} type - the type of this arg (such as 'input' or 'field')
|
||||
* @property {string} inputOp - the scratch-blocks shadow type for this arg
|
||||
* @property {string} inputName - the name this argument will take when provided to the block implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mapping of Scratch 2.0 opcode to Scratch 3.0 block metadata.
|
||||
* @type {object.<SB2SpecMap_blockInfo>}
|
||||
*/
|
||||
const specMap = {
|
||||
'forward:': {
|
||||
opcode: 'motion_movesteps',
|
||||
|
@ -1376,4 +1394,180 @@ const specMap = {
|
|||
argMap: []
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add to the specMap entries for an opcode from a Scratch 2.0 extension. Two entries will be made with the same
|
||||
* metadata; this is done to support projects saved by both older and newer versions of the Scratch 2.0 editor.
|
||||
* @param {string} sb2Extension - the Scratch 2.0 name of the extension
|
||||
* @param {string} sb2Opcode - the Scratch 2.0 opcode
|
||||
* @param {SB2SpecMap_blockInfo} blockInfo - the Scratch 3.0 block info
|
||||
*/
|
||||
const addExtensionOp = function (sb2Extension, sb2Opcode, blockInfo) {
|
||||
/**
|
||||
* This string separates the name of an extension and the name of an opcode in more recent Scratch 2.0 projects.
|
||||
* Earlier projects used '.' as a separator, up until we added the 'LEGO WeDo 2.0' extension...
|
||||
* @type {string}
|
||||
*/
|
||||
const sep = '\u001F'; // Unicode Unit Separator
|
||||
|
||||
// make one entry for projects saved by recent versions of the Scratch 2.0 editor
|
||||
specMap[`${sb2Extension}${sep}${sb2Opcode}`] = blockInfo;
|
||||
|
||||
// make a second for projects saved by older versions of the Scratch 2.0 editor
|
||||
specMap[`${sb2Extension}.${sb2Opcode}`] = blockInfo;
|
||||
};
|
||||
|
||||
const weDo2 = 'LEGO WeDo 2.0';
|
||||
|
||||
addExtensionOp(weDo2, 'motorOnFor', {
|
||||
opcode: 'wedo2.motorOnFor',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorID',
|
||||
inputName: 'MOTOR_ID'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'DURATION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'motorOn', {
|
||||
opcode: 'wedo2.motorOn',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorID',
|
||||
inputName: 'MOTOR_ID'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'motorOff', {
|
||||
opcode: 'wedo2.motorOff',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorID',
|
||||
inputName: 'MOTOR_ID'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'startMotorPower', {
|
||||
opcode: 'wedo2.startMotorPower',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorID',
|
||||
inputName: 'MOTOR_ID'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'POWER'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'setMotorDirection', {
|
||||
opcode: 'wedo2.setMotorDirection',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorID',
|
||||
inputName: 'MOTOR_ID'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.motorDirection',
|
||||
inputName: 'DIRECTION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'setLED', {
|
||||
opcode: 'wedo2.setLightHue',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'HUE'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'playNote', {
|
||||
opcode: 'wedo2.playNoteFor',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'NOTE'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'DURATION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'whenDistance', {
|
||||
opcode: 'wedo2.whenDistance',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.lessMore',
|
||||
inputName: 'OP'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'math_number',
|
||||
inputName: 'REFERENCE'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'whenTilted', {
|
||||
opcode: 'wedo2.whenTilted',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.tiltDirectionAny',
|
||||
inputName: 'DIRECTION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'getDistance', {
|
||||
opcode: 'wedo2.getDistance',
|
||||
argMap: []
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'isTilted', {
|
||||
opcode: 'wedo2.isTilted',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.tiltDirectionAny',
|
||||
inputName: 'DIRECTION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
addExtensionOp(weDo2, 'getTilt', {
|
||||
opcode: 'wedo2.getTiltAngle',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'wedo2.menu.tiltDirection',
|
||||
inputName: 'DIRECTION'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
module.exports = specMap;
|
||||
|
|
|
@ -13,6 +13,18 @@ const List = require('../engine/list');
|
|||
const {loadCostume} = require('../import/load-costume.js');
|
||||
const {loadSound} = require('../import/load-sound.js');
|
||||
|
||||
/**
|
||||
* @typedef {object} ImportedProject
|
||||
* @property {Array.<Target>} targets - the imported Scratch 3.0 target objects.
|
||||
* @property {ImportedExtensionsInfo} extensionsInfo - the ID of each extension actually used by this project.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ImportedExtensionsInfo
|
||||
* @property {Set.<string>} extensionIDs - the ID of each extension actually in use by blocks in this project.
|
||||
* @property {Map.<string, string>} extensionURLs - map of ID => URL from project metadata. May not match extensionIDs.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serializes the specified VM runtime.
|
||||
* @param {!Runtime} runtime VM runtime instance to be serialized.
|
||||
|
@ -41,13 +53,14 @@ const serialize = function (runtime) {
|
|||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||
* @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
|
||||
* @param {!Runtime} runtime Runtime object to load all structures into.
|
||||
* @return {?Target} Target created (stage or sprite).
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @return {!Promise.<Target>} Promise for the target created (stage or sprite), or null for unsupported objects.
|
||||
*/
|
||||
const parseScratchObject = function (object, runtime) {
|
||||
const parseScratchObject = function (object, runtime, extensions) {
|
||||
if (!object.hasOwnProperty('name')) {
|
||||
// Watcher/monitor - skip this object until those are implemented in VM.
|
||||
// @todo
|
||||
return;
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
// Blocks container for this object.
|
||||
const blocks = new Blocks();
|
||||
|
@ -61,7 +74,14 @@ const parseScratchObject = function (object, runtime) {
|
|||
}
|
||||
if (object.hasOwnProperty('blocks')) {
|
||||
for (const blockId in object.blocks) {
|
||||
blocks.createBlock(object.blocks[blockId]);
|
||||
const blockJSON = object.blocks[blockId];
|
||||
blocks.createBlock(blockJSON);
|
||||
|
||||
const dotIndex = blockJSON.opcode.indexOf('.');
|
||||
if (dotIndex >= 0) {
|
||||
const extensionId = blockJSON.opcode.substring(0, dotIndex);
|
||||
extensions.extensionIDs.add(extensionId);
|
||||
}
|
||||
}
|
||||
// console.log(blocks);
|
||||
}
|
||||
|
@ -155,14 +175,23 @@ const parseScratchObject = function (object, runtime) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Deserializes the specified representation of a VM runtime and loads it into
|
||||
* the provided runtime instance.
|
||||
* @param {object} json JSON representation of a VM runtime.
|
||||
* @param {Runtime} runtime Runtime instance
|
||||
* @returns {Promise} Promise that resolves to the list of targets after the project is deserialized
|
||||
* Deserialize the specified representation of a VM runtime and loads it into the provided runtime instance.
|
||||
* TODO: parse extension info (also, design extension info storage...)
|
||||
* @param {object} json - JSON representation of a VM runtime.
|
||||
* @param {Runtime} runtime - Runtime instance
|
||||
* @returns {Promise.<ImportedProject>} Promise that resolves to the list of targets after the project is deserialized
|
||||
*/
|
||||
const deserialize = function (json, runtime) {
|
||||
return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime)));
|
||||
const extensions = {
|
||||
extensionIDs: new Set(),
|
||||
extensionURLs: new Map()
|
||||
};
|
||||
return Promise.all(
|
||||
(json.targets || []).map(target => parseScratchObject(target, runtime, extensions))
|
||||
).then(targets => ({
|
||||
targets,
|
||||
extensions
|
||||
}));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -33,7 +33,7 @@ class VirtualMachine extends EventEmitter {
|
|||
/**
|
||||
* The "currently editing"/selected target ID for the VM.
|
||||
* Block events from any Blockly workspace are routed to this target.
|
||||
* @type {!string}
|
||||
* @type {Target}
|
||||
*/
|
||||
this.editingTarget = null;
|
||||
// Runtime emits are passed along as VM emits.
|
||||
|
@ -228,19 +228,42 @@ class VirtualMachine extends EventEmitter {
|
|||
deserializer = sb2;
|
||||
}
|
||||
|
||||
return deserializer.deserialize(json, this.runtime).then(targets => {
|
||||
this.clear();
|
||||
for (let n = 0; n < targets.length; n++) {
|
||||
if (targets[n] !== null) {
|
||||
this.runtime.targets.push(targets[n]);
|
||||
targets[n].updateAllDrawableProperties();
|
||||
}
|
||||
return deserializer.deserialize(json, this.runtime)
|
||||
.then(({targets, extensions}) =>
|
||||
this.installTargets(targets, extensions, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets.
|
||||
* @param {Array.<Target>} targets - the targets to be installed
|
||||
* @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets
|
||||
* @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite.
|
||||
* @returns {Promise} resolved once targets have been installed
|
||||
*/
|
||||
installTargets (targets, extensions, wholeProject) {
|
||||
const extensionPromises = [];
|
||||
extensions.extensionIDs.forEach(extensionID => {
|
||||
if (!this.extensionManager.isExtensionLoaded(extensionID)) {
|
||||
const extensionURL = extensions.extensionURLs.get(extensionID) || extensionID;
|
||||
extensionPromises.push(this.extensionManager.loadExtensionURL(extensionURL));
|
||||
}
|
||||
});
|
||||
|
||||
targets = targets.filter(target => !!target);
|
||||
|
||||
return Promise.all(extensionPromises).then(() => {
|
||||
if (wholeProject) {
|
||||
this.clear();
|
||||
}
|
||||
targets.forEach(target => {
|
||||
this.runtime.targets.push(target);
|
||||
(/** @type RenderedTarget */ target).updateAllDrawableProperties();
|
||||
});
|
||||
// Select the first target for editing, e.g., the first sprite.
|
||||
if (this.runtime.targets.length > 1) {
|
||||
this.editingTarget = this.runtime.targets[1];
|
||||
if (wholeProject && (targets.length > 1)) {
|
||||
this.editingTarget = targets[1];
|
||||
} else {
|
||||
this.editingTarget = this.runtime.targets[0];
|
||||
this.editingTarget = targets[0];
|
||||
}
|
||||
|
||||
// Update the VM user's knowledge of targets and blocks on the workspace.
|
||||
|
@ -267,17 +290,9 @@ class VirtualMachine extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
|
||||
// Select new sprite.
|
||||
return sb2.deserialize(json, this.runtime, true).then(targets => {
|
||||
this.runtime.targets.push(targets[0]);
|
||||
this.editingTarget = targets[0];
|
||||
this.editingTarget.updateAllDrawableProperties();
|
||||
|
||||
// Update the VM user's knowledge of targets and blocks on the workspace.
|
||||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
this.runtime.setEditingTarget(this.editingTarget);
|
||||
});
|
||||
return sb2.deserialize(json, this.runtime, true)
|
||||
.then(({targets, extensions}) =>
|
||||
this.installTargets(targets, extensions, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ test('default', t => {
|
|||
// Create runtime instance & load SB2 into it
|
||||
const rt = new runtime();
|
||||
rt.attachStorage(makeTestStorage());
|
||||
sb2.deserialize(json, rt).then(targets => {
|
||||
sb2.deserialize(json, rt).then(({targets}) => {
|
||||
// Test
|
||||
t.type(file, 'string');
|
||||
t.type(json, 'object');
|
||||
|
|
|
@ -48,7 +48,6 @@ test('pen', t => {
|
|||
vm.setCompatibilityMode(false);
|
||||
vm.setTurboMode(false);
|
||||
vm.loadProject(project)
|
||||
.then(() => vm.extensionManager.loadExtensionURL('pen')) /** @TODO: loadProject should load extensions */
|
||||
.then(() => {
|
||||
vm.greenFlag();
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ test('sb3-roundtrip', t => {
|
|||
return sb3.deserialize(serializedState, runtime2);
|
||||
});
|
||||
|
||||
return serializeAndDeserialize.then(targets => {
|
||||
return serializeAndDeserialize.then(({targets}) => {
|
||||
runtime2.targets = targets;
|
||||
testRuntimeState('copy', runtime2);
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ test('default', t => {
|
|||
|
||||
// Create runtime instance & load SB2 into it
|
||||
const rt = new Runtime();
|
||||
sb2.deserialize(json, rt).then(targets => {
|
||||
sb2.deserialize(json, rt).then(({targets}) => {
|
||||
// Test
|
||||
t.type(file, 'string');
|
||||
t.type(json, 'object');
|
||||
|
@ -60,7 +60,7 @@ test('data scoping', t => {
|
|||
|
||||
// Create runtime instance & load SB2 into it
|
||||
const rt = new Runtime();
|
||||
sb2.deserialize(json, rt).then(targets => {
|
||||
sb2.deserialize(json, rt).then(({targets}) => {
|
||||
const globalVariableIds = Object.keys(targets[0].variables);
|
||||
const localVariableIds = Object.keys(targets[1].variables);
|
||||
t.equal(targets[0].variables[globalVariableIds[0]].name, 'foo');
|
||||
|
|
|
@ -14,8 +14,8 @@ test('serialize', t => {
|
|||
|
||||
test('deserialize', t => {
|
||||
const vm = new VirtualMachine();
|
||||
sb3.deserialize('', vm.runtime).then(targets => {
|
||||
// @todo Analyize
|
||||
sb3.deserialize('', vm.runtime).then(({targets}) => {
|
||||
// @todo Analyze
|
||||
t.type(targets, 'object');
|
||||
t.end();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue