test: don't use process.exit to end tests

Newer versions of `tap` run more asynchronously, so sometimes using `process.nextTick(process.exit)`
to end a test would prevent the test from completing correctly. Removing all instances of
`process.nextTick(process.exit)` put tests into three categories:
* the test still worked correctly -- no fixup needed.
* the test would hang because the VM's `_steppingInterval` was keeping
  Node alive. These tests call a new `quit()` method which ends the
  stepping interval.
* the `load-extensions` test needed special attention because the "Video
  Sensing" extension starts its own loop using `setTimeout`. I added a
  `_stopLoop()` method on the extension and directly call that from the
  test. I'm not completely happy with this solution but anything more
  general would likely require a change to the extension spec, so I'm
  leaving that as a followup task.
This commit is contained in:
Christopher Willis-Ford 2022-06-06 12:00:10 -07:00
parent 605b1c2386
commit 840ffb5df0
69 changed files with 182 additions and 156 deletions

View file

@ -608,7 +608,7 @@ class Runtime extends EventEmitter {
static get PERIPHERAL_LIST_UPDATE () { static get PERIPHERAL_LIST_UPDATE () {
return 'PERIPHERAL_LIST_UPDATE'; return 'PERIPHERAL_LIST_UPDATE';
} }
/** /**
* Event name for when the user picks a bluetooth device to connect to * Event name for when the user picks a bluetooth device to connect to
* via Companion Device Manager (CDM) * via Companion Device Manager (CDM)
@ -2578,6 +2578,15 @@ class Runtime extends EventEmitter {
this.emit(Runtime.RUNTIME_STARTED); this.emit(Runtime.RUNTIME_STARTED);
} }
/**
* Quit the Runtime, clearing any handles which might keep the process alive.
* Do not use the runtime after calling this method. This method is meant for test shutdown.
*/
quit () {
clearInterval(this._steppingInterval);
this._steppingInterval = null;
}
/** /**
* Turn on profiling. * Turn on profiling.
* @param {Profiler/FrameCallback} onFrame A callback handle passed a * @param {Profiler/FrameCallback} onFrame A callback handle passed a

View file

@ -81,8 +81,8 @@ class ExtensionManager {
this.pendingWorkers = []; this.pendingWorkers = [];
/** /**
* Set of loaded extension URLs/IDs (equivalent for built-in extensions). * Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name.
* @type {Set.<string>} * @type {Map.<string,string>}
* @private * @private
*/ */
this._loadedExtensions = new Map(); this._loadedExtensions = new Map();

View file

@ -231,7 +231,8 @@ class Scratch3VideoSensingBlocks {
* @private * @private
*/ */
_loop () { _loop () {
setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL)); const loopTime = Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL);
this._loopInterval = setTimeout(this._loop.bind(this), loopTime);
// Add frame to detector // Add frame to detector
const time = Date.now(); const time = Date.now();
@ -251,6 +252,13 @@ class Scratch3VideoSensingBlocks {
} }
} }
/**
* Stop the video sampling loop. Only used for testing.
*/
_stopLoop () {
clearTimeout(this._loopInterval);
}
/** /**
* Create data for a menu in scratch-blocks format, consisting of an array * Create data for a menu in scratch-blocks format, consisting of an array
* of objects with text and value properties. The text is a translated * of objects with text and value properties. The text is a translated

View file

@ -175,6 +175,14 @@ class VirtualMachine extends EventEmitter {
this.runtime.start(); this.runtime.start();
} }
/**
* Quit the VM, clearing any handles which might keep the process alive.
* Do not use the runtime after calling this method. This method is meant for test shutdown.
*/
quit () {
this.runtime.quit();
}
/** /**
* "Green flag" handler - start all threads starting with a green flag. * "Green flag" handler - start all threads starting with a green flag.
*/ */

View file

