Includes scratch-gui, scratch-vm, scratch-blocks, scratch-render, scratch-l10n, and deployment config. Co-authored-by: Cursor <cursoragent@cursor.com>
179 lines
5.4 KiB
JavaScript
179 lines
5.4 KiB
JavaScript
const {test} = require('tap');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const VirtualMachine = require('../../src/virtual-machine');
|
|
const Sprite = require('../../src/sprites/sprite');
|
|
const RenderedTarget = require('../../src/sprites/rendered-target');
|
|
const sb3 = require('../../src/serialization/sb3');
|
|
const Runtime = require('../../src/engine/runtime');
|
|
|
|
test('serialize data', t => {
|
|
const vm = new VirtualMachine();
|
|
const rt = vm.runtime;
|
|
|
|
const target1 = new RenderedTarget(new Sprite(null, rt), rt);
|
|
const target2 = new RenderedTarget(new Sprite(null, rt), rt);
|
|
rt.addTarget(target1);
|
|
rt.addTarget(target2);
|
|
|
|
t.same(sb3.serialize(rt).extensionStorage, undefined, 'global - nothing when no extensions');
|
|
t.same(
|
|
sb3.serialize(rt).targets.map(i => i.extensionStorage),
|
|
[undefined, undefined],
|
|
'sprites - nothing when no extensions'
|
|
);
|
|
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test1',
|
|
blocks: []
|
|
})
|
|
});
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test2',
|
|
blocks: []
|
|
})
|
|
});
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test3',
|
|
blocks: []
|
|
})
|
|
});
|
|
|
|
t.same(sb3.serialize(rt).extensionStorage, undefined, 'global - nothing when no storage');
|
|
t.same(
|
|
sb3.serialize(rt).targets.map(i => i.extensionStorage),
|
|
[undefined, undefined],
|
|
'sprites - nothing when no storage'
|
|
);
|
|
|
|
const topLevelBlockBase = {
|
|
// this is not interesting for this test
|
|
inputs: {},
|
|
fields: {},
|
|
topLevel: true,
|
|
next: null,
|
|
parent: null
|
|
};
|
|
|
|
target1.blocks.createBlock({
|
|
...topLevelBlockBase,
|
|
id: 'block1',
|
|
opcode: 'test1_whatever'
|
|
});
|
|
target2.blocks.createBlock({
|
|
...topLevelBlockBase,
|
|
id: 'block2',
|
|
opcode: 'test2_whatever'
|
|
});
|
|
|
|
target1.extensionStorage.test1 = 1234321;
|
|
t.same(sb3.serialize(rt, target1.id).extensionStorage, {
|
|
test1: 1234321
|
|
}, 'target1 alone has test1');
|
|
t.same(sb3.serialize(rt, target2.id).extensionStorage, undefined, 'target2 alone does not have test1');
|
|
|
|
target1.extensionStorage.test1 = null;
|
|
t.same(sb3.serialize(rt, target1.id).extensionStorage, undefined, 'null is not serialized');
|
|
|
|
target1.extensionStorage.test1 = undefined;
|
|
t.same(sb3.serialize(rt, target1.id).extensionStorage, undefined, 'undefined is not serialized');
|
|
|
|
target1.extensionStorage.test1 = {it: 'works'};
|
|
target1.extensionStorage.test2 = true;
|
|
target1.extensionStorage.test3 = {should_not: 'be_saved'};
|
|
|
|
target2.extensionStorage.test1 = ['ok'];
|
|
delete target2.extensionStorage.test2;
|
|
delete target2.extensionStorage.test3;
|
|
|
|
rt.extensionStorage.test1 = 'global ok';
|
|
delete rt.extensionStorage.test2;
|
|
rt.extensionStorage.test3 = ['dont save this'];
|
|
|
|
const json = sb3.serialize(rt);
|
|
t.same(json.extensionStorage, {
|
|
test1: 'global ok'
|
|
}, 'final - global has test1');
|
|
t.same(json.targets.map(i => i.extensionStorage), [
|
|
{
|
|
test1: {
|
|
it: 'works'
|
|
},
|
|
test2: true
|
|
},
|
|
{
|
|
test1: ['ok']
|
|
}
|
|
], 'final - targets ok');
|
|
|
|
t.end();
|
|
});
|
|
|
|
test('deserialize project with data', t => {
|
|
const vm = new VirtualMachine();
|
|
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test1',
|
|
blocks: []
|
|
})
|
|
});
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test2',
|
|
blocks: []
|
|
})
|
|
});
|
|
vm.extensionManager._registerInternalExtension({
|
|
getInfo: () => ({
|
|
id: 'test3',
|
|
blocks: []
|
|
})
|
|
});
|
|
|
|
// trick it into thinking the extensions are real and loaded...
|
|
vm.extensionManager._loadedExtensions.set('test1', 'invalid');
|
|
vm.extensionManager._loadedExtensions.set('test2', 'invalid');
|
|
vm.extensionManager._loadedExtensions.set('test3', 'invalid');
|
|
|
|
const fixture = fs.readFileSync(path.resolve(__dirname, '../fixtures/tw-extension-storage.sb3'));
|
|
vm.loadProject(fixture).then(() => {
|
|
t.same(vm.runtime.extensionStorage, {
|
|
test1: 'global ok'
|
|
}, 'deserialized global');
|
|
t.same(vm.runtime.targets[0].extensionStorage, {
|
|
test1: {
|
|
it: 'works'
|
|
},
|
|
test2: true
|
|
}, 'deserialized target 0');
|
|
t.same(vm.runtime.targets[1].extensionStorage, {
|
|
test1: ['ok']
|
|
}, 'deserialized target 1');
|
|
t.same(vm.runtime.targets[2].extensionStorage, {}, 'deserialized target 2');
|
|
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
test('deserialize project with no data', t => {
|
|
const vm = new VirtualMachine();
|
|
const fixture = fs.readFileSync(path.resolve(__dirname, '../fixtures/tw-extension-storage-no-data.sb3'));
|
|
vm.loadProject(fixture).then(() => {
|
|
t.same(vm.runtime.extensionStorage, {}, 'deserialized global');
|
|
t.same(vm.runtime.targets[0].extensionStorage, {}, 'deserialized target 0');
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
test('dispose resets storage', t => {
|
|
const runtime = new Runtime();
|
|
runtime.extensionStorage.something = 3;
|
|
runtime.dispose();
|
|
t.same(runtime.extensionStorage, {});
|
|
t.end();
|
|
});
|