Spaces:
Running
Running
const test = require('tap').test; | |
const Blocks = require('../../src/engine/blocks'); | |
const Variable = require('../../src/engine/variable'); | |
const adapter = require('../../src/engine/adapter'); | |
const events = require('../fixtures/events.json'); | |
const Runtime = require('../../src/engine/runtime'); | |
test('spec', t => { | |
const b = new Blocks(new Runtime()); | |
t.type(Blocks, 'function'); | |
t.type(b, 'object'); | |
t.ok(b instanceof Blocks); | |
t.type(b._blocks, 'object'); | |
t.type(b._scripts, 'object'); | |
t.ok(Array.isArray(b._scripts)); | |
t.type(b.createBlock, 'function'); | |
t.type(b.moveBlock, 'function'); | |
t.type(b.changeBlock, 'function'); | |
t.type(b.deleteBlock, 'function'); | |
t.type(b.getBlock, 'function'); | |
t.type(b.getScripts, 'function'); | |
t.type(b.getNextBlock, 'function'); | |
t.type(b.getBranch, 'function'); | |
t.type(b.getOpcode, 'function'); | |
t.type(b.mutationToXML, 'function'); | |
t.type(b.updateSensingOfReference, 'function'); | |
t.end(); | |
}); | |
// Getter tests | |
test('getBlock', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
const block = b.getBlock('foo'); | |
t.type(block, 'object'); | |
const notBlock = b.getBlock('?'); | |
t.type(notBlock, 'undefined'); | |
t.end(); | |
}); | |
test('getScripts', t => { | |
const b = new Blocks(new Runtime()); | |
let scripts = b.getScripts(); | |
t.type(scripts, 'object'); | |
t.equals(scripts.length, 0); | |
// Create two top-level blocks and one not. | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo3', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
scripts = b.getScripts(); | |
t.type(scripts, 'object'); | |
t.equals(scripts.length, 2); | |
t.ok(scripts.indexOf('foo') > -1); | |
t.ok(scripts.indexOf('foo2') > -1); | |
t.equals(scripts.indexOf('foo3'), -1); | |
t.end(); | |
}); | |
test('getNextBlock', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
let next = b.getNextBlock('foo'); | |
t.equals(next, null); | |
// Add a block with "foo" as its next. | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: 'foo', | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
next = b.getNextBlock('foo2'); | |
t.equals(next, 'foo'); | |
// Block that doesn't exist. | |
const noBlock = b.getNextBlock('?'); | |
t.equals(noBlock, null); | |
t.end(); | |
}); | |
test('getBranch', t => { | |
const b = new Blocks(new Runtime()); | |
// Single branch | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
SUBSTACK: { | |
name: 'SUBSTACK', | |
block: 'foo2', | |
shadow: null | |
} | |
}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
const branch = b.getBranch('foo'); | |
t.equals(branch, 'foo2'); | |
const notBranch = b.getBranch('?'); | |
t.equals(notBranch, null); | |
t.end(); | |
}); | |
test('getBranch2', t => { | |
const b = new Blocks(new Runtime()); | |
// Second branch | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
SUBSTACK: { | |
name: 'SUBSTACK', | |
block: 'foo2', | |
shadow: null | |
}, | |
SUBSTACK2: { | |
name: 'SUBSTACK2', | |
block: 'foo3', | |
shadow: null | |
} | |
}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.createBlock({ | |
id: 'foo3', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
const branch1 = b.getBranch('foo', 1); | |
const branch2 = b.getBranch('foo', 2); | |
t.equals(branch1, 'foo2'); | |
t.equals(branch2, 'foo3'); | |
t.end(); | |
}); | |
test('getBranch with none', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
const noBranch = b.getBranch('foo'); | |
t.equals(noBranch, null); | |
t.end(); | |
}); | |
test('getOpcode', t => { | |
const b = new Blocks(new Runtime()); | |
const block = { | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}; | |
b.createBlock(block); | |
const opcode = b.getOpcode(block); | |
t.equals(opcode, 'TEST_BLOCK'); | |
const undefinedBlock = b.getBlock('?'); | |
const undefinedOpcode = b.getOpcode(undefinedBlock); | |
t.equals(undefinedOpcode, null); | |
t.end(); | |
}); | |
test('mutationToXML', t => { | |
const b = new Blocks(new Runtime()); | |
const testStringRaw = '"arbitrary" & \'complicated\' test string'; | |
const testStringEscaped = '\\"arbitrary\\" & 'complicated' test string'; | |
const mutation = { | |
tagName: 'mutation', | |
children: [], | |
blockInfo: { | |
text: testStringRaw | |
} | |
}; | |
const xml = b.mutationToXML(mutation); | |
t.equals( | |
xml, | |
`<mutation blockInfo="{"text":"${testStringEscaped}"}"></mutation>` | |
); | |
t.end(); | |
}); | |
// Block events tests | |
test('create', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
t.type(b._blocks.foo, 'object'); | |
t.equal(b._blocks.foo.opcode, 'TEST_BLOCK'); | |
t.notEqual(b._scripts.indexOf('foo'), -1); | |
t.end(); | |
}); | |
test('move', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'bar', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
// Attach 'bar' to the end of 'foo' | |
b.moveBlock({ | |
id: 'bar', | |
newParent: 'foo' | |
}); | |
t.equal(b._scripts.length, 1); | |
t.equal(Object.keys(b._blocks).length, 2); | |
t.equal(b._blocks.foo.next, 'bar'); | |
// Detach 'bar' from 'foo' | |
b.moveBlock({ | |
id: 'bar', | |
oldParent: 'foo' | |
}); | |
t.equal(b._scripts.length, 2); | |
t.equal(Object.keys(b._blocks).length, 2); | |
t.equal(b._blocks.foo.next, null); | |
t.end(); | |
}); | |
test('move into empty', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'bar', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.moveBlock({ | |
id: 'bar', | |
newInput: 'fooInput', | |
newParent: 'foo' | |
}); | |
t.equal(b._blocks.foo.inputs.fooInput.block, 'bar'); | |
t.end(); | |
}); | |
test('move no obscure shadow', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
fooInput: { | |
name: 'fooInput', | |
block: 'x', | |
shadow: 'y' | |
} | |
}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'bar', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.moveBlock({ | |
id: 'bar', | |
newInput: 'fooInput', | |
newParent: 'foo' | |
}); | |
t.equal(b._blocks.foo.inputs.fooInput.block, 'bar'); | |
t.equal(b._blocks.foo.inputs.fooInput.shadow, 'y'); | |
t.end(); | |
}); | |
test('move - attaching new shadow', t => { | |
const b = new Blocks(new Runtime()); | |
// Block/shadow are null to mimic state right after a procedure_call block | |
// is mutated by adding an input. The "move" will attach the new shadow. | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
fooInput: { | |
name: 'fooInput', | |
block: null, | |
shadow: null | |
} | |
}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'bar', | |
opcode: 'TEST_BLOCK', | |
shadow: true, | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.moveBlock({ | |
id: 'bar', | |
newInput: 'fooInput', | |
newParent: 'foo' | |
}); | |
t.equal(b._blocks.foo.inputs.fooInput.block, 'bar'); | |
t.equal(b._blocks.foo.inputs.fooInput.shadow, 'bar'); | |
t.end(); | |
}); | |
test('change', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: { | |
someField: { | |
name: 'someField', | |
value: 'initial-value' | |
} | |
}, | |
inputs: {}, | |
topLevel: true | |
}); | |
// Test that the field is updated | |
t.equal(b._blocks.foo.fields.someField.value, 'initial-value'); | |
b.changeBlock({ | |
element: 'field', | |
id: 'foo', | |
name: 'someField', | |
value: 'final-value' | |
}); | |
t.equal(b._blocks.foo.fields.someField.value, 'final-value'); | |
// Invalid cases | |
// No `element` | |
b.changeBlock({ | |
id: 'foo', | |
name: 'someField', | |
value: 'invalid-value' | |
}); | |
t.equal(b._blocks.foo.fields.someField.value, 'final-value'); | |
// No block ID | |
b.changeBlock({ | |
element: 'field', | |
name: 'someField', | |
value: 'invalid-value' | |
}); | |
t.equal(b._blocks.foo.fields.someField.value, 'final-value'); | |
// No such field | |
b.changeBlock({ | |
element: 'field', | |
id: 'foo', | |
name: 'someWrongField', | |
value: 'final-value' | |
}); | |
t.equal(b._blocks.foo.fields.someField.value, 'final-value'); | |
t.end(); | |
}); | |
test('delete', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.deleteBlock('foo'); | |
t.type(b._blocks.foo, 'undefined'); | |
t.equal(b._scripts.indexOf('foo'), -1); | |
t.end(); | |
}); | |
test('delete chain', t => { | |
// Create a chain of connected blocks and delete the top one. | |
// All of them should be deleted. | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: 'foo2', | |
fields: {}, | |
inputs: {}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: 'foo3', | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.createBlock({ | |
id: 'foo3', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.deleteBlock('foo'); | |
t.type(b._blocks.foo, 'undefined'); | |
t.type(b._blocks.foo2, 'undefined'); | |
t.type(b._blocks.foo3, 'undefined'); | |
t.equal(b._scripts.indexOf('foo'), -1); | |
t.equal(Object.keys(b._blocks).length, 0); | |
t.equal(b._scripts.length, 0); | |
t.end(); | |
}); | |
test('delete inputs', t => { | |
// Create a block with two inputs, one of which has its own input. | |
// Delete the block - all of them should be deleted. | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
input1: { | |
name: 'input1', | |
block: 'foo2', | |
shadow: 'foo2' | |
}, | |
SUBSTACK: { | |
name: 'SUBSTACK', | |
block: 'foo3', | |
shadow: null | |
} | |
}, | |
topLevel: true | |
}); | |
b.createBlock({ | |
id: 'foo2', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.createBlock({ | |
id: 'foo5', | |
opcode: 'TEST_OBSCURED_SHADOW', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.createBlock({ | |
id: 'foo3', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: { | |
subinput: { | |
name: 'subinput', | |
block: 'foo4', | |
shadow: 'foo5' | |
} | |
}, | |
topLevel: false | |
}); | |
b.createBlock({ | |
id: 'foo4', | |
opcode: 'TEST_BLOCK', | |
next: null, | |
fields: {}, | |
inputs: {}, | |
topLevel: false | |
}); | |
b.deleteBlock('foo'); | |
t.type(b._blocks.foo, 'undefined'); | |
t.type(b._blocks.foo2, 'undefined'); | |
t.type(b._blocks.foo3, 'undefined'); | |
t.type(b._blocks.foo4, 'undefined'); | |
t.type(b._blocks.foo5, 'undefined'); | |
t.equal(b._scripts.indexOf('foo'), -1); | |
t.equal(Object.keys(b._blocks).length, 0); | |
t.equal(b._scripts.length, 0); | |
t.end(); | |
}); | |
test('updateAssetName function updates name in sound field', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
fields: { | |
SOUND_MENU: { | |
name: 'SOUND_MENU', | |
value: 'name1' | |
} | |
} | |
}); | |
t.equals(b.getBlock('foo').fields.SOUND_MENU.value, 'name1'); | |
b.updateAssetName('name1', 'name2', 'sound'); | |
t.equals(b.getBlock('foo').fields.SOUND_MENU.value, 'name2'); | |
t.end(); | |
}); | |
test('updateAssetName function updates name in costume field', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
fields: { | |
COSTUME: { | |
name: 'COSTUME', | |
value: 'name1' | |
} | |
} | |
}); | |
t.equals(b.getBlock('foo').fields.COSTUME.value, 'name1'); | |
b.updateAssetName('name1', 'name2', 'costume'); | |
t.equals(b.getBlock('foo').fields.COSTUME.value, 'name2'); | |
t.end(); | |
}); | |
test('updateAssetName function updates name in backdrop field', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'foo', | |
fields: { | |
BACKDROP: { | |
name: 'BACKDROP', | |
value: 'name1' | |
} | |
} | |
}); | |
t.equals(b.getBlock('foo').fields.BACKDROP.value, 'name1'); | |
b.updateAssetName('name1', 'name2', 'backdrop'); | |
t.equals(b.getBlock('foo').fields.BACKDROP.value, 'name2'); | |
t.end(); | |
}); | |
test('updateAssetName function updates name in all sprite fields', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
fields: { | |
TOWARDS: { | |
name: 'TOWARDS', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
TO: { | |
name: 'TO', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id3', | |
fields: { | |
OBJECT: { | |
name: 'OBJECT', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id4', | |
fields: { | |
VIDEOONMENU2: { | |
name: 'VIDEOONMENU2', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id5', | |
fields: { | |
DISTANCETOMENU: { | |
name: 'DISTANCETOMENU', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id6', | |
fields: { | |
TOUCHINGOBJECTMENU: { | |
name: 'TOUCHINGOBJECTMENU', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id7', | |
fields: { | |
CLONE_OPTION: { | |
name: 'CLONE_OPTION', | |
value: 'name1' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.TOWARDS.value, 'name1'); | |
t.equals(b.getBlock('id2').fields.TO.value, 'name1'); | |
t.equals(b.getBlock('id3').fields.OBJECT.value, 'name1'); | |
t.equals(b.getBlock('id4').fields.VIDEOONMENU2.value, 'name1'); | |
t.equals(b.getBlock('id5').fields.DISTANCETOMENU.value, 'name1'); | |
t.equals(b.getBlock('id6').fields.TOUCHINGOBJECTMENU.value, 'name1'); | |
t.equals(b.getBlock('id7').fields.CLONE_OPTION.value, 'name1'); | |
b.updateAssetName('name1', 'name2', 'sprite'); | |
t.equals(b.getBlock('id1').fields.TOWARDS.value, 'name2'); | |
t.equals(b.getBlock('id2').fields.TO.value, 'name2'); | |
t.equals(b.getBlock('id3').fields.OBJECT.value, 'name2'); | |
t.equals(b.getBlock('id4').fields.VIDEOONMENU2.value, 'name2'); | |
t.equals(b.getBlock('id5').fields.DISTANCETOMENU.value, 'name2'); | |
t.equals(b.getBlock('id6').fields.TOUCHINGOBJECTMENU.value, 'name2'); | |
t.equals(b.getBlock('id7').fields.CLONE_OPTION.value, 'name2'); | |
t.end(); | |
}); | |
test('updateAssetName function updates name according to asset type', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
fields: { | |
SOUND_MENU: { | |
name: 'SOUND_MENU', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
COSTUME: { | |
name: 'COSTUME', | |
value: 'name1' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.SOUND_MENU.value, 'name1'); | |
t.equals(b.getBlock('id2').fields.COSTUME.value, 'name1'); | |
b.updateAssetName('name1', 'name2', 'sound'); | |
// only sound should get renamed | |
t.equals(b.getBlock('id1').fields.SOUND_MENU.value, 'name2'); | |
t.equals(b.getBlock('id2').fields.COSTUME.value, 'name1'); | |
t.end(); | |
}); | |
test('updateAssetName only updates given name', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
fields: { | |
COSTUME: { | |
name: 'COSTUME', | |
value: 'name1' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
COSTUME: { | |
name: 'COSTUME', | |
value: 'foo' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.COSTUME.value, 'name1'); | |
t.equals(b.getBlock('id2').fields.COSTUME.value, 'foo'); | |
b.updateAssetName('name1', 'name2', 'costume'); | |
t.equals(b.getBlock('id1').fields.COSTUME.value, 'name2'); | |
t.equals(b.getBlock('id2').fields.COSTUME.value, 'foo'); | |
t.end(); | |
}); | |
test('updateAssetName doesn\'t update name if name isn\'t being used', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
fields: { | |
BACKDROP: { | |
name: 'BACKDROP', | |
value: 'foo' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.BACKDROP.value, 'foo'); | |
b.updateAssetName('name1', 'name2', 'backdrop'); | |
t.equals(b.getBlock('id1').fields.BACKDROP.value, 'foo'); | |
t.end(); | |
}); | |
test('updateSensingOfReference renames variables in sensing_of block', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
opcode: 'sensing_of', | |
fields: { | |
PROPERTY: { | |
name: 'PROPERTY', | |
value: 'foo' | |
} | |
}, | |
inputs: { | |
OBJECT: { | |
name: 'OBJECT', | |
block: 'id2', | |
shadow: 'id2' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
OBJECT: { | |
name: 'OBJECT', | |
value: '_stage_' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
b.updateSensingOfReference('foo', 'bar', '_stage_'); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'bar'); | |
t.end(); | |
}); | |
test('updateSensingOfReference doesn\'t rename if block is inserted', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
opcode: 'sensing_of', | |
fields: { | |
PROPERTY: { | |
name: 'PROPERTY', | |
value: 'foo' | |
} | |
}, | |
inputs: { | |
OBJECT: { | |
name: 'OBJECT', | |
block: 'id3', | |
shadow: 'id2' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
OBJECT: { | |
name: 'OBJECT', | |
value: '_stage_' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id3', | |
opcode: 'answer' | |
}); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
b.updateSensingOfReference('foo', 'bar', '_stage_'); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
t.end(); | |
}); | |
test('updateSensingOfReference doesn\'t rename if name is not being used', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
opcode: 'sensing_of', | |
fields: { | |
PROPERTY: { | |
name: 'PROPERTY', | |
value: 'foo' | |
} | |
}, | |
inputs: { | |
OBJECT: { | |
name: 'OBJECT', | |
block: 'id2', | |
shadow: 'id2' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
OBJECT: { | |
name: 'OBJECT', | |
value: '_stage_' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
b.updateSensingOfReference('meow', 'meow2', '_stage_'); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
t.end(); | |
}); | |
test('updateSensingOfReference doesn\'t rename other targets\' variables', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'id1', | |
opcode: 'sensing_of', | |
fields: { | |
PROPERTY: { | |
name: 'PROPERTY', | |
value: 'foo' | |
} | |
}, | |
inputs: { | |
OBJECT: { | |
name: 'OBJECT', | |
block: 'id2', | |
shadow: 'id2' | |
} | |
} | |
}); | |
b.createBlock({ | |
id: 'id2', | |
fields: { | |
OBJECT: { | |
name: 'OBJECT', | |
value: '_stage_' | |
} | |
} | |
}); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
b.updateSensingOfReference('foo', 'bar', 'Cat'); | |
t.equals(b.getBlock('id1').fields.PROPERTY.value, 'foo'); | |
t.end(); | |
}); | |
test('updateTargetSpecificBlocks changes sprite clicked hat to stage clicked for stage', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock({ | |
id: 'originallySpriteClicked', | |
opcode: 'event_whenthisspriteclicked' | |
}); | |
b.createBlock({ | |
id: 'originallyStageClicked', | |
opcode: 'event_whenstageclicked' | |
}); | |
// originallySpriteClicked does not update when on a non-stage target | |
b.updateTargetSpecificBlocks(false /* isStage */); | |
t.equals(b.getBlock('originallySpriteClicked').opcode, 'event_whenthisspriteclicked'); | |
// originallySpriteClicked does update when on a stage target | |
b.updateTargetSpecificBlocks(true /* isStage */); | |
t.equals(b.getBlock('originallySpriteClicked').opcode, 'event_whenstageclicked'); | |
// originallyStageClicked does not update when on a stage target | |
b.updateTargetSpecificBlocks(true /* isStage */); | |
t.equals(b.getBlock('originallyStageClicked').opcode, 'event_whenstageclicked'); | |
// originallyStageClicked does update when on a non-stage target | |
b.updateTargetSpecificBlocks(false/* isStage */); | |
t.equals(b.getBlock('originallyStageClicked').opcode, 'event_whenthisspriteclicked'); | |
t.end(); | |
}); | |
test('getAllVariableAndListReferences returns an empty map references when variable blocks do not exist', t => { | |
const b = new Blocks(new Runtime()); | |
t.equal(Object.keys(b.getAllVariableAndListReferences()).length, 0); | |
t.end(); | |
}); | |
test('getAllVariableAndListReferences returns references when variable blocks exist', t => { | |
const b = new Blocks(new Runtime()); | |
let varListRefs = b.getAllVariableAndListReferences(); | |
t.equal(Object.keys(varListRefs).length, 0); | |
b.createBlock(adapter(events.mockVariableBlock)[0]); | |
b.createBlock(adapter(events.mockListBlock)[0]); | |
varListRefs = b.getAllVariableAndListReferences(); | |
t.equal(Object.keys(varListRefs).length, 2); | |
t.equal(Array.isArray(varListRefs['mock var id']), true); | |
t.equal(varListRefs['mock var id'].length, 1); | |
t.equal(varListRefs['mock var id'][0].type, Variable.SCALAR_TYPE); | |
t.equal(varListRefs['mock var id'][0].referencingField.value, 'a mock variable'); | |
t.equal(Array.isArray(varListRefs['mock list id']), true); | |
t.equal(varListRefs['mock list id'].length, 1); | |
t.equal(varListRefs['mock list id'][0].type, Variable.LIST_TYPE); | |
t.equal(varListRefs['mock list id'][0].referencingField.value, 'a mock list'); | |
t.end(); | |
}); | |
test('getAllVariableAndListReferences does not return broadcast blocks if the flag is left out', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock(adapter(events.mockBroadcastBlock)[0]); | |
b.createBlock(adapter(events.mockBroadcastBlock)[1]); | |
t.equal(Object.keys(b.getAllVariableAndListReferences()).length, 0); | |
t.end(); | |
}); | |
test('getAllVariableAndListReferences returns broadcast when we tell it to', t => { | |
const b = new Blocks(new Runtime()); | |
b.createBlock(adapter(events.mockVariableBlock)[0]); | |
// Make the broadcast block and its shadow (which includes the actual broadcast field). | |
b.createBlock(adapter(events.mockBroadcastBlock)[0]); | |
b.createBlock(adapter(events.mockBroadcastBlock)[1]); | |
const varListRefs = b.getAllVariableAndListReferences(null, true); | |
t.equal(Object.keys(varListRefs).length, 2); | |
t.equal(Array.isArray(varListRefs['mock var id']), true); | |
t.equal(varListRefs['mock var id'].length, 1); | |
t.equal(varListRefs['mock var id'][0].type, Variable.SCALAR_TYPE); | |
t.equal(varListRefs['mock var id'][0].referencingField.value, 'a mock variable'); | |
t.equal(Array.isArray(varListRefs['mock broadcast message id']), true); | |
t.equal(varListRefs['mock broadcast message id'].length, 1); | |
t.equal(varListRefs['mock broadcast message id'][0].type, Variable.BROADCAST_MESSAGE_TYPE); | |
t.equal(varListRefs['mock broadcast message id'][0].referencingField.value, 'my message'); | |
t.end(); | |
}); | |