From 7b055dbc7e227b2aeb82a51bb7ac8d62e9cc9da6 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Fri, 18 Nov 2022 16:01:41 -0800 Subject: [PATCH] chore(tests): test running procedure events (#6627) * chore: define necessary events * chore: cleanup test organization * chore: cleanup comment * chore: move assertEventFiredShallow to test helpers * chore: add tests for running the procedure change return events * chore: work on tests?? * chore: add tests for running the change return events * chore: add tests for running enable events * chore: add tests for procedure rename events * chore: add tests for renaming procedure parameters * chore: add tests for running procedure create events * chore: add tests for running procedure parameter create events * chore: add tests for running procedure delete events * chore: add tests for running procedure parameter delete events * chore: skip all tests for running procedure events --- core/events/events_abstract.ts | 4 +- .../event_procedure_change_return_test.js | 181 +++++++++++++++ tests/mocha/event_procedure_create_test.js | 155 +++++++++++++ tests/mocha/event_procedure_delete_test.js | 156 +++++++++++++ tests/mocha/event_procedure_enable_test.js | 175 +++++++++++++++ .../event_procedure_parameter_create_test.js | 200 +++++++++++++++++ .../event_procedure_parameter_delete_test.js | 200 +++++++++++++++++ .../event_procedure_parameter_rename_test.js | 206 ++++++++++++++++++ tests/mocha/event_procedure_rename_test.js | 170 +++++++++++++++ tests/mocha/index.html | 8 + tests/mocha/procedure_map_test.js | 82 +++---- tests/mocha/test_helpers/events.js | 43 ++++ 12 files changed, 1527 insertions(+), 53 deletions(-) create mode 100644 tests/mocha/event_procedure_change_return_test.js create mode 100644 tests/mocha/event_procedure_create_test.js create mode 100644 tests/mocha/event_procedure_delete_test.js create mode 100644 tests/mocha/event_procedure_enable_test.js create mode 100644 tests/mocha/event_procedure_parameter_create_test.js create mode 100644 tests/mocha/event_procedure_parameter_delete_test.js create mode 100644 tests/mocha/event_procedure_parameter_rename_test.js create mode 100644 tests/mocha/event_procedure_rename_test.js diff --git a/core/events/events_abstract.ts b/core/events/events_abstract.ts index dab2c8c00..39d01c040 100644 --- a/core/events/events_abstract.ts +++ b/core/events/events_abstract.ts @@ -113,10 +113,10 @@ export abstract class Abstract { * @param _forward True if run forward, false if run backward (undo). */ run(_forward: boolean) { - // Defined by subclasses. + // Defined by subclasses. Cannot be abstract b/c UI events do /not/ define + // this. } - /** * Get workspace the event belongs to. * diff --git a/tests/mocha/event_procedure_change_return_test.js b/tests/mocha/event_procedure_change_return_test.js new file mode 100644 index 000000000..b18e5d086 --- /dev/null +++ b/tests/mocha/event_procedure_change_return_test.js @@ -0,0 +1,181 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureChangeReturn'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Change Return Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + const DEFAULT_TYPES = null; + const NON_DEFAULT_TYPES = []; + + setup(function() { + this.createProcedureModel = (id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name', id); + }; + + this.createEventToState = (procedureModel) => { + return new Blockly.Events.ProcedureChangeReturn( + this.workspace, + procedureModel, + procedureModel.getReturnTypes()); + }; + }); + + suite('forward (redo)', function() { + test('the procedure with the matching ID has its return set', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + event.run(true /* forward */); + + chai.assert.equal( + initial.getReturnTypes(), + final.getReturnTypes(), + "Expected the procedure's return type to be toggled"); + }); + + test('changing the return fires a change return event', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureChangeReturn, + { + model: initial, + oldTypes: DEFAULT_TYPES, + }, + this.workspace.id); + }); + + test('noop return changes do not fire change return events', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureChangeReturn, + {}, + this.workspace.id); + }); + + test( + 'attempting to change the return of a procedure that ' + + 'does not exist in the map throws', + function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + const event = this.createEventToState(final); + + chai.assert.throws(() => { + event.run(true /* forward */); + }); + }); + }); + + suite('backward (undo)', function() { + test('the procedure with the matching ID has its return set', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setReturnTypes(NON_DEFAULT_TYPES); + undoable.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + event.run(false /* backward */); + + chai.assert.equal( + initial.getReturnTypes(), + DEFAULT_TYPES, + "Expected the procedure's return type to be toggled"); + }); + + test('changing the return fires a change return event', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setReturnTypes(NON_DEFAULT_TYPES); + undoable.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureChangeReturn, + { + model: initial, + oldTypes: DEFAULT_TYPES, + }, + this.workspace.id); + }); + + test('noop return changes do not fire change return events', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + undoable.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureChangeReturn, + {}, + this.workspace.id); + }); + + test( + 'attempting to change the return of a procedure that ' + + 'does not exist throws', + function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setReturnTypes(NON_DEFAULT_TYPES); + undoable.setReturnTypes(NON_DEFAULT_TYPES); + const event = this.createEventToState(undoable); + + chai.assert.throws(() => { + event.run(false /* backward */); + }); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_create_test.js b/tests/mocha/event_procedure_create_test.js new file mode 100644 index 000000000..295d2eab2 --- /dev/null +++ b/tests/mocha/event_procedure_create_test.js @@ -0,0 +1,155 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureCreate'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Create Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + setup(function() { + this.createProcedureModel = (name, id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, name, id); + }; + + this.createEventToState = (procedureModel) => { + return new Blockly.Events.ProcedureCreate(this.workspace, procedureModel); + }; + }); + + suite('forward', function() { + test('a procedure model is created if it does not exist', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + event.run(true /* forward */); + + const createdProc = this.procedureMap.get('test id'); + chai.assert.isDefined(createdProc, 'Expected the procedure to exist'); + chai.assert.equal( + createdProc.getName(), + model.getName(), + "Expected the procedure's name to match the model"); + chai.assert.equal( + createdProc.getId(), + model.getId(), + "Expected the procedure's id to match the model"); + }); + + test('creating a procedure model fires a create event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureCreate, + {model: this.procedureMap.get('testid')}, + this.workspace.id); + }); + + test( + 'a procedure model is not created if a model with a ' + + 'matching ID exists', + function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + event.run(true /* forward */); + + chai.assert.equal( + this.procedureMap.get('test id'), + model, + 'Expected the model in the procedure map to be the same ' + + 'as the original model'); + }); + + test('not creating a model does not fire a create event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureCreate, + {}, + this.workspace.id); + }); + }); + + suite('backward', function() { + test( + 'a procedure model is deleted if a model with a matching ID exists', + function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + event.run(false /* backward */); + + chai.assert.isUndefined( + this.procedureMap.get('test id'), + 'Expected the procedure to be deleted'); + }); + + test('deleting a model fires a delete event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureDelete, + {model}, + this.workspace.id); + }); + + test.skip( + 'a model is not deleted if no model with a matching ID exists', + function() { + // TODO: Do we want this to throw? warn? do nothing? + }); + + test('not deleting a model does not fire a delete event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureDelete, + {}, + this.workspace.id); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_delete_test.js b/tests/mocha/event_procedure_delete_test.js new file mode 100644 index 000000000..a4e8f397b --- /dev/null +++ b/tests/mocha/event_procedure_delete_test.js @@ -0,0 +1,156 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureDelete'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Delete Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + setup(function() { + this.createProcedureModel = (name, id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, name, id); + }; + + this.createEventToState = (procedureModel) => { + return new Blockly.Events.ProcedureDelete( + this.workspace, procedureModel); + }; + }); + + suite('forward', function() { + test( + 'a procedure model is deleted if a model with a matching ID exists', + function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + event.run(true /* forward */); + + chai.assert.isUndefined( + this.procedureMap.get('test id'), + 'Expected the procedure to be deleted'); + }); + + test('deleting a model fires a delete event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureDelete, + {model}, + this.workspace.id); + }); + + test( + 'a model is not deleted if if nodel with a matching ID exists', + function() { + // TODO: Figure out what we want to do here. + }); + + test('not deleting a model does not fire a delete event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureDelete, + {}, + this.workspace.id); + }); + }); + + suite('backward', function() { + test('a procedure model is created if it does not exist', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + event.run(false /* backward */); + + const createdProc = this.procedureMap.get('test id'); + chai.assert.isDefined(createdProc, 'Expected the procedure to exist'); + chai.assert.equal( + createdProc.getName(), + model.getName(), + "Expected the procedure's name to match the model"); + chai.assert.equal( + createdProc.getId(), + model.getId(), + "Expected the procedure's id to match the model"); + }); + + test('creating a procedure model fires a create event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureCreate, + {model: this.procedureMap.get('testid')}, + this.workspace.id); + }); + + test( + 'a procedure model is not created if a model with a ' + + 'matching ID exists', + function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + event.run(false /* backward */); + + chai.assert.equal( + this.procedureMap.get('test id'), + model, + 'Expected the model in the procedure map to be the same ' + + 'as the original model'); + }); + + test('not creating a model does not fire a create event', function() { + const model = this.createProcedureModel('test name', 'test id'); + const event = this.createEventToState(model); + this.procedureMap.add(model); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureCreate, + {}, + this.workspace.id); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_enable_test.js b/tests/mocha/event_procedure_enable_test.js new file mode 100644 index 000000000..30c32b7ea --- /dev/null +++ b/tests/mocha/event_procedure_enable_test.js @@ -0,0 +1,175 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureEnable'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Enable Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + setup(function() { + this.createProcedureModel = (id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name'); + }; + + this.createEventToState = (procedureModel) => { + return new Blockly.Events.ProcedureEnable(procedureModel); + }; + }); + + suite('forward', function() { + test('the procedure with the matching ID is toggled', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setEnabled(!final.getEnabled()); // Set it to the non-default. + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + event.run(true /* forward */); + + chai.assert.equal( + initial.getEnabled(), + final.getEnabled(), + "Expected the procedure's enabled state to be flipped"); + }); + + test('toggling a procedure fires an enable event', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setEnabled(!final.getEnabled()); // Set it to the non-default. + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureEnable, + {model: initial}, + this.workspace.id); + }); + + test('noop toggles do not fire enable events', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureEnable, + this.workspace.id); + }); + + test( + 'attempting to toggle a procedure that does not exist throws', + function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setEnabled(!final.getEnabled()); // Set it to the non-default. + const event = this.createEventToState(final); + + chai.assert.throws(() => { + event.run(true /* forward */); + }); + }); + }); + + suite('backward', function() { + test('the procedure with the matching ID is toggled', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + // Set them to be non-default. + const defaultEnabled = initial.getEnabled(); + initial.setEnabled(!defaultEnabled); + undoable.setEnabled(!defaultEnabled); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + event.run(false /* backward */); + + chai.assert.equal( + initial.getEnabled(), + defaultEnabled, + "Expected the procedure's enabled state to be flipped"); + }); + + test('toggling a procedure fires an enable event', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + // Set them to be non-default. + const defaultEnabled = initial.getEnabled(); + initial.setEnabled(!defaultEnabled); + undoable.setEnabled(!defaultEnabled); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureEnable, + {model: initial}, + this.workspace.id); + }); + + test('noop toggles do not fire enable events', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + // Set them to be non-default. + const defaultEnabled = initial.getEnabled(); + undoable.setEnabled(!defaultEnabled); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureEnable, + {}, + this.workspace.id); + }); + + test( + 'attempting to toggle a procedure that does not exist throws', + function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + // Set them to be non-default. + const defaultEnabled = initial.getEnabled(); + initial.setEnabled(!defaultEnabled); + undoable.setEnabled(!defaultEnabled); + const event = this.createEventToState(undoable); + + chai.assert.throws(() => { + event.run(false /* backward */); + }); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_parameter_create_test.js b/tests/mocha/event_procedure_parameter_create_test.js new file mode 100644 index 000000000..92348cf42 --- /dev/null +++ b/tests/mocha/event_procedure_parameter_create_test.js @@ -0,0 +1,200 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureParameterCreate'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Parameter Create Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + setup(function() { + this.createProcedureModel = (name, id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, name, id); + }; + + this.createProcedureAndParameter = + (procName, procId, paramName, paramId) => { + const param = new Blockly.procedures.ObservableParameterModel( + this.workspace, procName, paramId); + const proc = new Blockly.procedures.ObservableProcedureModel( + this.workspace, paramName, procId) + .insertParameter(param, 0); + return {param, proc}; + }; + + this.createEventToState = (procedureModel, parameterModel) => { + return new Blockly.Events.ProcedureParameterCreate( + this.workspace, procedureModel, parameterModel); + }; + }); + + suite('forward', function() { + test('a parameter is inserted if it does not exist', function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + const actualProc = this.createProcedureModel('test name', 'test id'); + this.procedureMap.add(actualProc); + + event.run(true /* forward */); + + const createdParam = actualProc.getParameter(0); + chai.assert.isDefined(createdParam, 'Expected the parameter to exist'); + chai.assert.equal( + createdParam.getName(), + modelParam.getName(), + "Expected the parameter's name to match the model"); + chai.assert.equal( + createdParam.getId(), + modelParam.getId(), + "Expected the parameter's id to match the model"); + }); + + test('inserting a parameter fires a create event', function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + const actualProc = this.createProcedureModel('test name', 'test id'); + this.procedureMap.add(actualProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterCreate, + { + model: actualProc, + parameter: actualProc.getParameter(0), + index: 0, + }, + this.workspace.id); + }); + + test( + 'a parameter is not created if a parameter with a ' + + 'matching ID and index already exists', + function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + this.procedureMap.add(modelProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + const actualProc = this.procedureMap.get('test id'); + chai.assert.equal( + actualProc, + modelProc, + 'Expected the procedure in the procedure map to not have changed'); + chai.assert.equal( + actualProc.getParameter(0), + modelParam, + 'Expected the parameter to not have changed'); + }); + + test( + 'not creating a parameter model does not fire a create event', + function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + this.procedureMap.add(modelProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterCreate, + {}, + this.workspace.id); + }); + }); + + suite('backward', function() { + test('a parameter is removed if it exists', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + + event.run(false /* backward */); + + chai.assert.isUndefined( + proc.getParameter(0), + 'Expected the parameter to be deleted'); + }); + + test('removing a parameter fires a delete event', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterDelete, + { + model: proc, + parameter: param, + index: 0, + }, + this.workspace.id); + }); + + test( + 'a parameter is not deleted if a parameter with a ' + + 'matching ID and index does not exist', + function() { + // TODO: Figure out what we want to do in this case. + }); + + test('not removing a parameter does not fire a delete event', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + proc.deleteParameter(0); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterDelete, + {}, + this.workspace.id); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_parameter_delete_test.js b/tests/mocha/event_procedure_parameter_delete_test.js new file mode 100644 index 000000000..d3dcc9ca0 --- /dev/null +++ b/tests/mocha/event_procedure_parameter_delete_test.js @@ -0,0 +1,200 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureParameterDelete'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Parameter Delete Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + setup(function() { + this.createProcedureModel = (name, id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, name, id); + }; + + this.createProcedureAndParameter = + (procName, procId, paramName, paramId) => { + const param = new Blockly.procedures.ObservableParameterModel( + this.workspace, procName, paramId); + const proc = new Blockly.procedures.ObservableProcedureModel( + this.workspace, paramName, procId) + .insertParameter(param, 0); + return {param, proc}; + }; + + this.createEventToState = (procedureModel, parameterModel) => { + return new Blockly.Events.ProcedureParameterCreate( + this.workspace, procedureModel, parameterModel); + }; + }); + + suite('forward', function() { + test('a parameter is removed if it exists', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + + event.run(true /* forward */); + + chai.assert.isUndefined( + proc.getParameter(0), + 'Expected the parameter to be deleted'); + }); + + test('removing a parameter fires a delete event', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterDelete, + { + model: proc, + parameter: param, + index: 0, + }, + this.workspace.id); + }); + + test( + 'a parameter is not deleted if a parameter with a ' + + 'matching ID and index does not exist', + function() { + // TODO: Figure out what we want to do in this case. + }); + + test('not removing a parameter does not fire a delete event', function() { + const {param, proc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(proc, param); + this.procedureMap.add(proc); + proc.deleteParameter(0); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterDelete, + {}, + this.workspace.id); + }); + }); + + suite('backward', function() { + test('a parameter is inserted if it does not exist', function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + const actualProc = this.createProcedureModel('test name', 'test id'); + this.procedureMap.add(actualProc); + + event.run(true /* forward */); + + const createdParam = actualProc.getParameter(0); + chai.assert.isDefined(createdParam, 'Expected the parameter to exist'); + chai.assert.equal( + createdParam.getName(), + modelParam.getName(), + "Expected the parameter's name to match the model"); + chai.assert.equal( + createdParam.getId(), + modelParam.getId(), + "Expected the parameter's id to match the model"); + }); + + test('inserting a parameter fires a create event', function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + const actualProc = this.createProcedureModel('test name', 'test id'); + this.procedureMap.add(actualProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterCreate, + { + model: actualProc, + parameter: actualProc.getParameter(0), + index: 0, + }, + this.workspace.id); + }); + + test( + 'a parameter is not created if a parameter with a ' + + 'matching ID and index already exists', + function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + this.procedureMap.add(modelProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + const actualProc = this.procedureMap.get('test id'); + chai.assert.equal( + actualProc, + modelProc, + 'Expected the procedure in the procedure map to not have changed'); + chai.assert.equal( + actualProc.getParameter(0), + modelParam, + 'Expected the parameter to not have changed'); + }); + + test( + 'not creating a parameter model does not fire a create event', + function() { + const {param: modelParam, proc: modelProc} = + this.createProcedureAndParameter( + 'test name', 'test id', 'test param name', 'test param id'); + const event = this.createEventToState(modelProc, modelParam); + this.procedureMap.add(modelProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterCreate, + {}, + this.workspace.id); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_parameter_rename_test.js b/tests/mocha/event_procedure_parameter_rename_test.js new file mode 100644 index 000000000..7bbaa2f3a --- /dev/null +++ b/tests/mocha/event_procedure_parameter_rename_test.js @@ -0,0 +1,206 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureParameterRename'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Parameter Rename Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + const DEFAULT_NAME = 'default'; + const NON_DEFAULT_NAME = 'non-default'; + + setup(function() { + this.createProcedureAndParameter = (procId, paramId) => { + const param = new Blockly.procedures.ObservableParameterModel( + this.workspace, DEFAULT_NAME, paramId); + const proc = new Blockly.procedures.ObservableProcedureModel( + this.workspace, 'test name', procId) + .insertParameter(param, 0); + return {param, proc}; + }; + + this.createEventToState = (procedureModel, parameterModel) => { + return new Blockly.Events.ProcedureRename( + this.workspace, + procedureModel, + parameterModel, + parameterModel.getName()); + }; + }); + + suite('forward', function() { + test('the parameter with the matching ID and index is renamed', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: finalParam, proc: finalProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + finalParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(finalProc, finalParam); + this.procedureMap.add(initialProc); + + event.run(true /* forward */); + + chai.assert.equal( + initialParam.getName(), + finalParam.getName(), + "Expected the procedure parameter's name to be changed"); + }); + + test('renaming a parameter fires a rename event', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: finalParam, proc: finalProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + finalParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(finalProc, finalParam); + this.procedureMap.add(initialProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterRename, + { + model: initialProc, + parameter: initialParam, + oldName: DEFAULT_NAME, + }, + this.workspace.id); + }); + + test('noop renames do not fire rename events', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: finalParam, proc: finalProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const event = this.createEventToState(finalProc, finalParam); + this.procedureMap.add(initialProc); + + this.eventSpy.resetHistory(); + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterRename, + {}, + this.workspace.id); + }); + + test( + 'attempting to rename a parameter that does not exist throws', + function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: finalParam, proc: finalProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + finalParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(finalProc, finalParam); + + chai.assert.throws(() => { + event.run(true /* forward */); + }); + }); + }); + + suite('backward', function() { + test('the parameter with the matching ID and index is renamed', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: undoableParam, proc: undoableProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + initialParam.setName(NON_DEFAULT_NAME); + undoableParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoableProc, undoableParam); + this.procedureMap.add(initialProc); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + chai.assert.equal( + initialParam.getName(), + DEFAULT_NAME, + "Expected the procedure parameter's name to be changed"); + }); + + test('renaming a parameter fires a rename event', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: undoableParam, proc: undoableProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + initialParam.setName(NON_DEFAULT_NAME); + undoableParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoableProc, undoableParam); + this.procedureMap.add(initialProc); + + this.eventSpy.resetHistory(); + event.run(false /* backward */); + + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureParameterRename, + { + model: initialProc, + parameter: initialParam, + oldName: NON_DEFAULT_NAME, + }, + this.workspace.id); + }); + + test('noop renames do not fire rename events', function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: undoableParam, proc: undoableProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + undoableParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoableProc, undoableParam); + this.procedureMap.add(initialProc); + + event.run(false /* backward */); + + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureParameterRename, + {}, + this.workspace.id); + }); + + test( + 'attempting to rename a parameter that does not exist throws', + function() { + const {param: initialParam, proc: initialProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + const {param: undoableParam, proc: undoableProc} = + this.createProcedureAndParameter('test procId', 'test paramId'); + initialParam.setName(NON_DEFAULT_NAME); + undoableParam.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoableProc, undoableParam); + this.procedureMap.add(initialProc); + + chai.assert.throws(() => { + event.run(false /* backward */); + }); + }); + }); + }); +}); diff --git a/tests/mocha/event_procedure_rename_test.js b/tests/mocha/event_procedure_rename_test.js new file mode 100644 index 000000000..b034438f7 --- /dev/null +++ b/tests/mocha/event_procedure_rename_test.js @@ -0,0 +1,170 @@ +/** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +goog.declareModuleId('Blockly.test.eventProcedureRename'); + +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; + + +// TODO (#6519): Unskip. +suite.skip('Procedure Rename Event', function() { + setup(function() { + sharedTestSetup.call(this); + this.workspace = new Blockly.Workspace(); + this.procedureMap = this.workspace.getProcedureMap(); + this.eventSpy = createChangeListenerSpy(this.workspace); + }); + + teardown(function() { + sharedTestTeardown.call(this); + }); + + suite('running', function() { + const DEFAULT_NAME = 'default'; + const NON_DEFAULT_NAME = 'non-default'; + + setup(function() { + this.createProcedureModel = (id) => { + return new Blockly.procedures.ObservableProcedureModel( + this.workspace, DEFAULT_NAME, id); + }; + + this.createEventToState = (procedureModel) => { + return new Blockly.Events.ProcedureRename( + this.workspace, + procedureModel, + procedureModel.getName()); + }; + }); + + suite('forward', function() { + test('the procedure with the matching ID is renamed', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + event.run(true /* forward */); + + chai.assert.equal( + initial.getName(), + final.getName(), + "Expected the procedure's name to be changed"); + }); + + test('renaming a procedure fires a rename event', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + event.run(true /* forward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureRename, + {model: initial, oldName: DEFAULT_NAME}, + this.workspace.id); + }); + + test('noop renames do not fire rename events', function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + const event = this.createEventToState(final); + this.procedureMap.add(initial); + + event.run(true /* forward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureRename, + {}, + this.workspace.id); + }); + + test( + 'attempting to rename a procedure that does not exist throws', + function() { + const initial = this.createProcedureModel('test id'); + const final = this.createProcedureModel('test id'); + final.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(final); + + chai.assert.throws(() => { + event.run(true /* forward */); + }); + }); + }); + + suite('backward', function() { + test('the procedure with the matching ID is renamed', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setName(NON_DEFAULT_NAME); + undoable.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + event.run(false /* backward */); + + chai.assert.equal( + initial.getName(), + DEFAULT_NAME, + "Expected the procedure's name to be changed"); + }); + + test('renaming a procedure fires a rename event', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setName(NON_DEFAULT_NAME); + undoable.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + event.run(false /* backward */); + + assertEventFiredShallow( + this.eventSpy, + Blockly.Events.ProcedureRename, + {model: initial, oldName: NON_DEFAULT_NAME}, + this.workspace.id); + }); + + test('noop renames do not fire rename events', function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoable); + this.procedureMap.add(initial); + + event.run(false /* backward */); + + assertEventNotFired( + this.eventSpy, + Blockly.Events.ProcedureRename, + {}, + this.workspace.id); + }); + + test( + 'attempting to rename a procedure that does not exist throws', + function() { + const initial = this.createProcedureModel('test id'); + const undoable = this.createProcedureModel('test id'); + initial.setName(NON_DEFAULT_NAME); + undoable.setName(NON_DEFAULT_NAME); + const event = this.createEventToState(undoable); + + chai.assert.throws(() => { + event.run(false /* backward */); + }); + }); + }); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index 11640c37a..ce43ace99 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -73,6 +73,14 @@ 'Blockly.test.eventCommentDelete', 'Blockly.test.eventCommentMove', 'Blockly.test.eventMarkerMove', + 'Blockly.test.eventProcedureCreate', + 'Blockly.test.eventProcedureDelete', + 'Blockly.test.eventProcedureRename', + 'Blockly.test.eventProcedureEnable', + 'Blockly.test.eventProcedureChangeReturn', + 'Blockly.test.eventProcedureParameterCreate', + 'Blockly.test.eventProcedureParameterDelete', + 'Blockly.test.eventProcedureParameterRename', 'Blockly.test.eventSelected', 'Blockly.test.eventThemeChange', 'Blockly.test.eventToolboxItemSelect', diff --git a/tests/mocha/procedure_map_test.js b/tests/mocha/procedure_map_test.js index 6d717a9db..80e3a37f1 100644 --- a/tests/mocha/procedure_map_test.js +++ b/tests/mocha/procedure_map_test.js @@ -5,7 +5,7 @@ */ import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown.js'; -import {assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; +import {assertEventFiredShallow, assertEventNotFired, createChangeListenerSpy} from './test_helpers/events.js'; goog.declareModuleId('Blockly.test.procedureMap'); @@ -202,25 +202,6 @@ suite('Procedure Map', function() { }); suite('event firing', function() { - function shallowMatch(expected) { - return (actual) => { - for (const key in expected) { - if (actual[key] !== expected[key]) { - return false; - } - } - return true; - }; - } - - function assertEventFired(spy, instanceType, properties, workspace) { - properties = {...properties, workspaceId: workspace.id}; - sinon.assert.calledWith( - spy, - sinon.match.instanceOf(instanceType) - .and(shallowMatch(properties))); - } - setup(function() { this.eventSpy = createChangeListenerSpy(this.workspace); }); @@ -235,11 +216,11 @@ suite('Procedure Map', function() { new Blockly.procedures.ObservableProcedureModel(this.workspace); this.procedureMap.set(procedureModel.getId(), procedureModel); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureCreate, {model: procedureModel}, - this.workspace); + this.workspace.id); }); test( @@ -264,11 +245,11 @@ suite('Procedure Map', function() { new Blockly.procedures.ObservableProcedureModel(this.workspace); this.procedureMap.add(procedureModel); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureCreate, {model: procedureModel}, - this.workspace); + this.workspace.id); }); test( @@ -285,7 +266,7 @@ suite('Procedure Map', function() { this.eventSpy, Blockly.Events.ProcedureCreate, {}, - this.workspace); + this.workspace.id); }); }); @@ -296,11 +277,11 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); this.procedureMap.delete(procedureModel.getId()); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureDelete, {model: procedureModel}, - this.workspace); + this.workspace.id); }); test( @@ -331,21 +312,21 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel3); this.procedureMap.clear(); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureDelete, {model: procedureModel1}, - this.workspace); - assertEventFired( + this.workspace.id); + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureDelete, {model: procedureModel2}, - this.workspace); - assertEventFired( + this.workspace.id); + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureDelete, {model: procedureModel3}, - this.workspace); + this.workspace.id); }); }); @@ -357,14 +338,14 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); procedureModel.setName('new name'); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureRename, { model: procedureModel, oldName: 'test name', }, - this.workspace); + this.workspace.id); }); test('rename events are not fired if the rename is noop', function() { @@ -405,11 +386,11 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); procedureModel.setEnabled(true); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureEnable, {model: procedureModel}, - this.workspace); + this.workspace.id); }); test('enable events are fired when a procedure is disabled', function() { @@ -418,11 +399,11 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); procedureModel.setEnabled(false); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureEnable, {model: procedureModel}, - this.workspace); + this.workspace.id); }); test('enable events are not fired if enabling is noop', function() { @@ -480,7 +461,7 @@ suite('Procedure Map', function() { this.workspace, 'test name'); procedureModel.insertParameter(parameterModel, 0); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureParameterCreate, { @@ -488,7 +469,7 @@ suite('Procedure Map', function() { parameter: parameterModel, index: 0, }, - this.workspace); + this.workspace.id); }); test( @@ -545,7 +526,7 @@ suite('Procedure Map', function() { procedureModel.insertParameter(parameterModel, 0); procedureModel.deleteParameter(0); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureParameterDelete, { @@ -553,7 +534,7 @@ suite('Procedure Map', function() { parameter: parameterModel, index: 0, }, - this.workspace); + this.workspace.id); }); test( @@ -568,7 +549,7 @@ suite('Procedure Map', function() { this.eventSpy, Blockly.Events.ProcedureParameterDelete, {}, - this.workspace); + this.workspace.id); }); test( @@ -605,8 +586,7 @@ suite('Procedure Map', function() { parameterModel.setName('new name'); - console.log(this.eventSpy.getCalls()); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureParameterRename, { @@ -614,7 +594,7 @@ suite('Procedure Map', function() { parameter: parameterModel, oldName: 'test name', }, - this.workspace); + this.workspace.id); }); test( @@ -684,14 +664,14 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); procedureModel.setReturnTypes([]); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureChangeReturn, { model: procedureModel, oldTypes: null, }, - this.workspace); + this.workspace.id); }); test( @@ -704,14 +684,14 @@ suite('Procedure Map', function() { this.procedureMap.add(procedureModel); procedureModel.setReturnTypes(null); - assertEventFired( + assertEventFiredShallow( this.eventSpy, Blockly.Events.ProcedureChangeReturn, { model: procedureModel, oldTypes: types, }, - this.workspace); + this.workspace.id); }); test( diff --git a/tests/mocha/test_helpers/events.js b/tests/mocha/test_helpers/events.js index 0f5839d9b..52d2a2553 100644 --- a/tests/mocha/test_helpers/events.js +++ b/tests/mocha/test_helpers/events.js @@ -130,6 +130,49 @@ export function assertEventFired(spy, instanceType, expectedProperties, sinon.assert.calledWith(spy, expectedEvent); } +/** + * Returns a matcher that asserts that the actual object has the same properties + * and values (shallowly equated) as the expected object. + * @param {!Object} expected The expected set of properties we expect the + * actual object to have. + * @return {function(*): boolean} A matcher that returns true if the `actual` + * object has all of the properties of the `expected` param, with the same + * values. + */ +function shallowMatch(expected) { + return (actual) => { + for (const key in expected) { + if (actual[key] !== expected[key]) { + return false; + } + } + return true; + }; +} + +/** + * Asserts that an event with the given values (shallowly evaluated) was fired. + * @param {!SinonSpy|!SinonSpyCall} spy The spy or spy call to use. + * @param {function(new:Blockly.Events.Abstract)} instanceType Expected instance + * type of event fired. + * @param {!Object} expectedProperties Map of of expected properties + * to check on fired event. + * @param {string} expectedWorkspaceId Expected workspace id of event fired. + * @param {?string=} expectedBlockId Expected block id of event fired. + */ +export function assertEventFiredShallow( + spy, instanceType, expectedProperties, expectedWorkspaceId, expectedBlockId) { + const properties = { + ...expectedProperties, + workspaceId: expectedWorkspaceId, + blockId: expectedBlockId, + }; + sinon.assert.calledWith( + spy, + sinon.match.instanceOf(instanceType) + .and(shallowMatch(properties))); +} + /** * Asserts that an event with the given values was not fired. * @param {!SpyCall} spy The spy to use.