mirror of
https://github.com/google/blockly.git
synced 2026-01-17 05:47:10 +01:00
* fix(build): Have buildShims clean up up after itself
We need to create a build/package.json file to allow node.js to
load build/src/core/blockly.js and the other chunk entry points
as ES modules (it forcibly assumes .js means CJS even if one is
trying to import, unless package.json says {"type": "module"}),
but this interferes with scripts/migration/js2ts doing a
require('build/deps.js'), which is _not_ an ES module.
Specific error message was:
/Users/cpcallen/src/blockly/scripts/migration/js2ts:56
require(path.resolve(__dirname, '../../build/deps.js'));
^
Error [ERR_REQUIRE_ESM]: require() of ES Module
/Users/cpcallen/src/blockly/build/deps.js from /Users/cpcallen/src/blockly/scripts/migration/js2ts
not supported.
deps.js is treated as an ES module file as it is a .js file whose
nearest parent package.json contains "type": "module" which
declares all .js files in that package scope as ES modules.
Instead rename deps.js to end in .cjs, change the requiring code
to use dynamic import() which is available in all CommonJS
modules, or change "type": "module" to "type": "commonjs" in
/Users/cpcallen/src/blockly/build/package.json to treat all .js
files as CommonJS (using .mjs for all ES modules instead).
at Object.<anonymous> (/Users/cpcallen/src/blockly/scripts/migration/js2ts:56:1) {
code: 'ERR_REQUIRE_ESM'
}
* chore(tests): Reorder to put interesting script nearer top of file
* chore(tests): Add missing imports of closure/goog/goog.js
These modules were depending on being loaded via the
debug module loader, which cannot be used without first loading
base.js as a script, and thereby defining goog.declareModuleId
as a side effect—but if they are to be loaded via direct import
statements then they need to actually import their own
dependencies.
This is a temporary measure as soon the goog.declareMouleId
calls can themselves be deleted.
* refactor(tests): Use import instead of bootstrap to load Blockly
* chores(build): Stop generating deps.mocha.js
This file was only needed by tests/mocha/index.html's use of
the debug module loader (via bootstrap.js), which has now been
removed.
* chore(tests): Remove unneeded goog.declareModuleId calls
These were only needed because these modules were previously
being loaded by goog.require and/or goog.bootstrap.
* chores(tests): Remove dead code
We are fully committed to proper modules now.
1633 lines
57 KiB
JavaScript
1633 lines
57 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import {assertVariableValues} from './variables.js';
|
|
import {assertWarnings} from './warnings.js';
|
|
import * as eventUtils from '../../../build/src/core/events/utils.js';
|
|
import {workspaceTeardown} from './setup_teardown.js';
|
|
|
|
export function testAWorkspace() {
|
|
setup(function () {
|
|
Blockly.defineBlocksWithJsonArray([
|
|
{
|
|
'type': 'get_var_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'field_variable',
|
|
'name': 'VAR',
|
|
'variableTypes': ['', 'type1', 'type2'],
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
});
|
|
|
|
teardown(function () {
|
|
// Clear Blockly.Event state.
|
|
eventUtils.setGroup(false);
|
|
while (!eventUtils.isEnabled()) {
|
|
eventUtils.enable();
|
|
}
|
|
sinon.restore();
|
|
});
|
|
|
|
function assertBlockVarModelName(workspace, blockIndex, name) {
|
|
const block = workspace.getTopBlocks(false)[blockIndex];
|
|
chai.assert.exists(block, 'Block at topBlocks[' + blockIndex + ']');
|
|
const varModel = block.getVarModels()[0];
|
|
chai.assert.exists(
|
|
varModel,
|
|
'VariableModel for block at topBlocks[' + blockIndex + ']',
|
|
);
|
|
const blockVarName = varModel.name;
|
|
chai.assert.equal(
|
|
blockVarName,
|
|
name,
|
|
'VariableModel name for block at topBlocks[' + blockIndex + ']',
|
|
);
|
|
}
|
|
|
|
function createVarBlocksNoEvents(workspace, ids) {
|
|
const blocks = [];
|
|
// Turn off events to avoid testing XML at the same time.
|
|
eventUtils.disable();
|
|
for (let i = 0, id; (id = ids[i]); i++) {
|
|
const block = new Blockly.Block(workspace, 'get_var_block');
|
|
block.inputList[0].fieldRow[0].setValue(id);
|
|
blocks.push(block);
|
|
}
|
|
eventUtils.enable();
|
|
return blocks;
|
|
}
|
|
|
|
suite('clear', function () {
|
|
test('Trivial', function () {
|
|
sinon.stub(eventUtils.TEST_ONLY, 'setGroupInternal').returns(null);
|
|
this.workspace.createVariable('name1', 'type1', 'id1');
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
this.workspace.newBlock('');
|
|
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
const varMapLength = this.workspace.getVariableMap().variableMap.size;
|
|
chai.assert.equal(varMapLength, 0);
|
|
});
|
|
|
|
test('No variables', function () {
|
|
sinon.stub(eventUtils.TEST_ONLY, 'setGroupInternal').returns(null);
|
|
this.workspace.newBlock('');
|
|
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
const varMapLength = this.workspace.getVariableMap().variableMap.size;
|
|
chai.assert.equal(varMapLength, 0);
|
|
});
|
|
});
|
|
|
|
suite('deleteVariable', function () {
|
|
setup(function () {
|
|
// Create two variables of different types.
|
|
this.var1 = this.workspace.createVariable('name1', 'type1', 'id1');
|
|
this.var2 = this.workspace.createVariable('name2', 'type2', 'id2');
|
|
// Create blocks to refer to both of them.
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id1', 'id2']);
|
|
});
|
|
|
|
test('deleteVariableById(id2) one usage', function () {
|
|
// Deleting variable one usage should not trigger confirm dialog.
|
|
const stub = sinon
|
|
.stub(Blockly.dialog.TEST_ONLY, 'confirmInternal')
|
|
.callsArgWith(1, true);
|
|
this.workspace.deleteVariableById('id2');
|
|
|
|
sinon.assert.notCalled(stub);
|
|
const variable = this.workspace.getVariableById('id2');
|
|
chai.assert.isNull(variable);
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
});
|
|
|
|
test('deleteVariableById(id1) multiple usages confirm', function () {
|
|
// Deleting variable with multiple usages triggers confirm dialog.
|
|
const stub = sinon
|
|
.stub(Blockly.dialog.TEST_ONLY, 'confirmInternal')
|
|
.callsArgWith(1, true);
|
|
this.workspace.deleteVariableById('id1');
|
|
|
|
sinon.assert.calledOnce(stub);
|
|
const variable = this.workspace.getVariableById('id1');
|
|
chai.assert.isNull(variable);
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
});
|
|
|
|
test('deleteVariableById(id1) multiple usages cancel', function () {
|
|
// Deleting variable with multiple usages triggers confirm dialog.
|
|
const stub = sinon
|
|
.stub(Blockly.dialog.TEST_ONLY, 'confirmInternal')
|
|
.callsArgWith(1, false);
|
|
this.workspace.deleteVariableById('id1');
|
|
|
|
sinon.assert.calledOnce(stub);
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertBlockVarModelName(this.workspace, 1, 'name1');
|
|
assertBlockVarModelName(this.workspace, 2, 'name2');
|
|
});
|
|
});
|
|
|
|
suite('renameVariableById', function () {
|
|
setup(function () {
|
|
this.workspace.createVariable('name1', 'type1', 'id1');
|
|
});
|
|
|
|
test('No references rename to name2', function () {
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
// Renaming should not have created a new variable.
|
|
chai.assert.equal(this.workspace.getAllVariables().length, 1);
|
|
});
|
|
|
|
test('Reference exists rename to name2', function () {
|
|
createVarBlocksNoEvents(this.workspace, ['id1']);
|
|
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
// Renaming should not have created a new variable.
|
|
chai.assert.equal(this.workspace.getAllVariables().length, 1);
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
});
|
|
|
|
test('Reference exists different capitalization rename to Name1', function () {
|
|
createVarBlocksNoEvents(this.workspace, ['id1']);
|
|
|
|
this.workspace.renameVariableById('id1', 'Name1');
|
|
assertVariableValues(this.workspace, 'Name1', 'type1', 'id1');
|
|
// Renaming should not have created a new variable.
|
|
chai.assert.equal(this.workspace.getAllVariables().length, 1);
|
|
assertBlockVarModelName(this.workspace, 0, 'Name1');
|
|
});
|
|
|
|
suite('Two variables rename overlap', function () {
|
|
test('Same type rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
// The second variable should remain unchanged.
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
// The first variable should have been deleted.
|
|
const variable = this.workspace.getVariableById('id1');
|
|
chai.assert.isNull(variable);
|
|
// There should only be one variable left.
|
|
chai.assert.equal(this.workspace.getAllVariables().length, 1);
|
|
|
|
// Both blocks should now reference variable with name2.
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
});
|
|
|
|
test('Different type rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
// Variables with different type are allowed to have the same name.
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
// Both blocks should now reference variable with name2.
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
});
|
|
|
|
test('Same type different capitalization rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
// The second variable should be updated.
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id2');
|
|
// The first variable should have been deleted.
|
|
const variable = this.workspace.getVariableById('id1');
|
|
chai.assert.isNull(variable);
|
|
// There should only be one variable left.
|
|
chai.assert.equal(this.workspace.getAllVariables().length, 1);
|
|
|
|
// Both blocks should now reference variable with Name2.
|
|
assertBlockVarModelName(this.workspace, 0, 'Name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'Name2');
|
|
});
|
|
|
|
test('Different type different capitalization rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
// Variables with different type are allowed to have the same name.
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id1');
|
|
// Second variable should remain unchanged.
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
// Only first block should use new capitalization.
|
|
assertBlockVarModelName(this.workspace, 0, 'Name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('getTopBlocks(ordered=true)', function () {
|
|
test('Empty workspace', function () {
|
|
chai.assert.equal(this.workspace.getTopBlocks(true).length, 0);
|
|
});
|
|
|
|
test('Flat workspace one block', function () {
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getTopBlocks(true).length, 1);
|
|
});
|
|
|
|
test('Flat workspace one block after dispose', function () {
|
|
const blockA = this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
blockA.dispose();
|
|
chai.assert.equal(this.workspace.getTopBlocks(true).length, 1);
|
|
});
|
|
|
|
test('Flat workspace two blocks', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getTopBlocks(true).length, 2);
|
|
});
|
|
|
|
test('Clear', function () {
|
|
this.workspace.clear();
|
|
chai.assert.equal(
|
|
this.workspace.getTopBlocks(true).length,
|
|
0,
|
|
'Clear empty workspace',
|
|
);
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getTopBlocks(true).length, 0);
|
|
});
|
|
});
|
|
|
|
suite('getTopBlocks(ordered=false)', function () {
|
|
test('Empty workspace', function () {
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
});
|
|
|
|
test('Flat workspace one block', function () {
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 1);
|
|
});
|
|
|
|
test('Flat workspace one block after dispose', function () {
|
|
const blockA = this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
blockA.dispose();
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 1);
|
|
});
|
|
|
|
test('Flat workspace two blocks', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 2);
|
|
});
|
|
|
|
test('Clear empty workspace', function () {
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
});
|
|
|
|
test('Clear non-empty workspace', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
});
|
|
});
|
|
|
|
suite('getAllBlocks', function () {
|
|
test('Empty workspace', function () {
|
|
chai.assert.equal(this.workspace.getAllBlocks(true).length, 0);
|
|
});
|
|
|
|
test('Flat workspace one block', function () {
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getAllBlocks(true).length, 1);
|
|
});
|
|
|
|
test('Flat workspace one block after dispose', function () {
|
|
const blockA = this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
blockA.dispose();
|
|
chai.assert.equal(this.workspace.getAllBlocks(true).length, 1);
|
|
});
|
|
|
|
test('Flat workspace two blocks', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
chai.assert.equal(this.workspace.getAllBlocks(true).length, 2);
|
|
});
|
|
|
|
test('Clear', function () {
|
|
this.workspace.clear();
|
|
chai.assert.equal(
|
|
this.workspace.getAllBlocks(true).length,
|
|
0,
|
|
'Clear empty workspace',
|
|
);
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.getAllBlocks(true).length, 0);
|
|
});
|
|
});
|
|
|
|
suite('remainingCapacity', function () {
|
|
setup(function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
});
|
|
|
|
test('No block limit', function () {
|
|
chai.assert.equal(this.workspace.remainingCapacity(), Infinity);
|
|
});
|
|
|
|
test('Under block limit', function () {
|
|
this.workspace.options.maxBlocks = 3;
|
|
chai.assert.equal(this.workspace.remainingCapacity(), 1);
|
|
this.workspace.options.maxBlocks = 4;
|
|
chai.assert.equal(this.workspace.remainingCapacity(), 2);
|
|
});
|
|
|
|
test('At block limit', function () {
|
|
this.workspace.options.maxBlocks = 2;
|
|
chai.assert.equal(this.workspace.remainingCapacity(), 0);
|
|
});
|
|
|
|
test('At block limit of 0 after clear', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
this.workspace.clear();
|
|
chai.assert.equal(this.workspace.remainingCapacity(), 0);
|
|
});
|
|
|
|
test('Over block limit', function () {
|
|
this.workspace.options.maxBlocks = 1;
|
|
chai.assert.equal(this.workspace.remainingCapacity(), -1);
|
|
});
|
|
|
|
test('Over block limit of 0', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
chai.assert.equal(this.workspace.remainingCapacity(), -2);
|
|
});
|
|
});
|
|
|
|
suite('remainingCapacityOfType', function () {
|
|
setup(function () {
|
|
this.workspace.newBlock('get_var_block');
|
|
this.workspace.newBlock('get_var_block');
|
|
this.workspace.options.maxInstances = {};
|
|
});
|
|
|
|
test('No instance limit', function () {
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
Infinity,
|
|
);
|
|
});
|
|
|
|
test('Under instance limit', function () {
|
|
this.workspace.options.maxInstances['get_var_block'] = 3;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
1,
|
|
'With maxInstances limit 3',
|
|
);
|
|
this.workspace.options.maxInstances['get_var_block'] = 4;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
2,
|
|
'With maxInstances limit 4',
|
|
);
|
|
});
|
|
|
|
test('Under instance limit with multiple block types', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.options.maxInstances['get_var_block'] = 3;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
1,
|
|
'With maxInstances limit 3',
|
|
);
|
|
this.workspace.options.maxInstances['get_var_block'] = 4;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
2,
|
|
'With maxInstances limit 4',
|
|
);
|
|
});
|
|
|
|
test('At instance limit', function () {
|
|
this.workspace.options.maxInstances['get_var_block'] = 2;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
0,
|
|
'With maxInstances limit 2',
|
|
);
|
|
});
|
|
|
|
test('At instance limit of 0 after clear', function () {
|
|
this.workspace.clear();
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
0,
|
|
);
|
|
});
|
|
|
|
test('At instance limit with multiple block types', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.options.maxInstances['get_var_block'] = 2;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
0,
|
|
'With maxInstances limit 2',
|
|
);
|
|
});
|
|
|
|
test('At instance limit of 0 with multiple block types', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
this.workspace.clear();
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
0,
|
|
);
|
|
});
|
|
|
|
test('Over instance limit', function () {
|
|
this.workspace.options.maxInstances['get_var_block'] = 1;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
-1,
|
|
'With maxInstances limit 1',
|
|
);
|
|
});
|
|
|
|
test('Over instance limit of 0', function () {
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
-2,
|
|
'With maxInstances limit 0',
|
|
);
|
|
});
|
|
|
|
test('Over instance limit with multiple block types', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.options.maxInstances['get_var_block'] = 1;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
-1,
|
|
'With maxInstances limit 1',
|
|
);
|
|
});
|
|
|
|
test('Over instance limit of 0 with multiple block types', function () {
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.newBlock('');
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
chai.assert.equal(
|
|
this.workspace.remainingCapacityOfType('get_var_block'),
|
|
-2,
|
|
'With maxInstances limit 0',
|
|
);
|
|
});
|
|
});
|
|
|
|
suite('isCapacityAvailable', function () {
|
|
setup(function () {
|
|
this.workspace.newBlock('get_var_block');
|
|
this.workspace.newBlock('get_var_block');
|
|
this.workspace.options.maxInstances = {};
|
|
});
|
|
|
|
test('Under block limit and no instance limit', function () {
|
|
this.workspace.options.maxBlocks = 3;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isTrue(this.workspace.isCapacityAvailable(typeCountsMap));
|
|
});
|
|
|
|
test('At block limit and no instance limit', function () {
|
|
this.workspace.options.maxBlocks = 2;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(this.workspace.isCapacityAvailable(typeCountsMap));
|
|
});
|
|
|
|
test('Over block limit of 0 and no instance limit', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(this.workspace.isCapacityAvailable(typeCountsMap));
|
|
});
|
|
|
|
test('Over block limit but under instance limit', function () {
|
|
this.workspace.options.maxBlocks = 1;
|
|
this.workspace.options.maxInstances['get_var_block'] = 3;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 1 and maxInstances limit 3',
|
|
);
|
|
});
|
|
|
|
test('Over block limit of 0 but under instance limit', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
this.workspace.options.maxInstances['get_var_block'] = 3;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 0 and maxInstances limit 3',
|
|
);
|
|
});
|
|
|
|
test('Over block limit but at instance limit', function () {
|
|
this.workspace.options.maxBlocks = 1;
|
|
this.workspace.options.maxInstances['get_var_block'] = 2;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 1 and maxInstances limit 2',
|
|
);
|
|
});
|
|
|
|
test('Over block limit and over instance limit', function () {
|
|
this.workspace.options.maxBlocks = 1;
|
|
this.workspace.options.maxInstances['get_var_block'] = 1;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 1 and maxInstances limit 1',
|
|
);
|
|
});
|
|
|
|
test('Over block limit of 0 and over instance limit', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
this.workspace.options.maxInstances['get_var_block'] = 1;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 0 and maxInstances limit 1',
|
|
);
|
|
});
|
|
|
|
test('Over block limit and over instance limit of 0', function () {
|
|
this.workspace.options.maxBlocks = 1;
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(
|
|
this.workspace.isCapacityAvailable(typeCountsMap),
|
|
'With maxBlocks limit 1 and maxInstances limit 0',
|
|
);
|
|
});
|
|
|
|
test('Over block limit of 0 and over instance limit of 0', function () {
|
|
this.workspace.options.maxBlocks = 0;
|
|
this.workspace.options.maxInstances['get_var_block'] = 0;
|
|
const typeCountsMap = {'get_var_block': 1};
|
|
chai.assert.isFalse(this.workspace.isCapacityAvailable(typeCountsMap));
|
|
});
|
|
});
|
|
|
|
suite('getById', function () {
|
|
setup(function () {
|
|
this.workspaceB = this.workspace.rendered
|
|
? new Blockly.WorkspaceSvg(new Blockly.Options({}))
|
|
: new Blockly.Workspace();
|
|
});
|
|
|
|
teardown(function () {
|
|
workspaceTeardown.call(this, this.workspaceB);
|
|
});
|
|
|
|
test('Trivial', function () {
|
|
chai.assert.equal(
|
|
Blockly.Workspace.getById(this.workspace.id),
|
|
this.workspace,
|
|
'Find workspace',
|
|
);
|
|
chai.assert.equal(
|
|
Blockly.Workspace.getById(this.workspaceB.id),
|
|
this.workspaceB,
|
|
'Find workspaceB',
|
|
);
|
|
});
|
|
|
|
test('Null id', function () {
|
|
chai.assert.isNull(Blockly.Workspace.getById(null));
|
|
});
|
|
|
|
test('Non-existent id', function () {
|
|
chai.assert.isNull(Blockly.Workspace.getById('badId'));
|
|
});
|
|
|
|
test('After dispose', function () {
|
|
this.workspaceB.dispose();
|
|
chai.assert.isNull(Blockly.Workspace.getById(this.workspaceB.id));
|
|
});
|
|
});
|
|
|
|
suite('getBlockById', function () {
|
|
setup(function () {
|
|
this.blockA = this.workspace.newBlock('');
|
|
this.blockB = this.workspace.newBlock('');
|
|
this.workspaceB = this.workspace.rendered
|
|
? new Blockly.WorkspaceSvg(new Blockly.Options({}))
|
|
: new Blockly.Workspace();
|
|
});
|
|
|
|
teardown(function () {
|
|
workspaceTeardown.call(this, this.workspaceB);
|
|
});
|
|
|
|
test('Trivial', function () {
|
|
chai.assert.equal(
|
|
this.workspace.getBlockById(this.blockA.id),
|
|
this.blockA,
|
|
);
|
|
chai.assert.equal(
|
|
this.workspace.getBlockById(this.blockB.id),
|
|
this.blockB,
|
|
);
|
|
});
|
|
|
|
test('Null id', function () {
|
|
chai.assert.isNull(this.workspace.getBlockById(null));
|
|
});
|
|
|
|
test('Non-existent id', function () {
|
|
chai.assert.isNull(this.workspace.getBlockById('badId'));
|
|
});
|
|
|
|
test('After dispose', function () {
|
|
this.blockA.dispose();
|
|
chai.assert.isNull(this.workspace.getBlockById(this.blockA.id));
|
|
chai.assert.equal(
|
|
this.workspace.getBlockById(this.blockB.id),
|
|
this.blockB,
|
|
);
|
|
});
|
|
|
|
test('After clear', function () {
|
|
this.workspace.clear();
|
|
chai.assert.isNull(this.workspace.getBlockById(this.blockA.id));
|
|
chai.assert.isNull(this.workspace.getBlockById(this.blockB.id));
|
|
});
|
|
});
|
|
|
|
suite('Undo/Redo', function () {
|
|
/**
|
|
* Assert that two nodes are equal.
|
|
* @param {!Element} actual the actual node.
|
|
* @param {!Element} expected the expected node.
|
|
*/
|
|
function assertNodesEqual(actual, expected) {
|
|
const actualString = '\n' + Blockly.Xml.domToPrettyText(actual) + '\n';
|
|
const expectedString =
|
|
'\n' + Blockly.Xml.domToPrettyText(expected) + '\n';
|
|
|
|
chai.assert.equal(actual.tagName, expected.tagName);
|
|
for (let i = 0, attr; (attr = expected.attributes[i]); i++) {
|
|
chai.assert.equal(
|
|
actual.getAttribute(attr.name),
|
|
attr.value,
|
|
`expected attribute ${attr.name} on ${actualString} to match ` +
|
|
`${expectedString}`,
|
|
);
|
|
}
|
|
chai.assert.equal(
|
|
actual.childElementCount,
|
|
expected.childElementCount,
|
|
`expected node ${actualString} to have the same children as node ` +
|
|
`${expectedString}`,
|
|
);
|
|
for (let i = 0; i < expected.childElementCount; i++) {
|
|
assertNodesEqual(actual.children[i], expected.children[i]);
|
|
}
|
|
}
|
|
|
|
suite('Undo Delete', function () {
|
|
setup(function () {
|
|
Blockly.defineBlocksWithJsonArray([
|
|
{
|
|
'type': 'stack_block',
|
|
'message0': '',
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
{
|
|
'type': 'row_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_value',
|
|
'name': 'INPUT',
|
|
},
|
|
],
|
|
'output': null,
|
|
},
|
|
{
|
|
'type': 'statement_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_statement',
|
|
'name': 'STATEMENT',
|
|
},
|
|
],
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
teardown(function () {
|
|
delete Blockly.Blocks['stack_block'];
|
|
delete Blockly.Blocks['row_block'];
|
|
delete Blockly.Blocks['statement_block'];
|
|
});
|
|
|
|
function testUndoDelete(xmlText) {
|
|
const xml = Blockly.utils.xml.textToDom(xmlText);
|
|
Blockly.Xml.domToBlock(xml, this.workspace);
|
|
this.workspace.getTopBlocks()[0].dispose(false);
|
|
this.workspace.undo();
|
|
const newXml = Blockly.Xml.workspaceToDom(this.workspace);
|
|
assertNodesEqual(newXml.firstChild, xml);
|
|
}
|
|
|
|
test('Stack', function () {
|
|
testUndoDelete.call(this, '<block type="stack_block" id="1"/>');
|
|
});
|
|
|
|
test('Row', function () {
|
|
testUndoDelete.call(this, '<block type="row_block" id="1"/>');
|
|
});
|
|
|
|
test('Statement', function () {
|
|
testUndoDelete.call(this, '<block type="statement_block" id="1"/>');
|
|
});
|
|
|
|
test('Stack w/ child', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' </next>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
|
|
test('Row w/ child', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
' </value>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
|
|
test('Statement w/ child', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' </statement>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
|
|
test('Stack w/ shadow', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <shadow type="stack_block" id="2"></shadow>' +
|
|
' </next>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
|
|
test('Row w/ shadow', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <shadow type="row_block" id="2"></shadow>' +
|
|
' </value>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
|
|
test('Statement w/ shadow', function () {
|
|
testUndoDelete.call(
|
|
this,
|
|
'<block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <shadow type="stack_block" id="2"></shadow>' +
|
|
' </statement>' +
|
|
'</block>',
|
|
);
|
|
});
|
|
});
|
|
|
|
suite('Undo Connect', function () {
|
|
setup(function () {
|
|
Blockly.defineBlocksWithJsonArray([
|
|
{
|
|
'type': 'stack_block',
|
|
'message0': '',
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
{
|
|
'type': 'row_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_value',
|
|
'name': 'INPUT',
|
|
},
|
|
],
|
|
'output': null,
|
|
},
|
|
{
|
|
'type': 'statement_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_statement',
|
|
'name': 'STATEMENT',
|
|
},
|
|
],
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
teardown(function () {
|
|
delete Blockly.Blocks['stack_block'];
|
|
delete Blockly.Blocks['row_block'];
|
|
delete Blockly.Blocks['statement_block'];
|
|
});
|
|
|
|
function testUndoConnect(xmlText, parentId, childId, func) {
|
|
const xml = Blockly.utils.xml.textToDom(xmlText);
|
|
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
|
|
|
const parent = this.workspace.getBlockById(parentId);
|
|
const child = this.workspace.getBlockById(childId);
|
|
func.call(this, parent, child);
|
|
this.workspace.undo();
|
|
|
|
const newXml = Blockly.Xml.workspaceToDom(this.workspace);
|
|
assertNodesEqual(newXml, xml);
|
|
}
|
|
|
|
test('Stack', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1"></block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.nextConnection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
|
|
test('Row', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1"></block>' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.getInput('INPUT').connection.connect(child.outputConnection);
|
|
});
|
|
});
|
|
|
|
test('Statement', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1"></block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent
|
|
.getInput('STATEMENT')
|
|
.connection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
|
|
test('Stack w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="3"></block>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.nextConnection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
|
|
test('Row w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <block type="row_block" id="3"></block>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.getInput('INPUT').connection.connect(child.outputConnection);
|
|
});
|
|
});
|
|
|
|
test('Statement w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <block type="stack_block" id="3"></block>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent
|
|
.getInput('STATEMENT')
|
|
.connection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
|
|
test('Stack w/ shadow', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <shadow type="stack_block" id="3"></shadow>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.nextConnection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
|
|
test('Row w/ shadow', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <shadow type="row_block" id="3"></shadow>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent.getInput('INPUT').connection.connect(child.outputConnection);
|
|
});
|
|
});
|
|
|
|
test('Statement w/ shadow', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <shadow type="stack_block" id="3"></shadow>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
'</xml>';
|
|
|
|
testUndoConnect.call(this, xml, '1', '2', (parent, child) => {
|
|
parent
|
|
.getInput('STATEMENT')
|
|
.connection.connect(child.previousConnection);
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('Undo Disconnect', function () {
|
|
setup(function () {
|
|
Blockly.defineBlocksWithJsonArray([
|
|
{
|
|
'type': 'stack_block',
|
|
'message0': '',
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
{
|
|
'type': 'row_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_value',
|
|
'name': 'INPUT',
|
|
},
|
|
],
|
|
'output': null,
|
|
},
|
|
{
|
|
'type': 'statement_block',
|
|
'message0': '%1',
|
|
'args0': [
|
|
{
|
|
'type': 'input_statement',
|
|
'name': 'STATEMENT',
|
|
},
|
|
],
|
|
'previousStatement': null,
|
|
'nextStatement': null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
teardown(function () {
|
|
delete Blockly.Blocks['stack_block'];
|
|
delete Blockly.Blocks['row_block'];
|
|
delete Blockly.Blocks['statement_block'];
|
|
});
|
|
|
|
function testUndoDisconnect(xmlText, childId) {
|
|
const xml = Blockly.utils.xml.textToDom(xmlText);
|
|
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
|
|
|
const child = this.workspace.getBlockById(childId);
|
|
if (child.outputConnection) {
|
|
child.outputConnection.disconnect();
|
|
} else {
|
|
child.previousConnection.disconnect();
|
|
}
|
|
this.workspace.undo();
|
|
|
|
const newXml = Blockly.Xml.workspaceToDom(this.workspace);
|
|
assertNodesEqual(newXml, xml);
|
|
}
|
|
|
|
test('Stack', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Row', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Statement', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Stack w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="2">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="3"></block>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Row w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <block type="row_block" id="2">' +
|
|
' <value name="INPUT">' +
|
|
' <block type="row_block" id="3"></block>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Statement w/ child', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <block type="statement_block" id="2">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <block type="stack_block" id="3"></block>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
|
|
test('Stack w/ shadow', function () {
|
|
// TODO: For some reason on next connections shadows are
|
|
// serialized second.
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="stack_block" id="1">' +
|
|
' <next>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' <shadow type="stack_block" id="3"></shadow>' +
|
|
' </next>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
chai.assert.equal(
|
|
this.workspace.getAllBlocks().length,
|
|
2,
|
|
'expected there to only be 2 blocks on the workspace ' +
|
|
'(check for shadows)',
|
|
);
|
|
});
|
|
|
|
test('Row w/ shadow', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="row_block" id="1">' +
|
|
' <value name="INPUT">' +
|
|
' <shadow type="row_block" id="3"></shadow>' +
|
|
' <block type="row_block" id="2"></block>' +
|
|
' </value>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
chai.assert.equal(
|
|
this.workspace.getAllBlocks().length,
|
|
2,
|
|
'expected there to only be 2 blocks on the workspace ' +
|
|
'(check for shadows)',
|
|
);
|
|
});
|
|
|
|
test('Statement w/ shadow', function () {
|
|
const xml =
|
|
'<xml>' +
|
|
' <block type="statement_block" id="1">' +
|
|
' <statement name="STATEMENT">' +
|
|
' <shadow type="stack_block" id="3"></shadow>' +
|
|
' <block type="stack_block" id="2"></block>' +
|
|
' </statement>' +
|
|
' </block>' +
|
|
'</xml>';
|
|
testUndoDisconnect.call(this, xml, '2');
|
|
});
|
|
});
|
|
|
|
suite('Variables', function () {
|
|
function createTwoVarsDifferentTypes(workspace) {
|
|
workspace.createVariable('name1', 'type1', 'id1');
|
|
workspace.createVariable('name2', 'type2', 'id2');
|
|
}
|
|
|
|
suite('createVariable', function () {
|
|
test('Undo only', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
|
|
this.workspace.undo();
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
});
|
|
|
|
test('Undo and redo', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
|
|
this.workspace.undo(true);
|
|
|
|
// Expect that variable 'id2' is recreated
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo();
|
|
this.workspace.undo();
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
this.workspace.undo(true);
|
|
|
|
// Expect that variable 'id1' is recreated
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
});
|
|
});
|
|
|
|
suite('deleteVariableById', function () {
|
|
test('Undo only no usages', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
this.workspace.deleteVariableById('id1');
|
|
this.workspace.deleteVariableById('id2');
|
|
|
|
this.workspace.undo();
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Undo only with usages', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
// Create blocks to refer to both of them.
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.deleteVariableById('id1');
|
|
this.workspace.deleteVariableById('id2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name1');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Reference exists no usages', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
this.workspace.deleteVariableById('id1');
|
|
this.workspace.deleteVariableById('id2');
|
|
|
|
this.workspace.undo();
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
// Expect that both variables are deleted
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
|
|
this.workspace.undo();
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
// Expect that variable 'id2' is recreated
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Reference exists with usages', function () {
|
|
createTwoVarsDifferentTypes(this.workspace);
|
|
// Create blocks to refer to both of them.
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.deleteVariableById('id1');
|
|
this.workspace.deleteVariableById('id2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
// Expect that both variables are deleted
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
chai.assert.isNull(this.workspace.getVariableById('id2'));
|
|
|
|
this.workspace.undo();
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name1');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
// Expect that variable 'id2' is recreated
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Delete same variable twice no usages', function () {
|
|
this.workspace.createVariable('name1', 'type1', 'id1');
|
|
this.workspace.deleteVariableById('id1');
|
|
const workspace = this.workspace;
|
|
assertWarnings(() => {
|
|
workspace.deleteVariableById('id1');
|
|
}, /Can't delete/);
|
|
|
|
// Check the undoStack only recorded one delete event.
|
|
const undoStack = this.workspace.undoStack_;
|
|
chai.assert.equal(undoStack[undoStack.length - 1].type, 'var_delete');
|
|
chai.assert.notEqual(
|
|
undoStack[undoStack.length - 2].type,
|
|
'var_delete',
|
|
);
|
|
|
|
// Undo delete
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
// Redo delete
|
|
this.workspace.undo(true);
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
|
|
// Redo delete, nothing should happen
|
|
this.workspace.undo(true);
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
});
|
|
|
|
test('Delete same variable twice with usages', function () {
|
|
this.workspace.createVariable('name1', 'type1', 'id1');
|
|
createVarBlocksNoEvents(this.workspace, ['id1']);
|
|
this.workspace.deleteVariableById('id1');
|
|
const workspace = this.workspace;
|
|
assertWarnings(() => {
|
|
workspace.deleteVariableById('id1');
|
|
}, /Can't delete/);
|
|
|
|
// Check the undoStack only recorded one delete event.
|
|
const undoStack = this.workspace.undoStack_;
|
|
chai.assert.equal(undoStack[undoStack.length - 1].type, 'var_delete');
|
|
chai.assert.equal(undoStack[undoStack.length - 2].type, 'delete');
|
|
chai.assert.notEqual(
|
|
undoStack[undoStack.length - 3].type,
|
|
'var_delete',
|
|
);
|
|
|
|
// Undo delete
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
// Redo delete
|
|
this.workspace.undo(true);
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
|
|
// Redo delete, nothing should happen
|
|
this.workspace.undo(true);
|
|
chai.assert.equal(this.workspace.getTopBlocks(false).length, 0);
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
});
|
|
});
|
|
|
|
suite('renameVariableById', function () {
|
|
setup(function () {
|
|
this.workspace.createVariable('name1', 'type1', 'id1');
|
|
});
|
|
|
|
test('Reference exists no usages rename to name2', function () {
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
});
|
|
|
|
test('Reference exists with usages rename to name2', function () {
|
|
createVarBlocksNoEvents(this.workspace, ['id1']);
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
this.workspace.undo(true);
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
});
|
|
|
|
test('Reference exists different capitalization no usages rename to Name1', function () {
|
|
this.workspace.renameVariableById('id1', 'Name1');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'Name1', 'type1', 'id1');
|
|
});
|
|
|
|
test('Reference exists different capitalization with usages rename to Name1', function () {
|
|
createVarBlocksNoEvents(this.workspace, ['id1']);
|
|
this.workspace.renameVariableById('id1', 'Name1');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
|
|
this.workspace.undo(true);
|
|
assertBlockVarModelName(this.workspace, 0, 'Name1');
|
|
assertVariableValues(this.workspace, 'Name1', 'type1', 'id1');
|
|
});
|
|
|
|
suite('Two variables rename overlap', function () {
|
|
test('Same type no usages rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
});
|
|
|
|
test('Same type with usages rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
});
|
|
|
|
test('Same type different capitalization no usages rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id2');
|
|
chai.assert.isNull(this.workspace.getVariable('name1'));
|
|
});
|
|
|
|
test('Same type different capitalization with usages rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type1', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
this.workspace.undo();
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id2');
|
|
chai.assert.isNull(this.workspace.getVariableById('id1'));
|
|
assertBlockVarModelName(this.workspace, 0, 'Name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'Name2');
|
|
});
|
|
|
|
test('Different type no usages rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Different type with usages rename variable with id1 to name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.renameVariableById('id1', 'name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'name2', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
});
|
|
|
|
test('Different type different capitalization no usages rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
});
|
|
|
|
test('Different type different capitalization with usages rename variable with id1 to Name2', function () {
|
|
this.workspace.createVariable('name2', 'type2', 'id2');
|
|
createVarBlocksNoEvents(this.workspace, ['id1', 'id2']);
|
|
this.workspace.renameVariableById('id1', 'Name2');
|
|
|
|
this.workspace.undo();
|
|
assertVariableValues(this.workspace, 'name1', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'name1');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
|
|
this.workspace.undo(true);
|
|
assertVariableValues(this.workspace, 'Name2', 'type1', 'id1');
|
|
assertVariableValues(this.workspace, 'name2', 'type2', 'id2');
|
|
assertBlockVarModelName(this.workspace, 0, 'Name2');
|
|
assertBlockVarModelName(this.workspace, 1, 'name2');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|