@ -27,8 +27,8 @@ test('default cat', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
vm.start(); vm.start();

View file

@ -33,8 +33,8 @@ test('importing sb2 project where block comment is converted to workspace commen
const invalidComments = targetComments.filter(comment => typeof comment.blockId === 'number'); const invalidComments = targetComments.filter(comment => typeof comment.blockId === 'number');
t.equal(invalidComments.length, 0); t.equal(invalidComments.length, 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -38,8 +38,8 @@ test('importing sb2 project where block comment is converted to workspace commen
const targetBlocks = Object.values(target.blocks._blocks); const targetBlocks = Object.values(target.blocks._blocks);
t.equal(targetBlocks.length, 0); t.equal(targetBlocks.length, 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -62,8 +62,8 @@ test('importing sb2 project with special chars in message names', t => {
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId); t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId); t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -62,8 +62,8 @@ test('importing sb3 project with special chars in message names', t => {
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId); t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId); t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -65,8 +65,8 @@ test('clone-cleanup', t => {
// The second batch of clones has deleted themselves; everything is finished // The second batch of clones has deleted themselves; everything is finished
verifyCounts(0, 0); verifyCounts(0, 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
break; break;
} }
}; };

View file

@ -37,6 +37,7 @@ test('importing an sb2 project with cloud variables', t => {
// when the message is being sent to the server rather than on the client // when the message is being sent to the server rather than on the client
t.equal(variable.isCloud, true); t.equal(variable.isCloud, true);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -60,6 +61,7 @@ test('importing an sb2 project with cloud variables at the limit for a project',
// All of the 8 stage variables should be cloud variables // All of the 8 stage variables should be cloud variables
t.equal(stageVars.filter(v => v.isCloud).length, 10); t.equal(stageVars.filter(v => v.isCloud).length, 10);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -85,6 +87,7 @@ test('importing an sb2 project with cloud variables exceeding the limit for a pr
// Only 8 of the variables should have the isCloud flag set to true // Only 8 of the variables should have the isCloud flag set to true
t.equal(stageVars.filter(v => v.isCloud).length, 10); t.equal(stageVars.filter(v => v.isCloud).length, 10);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -115,6 +118,7 @@ test('importing one project after the other resets cloud variable limit', t => {
t.equal(vm.runtime.canAddCloudVariable(), true); t.equal(vm.runtime.canAddCloudVariable(), true);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -145,8 +149,7 @@ test('local cloud variables get imported as regular variables', t => {
t.equal(spriteVars.length, 1); t.equal(spriteVars.length, 1);
t.equal(spriteVars[0].isCloud, false); t.equal(spriteVars[0].isCloud, false);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
}); });
}); });

View file

@ -35,6 +35,7 @@ test('importing an sb3 project with cloud variables', t => {
t.equal(Number(variable.value), 100); t.equal(Number(variable.value), 100);
t.equal(variable.isCloud, true); t.equal(variable.isCloud, true);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -58,6 +59,7 @@ test('importing an sb3 project with cloud variables at the limit for a project',
// All of the 10 stage variables should be cloud variables // All of the 10 stage variables should be cloud variables
t.equal(stageVars.filter(v => v.isCloud).length, 10); t.equal(stageVars.filter(v => v.isCloud).length, 10);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -83,6 +85,7 @@ test('importing an sb3 project with cloud variables exceeding the limit for a pr
// Only 8 of the variables should have the isCloud flag set to true // Only 8 of the variables should have the isCloud flag set to true
t.equal(stageVars.filter(v => v.isCloud).length, 10); t.equal(stageVars.filter(v => v.isCloud).length, 10);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -111,6 +114,7 @@ test('importing one project after the other resets cloud variable limit', t => {
t.equal(vm.runtime.canAddCloudVariable(), true); t.equal(vm.runtime.canAddCloudVariable(), true);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -141,8 +145,7 @@ test('local cloud variables get imported as regular variables', t => {
t.equal(spriteVars.length, 1); t.equal(spriteVars.length, 1);
t.equal(spriteVars[0].isCloud, false); t.equal(spriteVars[0].isCloud, false);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
}); });
}); });

View file

@ -70,8 +70,8 @@ test('importing sb2 project with comments', t => {
t.equal(stopAllBlock.comment, blockComments[4].id); t.equal(stopAllBlock.comment, blockComments[4].id);
t.equal(stopAllBlock.opcode, 'control_stop'); t.equal(stopAllBlock.opcode, 'control_stop');
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -70,8 +70,8 @@ test('load an sb3 project with comments', t => {
t.equal(stopAllBlock.comment, blockComments[4].id); t.equal(stopAllBlock.comment, blockComments[4].id);
t.equal(stopAllBlock.opcode, 'control_stop'); t.equal(stopAllBlock.opcode, 'control_stop');
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -19,8 +19,8 @@ test('complex', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Manipulate each target // Manipulate each target

View file

@ -15,8 +15,8 @@ test('control', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length > 0); t.ok(threads.length > 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -14,8 +14,8 @@ test('data', t => {
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', () => { vm.on('playgroundData', () => {
// @todo Additional tests // @todo Additional tests
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -27,8 +27,8 @@ test('default cat', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
vm.start(); vm.start();

View file

@ -15,8 +15,8 @@ test('event', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length > 0); t.ok(threads.length > 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -66,6 +66,8 @@ fs.readdirSync(executeDir)
log.suggest.deny('vm', 'error'); log.suggest.deny('vm', 'error');
t.tearDown(() => log.suggest.clear()); t.tearDown(() => log.suggest.clear());
const vm = new VirtualMachine();
// Map string messages to tap reporting methods. This will be used // Map string messages to tap reporting methods. This will be used
// with events from scratch's runtime emitted on block instructions. // with events from scratch's runtime emitted on block instructions.
let didPlan; let didPlan;
@ -86,6 +88,7 @@ fs.readdirSync(executeDir)
}, },
end () { end () {
didEnd = true; didEnd = true;
vm.quit();
t.end(); t.end();
} }
}; };
@ -100,7 +103,6 @@ fs.readdirSync(executeDir)
return reporters.comment(text); return reporters.comment(text);
}; };
const vm = new VirtualMachine();
vm.attachStorage(makeTestStorage()); vm.attachStorage(makeTestStorage());
// Start the VM and initialize some vm properties. // Start the VM and initialize some vm properties.
@ -138,6 +140,7 @@ fs.readdirSync(executeDir)
// it can be resolved. // it can be resolved.
if (!didEnd) { if (!didEnd) {
t.fail('did not say "end"'); t.fail('did not say "end"');
vm.quit();
t.end(); t.end();
} }
}); });

View file

@ -20,8 +20,8 @@ test('complex', t => {
const results = vm.runtime.targets[0].variables[resultKey].value; const results = vm.runtime.targets[0].variables[resultKey].value;
t.deepEqual(results, ['3', '2', '1', 'stage']); t.deepEqual(results, ['3', '2', '1', 'stage']);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -15,8 +15,8 @@ test('default', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -15,8 +15,8 @@ test('default', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -31,8 +31,8 @@ test('importing sb3 project with incorrect list monitor name', t => {
t.equal(monitorBlock.fields.LIST.value, renamedListName); t.equal(monitorBlock.fields.LIST.value, renamedListName);
} }
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -3,9 +3,19 @@ const tap = require('tap');
const {test} = tap; const {test} = tap;
const fs = require('fs'); const fs = require('fs');
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer; const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
const dispatch = require('../../src/dispatch/central-dispatch');
const VirtualMachine = require('../../src/index'); const VirtualMachine = require('../../src/index');
tap.tearDown(() => process.nextTick(process.exit)); /**
* Call _stopLoop() on the Video Sensing extension.
* @param {VirtualMachine} vm - a VM instance which has loaded the 'videoSensing' extension.
*/
const stopVideoLoop = vm => {
// TODO: provide a general way to tell extensions to shut down
// Ideally we'd just dispose of the extension's Worker...
const serviceName = vm.extensionManager._loadedExtensions.get('videoSensing');
dispatch.call(serviceName, '_stopLoop');
};
test('Load external extensions', async t => { test('Load external extensions', async t => {
const vm = new VirtualMachine(); const vm = new VirtualMachine();
@ -25,6 +35,9 @@ test('Load external extensions', async t => {
}); });
}); });
} }
stopVideoLoop(vm);
vm.quit();
t.end(); t.end();
}); });
@ -64,5 +77,7 @@ test('Load video sensing extension and video properties', async t => {
t.equal(vm.runtime.ioDevices.video._ghost, project.videoTransparency); t.equal(vm.runtime.ioDevices.video._ghost, project.videoTransparency);
} }
stopVideoLoop(vm);
vm.quit();
t.end(); t.end();
}); });

