diff options
Diffstat (limited to 'std/node/events_test.ts')
-rw-r--r-- | std/node/events_test.ts | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/std/node/events_test.ts b/std/node/events_test.ts new file mode 100644 index 000000000..0d8dd52e1 --- /dev/null +++ b/std/node/events_test.ts @@ -0,0 +1,403 @@ +import { test } from "../testing/mod.ts"; +import { + assert, + assertEquals, + fail, + assertThrows +} from "../testing/asserts.ts"; +import EventEmitter, { WrappedFunction, once } from "./events.ts"; + +const shouldNeverBeEmitted: Function = () => { + fail("Should never be called"); +}; + +test({ + name: + 'When adding a new event, "eventListener" event is fired before adding the listener', + fn() { + let eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("newListener", event => { + if (event !== "newListener") { + eventsFired.push("newListener"); + } + }); + testEmitter.on("event", () => { + eventsFired.push("event"); + }); + assertEquals(eventsFired, ["newListener"]); + eventsFired = []; + testEmitter.emit("event"); + assertEquals(eventsFired, ["event"]); + } +}); + +test({ + name: + 'When removing a listenert, "removeListener" event is fired after removal', + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("removeListener", () => { + eventsFired.push("removeListener"); + }); + const eventFunction = function(): void { + eventsFired.push("event"); + }; + testEmitter.on("event", eventFunction); + + assertEquals(eventsFired, []); + testEmitter.removeListener("event", eventFunction); + assertEquals(eventsFired, ["removeListener"]); + } +}); + +test({ + name: + "Default max listeners is 10, but can be changed by direct assignment only", + fn() { + assertEquals(EventEmitter.defaultMaxListeners, 10); + new EventEmitter().setMaxListeners(20); + assertEquals(EventEmitter.defaultMaxListeners, 10); + EventEmitter.defaultMaxListeners = 20; + assertEquals(EventEmitter.defaultMaxListeners, 20); + EventEmitter.defaultMaxListeners = 10; //reset back to original value + } +}); + +test({ + name: "addListener adds a listener, and listener count is correct", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(1, testEmitter.listenerCount("event")); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(2, testEmitter.listenerCount("event")); + } +}); + +test({ + name: "Emitted events are called synchronously in the order they were added", + fn() { + const testEmitter = new EventEmitter(); + const eventsFired = []; + testEmitter.on("event", oneArg => { + eventsFired.push("event(" + oneArg + ")"); + }); + testEmitter.on("event", (oneArg, twoArg) => { + eventsFired.push("event(" + oneArg + ", " + twoArg + ")"); + }); + + testEmitter.on("non-event", shouldNeverBeEmitted); + + testEmitter.on("event", (oneArg, twoArg, threeArg) => { + eventsFired.push( + "event(" + oneArg + ", " + twoArg + ", " + threeArg + ")" + ); + }); + testEmitter.emit("event", 1, 2, 3); + assertEquals(eventsFired, ["event(1)", "event(1, 2)", "event(1, 2, 3)"]); + } +}); + +test({ + name: "Registered event names are returned as strings or Sybols", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + const sym = Symbol("symbol"); + testEmitter.on(sym, shouldNeverBeEmitted); + assertEquals(testEmitter.eventNames(), ["event", sym]); + } +}); + +test({ + name: "You can set and get max listeners", + fn() { + const testEmitter = new EventEmitter(); + assertEquals(testEmitter.getMaxListeners(), 10); + testEmitter.setMaxListeners(20); + assertEquals(testEmitter.getMaxListeners(), 20); + } +}); + +test({ + name: "You can retrieve registered functions for an event", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("someOtherEvent", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + const testFunction = (): void => {}; + testEmitter.on("event", testFunction); + assertEquals(testEmitter.listeners("event"), [ + shouldNeverBeEmitted, + testFunction + ]); + } +}); + +test({ + name: "Off is alias for removeListener", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 1); + testEmitter.off("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 0); + } +}); + +test({ + name: "Event registration can be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter + .on("event", shouldNeverBeEmitted) + .on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + } +}); + +test({ + name: "Events can be registered to only fire once", + fn() { + let eventsFired = []; + const testEmitter = new EventEmitter(); + //prove multiple emits on same event first (when registered with 'on') + testEmitter.on("multiple event", () => { + eventsFired.push("multiple event"); + }); + testEmitter.emit("multiple event"); + testEmitter.emit("multiple event"); + assertEquals(eventsFired, ["multiple event", "multiple event"]); + + //now prove multiple events registered via 'once' only emit once + eventsFired = []; + testEmitter.once("single event", () => { + eventsFired.push("single event"); + }); + testEmitter.emit("single event"); + testEmitter.emit("single event"); + assertEquals(eventsFired, ["single event"]); + } +}); + +test({ + name: + "You can inject a listener into the start of the stack, rather than at the end", + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("event", () => { + eventsFired.push("first"); + }); + testEmitter.on("event", () => { + eventsFired.push("second"); + }); + testEmitter.prependListener("event", () => { + eventsFired.push("third"); + }); + testEmitter.emit("event"); + assertEquals(eventsFired, ["third", "first", "second"]); + } +}); + +test({ + name: 'You can prepend a "once" listener', + fn() { + const eventsFired = []; + const testEmitter = new EventEmitter(); + testEmitter.on("event", () => { + eventsFired.push("first"); + }); + testEmitter.on("event", () => { + eventsFired.push("second"); + }); + testEmitter.prependOnceListener("event", () => { + eventsFired.push("third"); + }); + testEmitter.emit("event"); + testEmitter.emit("event"); + assertEquals(eventsFired, ["third", "first", "second", "first", "second"]); + } +}); + +test({ + name: "Remove all listeners, which can also be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("other event", shouldNeverBeEmitted); + testEmitter.on("other event", shouldNeverBeEmitted); + testEmitter.once("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 3); + + testEmitter.removeAllListeners("event").removeAllListeners("other event"); + + assertEquals(testEmitter.listenerCount("event"), 0); + assertEquals(testEmitter.listenerCount("other event"), 0); + } +}); + +test({ + name: "Remove individual listeners, which can also be chained", + fn() { + const testEmitter = new EventEmitter(); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.on("event", shouldNeverBeEmitted); + testEmitter.once("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 1); + + testEmitter.removeListener("other event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 2); + assertEquals(testEmitter.listenerCount("other event"), 0); + + testEmitter + .removeListener("event", shouldNeverBeEmitted) + .removeListener("event", shouldNeverBeEmitted); + + assertEquals(testEmitter.listenerCount("event"), 0); + assertEquals(testEmitter.listenerCount("other event"), 0); + } +}); + +test({ + name: "It is OK to try to remove non-existant listener", + fn() { + const testEmitter = new EventEmitter(); + + const madeUpEvent = (): void => { + fail("Should never be called"); + }; + + testEmitter.on("event", shouldNeverBeEmitted); + assertEquals(testEmitter.listenerCount("event"), 1); + + testEmitter.removeListener("event", madeUpEvent); + testEmitter.removeListener("non-existant event", madeUpEvent); + + assertEquals(testEmitter.listenerCount("event"), 1); + } +}); + +test({ + name: "all listeners complete execution even if removed before execution", + fn() { + const testEmitter = new EventEmitter(); + let eventsProcessed = []; + const listenerB = (): number => eventsProcessed.push("B"); + const listenerA = (): void => { + eventsProcessed.push("A"); + testEmitter.removeListener("event", listenerB); + }; + + testEmitter.on("event", listenerA); + testEmitter.on("event", listenerB); + + testEmitter.emit("event"); + assertEquals(eventsProcessed, ["A", "B"]); + + eventsProcessed = []; + testEmitter.emit("event"); + assertEquals(eventsProcessed, ["A"]); + } +}); + +test({ + name: 'Raw listener will return event listener or wrapped "once" function', + fn() { + const testEmitter = new EventEmitter(); + const eventsProcessed = []; + const listenerA = (): number => eventsProcessed.push("A"); + const listenerB = (): number => eventsProcessed.push("B"); + testEmitter.on("event", listenerA); + testEmitter.once("once-event", listenerB); + + const rawListenersForEvent = testEmitter.rawListeners("event"); + const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); + + assertEquals(rawListenersForEvent.length, 1); + assertEquals(rawListenersForOnceEvent.length, 1); + assertEquals(rawListenersForEvent[0], listenerA); + assertEquals( + (rawListenersForOnceEvent[0] as WrappedFunction).listener, + listenerB + ); + } +}); + +test({ + name: + "Once wrapped raw listeners may be executed multiple times, until the wrapper is executed", + fn() { + const testEmitter = new EventEmitter(); + let eventsProcessed = []; + const listenerA = (): number => eventsProcessed.push("A"); + testEmitter.once("once-event", listenerA); + + const rawListenersForOnceEvent = testEmitter.rawListeners("once-event"); + const wrappedFn: WrappedFunction = rawListenersForOnceEvent[0] as WrappedFunction; + wrappedFn.listener(); + wrappedFn.listener(); + wrappedFn.listener(); + assertEquals(eventsProcessed, ["A", "A", "A"]); + + eventsProcessed = []; + wrappedFn(); // executing the wrapped listener function will remove it from the event + assertEquals(eventsProcessed, ["A"]); + assertEquals(testEmitter.listeners("once-event").length, 0); + } +}); + +test({ + name: "Can add once event listener to EventEmitter via standalone function", + async fn() { + const ee: EventEmitter = new EventEmitter(); + setTimeout(() => { + ee.emit("event", 42, "foo"); + }, 0); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const valueArr: any[] = await once(ee, "event"); + assertEquals(valueArr, [42, "foo"]); + } +}); + +test({ + name: "Can add once event listener to EventTarget via standalone function", + async fn() { + const et: EventTarget = new EventTarget(); + setTimeout(() => { + const event: Event = new Event("event", { composed: true }); + et.dispatchEvent(event); + }, 0); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const eventObj: any[] = await once(et, "event"); + assert(!eventObj[0].isTrusted); + } +}); + +test({ + name: "Only valid integers are allowed for max listeners", + fn() { + const ee: EventEmitter = new EventEmitter(); + ee.setMaxListeners(0); + assertThrows( + () => { + ee.setMaxListeners(-1); + }, + Error, + "must be >= 0" + ); + assertThrows( + () => { + ee.setMaxListeners(3.45); + }, + Error, + "must be 'an integer'" + ); + } +}); |