Tests and cleanup of addSprite3 so that .sprite3 format does not need to be nested inside an extra {targets: ...}

This commit is contained in:
Karishma Chadha 2018-05-03 16:19:29 -04:00
parent 9cc34d7406
commit 55352e88b1
7 changed files with 105 additions and 14 deletions

View file

@ -871,15 +871,17 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
* @param {object} json - JSON representation of a VM runtime.
* @param {Runtime} runtime - Runtime instance
* @param {JSZip} zip - Sb3 file describing this project (to load assets from)
* @param {boolean} isSingleSprite - If true treat as single sprite, else treat as whole project
* @returns {Promise.<ImportedProject>} Promise that resolves to the list of targets after the project is deserialized
*/
const deserialize = function (json, runtime, zip) {
const deserialize = function (json, runtime, zip, isSingleSprite) {
const extensions = {
extensionIDs: new Set(),
extensionURLs: new Map()
};
return Promise.all(
(json.targets || []).map(target => parseScratchObject(target, runtime, extensions, zip))
((isSingleSprite ? [json] : json.targets) || []).map(target =>
parseScratchObject(target, runtime, extensions, zip))
).then(targets => ({
targets,
extensions

View file

@ -385,6 +385,7 @@ class VirtualMachine extends EventEmitter {
* @return {!Promise} Promise that resolves after targets are installed.
*/
addSprite (input) {
const errorPrefix = 'Sprite Upload Error:';
if (typeof input === 'object' && !(input instanceof ArrayBuffer) &&
!ArrayBuffer.isView(input)) {
// If the input is an object and not any ArrayBuffer
@ -411,47 +412,47 @@ class VirtualMachine extends EventEmitter {
.then(validatedInput => {
const projectVersion = validatedInput[0].projectVersion;
if (projectVersion === 2) {
return this.addSprite2(validatedInput[0], validatedInput[1]);
return this._addSprite2(validatedInput[0], validatedInput[1]);
}
if (projectVersion === 3) {
return this.addSprite3(validatedInput[0], validatedInput[1]);
return this._addSprite3(validatedInput[0], validatedInput[1]);
}
return Promise.reject('Unable to verify sprite version.');
return Promise.reject(`${errorPrefix} Unable to verify sprite version.`);
})
.catch(error => {
// Intentionally rejecting here (want errors to be handled by caller)
if (error.hasOwnProperty('validationError')) {
return Promise.reject(JSON.stringify(error));
}
return Promise.reject(error);
return Promise.reject(`${errorPrefix} ${error}`);
});
}
/**
* Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format.
* @param {string} json JSON string representing the sprite.
* @param {object} sprite Object representing 2.0 sprite to be added.
* @param {?ArrayBuffer} zip Optional zip of assets being referenced by json
* @returns {Promise} Promise that resolves after the sprite is added
*/
addSprite2 (json, zip) {
_addSprite2 (sprite, zip) {
// Validate & parse
return sb2.deserialize(json, this.runtime, true, zip)
return sb2.deserialize(sprite, this.runtime, true, zip)
.then(({targets, extensions}) =>
this.installTargets(targets, extensions, false));
}
/**
* Add a single sb3 sprite.
* @param {string} target JSON string representing the sprite/target.
* @param {object} sprite Object rperesenting 3.0 sprite to be added.
* @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json
* @returns {Promise} Promise that resolves after the sprite is added
*/
addSprite3 (target, zip) {
_addSprite3 (sprite, zip) {
// Validate & parse
return sb3
.deserialize(target, this.runtime, zip)
.deserialize(sprite, this.runtime, zip, true)
.then(({targets, extensions}) => this.installTargets(targets, extensions, false));
}

BIN
test/fixtures/example_sprite.sprite2 vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,79 @@
const path = require('path');
const test = require('tap').test;
const makeTestStorage = require('../fixtures/make-test-storage');
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
const VirtualMachine = require('../../src/virtual-machine');
const RenderedTarget = require('../../src/sprites/rendered-target');
const projectUri = path.resolve(__dirname, '../fixtures/default.sb2');
const project = readFileToBuffer(projectUri);
const vm = new VirtualMachine();
test('spec', t => {
t.type(vm.addSprite, 'function');
t.end();
});
test('default cat', t => {
// Get default cat from .sprite2
const uri = path.resolve(__dirname, '../fixtures/example_sprite.sprite2');
const sprite = readFileToBuffer(uri);
vm.attachStorage(makeTestStorage());
// Evaluate playground data and exit
vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads);
t.ok(threads.length === 0);
t.end();
process.nextTick(process.exit);
});
vm.start();
vm.clear();
vm.setCompatibilityMode(false);
vm.setTurboMode(false);
t.doesNotThrow(() => {
vm.loadProject(project).then(() => {
t.equal(vm.runtime.targets.length, 2); // stage and default sprite
// Add another sprite
vm.addSprite(sprite).then(() => {
const targets = vm.runtime.targets;
// Test
t.type(targets, 'object');
t.equal(targets.length, 3);
const newTarget = targets[2];
t.ok(newTarget instanceof RenderedTarget);
t.type(newTarget.id, 'string');
t.type(newTarget.blocks, 'object');
t.type(newTarget.variables, 'object');
const varIds = Object.keys(newTarget.variables);
t.type(varIds.length, 1);
const variable = newTarget.variables[varIds[0]];
t.equal(variable.name, 'foo');
t.equal(variable.value, 0);
t.equal(newTarget.isOriginal, true);
t.equal(newTarget.currentCostume, 0);
t.equal(newTarget.isOriginal, true);
t.equal(newTarget.isStage, false);
t.equal(newTarget.sprite.name, 'Apple');
vm.greenFlag();
setTimeout(() => {
t.equal(variable.value, 10);
vm.getPlaygroundData();
vm.stopAll();
}, 1000);
});
});
});
});

View file

@ -73,7 +73,7 @@ test('complex', t => {
});
// Add sprite
vm.addSprite2(sprite);
vm.addSprite(sprite);
// Add backdrop
vm.addBackdrop(

View file

@ -15,7 +15,7 @@ test('interface', t => {
t.type(vm.postIOData, 'function');
t.type(vm.loadProject, 'function');
t.type(vm.addSprite2, 'function');
t.type(vm.addSprite, 'function');
t.type(vm.addCostume, 'function');
t.type(vm.addBackdrop, 'function');
t.type(vm.addSound, 'function');

View file

@ -2,6 +2,15 @@ const test = require('tap').test;
const VirtualMachine = require('../../src/virtual-machine.js');
const Sprite = require('../../src/sprites/sprite.js');
test('addSprite throws on invalid string', t => {
const vm = new VirtualMachine();
vm.addSprite('this is not a sprite')
.catch(e => {
t.equal(e.startsWith('Sprite Upload Error:'), true);
t.end();
});
});
test('renameSprite throws when there is no sprite with that id', t => {
const vm = new VirtualMachine();
vm.runtime.getTargetById = () => null;