View file

@ -17,8 +17,8 @@ test('sb2 project (originally from Scratch 1.4) with missing backdrop image shou
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
vm.start(); vm.start();

View file

@ -15,8 +15,8 @@ test('looks', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -122,8 +122,8 @@ test('importing sb2 project with monitors', t => {
t.equal(monitorRecord.spriteName, null); t.equal(monitorRecord.spriteName, null);
t.equal(monitorRecord.targetId, null); t.equal(monitorRecord.targetId, null);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -140,7 +140,6 @@ test('saving and loading sb2 project with monitors preserves sliderMin and slide
t.equal(monitorRecord.targetId, null); t.equal(monitorRecord.targetId, null);
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -247,8 +247,8 @@ test('importing sb3 project with monitors', t => {
t.equal(monitorRecord.targetId, null); t.equal(monitorRecord.targetId, null);
t.equal(vm.extensionManager.isExtensionLoaded('ev3'), true); t.equal(vm.extensionManager.isExtensionLoaded('ev3'), true);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -15,8 +15,8 @@ test('motion', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length > 0); t.ok(threads.length > 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -33,8 +33,8 @@ test('offline-custom-assets', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -37,8 +37,8 @@ test('pen', t => {
t.equal(originalPenState.penAttributes.diameter, 51); t.equal(originalPenState.penAttributes.diameter, 51);
t.equal(clonePenState.penAttributes.diameter, 42); t.equal(clonePenState.penAttributes.diameter, 42);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -15,8 +15,8 @@ test('procedure', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length === 0); t.ok(threads.length === 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -19,8 +19,8 @@ test('Running project should not emit project changed event', t => {
// Evaluate playground data and exit // Evaluate playground data and exit
vm.on('playgroundData', () => { vm.on('playgroundData', () => {
t.equal(projectChanged, false); t.equal(projectChanged, false);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -29,8 +29,8 @@ test('say/think and wait', t => {
// The test will fail if the project throws. // The test will fail if the project throws.
setTimeout(() => { setTimeout(() => {
vm.stopAll(); vm.stopAll();
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}, 2000); }, 2000);
}); });
}); });

View file

@ -25,8 +25,6 @@ const visibleTempoMonitorProjectUri = path.resolve(
__dirname, '../fixtures/visible-tempo-monitor-no-other-music-blocks.sb2'); __dirname, '../fixtures/visible-tempo-monitor-no-other-music-blocks.sb2');
const visibleTempoMonitorProject = readFileToBuffer(visibleTempoMonitorProjectUri); const visibleTempoMonitorProject = readFileToBuffer(visibleTempoMonitorProjectUri);
tap.tearDown(() => process.nextTick(process.exit));
test('loading sb2 project with invisible video monitor should not load monitor or extension', t => { test('loading sb2 project with invisible video monitor should not load monitor or extension', t => {
const vm = new VirtualMachine(); const vm = new VirtualMachine();
vm.attachStorage(makeTestStorage()); vm.attachStorage(makeTestStorage());
@ -39,6 +37,7 @@ test('loading sb2 project with invisible video monitor should not load monitor o
vm.loadProject(invisibleVideoMonitorProject).then(() => { vm.loadProject(invisibleVideoMonitorProject).then(() => {
t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false); t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false);
t.equal(vm.runtime._monitorState.size, 0); t.equal(vm.runtime._monitorState.size, 0);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -55,6 +54,7 @@ test('loading sb2 project with visible video monitor should not load extension',
vm.loadProject(visibleVideoMonitorProject).then(() => { vm.loadProject(visibleVideoMonitorProject).then(() => {
t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false); t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false);
t.equal(vm.runtime._monitorState.size, 0); t.equal(vm.runtime._monitorState.size, 0);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -86,6 +86,7 @@ test('sb2 project with invisible music monitor should not load monitor or extens
vm.loadProject(invisibleTempoMonitorProject).then(() => { vm.loadProject(invisibleTempoMonitorProject).then(() => {
t.equal(vm.extensionManager.isExtensionLoaded('music'), false); t.equal(vm.extensionManager.isExtensionLoaded('music'), false);
t.equal(vm.runtime._monitorState.size, 0); t.equal(vm.runtime._monitorState.size, 0);
vm.quit();
t.end(); t.end();
}); });
}); });
@ -104,6 +105,7 @@ test('sb2 project with visible music monitor should load monitor and extension',
t.equal(vm.runtime._monitorState.size, 1); t.equal(vm.runtime._monitorState.size, 1);
t.equal(vm.runtime._monitorState.has('music_getTempo'), true); t.equal(vm.runtime._monitorState.has('music_getTempo'), true);
t.equal(vm.runtime._monitorState.get('music_getTempo').visible, true); t.equal(vm.runtime._monitorState.get('music_getTempo').visible, true);
vm.quit();
t.end(); t.end();
}); });
}); });

View file

@ -71,14 +71,14 @@ const test = tap.test;
test('load sb2 project with corrupted bitmap costume file', t => { test('load sb2 project with corrupted bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[1]; const greenGuySprite = vm.runtime.targets[1];
t.equal(greenGuySprite.getName(), 'GreenGuy'); t.equal(greenGuySprite.getName(), 'GreenGuy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const corruptedCostume = greenGuySprite.getCostumes()[0]; const corruptedCostume = greenGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'GreenGuy'); t.equal(corruptedCostume.name, 'GreenGuy');
t.equal(corruptedCostume.assetId, defaultBitmapAssetId); t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
@ -96,14 +96,14 @@ test('load and then save project with corrupted bitmap costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = resavedProject.targets[1]; const greenGuySprite = resavedProject.targets[1];
t.equal(greenGuySprite.name, 'GreenGuy'); t.equal(greenGuySprite.name, 'GreenGuy');
t.equal(greenGuySprite.costumes.length, 1); t.equal(greenGuySprite.costumes.length, 1);
const corruptedCostume = greenGuySprite.costumes[0]; const corruptedCostume = greenGuySprite.costumes[0];
t.equal(corruptedCostume.name, 'GreenGuy'); t.equal(corruptedCostume.name, 'GreenGuy');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.png`); t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -30,7 +30,7 @@ global.Image = function () {
width: 1, width: 1,
height: 1 height: 1
}; };
setTimeout(() => image.onload(), 1000); setTimeout(() => image.onload(), 1000);
return image; return image;
}; };
@ -73,14 +73,14 @@ const test = tap.test;
test('load sb2 project with corrupted vector costume file', t => { test('load sb2 project with corrupted vector costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[1]; const blueGuySprite = vm.runtime.targets[1];
t.equal(blueGuySprite.getName(), 'Blue Guy'); t.equal(blueGuySprite.getName(), 'Blue Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const corruptedCostume = blueGuySprite.getCostumes()[0]; const corruptedCostume = blueGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'Blue Guy 2'); t.equal(corruptedCostume.name, 'Blue Guy 2');
t.equal(corruptedCostume.assetId, defaultVectorAssetId); t.equal(corruptedCostume.assetId, defaultVectorAssetId);
@ -98,14 +98,14 @@ test('load and then save project with corrupted vector costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = resavedProject.targets[1]; const blueGuySprite = resavedProject.targets[1];
t.equal(blueGuySprite.name, 'Blue Guy'); t.equal(blueGuySprite.name, 'Blue Guy');
t.equal(blueGuySprite.costumes.length, 1); t.equal(blueGuySprite.costumes.length, 1);
const corruptedCostume = blueGuySprite.costumes[0]; const corruptedCostume = blueGuySprite.costumes[0];
t.equal(corruptedCostume.name, 'Blue Guy 2'); t.equal(corruptedCostume.name, 'Blue Guy 2');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -124,5 +124,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`); t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -56,14 +56,14 @@ const test = tap.test;
test('loading sb2 project with missing bitmap costume file', t => { test('loading sb2 project with missing bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[1]; const greenGuySprite = vm.runtime.targets[1];
t.equal(greenGuySprite.getName(), 'GreenGuy'); t.equal(greenGuySprite.getName(), 'GreenGuy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const missingCostume = greenGuySprite.getCostumes()[0]; const missingCostume = greenGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'GreenGuy'); t.equal(missingCostume.name, 'GreenGuy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -81,14 +81,14 @@ test('load and then save sb2 project with missing costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = resavedProject.targets[1]; const greenGuySprite = resavedProject.targets[1];
t.equal(greenGuySprite.name, 'GreenGuy'); t.equal(greenGuySprite.name, 'GreenGuy');
t.equal(greenGuySprite.costumes.length, 1); t.equal(greenGuySprite.costumes.length, 1);
const missingCostume = greenGuySprite.costumes[0]; const missingCostume = greenGuySprite.costumes[0];
t.equal(missingCostume.name, 'GreenGuy'); t.equal(missingCostume.name, 'GreenGuy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -102,10 +102,9 @@ test('load and then save sb2 project with missing costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime); const costumeDescs = serializeCostumes(vm.runtime);
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`); t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -55,14 +55,14 @@ const test = tap.test;
test('loading sb2 project with missing vector costume file', t => { test('loading sb2 project with missing vector costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[1]; const blueGuySprite = vm.runtime.targets[1];
t.equal(blueGuySprite.getName(), 'Blue Guy'); t.equal(blueGuySprite.getName(), 'Blue Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const missingCostume = blueGuySprite.getCostumes()[0]; const missingCostume = blueGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'Blue Guy 2'); t.equal(missingCostume.name, 'Blue Guy 2');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -80,14 +80,14 @@ test('load and then save sb2 project with missing costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = resavedProject.targets[1]; const blueGuySprite = resavedProject.targets[1];
t.equal(blueGuySprite.name, 'Blue Guy'); t.equal(blueGuySprite.name, 'Blue Guy');
t.equal(blueGuySprite.costumes.length, 1); t.equal(blueGuySprite.costumes.length, 1);
const missingCostume = blueGuySprite.costumes[0]; const missingCostume = blueGuySprite.costumes[0];
t.equal(missingCostume.name, 'Blue Guy 2'); t.equal(missingCostume.name, 'Blue Guy 2');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -101,10 +101,9 @@ test('load and then save sb2 project with missing costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime); const costumeDescs = serializeCostumes(vm.runtime);
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`); t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -71,14 +71,14 @@ const test = tap.test;
test('load sb3 project with corrupted bitmap costume file', t => { test('load sb3 project with corrupted bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[1]; const greenGuySprite = vm.runtime.targets[1];
t.equal(greenGuySprite.getName(), 'Green Guy'); t.equal(greenGuySprite.getName(), 'Green Guy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const corruptedCostume = greenGuySprite.getCostumes()[0]; const corruptedCostume = greenGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'Green Guy'); t.equal(corruptedCostume.name, 'Green Guy');
t.equal(corruptedCostume.assetId, defaultBitmapAssetId); t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
@ -96,14 +96,14 @@ test('load and then save project with corrupted bitmap costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = resavedProject.targets[1]; const greenGuySprite = resavedProject.targets[1];
t.equal(greenGuySprite.name, 'Green Guy'); t.equal(greenGuySprite.name, 'Green Guy');
t.equal(greenGuySprite.costumes.length, 1); t.equal(greenGuySprite.costumes.length, 1);
const corruptedCostume = greenGuySprite.costumes[0]; const corruptedCostume = greenGuySprite.costumes[0];
t.equal(corruptedCostume.name, 'Green Guy'); t.equal(corruptedCostume.name, 'Green Guy');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.png`); t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -32,7 +32,7 @@ const FakeAudioEngine = function () {
if (soundDataString.includes('here is some')) { if (soundDataString.includes('here is some')) {
return Promise.reject(new Error('mock audio engine broke')); return Promise.reject(new Error('mock audio engine broke'));
} }
// Otherwise return fake data // Otherwise return fake data
return Promise.resolve({ return Promise.resolve({
id: fakeId++, id: fakeId++,
@ -65,14 +65,14 @@ const test = tap.test;
test('load sb3 project with corrupted sound file', t => { test('load sb3 project with corrupted sound file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const catSprite = vm.runtime.targets[1]; const catSprite = vm.runtime.targets[1];
t.equal(catSprite.getName(), 'Sprite1'); t.equal(catSprite.getName(), 'Sprite1');
t.equal(catSprite.getSounds().length, 1); t.equal(catSprite.getSounds().length, 1);
const corruptedSound = catSprite.getSounds()[0]; const corruptedSound = catSprite.getSounds()[0];
t.equal(corruptedSound.name, 'Boop Sound Recording'); t.equal(corruptedSound.name, 'Boop Sound Recording');
t.equal(corruptedSound.assetId, defaultSoundAssetId); t.equal(corruptedSound.assetId, defaultSoundAssetId);
@ -90,14 +90,14 @@ test('load and then save project with corrupted sound file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const catSprite = resavedProject.targets[1]; const catSprite = resavedProject.targets[1];
t.equal(catSprite.name, 'Sprite1'); t.equal(catSprite.name, 'Sprite1');
t.equal(catSprite.sounds.length, 1); t.equal(catSprite.sounds.length, 1);
const corruptedSound = catSprite.sounds[0]; const corruptedSound = catSprite.sounds[0];
t.equal(corruptedSound.name, 'Boop Sound Recording'); t.equal(corruptedSound.name, 'Boop Sound Recording');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -116,5 +116,4 @@ test('serializeSounds saves orignal broken sound', t => {
t.equal(sound.fileName, `${brokenSoundMd5}.wav`); t.equal(sound.fileName, `${brokenSoundMd5}.wav`);
t.equal(md5(sound.fileContent), brokenSoundMd5); t.equal(md5(sound.fileContent), brokenSoundMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -52,14 +52,14 @@ const test = tap.test;
test('load sb3 project with corrupted vector costume file', t => { test('load sb3 project with corrupted vector costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[1]; const blueGuySprite = vm.runtime.targets[1];
t.equal(blueGuySprite.getName(), 'Blue Square Guy'); t.equal(blueGuySprite.getName(), 'Blue Square Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const corruptedCostume = blueGuySprite.getCostumes()[0]; const corruptedCostume = blueGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'costume1'); t.equal(corruptedCostume.name, 'costume1');
t.equal(corruptedCostume.assetId, defaultVectorAssetId); t.equal(corruptedCostume.assetId, defaultVectorAssetId);
@ -77,14 +77,14 @@ test('load and then save project with corrupted vector costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = resavedProject.targets[1]; const blueGuySprite = resavedProject.targets[1];
t.equal(blueGuySprite.name, 'Blue Square Guy'); t.equal(blueGuySprite.name, 'Blue Square Guy');
t.equal(blueGuySprite.costumes.length, 1); t.equal(blueGuySprite.costumes.length, 1);
const corruptedCostume = blueGuySprite.costumes[0]; const corruptedCostume = blueGuySprite.costumes[0];
t.equal(corruptedCostume.name, 'costume1'); t.equal(corruptedCostume.name, 'costume1');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -103,5 +103,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`); t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -56,14 +56,14 @@ const test = tap.test;
test('loading sb3 project with missing bitmap costume file', t => { test('loading sb3 project with missing bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[1]; const greenGuySprite = vm.runtime.targets[1];
t.equal(greenGuySprite.getName(), 'Green Guy'); t.equal(greenGuySprite.getName(), 'Green Guy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const missingCostume = greenGuySprite.getCostumes()[0]; const missingCostume = greenGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'Green Guy'); t.equal(missingCostume.name, 'Green Guy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -81,14 +81,14 @@ test('load and then save sb3 project with missing costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = resavedProject.targets[1]; const greenGuySprite = resavedProject.targets[1];
t.equal(greenGuySprite.name, 'Green Guy'); t.equal(greenGuySprite.name, 'Green Guy');
t.equal(greenGuySprite.costumes.length, 1); t.equal(greenGuySprite.costumes.length, 1);
const missingCostume = greenGuySprite.costumes[0]; const missingCostume = greenGuySprite.costumes[0];
t.equal(missingCostume.name, 'Green Guy'); t.equal(missingCostume.name, 'Green Guy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -102,10 +102,9 @@ test('load and then save sb3 project with missing costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime); const costumeDescs = serializeCostumes(vm.runtime);
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`); t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -32,13 +32,13 @@ const test = tap.test;
test('loading sb3 project with missing sound file', t => { test('loading sb3 project with missing sound file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const catSprite = vm.runtime.targets[1]; const catSprite = vm.runtime.targets[1];
t.equal(catSprite.getSounds().length, 1); t.equal(catSprite.getSounds().length, 1);
const missingSound = catSprite.getSounds()[0]; const missingSound = catSprite.getSounds()[0];
t.equal(missingSound.name, 'Boop Sound Recording'); t.equal(missingSound.name, 'Boop Sound Recording');
// Sound should have original data but no asset // Sound should have original data but no asset
@ -57,14 +57,14 @@ test('load and then save sb3 project with missing sound file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const catSprite = resavedProject.targets[1]; const catSprite = resavedProject.targets[1];
t.equal(catSprite.name, 'Sprite1'); t.equal(catSprite.name, 'Sprite1');
t.equal(catSprite.sounds.length, 1); t.equal(catSprite.sounds.length, 1);
const missingSound = catSprite.sounds[0]; const missingSound = catSprite.sounds[0];
t.equal(missingSound.name, 'Boop Sound Recording'); t.equal(missingSound.name, 'Boop Sound Recording');
// Costume should have both default sound data (e.g. "Gray Question Sound" ^_^) and original data // Costume should have both default sound data (e.g. "Gray Question Sound" ^_^) and original data
@ -78,10 +78,9 @@ test('load and then save sb3 project with missing sound file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const soundDescs = serializeSounds(vm.runtime); const soundDescs = serializeSounds(vm.runtime);
t.equal(soundDescs.length, 1); // Should only have one sound, the pop sound for the stage t.equal(soundDescs.length, 1); // Should only have one sound, the pop sound for the stage
t.not(soundDescs[0].fileName, `${missingSoundAssetId}.wav`); t.not(soundDescs[0].fileName, `${missingSoundAssetId}.wav`);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -35,14 +35,14 @@ const test = tap.test;
test('loading sb3 project with missing vector costume file', t => { test('loading sb3 project with missing vector costume file', t => {
t.equal(vm.runtime.targets.length, 2); t.equal(vm.runtime.targets.length, 2);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[1]; const blueGuySprite = vm.runtime.targets[1];
t.equal(blueGuySprite.getName(), 'Blue Square Guy'); t.equal(blueGuySprite.getName(), 'Blue Square Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const missingCostume = blueGuySprite.getCostumes()[0]; const missingCostume = blueGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'costume1'); t.equal(missingCostume.name, 'costume1');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -60,14 +60,14 @@ test('load and then save sb3 project with missing costume file', t => {
const resavedProject = JSON.parse(vm.toJSON()); const resavedProject = JSON.parse(vm.toJSON());
t.equal(resavedProject.targets.length, 2); t.equal(resavedProject.targets.length, 2);
const stage = resavedProject.targets[0]; const stage = resavedProject.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = resavedProject.targets[1]; const blueGuySprite = resavedProject.targets[1];
t.equal(blueGuySprite.name, 'Blue Square Guy'); t.equal(blueGuySprite.name, 'Blue Square Guy');
t.equal(blueGuySprite.costumes.length, 1); t.equal(blueGuySprite.costumes.length, 1);
const missingCostume = blueGuySprite.costumes[0]; const missingCostume = blueGuySprite.costumes[0];
t.equal(missingCostume.name, 'costume1'); t.equal(missingCostume.name, 'costume1');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -81,10 +81,9 @@ test('load and then save sb3 project with missing costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime); const costumeDescs = serializeCostumes(vm.runtime);
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`); t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -15,8 +15,8 @@ test('sensing', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length > 0); t.ok(threads.length > 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -20,8 +20,8 @@ test('sound', t => {
vm.on('playgroundData', e => { vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads); const threads = JSON.parse(e.threads);
t.ok(threads.length > 0); t.ok(threads.length > 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -76,14 +76,14 @@ const test = tap.test;
test('load sprite2 with corrupted bitmap costume file', t => { test('load sprite2 with corrupted bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[2]; const greenGuySprite = vm.runtime.targets[2];
t.equal(greenGuySprite.getName(), 'GreenGuy'); t.equal(greenGuySprite.getName(), 'GreenGuy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const corruptedCostume = greenGuySprite.getCostumes()[0]; const corruptedCostume = greenGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'GreenGuy'); t.equal(corruptedCostume.name, 'GreenGuy');
t.equal(corruptedCostume.assetId, defaultBitmapAssetId); t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
@ -102,7 +102,7 @@ test('load and then save sprite with corrupted costume file', t => {
t.equal(resavedSprite.name, 'GreenGuy'); t.equal(resavedSprite.name, 'GreenGuy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const corruptedCostume = resavedSprite.costumes[0]; const corruptedCostume = resavedSprite.costumes[0];
t.equal(corruptedCostume.name, 'GreenGuy'); t.equal(corruptedCostume.name, 'GreenGuy');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -121,5 +121,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.png`); t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -77,14 +77,14 @@ const test = tap.test;
test('load sprite2 with corrupted vector costume file', t => { test('load sprite2 with corrupted vector costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[2]; const blueGuySprite = vm.runtime.targets[2];
t.equal(blueGuySprite.getName(), 'Blue Guy'); t.equal(blueGuySprite.getName(), 'Blue Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const corruptedCostume = blueGuySprite.getCostumes()[0]; const corruptedCostume = blueGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'Blue Guy 2'); t.equal(corruptedCostume.name, 'Blue Guy 2');
t.equal(corruptedCostume.assetId, defaultVectorAssetId); t.equal(corruptedCostume.assetId, defaultVectorAssetId);
@ -103,7 +103,7 @@ test('load and then save sprite with corrupted costume file', t => {
t.equal(resavedSprite.name, 'Blue Guy'); t.equal(resavedSprite.name, 'Blue Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const corruptedCostume = resavedSprite.costumes[0]; const corruptedCostume = resavedSprite.costumes[0];
t.equal(corruptedCostume.name, 'Blue Guy 2'); t.equal(corruptedCostume.name, 'Blue Guy 2');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`); t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -59,14 +59,14 @@ const test = tap.test;
test('loading sprite2 with missing bitmap costume file', t => { test('loading sprite2 with missing bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[2]; const greenGuySprite = vm.runtime.targets[2];
t.equal(greenGuySprite.getName(), 'GreenGuy'); t.equal(greenGuySprite.getName(), 'GreenGuy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const missingCostume = greenGuySprite.getCostumes()[0]; const missingCostume = greenGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'GreenGuy'); t.equal(missingCostume.name, 'GreenGuy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -85,7 +85,7 @@ test('load and then save sprite2 with missing bitmap costume file', t => {
t.equal(resavedSprite.name, 'GreenGuy'); t.equal(resavedSprite.name, 'GreenGuy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const missingCostume = resavedSprite.costumes[0]; const missingCostume = resavedSprite.costumes[0];
t.equal(missingCostume.name, 'GreenGuy'); t.equal(missingCostume.name, 'GreenGuy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -99,9 +99,8 @@ test('load and then save sprite2 with missing bitmap costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id); const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
t.equal(costumeDescs.length, 0); t.equal(costumeDescs.length, 0);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -59,14 +59,14 @@ const test = tap.test;
test('loading sprite2 with missing vector costume file', t => { test('loading sprite2 with missing vector costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[2]; const blueGuySprite = vm.runtime.targets[2];
t.equal(blueGuySprite.getName(), 'Blue Guy'); t.equal(blueGuySprite.getName(), 'Blue Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const missingCostume = blueGuySprite.getCostumes()[0]; const missingCostume = blueGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'Blue Guy 2'); t.equal(missingCostume.name, 'Blue Guy 2');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -85,7 +85,7 @@ test('load and then save sprite2 with missing vector costume file', t => {
t.equal(resavedSprite.name, 'Blue Guy'); t.equal(resavedSprite.name, 'Blue Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const missingCostume = resavedSprite.costumes[0]; const missingCostume = resavedSprite.costumes[0];
t.equal(missingCostume.name, 'Blue Guy 2'); t.equal(missingCostume.name, 'Blue Guy 2');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -99,9 +99,8 @@ test('load and then save sprite2 with missing vector costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id); const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
t.equal(costumeDescs.length, 0); t.equal(costumeDescs.length, 0);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -76,14 +76,14 @@ const test = tap.test;
test('load sprite3 with corrupted bitmap costume file', t => { test('load sprite3 with corrupted bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[2]; const greenGuySprite = vm.runtime.targets[2];
t.equal(greenGuySprite.getName(), 'Green Guy'); t.equal(greenGuySprite.getName(), 'Green Guy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const corruptedCostume = greenGuySprite.getCostumes()[0]; const corruptedCostume = greenGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'Green Guy'); t.equal(corruptedCostume.name, 'Green Guy');
t.equal(corruptedCostume.assetId, defaultBitmapAssetId); t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
@ -102,7 +102,7 @@ test('load and then save sprite with corrupted costume file', t => {
t.equal(resavedSprite.name, 'Green Guy'); t.equal(resavedSprite.name, 'Green Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const corruptedCostume = resavedSprite.costumes[0]; const corruptedCostume = resavedSprite.costumes[0];
t.equal(corruptedCostume.name, 'Green Guy'); t.equal(corruptedCostume.name, 'Green Guy');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -121,5 +121,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.png`); t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -57,14 +57,14 @@ const test = tap.test;
test('load sprite3 with corrupted vector costume file', t => { test('load sprite3 with corrupted vector costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[2]; const blueGuySprite = vm.runtime.targets[2];
t.equal(blueGuySprite.getName(), 'Blue Square Guy'); t.equal(blueGuySprite.getName(), 'Blue Square Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const corruptedCostume = blueGuySprite.getCostumes()[0]; const corruptedCostume = blueGuySprite.getCostumes()[0];
t.equal(corruptedCostume.name, 'costume1'); t.equal(corruptedCostume.name, 'costume1');
t.equal(corruptedCostume.assetId, defaultVectorAssetId); t.equal(corruptedCostume.assetId, defaultVectorAssetId);
@ -83,7 +83,7 @@ test('load and then save sprite with corrupted costume file', t => {
t.equal(resavedSprite.name, 'Blue Square Guy'); t.equal(resavedSprite.name, 'Blue Square Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const corruptedCostume = resavedSprite.costumes[0]; const corruptedCostume = resavedSprite.costumes[0];
t.equal(corruptedCostume.name, 'costume1'); t.equal(corruptedCostume.name, 'costume1');
// Resaved project costume should have the metadata that corresponds to the original broken costume // Resaved project costume should have the metadata that corresponds to the original broken costume
@ -102,5 +102,4 @@ test('serializeCostume saves orignal broken costume', t => {
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`); t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
t.equal(md5(costume.fileContent), brokenCostumeMd5); t.equal(md5(costume.fileContent), brokenCostumeMd5);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -59,14 +59,14 @@ const test = tap.test;
test('loading sprite3 with missing bitmap costume file', t => { test('loading sprite3 with missing bitmap costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const greenGuySprite = vm.runtime.targets[2]; const greenGuySprite = vm.runtime.targets[2];
t.equal(greenGuySprite.getName(), 'Green Guy'); t.equal(greenGuySprite.getName(), 'Green Guy');
t.equal(greenGuySprite.getCostumes().length, 1); t.equal(greenGuySprite.getCostumes().length, 1);
const missingCostume = greenGuySprite.getCostumes()[0]; const missingCostume = greenGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'Green Guy'); t.equal(missingCostume.name, 'Green Guy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -85,7 +85,7 @@ test('load and then save sprite3 with missing bitmap costume file', t => {
t.equal(resavedSprite.name, 'Green Guy'); t.equal(resavedSprite.name, 'Green Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const missingCostume = resavedSprite.costumes[0]; const missingCostume = resavedSprite.costumes[0];
t.equal(missingCostume.name, 'Green Guy'); t.equal(missingCostume.name, 'Green Guy');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -99,9 +99,8 @@ test('load and then save sprite3 with missing bitmap costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id); const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
t.equal(costumeDescs.length, 0); t.equal(costumeDescs.length, 0);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -39,14 +39,14 @@ const test = tap.test;
test('loading sprite3 with missing vector costume file', t => { test('loading sprite3 with missing vector costume file', t => {
t.equal(vm.runtime.targets.length, 3); t.equal(vm.runtime.targets.length, 3);
const stage = vm.runtime.targets[0]; const stage = vm.runtime.targets[0];
t.ok(stage.isStage); t.ok(stage.isStage);
const blueGuySprite = vm.runtime.targets[2]; const blueGuySprite = vm.runtime.targets[2];
t.equal(blueGuySprite.getName(), 'Blue Square Guy'); t.equal(blueGuySprite.getName(), 'Blue Square Guy');
t.equal(blueGuySprite.getCostumes().length, 1); t.equal(blueGuySprite.getCostumes().length, 1);
const missingCostume = blueGuySprite.getCostumes()[0]; const missingCostume = blueGuySprite.getCostumes()[0];
t.equal(missingCostume.name, 'costume1'); t.equal(missingCostume.name, 'costume1');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -65,7 +65,7 @@ test('load and then save sprite3 with missing vector costume file', t => {
t.equal(resavedSprite.name, 'Blue Square Guy'); t.equal(resavedSprite.name, 'Blue Square Guy');
t.equal(resavedSprite.costumes.length, 1); t.equal(resavedSprite.costumes.length, 1);
const missingCostume = resavedSprite.costumes[0]; const missingCostume = resavedSprite.costumes[0];
t.equal(missingCostume.name, 'costume1'); t.equal(missingCostume.name, 'costume1');
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data // Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
@ -79,9 +79,8 @@ test('load and then save sprite3 with missing vector costume file', t => {
test('serializeCostume does not save data for missing costume', t => { test('serializeCostume does not save data for missing costume', t => {
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id); const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
t.equal(costumeDescs.length, 0); t.equal(costumeDescs.length, 0);
t.end(); t.end();
process.nextTick(process.exit);
}); });

View file

@ -22,8 +22,8 @@ test('stack click activates the stack', t => {
vm.on('playgroundData', () => { vm.on('playgroundData', () => {
// The sprite should have moved 100 to the right // The sprite should have moved 100 to the right
t.equal(vm.editingTarget.x, 100); t.equal(vm.editingTarget.x, 100);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -48,7 +48,7 @@ test('unknown opcode', t => {
t.true(blocks.getBlock(fourthBlockInputId).shadow); t.true(blocks.getBlock(fourthBlockInputId).shadow);
t.equal(blocks.getBlock(fourthBlockInputId).opcode, 'sound_sounds_menu'); t.equal(blocks.getBlock(fourthBlockInputId).opcode, 'sound_sounds_menu');
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
}); });

View file

@ -33,7 +33,7 @@ test('unknown opcode', t => {
t.equal(blocks.getBlock(secondBlockId).opcode, 'control_forever'); t.equal(blocks.getBlock(secondBlockId).opcode, 'control_forever');
t.equal(innerBlockId, null); t.equal(innerBlockId, null);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
}); });

View file

@ -49,7 +49,7 @@ test('unknown opcode', t => {
t.equal(undefinedComment.x, 0); t.equal(undefinedComment.x, 0);
t.equal(undefinedComment.y, 0); t.equal(undefinedComment.y, 0);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
}); });

View file

@ -35,8 +35,8 @@ test('importing one project after the other resets monitored variables', t => {
const jamalVarBlock = vm.runtime.monitorBlocks.getBlock(jamalVarId); const jamalVarBlock = vm.runtime.monitorBlocks.getBlock(jamalVarId);
t.notOk(jamalVarBlock); t.notOk(jamalVarBlock);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
}); });
}); });

View file

@ -114,8 +114,8 @@ test('importing sb2 project with special chars in variable names', t => {
t.equal(bananasVarBlocks.length, 1); t.equal(bananasVarBlocks.length, 1);
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId); t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -114,8 +114,8 @@ test('importing sb3 project with special chars in variable names', t => {
t.equal(bananasVarBlocks.length, 1); t.equal(bananasVarBlocks.length, 1);
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId); t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);
vm.quit();
t.end(); t.end();
process.nextTick(process.exit);
}); });
// Start VM, load project, and run // Start VM, load project, and run

View file

@ -6,8 +6,6 @@ const Runtime = require('../../src/engine/runtime');
const MonitorRecord = require('../../src/engine/monitor-record'); const MonitorRecord = require('../../src/engine/monitor-record');
const {Map} = require('immutable'); const {Map} = require('immutable');
tap.tearDown(() => process.nextTick(process.exit));
const test = tap.test; const test = tap.test;
test('spec', t => { test('spec', t => {
@ -193,6 +191,7 @@ test('Starting the runtime emits an event', t => {
}); });
rt.start(); rt.start();
t.equal(started, true); t.equal(started, true);
rt.quit();
t.end(); t.end();
}); });
@ -209,6 +208,7 @@ test('Runtime cannot be started while already running', t => {
// Starting again should not emit another event // Starting again should not emit another event
rt.start(); rt.start();
t.equal(started, false); t.equal(started, false);
rt.quit();
t.end(); t.end();
}); });
@ -224,6 +224,7 @@ test('setCompatibilityMode restarts if it was already running', t => {
rt.setCompatibilityMode(true); rt.setCompatibilityMode(true);
t.equal(started, true); t.equal(started, true);
rt.quit();
t.end(); t.end();
}); });

View file

@ -27,8 +27,6 @@ tap.beforeEach(() => {
}); });
}); });
tap.tearDown(() => process.nextTick(process.exit));
const test = tap.test; const test = tap.test;
test('Adding a sprite (from sprite2) should emit a project changed event', t => { test('Adding a sprite (from sprite2) should emit a project changed event', t => {

View file

@ -53,8 +53,6 @@ tap.beforeEach(() => {
}); });
}); });
tap.tearDown(() => process.nextTick(process.exit));
const test = tap.test; const test = tap.test;
test('Creating a block should emit a project changed event', t => { test('Creating a block should emit a project changed event', t => {

View file

@ -4,8 +4,6 @@ const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer
const makeTestStorage = require('../fixtures/make-test-storage'); const makeTestStorage = require('../fixtures/make-test-storage');
const VirtualMachine = require('../../src/virtual-machine'); const VirtualMachine = require('../../src/virtual-machine');
tap.tearDown(() => process.nextTick(process.exit));
const test = tap.test; const test = tap.test;
// Test that loading a project does not emit a project change // Test that loading a project does not emit a project change

View file

@ -8,8 +8,6 @@ const Renderer = require('../fixtures/fake-renderer');
const Runtime = require('../../src/engine/runtime'); const Runtime = require('../../src/engine/runtime');
const RenderedTarget = require('../../src/sprites/rendered-target'); const RenderedTarget = require('../../src/sprites/rendered-target');
tap.tearDown(() => process.nextTick(process.exit));
const test = tap.test; const test = tap.test;
test('deleteSound returns function after deleting or null if nothing was deleted', t => { test('deleteSound returns function after deleting or null if nothing was deleted', t => {
@ -1016,6 +1014,7 @@ test('Starting the VM emits an event', t => {
}); });
vm.start(); vm.start();
t.equal(started, true); t.equal(started, true);
vm.quit();
t.end(); t.end();
}); });