Includes scratch-gui, scratch-vm, scratch-blocks, scratch-render, scratch-l10n, and deployment config. Co-authored-by: Cursor <cursoragent@cursor.com>
225 lines
7.3 KiB
JavaScript
225 lines
7.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
const {test} = require('tap');
|
|
const VirtualMachine = require('../../src/virtual-machine');
|
|
const Scratch = require('../../src/extension-support/tw-extension-api-common');
|
|
|
|
// Based on https://github.com/TurboWarp/scratch-vm/pull/141
|
|
class TestExtensionUsingReturn {
|
|
getInfo () {
|
|
return {
|
|
id: 'loopsAndThings',
|
|
name: 'Loops and things test - return',
|
|
blocks: [
|
|
{
|
|
opcode: 'conditional',
|
|
blockType: Scratch.BlockType.CONDITIONAL,
|
|
text: 'run branch [BRANCH] of',
|
|
arguments: {
|
|
BRANCH: {
|
|
type: Scratch.ArgumentType.NUMBER,
|
|
defaultValue: 1
|
|
}
|
|
},
|
|
branchCount: 3
|
|
},
|
|
{
|
|
opcode: 'loop',
|
|
blockType: Scratch.BlockType.LOOP,
|
|
text: 'my repeat [TIMES]',
|
|
arguments: {
|
|
TIMES: {
|
|
type: Scratch.ArgumentType.NUMBER,
|
|
defaultValue: 10
|
|
}
|
|
}
|
|
},
|
|
'---',
|
|
{
|
|
opcode: 'testPromise',
|
|
blockType: Scratch.BlockType.REPORTER,
|
|
text: 'return [VALUE] in a Promise',
|
|
arguments: {
|
|
VALUE: {
|
|
type: Scratch.ArgumentType.STRING,
|
|
defaultValue: ''
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
conditional ({BRANCH}) {
|
|
return Scratch.Cast.toNumber(BRANCH);
|
|
}
|
|
|
|
loop ({TIMES}, util) {
|
|
const times = Math.round(Scratch.Cast.toNumber(TIMES));
|
|
if (typeof util.stackFrame.loopCounter === 'undefined') {
|
|
util.stackFrame.loopCounter = times;
|
|
}
|
|
util.stackFrame.loopCounter--;
|
|
if (util.stackFrame.loopCounter >= 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
testPromise ({VALUE}) {
|
|
return Promise.resolve(VALUE);
|
|
}
|
|
}
|
|
|
|
class TestExtensionUsingStartBranch {
|
|
getInfo () {
|
|
return {
|
|
id: 'loopsAndThings',
|
|
name: 'Loops and things test - startBranch',
|
|
blocks: [
|
|
{
|
|
opcode: 'conditional',
|
|
blockType: Scratch.BlockType.CONDITIONAL,
|
|
text: 'run branch [BRANCH] of',
|
|
arguments: {
|
|
BRANCH: {
|
|
type: Scratch.ArgumentType.NUMBER,
|
|
defaultValue: 1
|
|
}
|
|
},
|
|
branchCount: 3
|
|
},
|
|
{
|
|
opcode: 'loop',
|
|
blockType: Scratch.BlockType.LOOP,
|
|
text: 'my repeat [TIMES]',
|
|
arguments: {
|
|
TIMES: {
|
|
type: Scratch.ArgumentType.NUMBER,
|
|
defaultValue: 10
|
|
}
|
|
}
|
|
},
|
|
'---',
|
|
{
|
|
opcode: 'testPromise',
|
|
blockType: Scratch.BlockType.REPORTER,
|
|
text: 'return [VALUE] in a Promise',
|
|
arguments: {
|
|
VALUE: {
|
|
type: Scratch.ArgumentType.STRING,
|
|
defaultValue: ''
|
|
}
|
|
}
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
conditional ({BRANCH}, util) {
|
|
util.startBranch(Scratch.Cast.toNumber(BRANCH), false);
|
|
}
|
|
|
|
loop ({TIMES}, util) {
|
|
const times = Math.round(Scratch.Cast.toNumber(TIMES));
|
|
if (typeof util.stackFrame.loopCounter === 'undefined') {
|
|
util.stackFrame.loopCounter = times;
|
|
}
|
|
util.stackFrame.loopCounter--;
|
|
if (util.stackFrame.loopCounter >= 0) {
|
|
util.startBranch(1, true);
|
|
}
|
|
}
|
|
|
|
testPromise ({VALUE}) {
|
|
return Promise.resolve(VALUE);
|
|
}
|
|
}
|
|
|
|
/* eslint-disable no-loop-func */
|
|
|
|
for (const Extension of [TestExtensionUsingReturn, TestExtensionUsingStartBranch]) {
|
|
for (const compilerEnabled of [false, true]) {
|
|
test(`CONDITIONAL - ${Extension.name} - ${compilerEnabled ? 'compiled' : 'interpreted'}`, t => {
|
|
t.plan(1);
|
|
|
|
const vm = new VirtualMachine();
|
|
vm.setCompilerOptions({
|
|
enabled: compilerEnabled
|
|
});
|
|
vm.extensionManager.addBuiltinExtension('loopsAndThings', Extension);
|
|
vm.runtime.on('COMPILE_ERROR', () => {
|
|
t.fail('Compile error');
|
|
});
|
|
|
|
vm.loadProject(fs.readFileSync(path.join(__dirname, '../fixtures/tw-conditional.sb3'))).then(() => {
|
|
let okayCount = 0;
|
|
vm.runtime.on('SAY', (target, type, text) => {
|
|
if (text === 'OK!') {
|
|
okayCount++;
|
|
} else if (text === 'end') {
|
|
vm.quit();
|
|
t.equal(okayCount, 5);
|
|
t.end();
|
|
} else {
|
|
t.fail(`Unexpected text: ${text}`);
|
|
}
|
|
});
|
|
|
|
vm.greenFlag();
|
|
vm.start();
|
|
});
|
|
});
|
|
|
|
test(`LOOP - ${Extension.name} - ${compilerEnabled ? 'compiled' : 'interpreted'}`, t => {
|
|
t.plan(1);
|
|
|
|
const vm = new VirtualMachine();
|
|
vm.setCompilerOptions({
|
|
enabled: compilerEnabled
|
|
});
|
|
vm.extensionManager.addBuiltinExtension('loopsAndThings', Extension);
|
|
vm.runtime.on('COMPILE_ERROR', () => {
|
|
t.fail('Compile error');
|
|
});
|
|
|
|
vm.loadProject(fs.readFileSync(path.join(__dirname, '../fixtures/tw-loop.sb3'))).then(() => {
|
|
vm.runtime.on('SAY', (target, type, text) => {
|
|
vm.quit();
|
|
t.equal(text, 'a 3 b 12 c 48 frames 64');
|
|
t.end();
|
|
});
|
|
|
|
vm.greenFlag();
|
|
vm.start();
|
|
});
|
|
});
|
|
|
|
test(`beyond branchCount - ${Extension.name} - ${compilerEnabled ? 'compiled' : 'interpreted'}`, t => {
|
|
t.plan(1);
|
|
|
|
const vm = new VirtualMachine();
|
|
vm.setCompilerOptions({
|
|
enabled: compilerEnabled
|
|
});
|
|
vm.extensionManager.addBuiltinExtension('loopsAndThings', Extension);
|
|
vm.runtime.on('COMPILE_ERROR', () => {
|
|
t.fail('Compile error');
|
|
});
|
|
|
|
vm.loadProject(fs.readFileSync(path.join(__dirname, '../fixtures/tw-beyond-branchCount.sb3'))).then(() => {
|
|
vm.runtime.on('SAY', (target, type, text) => {
|
|
if (text === 'BeyondBranchCount') {
|
|
t.pass('BeyondBranchCount');
|
|
} else if (text === 'end') {
|
|
vm.quit();
|
|
t.end();
|
|
}
|
|
});
|
|
|
|
vm.greenFlag();
|
|
vm.start();
|
|
});
|
|
});
|
|
}
|
|
}
|