Files
blockly/tests/mocha/test_helpers/workspace.js
Christopher Allen 6f20ac290d refactor(tests): Use import instead of goog.bootstrap to load Blockly in mocha tests (#7406)
* 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.
2023-08-18 18:06:52 +01:00

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');
});
});
});
});
});
}