82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
// https://github.com/oven-sh/bun/issues/24147
|
|
// EventEmitter: this._events becomes undefined when removeAllListeners()
|
|
// called from event handler with removeListener meta-listener
|
|
|
|
import { EventEmitter } from "events";
|
|
import assert from "node:assert";
|
|
import { test } from "node:test";
|
|
|
|
test("removeAllListeners() from event handler with removeListener meta-listener", () => {
|
|
const emitter = new EventEmitter();
|
|
|
|
emitter.on("test", () => {
|
|
// This should not crash even though there are no 'foo' listeners
|
|
emitter.removeAllListeners("foo");
|
|
});
|
|
|
|
// Register a removeListener meta-listener to trigger the bug
|
|
emitter.on("removeListener", () => {});
|
|
|
|
// This should not throw
|
|
assert.doesNotThrow(() => emitter.emit("test"));
|
|
});
|
|
|
|
test("removeAllListeners() with actual listeners to remove", () => {
|
|
const emitter = new EventEmitter();
|
|
let fooCallCount = 0;
|
|
let removeListenerCallCount = 0;
|
|
|
|
emitter.on("foo", () => fooCallCount++);
|
|
emitter.on("foo", () => fooCallCount++);
|
|
|
|
emitter.on("test", () => {
|
|
// Remove all 'foo' listeners while inside an event handler
|
|
emitter.removeAllListeners("foo");
|
|
});
|
|
|
|
// Track removeListener calls
|
|
emitter.on("removeListener", () => {
|
|
removeListenerCallCount++;
|
|
});
|
|
|
|
// Emit test event which triggers removeAllListeners
|
|
emitter.emit("test");
|
|
|
|
// Verify listeners were removed
|
|
assert.strictEqual(emitter.listenerCount("foo"), 0);
|
|
|
|
// Verify removeListener was called twice (once for each foo listener)
|
|
assert.strictEqual(removeListenerCallCount, 2);
|
|
|
|
// Verify foo listeners were never called
|
|
assert.strictEqual(fooCallCount, 0);
|
|
});
|
|
|
|
test("nested removeAllListeners() calls", () => {
|
|
const emitter = new EventEmitter();
|
|
const events: string[] = [];
|
|
|
|
emitter.on("outer", () => {
|
|
events.push("outer-start");
|
|
emitter.removeAllListeners("inner");
|
|
events.push("outer-end");
|
|
});
|
|
|
|
emitter.on("inner", () => {
|
|
events.push("inner");
|
|
});
|
|
|
|
emitter.on("removeListener", type => {
|
|
events.push(`removeListener:${String(type)}`);
|
|
});
|
|
|
|
// This should not crash
|
|
assert.doesNotThrow(() => emitter.emit("outer"));
|
|
|
|
// Verify correct execution order
|
|
assert.deepStrictEqual(events, ["outer-start", "removeListener:inner", "outer-end"]);
|
|
|
|
// Verify inner listeners were removed
|
|
assert.strictEqual(emitter.listenerCount("inner"), 0);
|
|
});
|