/** * @license * Visual Blocks Editor * * Copyright 2017 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Tests for Blockly.Workspace.undo. * @author marisaleung@google.com (Marisa Leung) */ 'use strict'; goog.require('goog.events.EventHandler'); goog.require('goog.testing'); goog.require('goog.testing.events'); goog.require('goog.testing.MockControl'); 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(); mockControl_ = new goog.testing.MockControl(); Blockly.Events.fire = temporary_fireEvent; } function undoRedoTest_tearDown() { undefineGetVarBlock(); mockControl_.$tearDown(); 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); createMockBlock(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'); createMockBlock('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(); createMockBlock('id1'); createMockBlock('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(); createMockBlock('id1'); createMockBlock('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'); createMockBlock('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(); }