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:
127
scratch-vm/test/integration/internal-extension.js
Normal file
127
scratch-vm/test/integration/internal-extension.js
Normal file
@@ -0,0 +1,127 @@
|
||||
const test = require('tap').test;
|
||||
const Worker = require('tiny-worker');
|
||||
|
||||
const BlockType = require('../../src/extension-support/block-type');
|
||||
|
||||
const dispatch = require('../../src/dispatch/central-dispatch');
|
||||
const VirtualMachine = require('../../src/virtual-machine');
|
||||
|
||||
const Sprite = require('../../src/sprites/sprite');
|
||||
const RenderedTarget = require('../../src/sprites/rendered-target');
|
||||
|
||||
// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead.
|
||||
dispatch.workerClass = Worker;
|
||||
|
||||
class TestInternalExtension {
|
||||
constructor () {
|
||||
this.status = {};
|
||||
this.status.constructorCalled = true;
|
||||
}
|
||||
|
||||
getInfo () {
|
||||
this.status.getInfoCalled = true;
|
||||
return {
|
||||
id: 'testInternalExtension',
|
||||
name: 'Test Internal Extension',
|
||||
blocks: [
|
||||
{
|
||||
opcode: 'go'
|
||||
}
|
||||
],
|
||||
menus: {
|
||||
simpleMenu: this._buildAMenu(),
|
||||
dynamicMenu: '_buildDynamicMenu'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
go (args, util, blockInfo) {
|
||||
this.status.goCalled = true;
|
||||
return blockInfo;
|
||||
}
|
||||
|
||||
_buildAMenu () {
|
||||
this.status.buildMenuCalled = true;
|
||||
return ['abcd', 'efgh', 'ijkl'];
|
||||
}
|
||||
|
||||
_buildDynamicMenu () {
|
||||
this.status.buildDynamicMenuCalled = true;
|
||||
return [1, 2, 3, 4, 6];
|
||||
}
|
||||
}
|
||||
|
||||
test('internal extension', t => {
|
||||
const vm = new VirtualMachine();
|
||||
|
||||
const extension = new TestInternalExtension();
|
||||
t.ok(extension.status.constructorCalled);
|
||||
|
||||
t.notOk(extension.status.getInfoCalled);
|
||||
vm.extensionManager._registerInternalExtension(extension);
|
||||
t.ok(extension.status.getInfoCalled);
|
||||
|
||||
const func = vm.runtime.getOpcodeFunction('testInternalExtension_go');
|
||||
t.type(func, 'function');
|
||||
|
||||
t.notOk(extension.status.goCalled);
|
||||
const goBlockInfo = func();
|
||||
t.ok(extension.status.goCalled);
|
||||
|
||||
// The 'go' block returns its own blockInfo. Make sure it matches the expected info.
|
||||
// Note that the extension parser fills in missing fields so there are more fields here than in `getInfo`.
|
||||
const expectedBlockInfo = {
|
||||
arguments: {},
|
||||
blockAllThreads: false,
|
||||
blockType: BlockType.COMMAND,
|
||||
func: goBlockInfo.func, // Cheat since we don't have a good way to ensure we generate the same function
|
||||
opcode: 'go',
|
||||
terminal: false,
|
||||
text: 'go'
|
||||
};
|
||||
t.deepEqual(goBlockInfo, expectedBlockInfo);
|
||||
|
||||
// There should be 2 menus - one is an array, one is the function to call.
|
||||
t.equal(vm.runtime._blockInfo[0].menus.length, 2);
|
||||
// First menu has 3 items.
|
||||
t.equal(
|
||||
vm.runtime._blockInfo[0].menus[0].json.args0[0].options.length, 3);
|
||||
// Second menu is a dynamic menu and therefore should be a function.
|
||||
t.type(
|
||||
vm.runtime._blockInfo[0].menus[1].json.args0[0].options, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('load sync', t => {
|
||||
const vm = new VirtualMachine();
|
||||
vm.extensionManager.loadExtensionIdSync('coreExample');
|
||||
t.ok(vm.extensionManager.isExtensionLoaded('coreExample'));
|
||||
|
||||
t.equal(vm.runtime._blockInfo.length, 1);
|
||||
|
||||
// blocks should be an array of two items: a button pseudo-block and a reporter block.
|
||||
t.equal(vm.runtime._blockInfo[0].blocks.length, 3);
|
||||
t.type(vm.runtime._blockInfo[0].blocks[0].info, 'object');
|
||||
t.type(vm.runtime._blockInfo[0].blocks[0].info.func, 'MAKE_A_VARIABLE');
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[0].info.blockType, 'button');
|
||||
t.type(vm.runtime._blockInfo[0].blocks[1].info, 'object');
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[1].info.opcode, 'exampleOpcode');
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[1].info.blockType, 'reporter');
|
||||
t.type(vm.runtime._blockInfo[0].blocks[2].info, 'object');
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[2].info.opcode, 'exampleWithInlineImage');
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[2].info.blockType, 'command');
|
||||
|
||||
// Test the opcode function
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[1].info.func(), 'no stage yet');
|
||||
|
||||
const sprite = new Sprite(null, vm.runtime);
|
||||
sprite.name = 'Stage';
|
||||
const stage = new RenderedTarget(sprite, vm.runtime);
|
||||
stage.isStage = true;
|
||||
vm.runtime.targets = [stage];
|
||||
|
||||
t.equal(vm.runtime._blockInfo[0].blocks[1].info.func(), 'Stage');
|
||||
|
||||
t.end();
|
||||
});
|
||||
Reference in New Issue
Block a user