Skip to main content
Module

std/node/events_test.ts

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
const { test } = Deno;import { assert, assertEquals, fail, assertThrows,} from "../testing/asserts.ts";import EventEmitter, { WrappedFunction, once, on } 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: string[] = []; const testEmitter = new EventEmitter(); testEmitter.on("newListener", (event: string) => { 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: string[] = []; 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: string[] = []; testEmitter.on("event", (oneArg: string) => { eventsFired.push("event(" + oneArg + ")"); }); testEmitter.on("event", (oneArg: string, twoArg: string) => { eventsFired.push("event(" + oneArg + ", " + twoArg + ")"); });
testEmitter.on("non-event", shouldNeverBeEmitted);
testEmitter.on( "event", (oneArg: string, twoArg: string, threeArg: string) => { 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: string[] = []; 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: string[] = []; 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: string[] = []; 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: string[] = []; 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: string[] = []; 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: string[] = []; 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 = 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 = new EventEmitter(); ee.setMaxListeners(0); assertThrows( () => { ee.setMaxListeners(-1); }, Error, "must be >= 0" ); assertThrows( () => { ee.setMaxListeners(3.45); }, Error, "must be 'an integer'" ); },});
test({ name: "ErrorMonitor can spy on error events without consuming them", fn() { const ee = new EventEmitter(); let events: string[] = []; //unhandled error scenario should throw assertThrows( () => { ee.emit("error"); }, Error, "Unhandled error" );
ee.on(EventEmitter.errorMonitor, () => { events.push("errorMonitor event"); });
//error is still unhandled but also intercepted by error monitor assertThrows( () => { ee.emit("error"); }, Error, "Unhandled error" ); assertEquals(events, ["errorMonitor event"]);
//A registered error handler won't throw, but still be monitored events = []; ee.on("error", () => { events.push("error"); }); ee.emit("error"); assertEquals(events, ["errorMonitor event", "error"]); },});
test({ name: "asyncronous iteration of events are handled as expected", async fn() { const ee = new EventEmitter(); setTimeout(() => { ee.emit("foo", "bar"); ee.emit("bar", 24); ee.emit("foo", 42); }, 0);
const iterable = on(ee, "foo");
const expected = [["bar"], [42]];
for await (const event of iterable) { const current = expected.shift();
assertEquals(current, event);
if (expected.length === 0) { break; } } assertEquals(ee.listenerCount("foo"), 0); assertEquals(ee.listenerCount("error"), 0); },});
test({ name: "asyncronous error handling of emitted events works as expected", async fn() { const ee = new EventEmitter(); const _err = new Error("kaboom"); setTimeout(() => { ee.emit("error", _err); }, 0);
const iterable = on(ee, "foo"); let thrown = false;
try { // eslint-disable-next-line @typescript-eslint/no-unused-vars for await (const event of iterable) { fail("no events should be processed due to the error thrown"); } } catch (err) { thrown = true; assertEquals(err, _err); } assertEquals(thrown, true); },});
test({ name: "error thrown during asyncronous processing of events is handled", async fn() { const ee = new EventEmitter(); const _err = new Error("kaboom"); setTimeout(() => { ee.emit("foo", 42); ee.emit("error", _err); }, 0);
const iterable = on(ee, "foo"); const expected = [[42]]; let thrown = false;
try { for await (const event of iterable) { const current = expected.shift(); assertEquals(current, event); } } catch (err) { thrown = true; assertEquals(err, _err); } assertEquals(thrown, true); assertEquals(ee.listenerCount("foo"), 0); assertEquals(ee.listenerCount("error"), 0); },});
test({ name: "error thrown in processing loop of asyncronous event prevents processing of additional events", async fn() { const ee = new EventEmitter(); const _err = new Error("kaboom");
setTimeout(() => { ee.emit("foo", 42); ee.emit("foo", 999); }, 0);
try { for await (const event of on(ee, "foo")) { assertEquals(event, [42]); throw _err; } } catch (err) { assertEquals(err, _err); }
assertEquals(ee.listenerCount("foo"), 0); assertEquals(ee.listenerCount("error"), 0); },});
test({ name: "asyncronous iterator next() works as expected", async fn() { const ee = new EventEmitter(); const iterable = on(ee, "foo");
setTimeout(function () { ee.emit("foo", "bar"); ee.emit("foo", 42); iterable.return(); }, 0);
const results = await Promise.all([ iterable.next(), iterable.next(), iterable.next(), ]);
assertEquals(results, [ { value: ["bar"], done: false, }, { value: [42], done: false, }, { value: undefined, done: true, }, ]);
assertEquals(await iterable.next(), { value: undefined, done: true, }); },});
test({ name: "async iterable throw handles various scenarios", async fn() { const ee = new EventEmitter(); const iterable = on(ee, "foo");
setTimeout(() => { ee.emit("foo", "bar"); ee.emit("foo", 42); // lost in the queue iterable.throw(_err); }, 0);
const _err = new Error("kaboom"); let thrown = false;
const expected = [["bar"], [42]];
try { for await (const event of iterable) { assertEquals(event, expected.shift()); } } catch (err) { thrown = true; assertEquals(err, _err); } assertEquals(thrown, true); assertEquals(expected.length, 0); assertEquals(ee.listenerCount("foo"), 0); assertEquals(ee.listenerCount("error"), 0); },});