Initial commit of 001code-html Scratch frontend project.
Includes scratch-gui, scratch-vm, scratch-blocks, scratch-render, scratch-l10n, and deployment config. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
226
scratch-vm/test/integration/tw_hats_and_events.js
Normal file
226
scratch-vm/test/integration/tw_hats_and_events.js
Normal file
@@ -0,0 +1,226 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const {test} = require('tap');
|
||||
const VM = require('../../src/virtual-machine');
|
||||
const BlockType = require('../../src/extension-support/block-type');
|
||||
const ArgumentType = require('../../src/extension-support/argument-type');
|
||||
|
||||
const compilerAndInterpreter = (name, callback) => {
|
||||
test(`${name} - interpreted`, t => {
|
||||
callback(t, {
|
||||
enabled: false
|
||||
});
|
||||
});
|
||||
test(`${name} - compiled`, t => {
|
||||
callback(t, {
|
||||
enabled: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const fixture = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'tw-hats-and-events.sb3'));
|
||||
|
||||
compilerAndInterpreter('hats and events', (t, co) => {
|
||||
const vm = new VM();
|
||||
vm.setCompilerOptions(co);
|
||||
|
||||
let log = [];
|
||||
let hatReturns = false;
|
||||
class TestExtension {
|
||||
getInfo () {
|
||||
return {
|
||||
id: 'testpredicate',
|
||||
name: 'Test Predicate',
|
||||
blocks: [
|
||||
{
|
||||
opcode: 'event',
|
||||
blockType: BlockType.EVENT,
|
||||
text: 'event block',
|
||||
isEdgeActivated: false
|
||||
},
|
||||
{
|
||||
opcode: 'hat',
|
||||
blockType: BlockType.HAT,
|
||||
text: 'hat block',
|
||||
isEdgeActivated: false
|
||||
},
|
||||
{
|
||||
opcode: 'complexhat',
|
||||
blockType: BlockType.HAT,
|
||||
text: 'complex hat [MENU] if [INPUT]',
|
||||
isEdgeActivated: false,
|
||||
arguments: {
|
||||
MENU: {
|
||||
menu: 'test'
|
||||
},
|
||||
INPUT: {
|
||||
type: ArgumentType.STRING,
|
||||
defaultValue: 'default'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'clickme',
|
||||
blockType: BlockType.HAT,
|
||||
text: 'stack click test [INPUT]',
|
||||
isEdgeActivated: false,
|
||||
arguments: {
|
||||
INPUT: {
|
||||
type: ArgumentType.STRING,
|
||||
defaultValue: 'default'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
menus: {
|
||||
test: {
|
||||
acceptReporters: false,
|
||||
items: ['a', 'b', 'c']
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
event () {
|
||||
log.push('this should never run');
|
||||
}
|
||||
hat () {
|
||||
log.push(`hat ${hatReturns}`);
|
||||
return hatReturns;
|
||||
}
|
||||
complexhat ({INPUT}) {
|
||||
log.push(`complex hat ${INPUT}`);
|
||||
return !!INPUT;
|
||||
}
|
||||
clickme () {
|
||||
log.push('clickme');
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
vm.extensionManager.addBuiltinExtension('testpredicate', TestExtension);
|
||||
|
||||
vm.on('COMPILE_ERROR', () => {
|
||||
t.fail('Compile error');
|
||||
});
|
||||
|
||||
vm.loadProject(fixture).then(async () => {
|
||||
log = vm.runtime.getTargetForStage().lookupVariableByNameAndType('log', 'list').value;
|
||||
t.same(log, [], 'sanity check - log starts empty');
|
||||
|
||||
// Let it run for a bit. Nothing should happen on its own.
|
||||
for (let i = 0; i < 5; i++) {
|
||||
vm.runtime._step();
|
||||
}
|
||||
t.same(log, [], 'nothing happens initially');
|
||||
|
||||
// See if events work
|
||||
vm.runtime.startHats('testpredicate_event');
|
||||
t.same(log, [], 'event function does not get called, even if it exists');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['event'], 'ran event script');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test hat that returns false
|
||||
hatReturns = false;
|
||||
vm.runtime.startHats('testpredicate_hat');
|
||||
t.same(log, ['hat false'], 'ran hat function');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['hat false'], 'did not run hat script');
|
||||
|
||||
// Test hat that returns true
|
||||
hatReturns = true;
|
||||
vm.runtime.startHats('testpredicate_hat');
|
||||
t.same(log, [
|
||||
'hat false',
|
||||
'hat true'
|
||||
], 'ran hat function');
|
||||
vm.runtime._step();
|
||||
t.same(log, [
|
||||
'hat false',
|
||||
'hat true',
|
||||
'hat'
|
||||
], 'ran hat script');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test hat that returns false in a Promise
|
||||
hatReturns = Promise.resolve(false);
|
||||
vm.runtime.startHats('testpredicate_hat');
|
||||
t.same(log, ['hat [object Promise]'], 'ran hat function');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['hat [object Promise]'], 'hat script does not run before promise finishes');
|
||||
await Promise.resolve(); // Allow promise to be processed
|
||||
vm.runtime._step();
|
||||
t.same(log, ['hat [object Promise]'], 'hat script still does not run');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test hat that returns true in a Promise
|
||||
hatReturns = Promise.resolve(true);
|
||||
vm.runtime.startHats('testpredicate_hat');
|
||||
t.same(log, ['hat [object Promise]'], 'ran hat function');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['hat [object Promise]'], 'hat script does not run before promise finishes');
|
||||
await Promise.resolve();
|
||||
vm.runtime._step();
|
||||
t.same(log, ['hat [object Promise]', 'hat'], 'hat script runs after promise finishes');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test complex hat
|
||||
vm.runtime.startHats('testpredicate_complexhat', {
|
||||
MENU: 'a'
|
||||
});
|
||||
t.same(log, [
|
||||
'complex hat ',
|
||||
'complex hat 1'
|
||||
], 'ran complex hat functions');
|
||||
vm.runtime._step();
|
||||
t.same(log, [
|
||||
'complex hat ',
|
||||
'complex hat 1',
|
||||
'complex hat a 2'
|
||||
], 'ran complex hat script');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test complex hat with a complex input
|
||||
vm.runtime.startHats('testpredicate_complexhat', {
|
||||
MENU: 'b'
|
||||
});
|
||||
t.same(log, [], 'control flow in complex inputs is not run immediately');
|
||||
vm.runtime._step();
|
||||
t.same(log, [
|
||||
'evaluated block ',
|
||||
'evaluated block 1'
|
||||
], 'evaluated complex inputs but not hat function');
|
||||
vm.runtime._step();
|
||||
t.same(log, [
|
||||
'evaluated block ',
|
||||
'evaluated block 1',
|
||||
'complex hat ',
|
||||
'complex hat 1',
|
||||
'complex hat b 2'
|
||||
], 'evaluated complex hat functions and scripts');
|
||||
|
||||
log.length = 0;
|
||||
|
||||
// Test that in stackClick mode, the hat block still gets run, but the result is ignored
|
||||
const sprite = vm.runtime.targets[1];
|
||||
const allBlocks = Object.values(sprite.sprite.blocks._blocks);
|
||||
const clickBlockId = allBlocks.find(i => i.opcode === 'testpredicate_clickme').id;
|
||||
vm.runtime._pushThread(clickBlockId, sprite, {
|
||||
stackClick: true
|
||||
});
|
||||
t.same(log, [], 'stackClick does not run anything immediately');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['evaluated block something'], 'stackClick hat ran input');
|
||||
vm.runtime._step();
|
||||
t.same(log, ['evaluated block something', 'clickme'], 'stackClick hat input evaluated');
|
||||
await Promise.resolve(); // Allow promise to be processed
|
||||
vm.runtime._step();
|
||||
t.same(log, ['evaluated block something', 'clickme', 'stack click'], 'stackClick hat script ran');
|
||||
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user