diff --git a/core/block.ts b/core/block.ts index ee81f1cf8..8dea2fed6 100644 --- a/core/block.ts +++ b/core/block.ts @@ -20,6 +20,7 @@ import './events/events_block_create.js'; import './events/events_block_delete.js'; import {Blocks} from './blocks.js'; +import {BlockDelete} from './events/events_block_delete.js'; import type {Comment} from './comment.js'; import * as common from './common.js'; import {Connection} from './connection.js'; @@ -330,10 +331,6 @@ export class Block implements IASTNodeLocation, IDeletable { eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_DELETE))(this)); } - if (this.onchangeWrapper_) { - this.workspace.removeChangeListener(this.onchangeWrapper_); - } - eventUtils.disable(); try { @@ -1027,7 +1024,13 @@ export class Block implements IASTNodeLocation, IDeletable { this.workspace.removeChangeListener(this.onchangeWrapper_); } this.onchange = onchangeFn; - this.onchangeWrapper_ = onchangeFn.bind(this); + this.onchangeWrapper_ = (e) => { + onchangeFn.call(this, e); + if (e.type === eventUtils.BLOCK_DELETE && + (e as BlockDelete).blockId === this.id) { + this.workspace.removeChangeListener(this.onchangeWrapper_!); + } + }; this.workspace.addChangeListener(this.onchangeWrapper_); } diff --git a/tests/mocha/event_block_delete_test.js b/tests/mocha/event_block_delete_test.js index 4f03f6629..6fec23b40 100644 --- a/tests/mocha/event_block_delete_test.js +++ b/tests/mocha/event_block_delete_test.js @@ -12,7 +12,8 @@ import {sharedTestSetup, sharedTestTeardown} from './test_helpers/setup_teardown suite('Block Delete Event', function() { setup(function() { - sharedTestSetup.call(this); + const {clock} = sharedTestSetup.call(this, {fireEventsNow: false}); + this.clock = clock; defineRowBlock(); this.workspace = new Blockly.Workspace(); }); @@ -22,9 +23,9 @@ suite('Block Delete Event', function() { }); suite('Receiving', function() { - test('blocks receive their own delete events', function() { + test('blocks receive their own delete events', function(done) { Blockly.Blocks['test'] = { - onchange: function(e) {}, + onchange: function(e) { }, }; // Need to stub the definition, because the property on the definition is // what gets registered as an event listener. @@ -32,10 +33,13 @@ suite('Block Delete Event', function() { const testBlock = this.workspace.newBlock('test'); testBlock.dispose(); + this.clock.tick(2); // Fire events. The built-in timeout is 0. const deleteClass = eventUtils.get(eventUtils.BLOCK_DELETE); - chai.assert.isTrue(spy.calledOnce); - chai.assert.isTrue(spy.getCall(0).args[0] instanceof deleteClass); + chai.assert.isTrue( + spy.calledWith(sinon.match.instanceOf(deleteClass)), + 'Expected the block to receive its own delete event.'); + done(); }); }); diff --git a/tests/mocha/test_helpers/setup_teardown.js b/tests/mocha/test_helpers/setup_teardown.js index 669c6564b..f77d9d4d7 100644 --- a/tests/mocha/test_helpers/setup_teardown.js +++ b/tests/mocha/test_helpers/setup_teardown.js @@ -105,6 +105,8 @@ function wrapDefineBlocksWithJsonArrayWithCleanup_(sharedCleanupObj) { * * @param {Object} options Options to enable/disable setup * of certain stubs. + * @return {{clock: *}} The fake clock (as part of an object to make refactoring + * easier). */ export function sharedTestSetup(options = {}) { this.sharedSetupCalled_ = true; @@ -122,6 +124,9 @@ export function sharedTestSetup(options = {}) { this.blockTypesCleanup_ = this.sharedCleanup.blockTypesCleanup_; this.messagesCleanup_ = this.sharedCleanup.messagesCleanup_; wrapDefineBlocksWithJsonArrayWithCleanup_(this.sharedCleanup); + return { + clock: this.clock, + }; } /**