diff --git a/tests/jsunit/index.html b/tests/jsunit/index.html index f1324018a..4513c7b54 100644 --- a/tests/jsunit/index.html +++ b/tests/jsunit/index.html @@ -17,8 +17,6 @@ - - diff --git a/tests/jsunit/workspace_undo_redo_test.js b/tests/jsunit/workspace_undo_redo_test.js deleted file mode 100644 index d9745d700..000000000 --- a/tests/jsunit/workspace_undo_redo_test.js +++ /dev/null @@ -1,363 +0,0 @@ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - - /** - * @fileoverview Tests for Blockly.Workspace.undo. - * @author marisaleung@google.com (Marisa Leung) - */ -'use strict'; - - -var workspace; -var mockControl_; -var savedFireFunc = Blockly.Events.fire; - -function temporary_fireEvent(event) { - if (!Blockly.Events.isEnabled()) { - return; - } - Blockly.Events.FIRE_QUEUE_.push(event); - Blockly.Events.fireNow_(); -} - -function undoRedoTest_setUp() { - defineGetVarBlock(); - workspace = new Blockly.Workspace(); - Blockly.Events.fire = temporary_fireEvent; -} - -function undoRedoTest_tearDown() { - undefineGetVarBlock(); - if (mockControl_) { - mockControl_.restore(); - } - workspace.dispose(); - Blockly.Events.fire = savedFireFunc; -} - -/** - * Check that the top block with the given index contains a variable with - * the given name. - * @param {number} blockIndex The index of the top block. - * @param {string} name The expected name of the variable in the block. - */ -function undoRedoTest_checkBlockVariableName(blockIndex, name) { - var blockVarName = workspace.topBlocks_[blockIndex].getVarModels()[0].name; - assertEquals(name, blockVarName); -} - -function createTwoVarsEmptyType() { - workspace.createVariable('name1', '', 'id1'); - workspace.createVariable('name2', '', 'id2'); -} - -function createTwoVarsDifferentTypes() { - workspace.createVariable('name1', 'type1', 'id1'); - workspace.createVariable('name2', 'type2', 'id2'); -} - -function test_undoCreateVariable_Trivial() { - undoRedoTest_setUp(); - createTwoVarsDifferentTypes(); - - workspace.undo(); - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - assertNull(workspace.getVariableById('id2')); - workspace.undo(); - assertNull(workspace.getVariableById('id1')); - assertNull(workspace.getVariableById('id2')); - undoRedoTest_tearDown(); -} - -function test_redoAndUndoCreateVariable_Trivial() { - undoRedoTest_setUp(); - createTwoVarsDifferentTypes(); - - workspace.undo(); - workspace.undo(true); - - // Expect that variable 'id2' is recreated - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - - workspace.undo(); - workspace.undo(); - workspace.undo(true); - - // Expect that variable 'id1' is recreated - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - assertNull(workspace.getVariableById('id2')); - undoRedoTest_tearDown(); -} - -function test_undoDeleteVariable_NoBlocks() { - undoRedoTest_setUp(); - createTwoVarsDifferentTypes(); - workspace.deleteVariableById('id1'); - workspace.deleteVariableById('id2'); - - workspace.undo(); - assertNull(workspace.getVariableById('id1')); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - - workspace.undo(); - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - undoRedoTest_tearDown(); -} - -function test_undoDeleteVariable_WithBlocks() { - undoRedoTest_setUp(); - - createTwoVariablesAndBlocks(workspace); - - workspace.deleteVariableById('id1'); - workspace.deleteVariableById('id2'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name2'); - assertNull(workspace.getVariableById('id1')); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name2'); - undoRedoTest_checkBlockVariableName(1, 'name1'); - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - undoRedoTest_tearDown(); -} - -function test_redoAndUndoDeleteVariable_NoBlocks() { - undoRedoTest_setUp(); - - createTwoVarsDifferentTypes(); - - workspace.deleteVariableById('id1'); - workspace.deleteVariableById('id2'); - - workspace.undo(); - workspace.undo(true); - // Expect that both variables are deleted - assertNull(workspace.getVariableById('id1')); - assertNull(workspace.getVariableById('id2')); - - workspace.undo(); - workspace.undo(); - workspace.undo(true); - // Expect that variable 'id2' is recreated - assertNull(workspace.getVariableById('id1')); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - undoRedoTest_tearDown(); -} - -function test_redoAndUndoDeleteVariable_WithBlocks() { - undoRedoTest_setUp(); - - createTwoVariablesAndBlocks(workspace); - - workspace.deleteVariableById('id1'); - workspace.deleteVariableById('id2'); - - workspace.undo(); - workspace.undo(true); - // Expect that both variables are deleted - assertEquals(0, workspace.topBlocks_.length); - assertNull(workspace.getVariableById('id1')); - assertNull(workspace.getVariableById('id2')); - - workspace.undo(); - workspace.undo(); - workspace.undo(true); - // Expect that variable 'id2' is recreated - undoRedoTest_checkBlockVariableName(0, 'name2'); - assertNull(workspace.getVariableById('id1')); - checkVariableValues(workspace, 'name2', 'type2', 'id2'); - undoRedoTest_tearDown(); -} - -function test_redoAndUndoDeleteVariableTwice_NoBlocks() { - undoRedoTest_setUp(); - workspace.createVariable('name1', 'type1', 'id1'); - workspace.deleteVariableById('id1'); - workspace.deleteVariableById('id1'); - - // Check the undoStack only recorded one delete event. - var undoStack = workspace.undoStack_; - assertEquals('var_delete', undoStack[undoStack.length-1].type); - assertNotEquals('var_delete', undoStack[undoStack.length-2].type); - - // undo delete - workspace.undo(); - checkVariableValues(workspace, 'name1', 'type1', 'id1'); - - // redo delete - workspace.undo(true); - assertNull(workspace.getVariableById('id1')); - - // redo delete, nothing should happen - workspace.undo(true); - assertNull(workspace.getVariableById('id1')); - undoRedoTest_tearDown(); -} - -function test_redoAndUndoDeleteVariableTwice_WithBlocks() { - undoRedoTest_setUp(); - var id = 'id1'; - workspace.createVariable('name1', 'type1', id); - createMockVarBlock(workspace, id); - workspace.deleteVariableById(id); - workspace.deleteVariableById(id); - - // Check the undoStack only recorded one delete event. - var undoStack = workspace.undoStack_; - assertEquals('var_delete', undoStack[undoStack.length-1].type); - assertEquals('delete', undoStack[undoStack.length-2].type); - assertNotEquals('var_delete', undoStack[undoStack.length-3].type); - - // undo delete - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name1'); - checkVariableValues(workspace, 'name1', 'type1', id); - - // redo delete - workspace.undo(true); - assertEquals(0, workspace.topBlocks_.length); - assertNull(workspace.getVariableById(id)); - - // redo delete, nothing should happen - workspace.undo(true); - assertEquals(0, workspace.topBlocks_.length); - assertNull(workspace.getVariableById(id)); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_OneExists_NoBlocks() { - undoRedoTest_setUp(); - workspace.createVariable('name1', '', 'id1'); - workspace.renameVariableById('id1', 'name2'); - - workspace.undo(); - checkVariableValues(workspace, 'name1', '', 'id1'); - - workspace.undo(true); - checkVariableValues(workspace, 'name2', '', 'id1'); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_OneExists_WithBlocks() { - undoRedoTest_setUp(); - workspace.createVariable('name1', '', 'id1'); - createMockVarBlock(workspace, 'id1'); - workspace.renameVariableById('id1', 'name2'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name1'); - checkVariableValues(workspace, 'name1', '', 'id1'); - - workspace.undo(true); - checkVariableValues(workspace, 'name2', '', 'id1'); - undoRedoTest_checkBlockVariableName(0, 'name2'); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_BothExist_NoBlocks() { - undoRedoTest_setUp(); - createTwoVarsEmptyType(); - workspace.renameVariableById('id1', 'name2'); - - workspace.undo(); - checkVariableValues(workspace, 'name1', '', 'id1'); - checkVariableValues(workspace, 'name2', '', 'id2'); - - workspace.undo(true); - checkVariableValues(workspace, 'name2', '', 'id2'); - assertNull(workspace.getVariableById('id1')); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_BothExist_WithBlocks() { - undoRedoTest_setUp(); - createTwoVarsEmptyType(); - createMockVarBlock(workspace, 'id1'); - createMockVarBlock(workspace, 'id2'); - workspace.renameVariableById('id1', 'name2'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name1'); - undoRedoTest_checkBlockVariableName(1, 'name2'); - checkVariableValues(workspace, 'name1', '', 'id1'); - checkVariableValues(workspace, 'name2', '', 'id2'); - - workspace.undo(true); - undoRedoTest_checkBlockVariableName(0, 'name2'); - undoRedoTest_checkBlockVariableName(1, 'name2'); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_BothExistCaseChange_NoBlocks() { - undoRedoTest_setUp(); - createTwoVarsEmptyType(); - workspace.renameVariableById('id1', 'Name2'); - - workspace.undo(); - checkVariableValues(workspace, 'name1', '', 'id1'); - checkVariableValues(workspace, 'name2', '', 'id2'); - - workspace.undo(true); - checkVariableValues(workspace, 'Name2', '', 'id2'); - assertNull(workspace.getVariable('name1')); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_BothExistCaseChange_WithBlocks() { - undoRedoTest_setUp(); - createTwoVarsEmptyType(); - createMockVarBlock(workspace, 'id1'); - createMockVarBlock(workspace, 'id2'); - workspace.renameVariableById('id1', 'Name2'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name1'); - undoRedoTest_checkBlockVariableName(1, 'name2'); - checkVariableValues(workspace, 'name1', '', 'id1'); - checkVariableValues(workspace, 'name2', '', 'id2'); - - workspace.undo(true); - checkVariableValues(workspace, 'Name2', '', 'id2'); - assertNull(workspace.getVariableById('id1')); - undoRedoTest_checkBlockVariableName(0, 'Name2'); - undoRedoTest_checkBlockVariableName(1, 'Name2'); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_OnlyCaseChange_NoBlocks() { - undoRedoTest_setUp(); - workspace.createVariable('name1', '', 'id1'); - workspace.renameVariableById('id1', 'Name1'); - - workspace.undo(); - checkVariableValues(workspace, 'name1', '', 'id1'); - - workspace.undo(true); - checkVariableValues(workspace, 'Name1', '', 'id1'); - undoRedoTest_tearDown(); -} - -function test_undoRedoRenameVariable_OnlyCaseChange_WithBlocks() { - undoRedoTest_setUp(); - workspace.createVariable('name1', '', 'id1'); - createMockVarBlock(workspace, 'id1'); - workspace.renameVariableById('id1', 'Name1'); - - workspace.undo(); - undoRedoTest_checkBlockVariableName(0, 'name1'); - checkVariableValues(workspace, 'name1', '', 'id1'); - - workspace.undo(true); - checkVariableValues(workspace, 'Name1', '', 'id1'); - undoRedoTest_checkBlockVariableName(0, 'Name1'); - undoRedoTest_tearDown(); -} diff --git a/tests/mocha/run_mocha_tests_in_browser.js b/tests/mocha/run_mocha_tests_in_browser.js index 218806807..f78871811 100644 --- a/tests/mocha/run_mocha_tests_in_browser.js +++ b/tests/mocha/run_mocha_tests_in_browser.js @@ -40,7 +40,7 @@ async function runMochaTestsInBrowser() { var elem = await browser.$('#failureCount'); var text = await elem.getAttribute('tests_failed'); return text != 'unset'; - }, 12000); + }, 20000); const elem = await browser.$('#failureCount'); const numOfFailure = await elem.getAttribute('tests_failed'); diff --git a/tests/mocha/workspace_test.js b/tests/mocha/workspace_test.js index 43066c830..7a20a63aa 100644 --- a/tests/mocha/workspace_test.js +++ b/tests/mocha/workspace_test.js @@ -29,6 +29,30 @@ suite('Workspace', function() { sinon.restore(); }); + function assertBlockVarModelName(workspace, blockIndex, name) { + var block = workspace.topBlocks_[blockIndex]; + chai.assert.exists(block, 'Block at topBlocks_[' + blockIndex + ']'); + var varModel = block.getVarModels()[0]; + chai.assert.exists(varModel, + 'VariableModel for block at topBlocks_[' + blockIndex + ']'); + var blockVarName = varModel.name; + chai.assert.equal(blockVarName, name, + 'VariableModel name for block at topBlocks_[' + blockIndex + ']'); + } + + function createVarBlocksNoEvents(workspace, ids) { + var blocks = []; + // Turn off events to avoid testing XML at the same time. + Blockly.Events.disable(); + for (var i = 0, id; (id = ids[i]); i++) { + var block = new Blockly.Block(workspace, 'get_var_block'); + block.inputList[0].fieldRow[0].setValue(id); + blocks.push(block); + } + Blockly.Events.enable(); + return blocks; + } + suite('clear', function() { test('Trivial', function() { sinon.stub(Blockly.Events, "setGroup").returns(null); @@ -61,29 +85,20 @@ suite('Workspace', function() { this.var1 = this.workspace.createVariable('name1', 'type1', 'id1'); this.var2 = this.workspace.createVariable('name2', 'type2', 'id2'); // Create blocks to refer to both of them. - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id2'); - Blockly.Events.enable(); + createVarBlocksNoEvents(this.workspace, ['id1', 'id1', 'id2']); }); - test('deleteVariableInternal_', function() { + test('deleteVariableInternal_(id1)', function() { var uses = this.workspace.getVariableUsesById(this.var1.getId()); this.workspace.deleteVariableInternal_(this.var1, uses); var variable = this.workspace.getVariableById('id1'); chai.assert.isNull(variable); - var blockVarName = this.workspace.topBlocks_[0].getVarModels()[0].name; assertVariableValues(this.workspace, 'name2', 'type2', 'id2'); - chai.assert.equal(blockVarName, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name2'); }); - test('deleteVariableById one usage', function() { + test('deleteVariableById(id2) one usage', function() { // Deleting variable one usage should not trigger confirm dialog. var stub = sinon.stub(Blockly, "confirm").callsArgWith(1, true); @@ -93,11 +108,10 @@ suite('Workspace', function() { var variable = this.workspace.getVariableById('id2'); chai.assert.isNull(variable); assertVariableValues(this.workspace, 'name1', 'type1', 'id1'); - var blockVarName = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVarName, 'name1'); + assertBlockVarModelName(this.workspace, 0, 'name1'); }); - test('deleteVariableById multiple usages confirm', function() { + test('deleteVariableById(id1) multiple usages confirm', function() { // Deleting variable with multiple usages triggers confirm dialog. var stub = sinon.stub(Blockly, "confirm").callsArgWith(1, true); @@ -107,11 +121,10 @@ suite('Workspace', function() { var variable = this.workspace.getVariableById('id1'); chai.assert.isNull(variable); assertVariableValues(this.workspace, 'name2', 'type2', 'id2'); - var blockVarName = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVarName, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name2'); }); - test('deleteVariableById multiple usages cancel', function() { + test('deleteVariableById(id1) multiple usages cancel', function() { // Deleting variable with multiple usages triggers confirm dialog. var stub = sinon.stub(Blockly, "confirm").callsArgWith(1, false); @@ -120,66 +133,48 @@ suite('Workspace', function() { sinon.assert.calledOnce(stub); assertVariableValues(this.workspace, 'name1', 'type1', 'id1'); assertVariableValues(this.workspace, 'name2', 'type2', 'id2'); - var blockVarName1 = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVarName1, 'name1'); - var blockVarName2 = this.workspace.topBlocks_[1].getVarModels()[0].name; - chai.assert.equal(blockVarName2, 'name1'); - var blockVarName3 = this.workspace.topBlocks_[2].getVarModels()[0].name; - chai.assert.equal(blockVarName3, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name1'); + assertBlockVarModelName(this.workspace, 1, 'name1'); + assertBlockVarModelName(this.workspace, 2, 'name2'); }); }); - suite('renameVariable', function() { + suite('renameVariableById', function() { setup(function() { this.workspace.createVariable('name1', 'type1', 'id1'); }); - test('No references', function() { + + 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', function() { - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - Blockly.Events.enable(); + 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); - var blockVarName = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVarName, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name2'); }); - test('Reference exists different capitalizations', function() { - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - Blockly.Events.enable(); + 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); - var blockVarName = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVarName, 'Name1'); + assertBlockVarModelName(this.workspace, 0, 'Name1'); }); suite('Two variables rename overlap', function() { - test('Same type', function() { + test('Same type rename variable with id1 to name2', function() { this.workspace.createVariable('name2', 'type1', 'id2'); - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id2'); - Blockly.Events.enable(); + createVarBlocksNoEvents(this.workspace, ['id1', 'id2']); this.workspace.renameVariableById('id1', 'name2'); @@ -192,21 +187,13 @@ suite('Workspace', function() { chai.assert.equal(this.workspace.getAllVariables().length, 1); // Both blocks should now reference variable with name2. - var blockVar1 = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVar1, 'name2'); - var blockVar2 = this.workspace.topBlocks_[1].getVarModels()[0].name; - chai.assert.equal(blockVar2, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name2'); + assertBlockVarModelName(this.workspace, 1, 'name2'); }); - test('Different type', function() { + test('Different type rename variable with id1 to name2', function() { this.workspace.createVariable('name2', 'type2', 'id2'); - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id2'); - Blockly.Events.enable(); + createVarBlocksNoEvents(this.workspace, ['id1', 'id2']); this.workspace.renameVariableById('id1', 'name2'); @@ -215,21 +202,13 @@ suite('Workspace', function() { assertVariableValues(this.workspace, 'name2', 'type2', 'id2'); // Both blocks should now reference variable with name2. - var blockVar1 = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVar1, 'name2'); - var blockVar2 = this.workspace.topBlocks_[1].getVarModels()[0].name; - chai.assert.equal(blockVar2, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'name2'); + assertBlockVarModelName(this.workspace, 1, 'name2'); }); - test('Same type different capitalization', function() { + test('Same type different capitalization rename variable with id1 to Name2', function() { this.workspace.createVariable('name2', 'type1', 'id2'); - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id2'); - Blockly.Events.enable(); + createVarBlocksNoEvents(this.workspace, ['id1', 'id2']); this.workspace.renameVariableById('id1', 'Name2'); @@ -242,21 +221,13 @@ suite('Workspace', function() { chai.assert.equal(this.workspace.getAllVariables().length, 1); // Both blocks should now reference variable with Name2. - var blockVar1 = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVar1, 'Name2'); - var blockVar2 = this.workspace.topBlocks_[1].getVarModels()[0].name; - chai.assert.equal(blockVar2, 'Name2'); + assertBlockVarModelName(this.workspace, 0, 'Name2'); + assertBlockVarModelName(this.workspace, 1, 'Name2'); }); - test('Different type different capitalization', function() { + test('Different type different capitalization rename variable with id1 to Name2', function() { this.workspace.createVariable('name2', 'type2', 'id2'); - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id1'); - block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('id2'); - Blockly.Events.enable(); + createVarBlocksNoEvents(this.workspace, ['id1', 'id2']); this.workspace.renameVariableById('id1', 'Name2'); @@ -266,10 +237,8 @@ suite('Workspace', function() { assertVariableValues(this.workspace, 'name2', 'type2', 'id2'); // Only first block should use new capitalization. - var blockVar1 = this.workspace.topBlocks_[0].getVarModels()[0].name; - chai.assert.equal(blockVar1, 'Name2'); - var blockVar2 = this.workspace.topBlocks_[1].getVarModels()[0].name; - chai.assert.equal(blockVar2, 'name2'); + assertBlockVarModelName(this.workspace, 0, 'Name2'); + assertBlockVarModelName(this.workspace, 1, 'name2'); }); }); }); @@ -294,11 +263,7 @@ suite('Workspace', function() { // Flyout.init usually does this binding. this.workspace.variableMap_ = this.targetWorkspace.getVariableMap(); - // Turn off events to avoid testing XML at the same time. - Blockly.Events.disable(); - var block = new Blockly.Block(this.workspace, 'get_var_block'); - block.inputList[0].fieldRow[0].setValue('1'); - Blockly.Events.enable(); + var block = createVarBlocksNoEvents(this.workspace, ['1'])[0]; this.workspace.removeTopBlock(block); this.workspace.addTopBlock(block); @@ -704,4 +669,387 @@ suite('Workspace', function() { chai.assert.isNull(this.workspace.getBlockById(this.blockB)); }); }); + + suite('Undo/Redo', function() { + function temporary_fireEvent(event) { + if (!Blockly.Events.isEnabled()) { + return; + } + Blockly.Events.FIRE_QUEUE_.push(event); + Blockly.Events.fireNow_(); + } + + setup(function() { + this.savedFireFunc_ = Blockly.Events.fire; + Blockly.Events.fire = temporary_fireEvent; + temporary_fireEvent.firedEvents_ = []; + }); + + teardown(function() { + Blockly.Events.fire = this.savedFireFunc_; + }); + + 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.topBlocks_.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'); + this.workspace.deleteVariableById('id1'); + + // Check the undoStack only recorded one delete event. + var 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'); + this.workspace.deleteVariableById('id1'); + + // Check the undoStack only recorded one delete event. + var 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.topBlocks_.length, 0); + chai.assert.isNull(this.workspace.getVariableById('id1')); + + // Redo delete, nothing should happen + this.workspace.undo(true); + chai.assert.equal(this.workspace.topBlocks_.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'); + }); + }); + }); + }); });