From 1c413f0442ccf5ce3170e5b3f068f013ea4ac17e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 14 Dec 2018 10:33:35 -0800 Subject: [PATCH] More testing with mocha --- package.json | 3 +- tests/mocha/event_test.js | 385 +++++++++++++++++++++++++++++ tests/mocha/field_variable_test.js | 162 ++++++++++++ tests/mocha/index.html | 3 + tests/mocha/test_helpers.js | 35 +++ 5 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 tests/mocha/event_test.js create mode 100644 tests/mocha/field_variable_test.js diff --git a/package.json b/package.json index abda73acb..61cf7eef7 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "unused": true }, "dependencies": { - "jsdom": "^13.0.0" + "jsdom": "^13.0.0", + "sinon": "^7.2.0" } } diff --git a/tests/mocha/event_test.js b/tests/mocha/event_test.js new file mode 100644 index 000000000..23c9000c0 --- /dev/null +++ b/tests/mocha/event_test.js @@ -0,0 +1,385 @@ + +// Declare some globals to make eslint happier. +// TODO: make an eslint config that applies to this directory and put this +// configuration in that file, instead of inline. +/* global suite, test, chai, setup, teardown */ +/* global sinon */ + +/* global assertNotNull, assertNotUndefined, assertNull, assertEquals, isEqualArrays, assertUndefined */ +suite('Events', function() { + setup(function() { + this.workspace = new Blockly.Workspace(); + Blockly.defineBlocksWithJsonArray([{ + 'type': 'field_variable_test_block', + 'message0': '%1', + 'args0': [ + { + 'type': 'field_variable', + 'name': 'VAR', + 'variable': 'item' + } + ], + }, + { + 'type': 'simple_test_block', + 'message0': 'simple test block' + }]); + }); + + teardown(function() { + delete Blockly.Blocks['field_variable_test_block']; + delete Blockly.Blocks['simple_test_block']; + this.workspace.dispose(); + + // Clear Blockly.Event state. + Blockly.Events.setGroup(false); + Blockly.Events.disabled_ = 0; + }); + + function checkExactEventValues(event, values) { + var keys = Object.keys(values); + for (var i = 0, field; field = keys[i]; i++) { + assertEquals(values[field], event[field]); + } + } + + function checkCreateEventValues(event, block, ids, type) { + var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); + var result_xml = Blockly.Xml.domToText(event.xml); + assertEquals(expected_xml, result_xml); + isEqualArrays(ids, event.ids); + assertEquals(type, event.type); + } + + function checkDeleteEventValues(event, block, ids, type) { + var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); + var result_xml = Blockly.Xml.domToText(event.oldXml); + assertEquals(expected_xml, result_xml); + isEqualArrays(ids, event.ids); + assertEquals(type, event.type); + } + + suite('Constructors', function() { + test('Abstract', function() { + var event = new Blockly.Events.Abstract(); + assertUndefined(event.blockId); + assertUndefined(event.workspaceId); + assertUndefined(event.varId); + checkExactEventValues(event, {'group': '', 'recordUndo': true}); + }); + + test('UI event without block', function() { + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(null, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': null, + 'workspaceId': null, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + }); + }); + + suite('With simple blocks', function() { + setup(function() { + this.FAKE_ID = 'hedgehog'; + sinon.stub(Blockly.utils, "genUid").returns(this.FAKE_ID); + + // Disable events while constructing the block: this is a test of the + // Blockly.Event constructors, not the block constructor. + Blockly.Events.disable(); + this.block = new Blockly.Block(this.workspace, 'simple_test_block'); + Blockly.Events.enable(); + sinon.restore(); + }); + + teardown(function() { + }); + + test('Block base', function() { + var event = new Blockly.Events.BlockBase(this.block); + assertUndefined(event.varId); + checkExactEventValues(event, + { + 'blockId': this.FAKE_ID, + 'workspaceId': this.workspace.id, + 'group': '', + 'recordUndo': true + }); + }); + + test('Create', function() { + var event = new Blockly.Events.Create(this.block); + checkCreateEventValues(event, this.block, [this.FAKE_ID], 'create'); + }); + + test('Block create', function() { + var event = new Blockly.Events.BlockCreate(this.block); + checkCreateEventValues(event, this.block, [this.FAKE_ID], 'create'); + }); + + test('Delete', function() { + var event = new Blockly.Events.Delete(this.block); + checkDeleteEventValues(event, this.block, [this.FAKE_ID], 'delete'); + }); + + test('Block delete', function() { + var event = new Blockly.Events.BlockDelete(this.block); + checkDeleteEventValues(event, this.block, [this.FAKE_ID], 'delete'); + }); + + test('UI event with block', function() { + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(this.block, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': this.FAKE_ID, + 'workspaceId': this.workspace.id, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + }); + }); + + suite('Move', function() { + test('Move by coordinate', function() { + var coordinate = new goog.math.Coordinate(3, 4); + this.block.xy_ = coordinate; + + var event = new Blockly.Events.Move(this.block); + checkExactEventValues(event, + {'oldCoordinate': coordinate, 'type': 'move'}); + }); + + test('Block move by coordinate', function() { + var coordinate = new goog.math.Coordinate(3, 4); + this.block.xy_ = coordinate; + + var event = new Blockly.Events.BlockMove(this.block); + checkExactEventValues(event, + {'oldCoordinate': coordinate, 'type': 'move'}); + }); + + suite('Move by parent', function() { + setup(function() { + sinon.stub(Blockly.utils, "genUid").returns("parent"); + Blockly.Events.disable(); + this.parentBlock = new Blockly.Block(this.workspace, 'simple_test_block'); + Blockly.Events.enable(); + sinon.restore(); + + this.block.parentBlock_ = this.parentBlock; + this.block.xy_ = new goog.math.Coordinate(3, 4); + }); + + teardown(function() { + this.block.parentBlock_ = null; + }); + + test('Move by parent', function() { + // Expect the oldParentId to be set but not the oldCoordinate to be set. + var event = new Blockly.Events.Move(this.block); + checkExactEventValues(event, {'oldCoordinate': undefined, + 'oldParentId': 'parent', 'type': 'move'}); + }); + + test('Block move by parent', function() { + // Expect the oldParentId to be set but not the oldCoordinate to be set. + var event = new Blockly.Events.BlockMove(this.block); + checkExactEventValues(event, {'oldCoordinate': undefined, + 'oldParentId': 'parent', 'type': 'move'}); + }); + }); + }); + }); + + suite('With variable getter blocks', function() { + setup(function() { + // Disable events while constructing the block: this is a test of the + // Blockly.Event constructors, not the block constructor. + Blockly.Events.disable(); + this.block = new Blockly.Block(this.workspace, 'field_variable_test_block'); + Blockly.Events.enable(); + }); + + teardown(function() { + + }); + + test('Change', function() { + var event = + new Blockly.Events.Change(this.block, 'field', 'VAR', 'id1', 'id2'); + checkExactEventValues(event, {'element': 'field', 'name': 'VAR', + 'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'}); + }); + + test('Block change', function() { + var event = new Blockly.Events.BlockChange( + this.block, 'field', 'VAR', 'id1', 'id2'); + checkExactEventValues(event, {'element': 'field', 'name': 'VAR', + 'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'}); + }); + }); + }); + + suite('Variable events', function() { + setup(function() { + this.variable = this.workspace.createVariable('name1', 'type1', 'id1'); + }); + + /** + * Check if a variable with the given values exists. + * @param {Blockly.Workspace|Blockly.VariableMap} container The workspace or + * variableMap the checked variable belongs to. + * @param {!string} name The expected name of the variable. + * @param {!string} type The expected type of the variable. + * @param {!string} id The expected id of the variable. + */ + function checkVariableValues(container, name, type, id) { + var variable = container.getVariableById(id); + assertNotUndefined(variable); + assertEquals(name, variable.name); + assertEquals(type, variable.type); + assertEquals(id, variable.getId()); + } + suite('Constructors', function() { + test('Var base', function() { + var event = new Blockly.Events.VarBase(this.variable); + assertUndefined(event.blockId); + checkExactEventValues(event, {'varId': 'id1', + 'workspaceId': this.workspace.id, 'group': '', 'recordUndo': true}); + }); + + test('Var create', function() { + var event = new Blockly.Events.VarCreate(this.variable); + checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1', + 'type': 'var_create'}); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1', + 'varId':'id1', 'type': 'var_delete'}); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + checkExactEventValues(event, {'varId': 'id1', 'oldName': 'name1', + 'newName': 'name2', 'type': 'var_rename'}); + }); + }); + + suite('fromJson', function() { + test('Var create', function() { + var event = new Blockly.Events.VarCreate(this.variable); + var event2 = new Blockly.Events.VarCreate(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + var event2 = new Blockly.Events.VarDelete(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, ''); + var event2 = new Blockly.Events.VarRename(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + }); + + suite('toJson', function() { + test('Var create', function() { + var variable = this.workspace.createVariable('name1', 'type1', 'id1'); + var event = new Blockly.Events.VarCreate(variable); + var json = event.toJson(); + var expectedJson = ({type: "var_create", varId: "id1", varType: "type1", + varName: "name1"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + var json = event.toJson(); + var expectedJson = ({type: "var_delete", varId: "id1", varType: "type1", + varName: "name1"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + var json = event.toJson(); + var expectedJson = ({type: "var_rename", varId: "id1", oldName: "name1", + newName: "name2"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + }); + + suite('Run Forward', function() { + test('Var create', function() { + var json = {type: "var_create", varId: "id1", varType: "type1", + varName: "name1"}; + var event = Blockly.Events.fromJson(json, this.workspace); + assertNull(this.workspace.getVariableById('id1')); + event.run(true); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + assertNotNull(this.workspace.getVariableById('id1')); + event.run(true); + assertNull(this.workspace.getVariableById('id1')); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + event.run(true); + assertNull(this.workspace.getVariable('name1')); + checkVariableValues(this.workspace, 'name2', 'type1', 'id1'); + }); + }); + suite('Run Backward', function() { + test('Var create', function() { + var variable = this.workspace.createVariable('name1', 'type1', 'id1'); + var event = new Blockly.Events.VarCreate(variable); + assertNotNull(this.workspace.getVariableById('id1')); + event.run(false); + }); + + test('Var delete', function() { + var json = {type: "var_delete", varId: "id1", varType: "type1", + varName: "name1"}; + var event = Blockly.Events.fromJson(json, this.workspace); + assertNull(this.workspace.getVariableById('id1')); + event.run(false); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + event.run(false); + assertNull(this.workspace.getVariable('name2')); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + }); + }); +}); diff --git a/tests/mocha/field_variable_test.js b/tests/mocha/field_variable_test.js new file mode 100644 index 000000000..e92637ab0 --- /dev/null +++ b/tests/mocha/field_variable_test.js @@ -0,0 +1,162 @@ + +// Declare some globals to make eslint happier. +// TODO: make an eslint config that applies to this directory and put this +// configuration in that file, instead of inline. +/* global suite, test, chai, setup, teardown */ +/* global sinon */ + +/* global assertFalse, assertTrue, assertNull, assertEquals, isEqualArrays */ + +suite('Variable Fields', function() { + function getMockBlock(workspace) { + return { + 'workspace': workspace, + 'isShadow': function() { + return false; + } + }; + } + function fieldVariable_createAndInitField(workspace) { + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(workspace); + fieldVariable.setSourceBlock(mockBlock); + // No view to initialize, but still need to init the model. + fieldVariable.initModel(); + return fieldVariable; + } + + setup(function() { + this.workspace = new Blockly.Workspace(); + }); + teardown(function() { + this.workspace.dispose(); + }); + + test('Constructor', function() { + var fieldVariable = new Blockly.FieldVariable('name1'); + // The field does not have a variable until after init() is called. + assertEquals('', fieldVariable.getText()); + assertNull(fieldVariable.getValue()); + }); + + test('Set value by ID', function() { + this.workspace.createVariable('name2', null, 'id2'); + + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + var oldId = fieldVariable.getValue(); + + fieldVariable.setValue('id2'); + // Setting value by ID gives us the right text as well. + assertEquals('name2', fieldVariable.getText()); + assertEquals('id2', fieldVariable.getValue()); + chai.assert.notEqual(oldId, fieldVariable.getValue()); + }); + + test('Set value: variable does not exist', function() { + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + chai.assert.throws(function() { + fieldVariable.setValue('id1'); + }); + }); + + test('Dropdown contains variables', function() { + // Expect that the dropdown options will contain the variables that exist + this.workspace.createVariable('name1', '', 'id1'); + this.workspace.createVariable('name2', '', 'id2'); + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + + // Expect that variables created after field creation will show up too. + this.workspace.createVariable('name3', '', 'id3'); + + var result_options = Blockly.FieldVariable.dropdownCreate.call( + fieldVariable); + + // Expect three variable options and a rename option. + assertEquals(result_options.length, 4); + isEqualArrays(result_options[0], ['name1', 'id1']); + isEqualArrays(result_options[1], ['name2', 'id2']); + isEqualArrays(result_options[2], ['name3', 'id3']); + + }); + + suite('Get variable types', function() { + setup(function() { + this.workspace.createVariable('name1', 'type1'); + this.workspace.createVariable('name2', 'type2'); + }); + + test('variableTypes is undefined', function() { + // Expect that since variableTypes is undefined, only type empty string + // will be returned (regardless of what types are available on the workspace). + var fieldVariable = new Blockly.FieldVariable('name1'); + var resultTypes = fieldVariable.getVariableTypes_(); + isEqualArrays(resultTypes, ['']); + }); + + test('variableTypes is explicit', function() { + // Expect that since variableTypes is defined, it will be the return + // value, regardless of what types are available on the workspace. + var fieldVariable = new Blockly.FieldVariable( + 'name1', null, ['type1', 'type2'], 'type1'); + var resultTypes = fieldVariable.getVariableTypes_(); + isEqualArrays(resultTypes, ['type1', 'type2']); + assertEquals('Default type was wrong', 'type1', + fieldVariable.defaultType_); + }); + + test('variableTypes is null', function() { + // Expect all variable types to be returned. + // The field does not need to be initialized to do this--it just needs + // a pointer to the workspace. + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(this.workspace); + fieldVariable.setSourceBlock(mockBlock); + fieldVariable.variableTypes = null; + + var resultTypes = fieldVariable.getVariableTypes_(); + // The empty string is always one of the options. + isEqualArrays(resultTypes, ['type1', 'type2', '']); + }); + + test('variableTypes is the empty list', function() { + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(this.workspace); + fieldVariable.setSourceBlock(mockBlock); + fieldVariable.variableTypes = []; + + chai.assert.throws(function() { + fieldVariable.getVariableTypes_(); + }); + }); + }); + + suite('Default types', function() { + test('Default type exists', function() { + var fieldVariable = new Blockly.FieldVariable(null, null, ['b'], 'b'); + assertEquals('The variable field\'s default type should be "b"', + 'b', fieldVariable.defaultType_); + }); + + test('No default type', function() { + var fieldVariable = new Blockly.FieldVariable(null); + assertEquals('The variable field\'s default type should be the empty string', + '', fieldVariable.defaultType_); + assertNull('The variable field\'s allowed types should be null', + fieldVariable.variableTypes); + }); + + test('Default type mismatch', function() { + // Invalid default type when creating a variable field. + chai.assert.throws(function() { + var _fieldVariable = new Blockly.FieldVariable(null, null, ['a'], 'b'); + }); + }); + + test('Default type mismatch with empty array', function() { + // Invalid default type when creating a variable field. + chai.assert.throws(function() { + var _fieldVariable = new Blockly.FieldVariable(null, null, ['a']); + }); + }); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index c4258eebd..bc18b8ab1 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -12,6 +12,7 @@
+ + +