2018-04-09 18:34:46 -04:00
|
|
|
const test = require('tap').test;
|
|
|
|
|
|
|
|
const ArgumentType = require('../../src/extension-support/argument-type');
|
|
|
|
const BlockType = require('../../src/extension-support/block-type');
|
|
|
|
const Runtime = require('../../src/engine/runtime');
|
|
|
|
const ScratchBlocksConstants = require('../../src/engine/scratch-blocks-constants');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @type {ExtensionMetadata}
|
|
|
|
*/
|
|
|
|
const testExtensionInfo = {
|
|
|
|
id: 'test',
|
|
|
|
name: 'fake test extension',
|
|
|
|
blocks: [
|
2019-04-01 20:55:07 -04:00
|
|
|
{
|
2019-04-03 01:44:23 -04:00
|
|
|
func: 'MAKE_A_VARIABLE',
|
2019-04-01 20:55:07 -04:00
|
|
|
blockType: BlockType.BUTTON,
|
|
|
|
text: 'this is a button'
|
|
|
|
},
|
2018-04-09 18:34:46 -04:00
|
|
|
{
|
|
|
|
opcode: 'reporter',
|
|
|
|
blockType: BlockType.REPORTER,
|
|
|
|
text: 'simple text'
|
|
|
|
},
|
|
|
|
'---', // separator between groups of blocks in an extension
|
|
|
|
{
|
|
|
|
opcode: 'command',
|
|
|
|
blockType: BlockType.COMMAND,
|
|
|
|
text: 'text with [ARG]',
|
|
|
|
arguments: {
|
|
|
|
ARG: {
|
|
|
|
type: ArgumentType.STRING
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
opcode: 'ifElse',
|
|
|
|
blockType: BlockType.CONDITIONAL,
|
|
|
|
branchCount: 2,
|
|
|
|
text: [
|
|
|
|
'test if [THING] is spiffy and if so then',
|
|
|
|
'or elsewise'
|
|
|
|
],
|
|
|
|
arguments: {
|
|
|
|
THING: {
|
|
|
|
type: ArgumentType.BOOLEAN
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
opcode: 'loop',
|
|
|
|
blockType: BlockType.LOOP, // implied branchCount of 1 unless otherwise stated
|
|
|
|
isTerminal: true,
|
|
|
|
text: [
|
|
|
|
'loopty [MANY] loops'
|
|
|
|
],
|
|
|
|
arguments: {
|
|
|
|
MANY: {
|
|
|
|
type: ArgumentType.NUMBER
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
2019-04-01 20:55:07 -04:00
|
|
|
const testButton = function (t, button) {
|
|
|
|
t.same(button.json, null); // should be null or undefined
|
2019-04-03 01:44:23 -04:00
|
|
|
t.equal(button.xml, '<button text="this is a button" callbackKey="MAKE_A_VARIABLE"></button>');
|
2019-04-01 20:55:07 -04:00
|
|
|
};
|
|
|
|
|
2018-04-09 18:34:46 -04:00
|
|
|
const testReporter = function (t, reporter) {
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(reporter.json.type, 'test_reporter');
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(reporter.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_ROUND);
|
|
|
|
t.equal(reporter.json.output, 'String');
|
|
|
|
t.notOk(reporter.json.hasOwnProperty('previousStatement'));
|
|
|
|
t.notOk(reporter.json.hasOwnProperty('nextStatement'));
|
|
|
|
t.equal(reporter.json.message0, 'simple text');
|
|
|
|
t.notOk(reporter.json.hasOwnProperty('message1'));
|
|
|
|
t.notOk(reporter.json.hasOwnProperty('args0'));
|
|
|
|
t.notOk(reporter.json.hasOwnProperty('args1'));
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(reporter.xml, '<block type="test_reporter"></block>');
|
2018-04-09 18:34:46 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const testSeparator = function (t, separator) {
|
2019-04-01 20:55:07 -04:00
|
|
|
t.same(separator.json, null); // should be null or undefined
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(separator.xml, '<sep gap="36"/>');
|
|
|
|
};
|
|
|
|
|
|
|
|
const testCommand = function (t, command) {
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(command.json.type, 'test_command');
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(command.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
|
|
|
|
t.assert(command.json.hasOwnProperty('previousStatement'));
|
|
|
|
t.assert(command.json.hasOwnProperty('nextStatement'));
|
|
|
|
t.equal(command.json.message0, 'text with %1');
|
|
|
|
t.notOk(command.json.hasOwnProperty('message1'));
|
|
|
|
t.strictSame(command.json.args0[0], {
|
|
|
|
type: 'input_value',
|
|
|
|
name: 'ARG'
|
|
|
|
});
|
|
|
|
t.notOk(command.json.hasOwnProperty('args1'));
|
|
|
|
t.equal(command.xml,
|
2018-04-20 10:29:29 -04:00
|
|
|
'<block type="test_command"><value name="ARG"><shadow type="text"><field name="TEXT">' +
|
2018-04-09 18:34:46 -04:00
|
|
|
'</field></shadow></value></block>');
|
|
|
|
};
|
|
|
|
|
|
|
|
const testConditional = function (t, conditional) {
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(conditional.json.type, 'test_ifElse');
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(conditional.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
|
|
|
|
t.ok(conditional.json.hasOwnProperty('previousStatement'));
|
|
|
|
t.ok(conditional.json.hasOwnProperty('nextStatement'));
|
|
|
|
t.equal(conditional.json.message0, 'test if %1 is spiffy and if so then');
|
|
|
|
t.equal(conditional.json.message1, '%1'); // placeholder for substack #1
|
|
|
|
t.equal(conditional.json.message2, 'or elsewise');
|
|
|
|
t.equal(conditional.json.message3, '%1'); // placeholder for substack #2
|
|
|
|
t.notOk(conditional.json.hasOwnProperty('message4'));
|
|
|
|
t.strictSame(conditional.json.args0[0], {
|
|
|
|
type: 'input_value',
|
|
|
|
name: 'THING',
|
|
|
|
check: 'Boolean'
|
|
|
|
});
|
|
|
|
t.strictSame(conditional.json.args1[0], {
|
|
|
|
type: 'input_statement',
|
|
|
|
name: 'SUBSTACK'
|
|
|
|
});
|
|
|
|
t.notOk(conditional.json.hasOwnProperty(conditional.json.args2));
|
|
|
|
t.strictSame(conditional.json.args3[0], {
|
|
|
|
type: 'input_statement',
|
|
|
|
name: 'SUBSTACK2'
|
|
|
|
});
|
|
|
|
t.notOk(conditional.json.hasOwnProperty('args4'));
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(conditional.xml, '<block type="test_ifElse"><value name="THING"></value></block>');
|
2018-04-09 18:34:46 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const testLoop = function (t, loop) {
|
2018-04-20 10:29:29 -04:00
|
|
|
t.equal(loop.json.type, 'test_loop');
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(loop.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
|
|
|
|
t.ok(loop.json.hasOwnProperty('previousStatement'));
|
|
|
|
t.notOk(loop.json.hasOwnProperty('nextStatement')); // isTerminal is set on this block
|
|
|
|
t.equal(loop.json.message0, 'loopty %1 loops');
|
|
|
|
t.equal(loop.json.message1, '%1'); // placeholder for substack
|
|
|
|
t.equal(loop.json.message2, '%1'); // placeholder for loop arrow
|
|
|
|
t.notOk(loop.json.hasOwnProperty('message3'));
|
|
|
|
t.strictSame(loop.json.args0[0], {
|
|
|
|
type: 'input_value',
|
|
|
|
name: 'MANY'
|
|
|
|
});
|
|
|
|
t.strictSame(loop.json.args1[0], {
|
|
|
|
type: 'input_statement',
|
|
|
|
name: 'SUBSTACK'
|
|
|
|
});
|
|
|
|
t.equal(loop.json.lastDummyAlign2, 'RIGHT'); // move loop arrow to right side
|
|
|
|
t.equal(loop.json.args2[0].type, 'field_image');
|
|
|
|
t.equal(loop.json.args2[0].flip_rtl, true);
|
|
|
|
t.notOk(loop.json.hasOwnProperty('args3'));
|
|
|
|
t.equal(loop.xml,
|
2018-04-20 10:29:29 -04:00
|
|
|
'<block type="test_loop"><value name="MANY"><shadow type="math_number"><field name="NUM">' +
|
2018-04-09 18:34:46 -04:00
|
|
|
'</field></shadow></value></block>');
|
|
|
|
};
|
|
|
|
|
|
|
|
test('registerExtensionPrimitives', t => {
|
|
|
|
const runtime = new Runtime();
|
|
|
|
|
2019-04-18 12:45:22 -04:00
|
|
|
runtime.on(Runtime.EXTENSION_ADDED, categoryInfo => {
|
|
|
|
const blocksInfo = categoryInfo.blocks;
|
2018-04-09 18:34:46 -04:00
|
|
|
t.equal(blocksInfo.length, testExtensionInfo.blocks.length);
|
|
|
|
|
2019-04-03 14:02:54 -04:00
|
|
|
blocksInfo.forEach(blockInfo => {
|
|
|
|
// `true` here means "either an object or a non-empty string but definitely not null or undefined"
|
|
|
|
t.true(blockInfo.info, 'Every block and pseudo-block must have a non-empty "info" field');
|
|
|
|
});
|
|
|
|
|
2018-04-09 18:34:46 -04:00
|
|
|
// Note that this also implicitly tests that block order is preserved
|
2019-04-01 20:55:07 -04:00
|
|
|
const [button, reporter, separator, command, conditional, loop] = blocksInfo;
|
2018-04-09 18:34:46 -04:00
|
|
|
|
2019-04-01 20:55:07 -04:00
|
|
|
testButton(t, button);
|
2018-04-09 18:34:46 -04:00
|
|
|
testReporter(t, reporter);
|
|
|
|
testSeparator(t, separator);
|
|
|
|
testCommand(t, command);
|
|
|
|
testConditional(t, conditional);
|
|
|
|
testLoop(t, loop);
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
|
|
|
runtime._registerExtensionPrimitives(testExtensionInfo);
|
|
|
|
});
|