mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
885 lines
23 KiB
TypeScript
885 lines
23 KiB
TypeScript
import { sleep } from "bun";
|
|
import { describe, expect, mock, test } from "bun:test";
|
|
import { createRequire } from "module";
|
|
|
|
// this is also testing that imports with default and named imports in the same statement work
|
|
// our transpiler transform changes this to a var with import.meta.require
|
|
import EventEmitter, { captureRejectionSymbol, getEventListeners, getMaxListeners, setMaxListeners } from "node:events";
|
|
|
|
describe("node:events", () => {
|
|
test("captureRejectionSymbol", () => {
|
|
expect(EventEmitter.captureRejectionSymbol).toBeDefined();
|
|
expect(captureRejectionSymbol).toBeDefined();
|
|
expect(captureRejectionSymbol).toBe(EventEmitter.captureRejectionSymbol);
|
|
});
|
|
|
|
test("once", done => {
|
|
const emitter = new EventEmitter();
|
|
EventEmitter.once(emitter, "hey").then(x => {
|
|
try {
|
|
expect(x).toEqual([1, 5]);
|
|
} catch (error) {
|
|
done(error);
|
|
}
|
|
done();
|
|
});
|
|
emitter.emit("hey", 1, 5);
|
|
});
|
|
|
|
test("once (abort)", done => {
|
|
const emitter = new EventEmitter();
|
|
const controller = new AbortController();
|
|
EventEmitter.once(emitter, "hey", { signal: controller.signal })
|
|
.then(() => done(new Error("Should not be called")))
|
|
.catch(() => done());
|
|
controller.abort();
|
|
});
|
|
|
|
test("once (two events in same tick)", done => {
|
|
const emitter = new EventEmitter();
|
|
EventEmitter.once(emitter, "hey").then(() => {
|
|
EventEmitter.once(emitter, "hey").then(data => {
|
|
try {
|
|
expect(data).toEqual([3]);
|
|
} catch (error) {
|
|
done(error);
|
|
}
|
|
done();
|
|
});
|
|
setTimeout(() => {
|
|
emitter.emit("hey", 3);
|
|
}, 10);
|
|
});
|
|
emitter.emit("hey", 1);
|
|
emitter.emit("hey", 2);
|
|
});
|
|
|
|
/// https://github.com/oven-sh/bun/issues/4518
|
|
test("once removes the listener afterwards", async () => {
|
|
const emitter = new EventEmitter();
|
|
process.nextTick(() => {
|
|
emitter.emit("hey", 1);
|
|
});
|
|
const promise = EventEmitter.once(emitter, "hey");
|
|
expect(emitter.listenerCount("hey")).toBe(1);
|
|
await promise;
|
|
expect(emitter.listenerCount("hey")).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("EventEmitter", () => {
|
|
test("getEventListeners", () => {
|
|
expect(getEventListeners(new EventEmitter(), "hey").length).toBe(0);
|
|
const emitter = new EventEmitter();
|
|
emitter.on("hey", () => {});
|
|
expect(getEventListeners(emitter, "hey").length).toBe(1);
|
|
});
|
|
|
|
test("constructor", () => {
|
|
var emitter = new EventEmitter();
|
|
emitter.setMaxListeners(100);
|
|
expect(emitter.getMaxListeners()).toBe(100);
|
|
});
|
|
|
|
test("removeAllListeners()", () => {
|
|
var emitter = new EventEmitter() as any;
|
|
var ran = false;
|
|
emitter.on("hey", () => {
|
|
ran = true;
|
|
});
|
|
emitter.on("hey", () => {
|
|
ran = true;
|
|
});
|
|
emitter.on("exit", () => {
|
|
ran = true;
|
|
});
|
|
const { _events } = emitter;
|
|
emitter.removeAllListeners();
|
|
expect(emitter.listenerCount("hey")).toBe(0);
|
|
expect(emitter.listenerCount("exit")).toBe(0);
|
|
emitter.emit("hey");
|
|
emitter.emit("exit");
|
|
expect(ran).toBe(false);
|
|
expect(_events).not.toBe(emitter._events); // This looks wrong but node.js replaces it too
|
|
emitter.on("hey", () => {
|
|
ran = true;
|
|
});
|
|
emitter.emit("hey");
|
|
expect(ran).toBe(true);
|
|
expect(emitter.listenerCount("hey")).toBe(1);
|
|
});
|
|
|
|
test("removeAllListeners(type)", () => {
|
|
var emitter = new EventEmitter();
|
|
var ran = false;
|
|
emitter.on("hey", () => {
|
|
ran = true;
|
|
});
|
|
emitter.on("exit", () => {
|
|
ran = true;
|
|
});
|
|
expect(emitter.listenerCount("hey")).toBe(1);
|
|
emitter.removeAllListeners("hey");
|
|
expect(emitter.listenerCount("hey")).toBe(0);
|
|
expect(emitter.listenerCount("exit")).toBe(1);
|
|
emitter.emit("hey");
|
|
expect(ran).toBe(false);
|
|
emitter.emit("exit");
|
|
expect(ran).toBe(true);
|
|
});
|
|
|
|
// These are also tests for the done() function in the test runner.
|
|
describe("emit", () => {
|
|
test("different tick", done => {
|
|
var emitter = new EventEmitter();
|
|
emitter.on("wow", () => done());
|
|
queueMicrotask(() => {
|
|
emitter.emit("wow");
|
|
});
|
|
});
|
|
|
|
// Unlike Jest, bun supports async and done
|
|
test("async microtask before", done => {
|
|
(async () => {
|
|
await 1;
|
|
var emitter = new EventEmitter();
|
|
emitter.on("wow", () => done());
|
|
emitter.emit("wow");
|
|
})();
|
|
});
|
|
|
|
test("async microtask after", done => {
|
|
(async () => {
|
|
var emitter = new EventEmitter();
|
|
emitter.on("wow", () => done());
|
|
await 1;
|
|
emitter.emit("wow");
|
|
})();
|
|
});
|
|
|
|
test("same tick", done => {
|
|
var emitter = new EventEmitter();
|
|
|
|
emitter.on("wow", () => done());
|
|
|
|
emitter.emit("wow");
|
|
});
|
|
|
|
test("setTimeout task", done => {
|
|
var emitter = new EventEmitter();
|
|
emitter.on("wow", () => done());
|
|
setTimeout(() => emitter.emit("wow"), 1);
|
|
});
|
|
|
|
test("emit multiple values", () => {
|
|
const emitter = new EventEmitter();
|
|
|
|
const receivedVals: number[] = [];
|
|
emitter.on("multiple-vals", (val1, val2, val3) => {
|
|
receivedVals[0] = val1;
|
|
receivedVals[1] = val2;
|
|
receivedVals[2] = val3;
|
|
});
|
|
|
|
emitter.emit("multiple-vals", 1, 2, 3);
|
|
|
|
expect(receivedVals).toEqual([1, 2, 3]);
|
|
});
|
|
});
|
|
|
|
test("addListener return type", () => {
|
|
var myEmitter = new EventEmitter();
|
|
expect(myEmitter.addListener("foo", () => {})).toBe(myEmitter);
|
|
});
|
|
|
|
test("addListener validates function", () => {
|
|
var myEmitter = new EventEmitter();
|
|
expect(() => myEmitter.addListener("foo", {} as any)).toThrow();
|
|
});
|
|
|
|
test("removeListener return type", () => {
|
|
var myEmitter = new EventEmitter();
|
|
expect(myEmitter.removeListener("foo", () => {})).toBe(myEmitter);
|
|
});
|
|
|
|
test("once", () => {
|
|
var myEmitter = new EventEmitter();
|
|
var calls = 0;
|
|
|
|
const fn = () => {
|
|
calls++;
|
|
};
|
|
|
|
myEmitter.once("foo", fn);
|
|
|
|
expect(myEmitter.listenerCount("foo")).toBe(1);
|
|
expect(myEmitter.listeners("foo")).toEqual([fn]);
|
|
|
|
myEmitter.emit("foo");
|
|
myEmitter.emit("foo");
|
|
|
|
expect(calls).toBe(1);
|
|
expect(myEmitter.listenerCount("foo")).toBe(0);
|
|
});
|
|
|
|
test("addListener/removeListener aliases", () => {
|
|
expect(EventEmitter.prototype.addListener).toBe(EventEmitter.prototype.on);
|
|
expect(EventEmitter.prototype.removeListener).toBe(EventEmitter.prototype.off);
|
|
});
|
|
|
|
test("prependListener", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const order: number[] = [];
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(1);
|
|
});
|
|
|
|
myEmitter.prependListener("foo", () => {
|
|
order.push(2);
|
|
});
|
|
|
|
myEmitter.prependListener("foo", () => {
|
|
order.push(3);
|
|
});
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(4);
|
|
});
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([3, 2, 1, 4]);
|
|
});
|
|
|
|
test("prependOnceListener", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const order: number[] = [];
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(1);
|
|
});
|
|
|
|
myEmitter.prependOnceListener("foo", () => {
|
|
order.push(2);
|
|
});
|
|
myEmitter.prependOnceListener("foo", () => {
|
|
order.push(3);
|
|
});
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(4);
|
|
});
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([3, 2, 1, 4]);
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([3, 2, 1, 4, 1, 4]);
|
|
});
|
|
|
|
test("prependListener in callback", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const order: number[] = [];
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(1);
|
|
});
|
|
|
|
myEmitter.once("foo", () => {
|
|
myEmitter.prependListener("foo", () => {
|
|
order.push(2);
|
|
});
|
|
});
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(3);
|
|
});
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([1, 3]);
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([1, 3, 2, 1, 3]);
|
|
});
|
|
|
|
test("addListener in callback", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const order: number[] = [];
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(1);
|
|
});
|
|
|
|
myEmitter.once("foo", () => {
|
|
myEmitter.addListener("foo", () => {
|
|
order.push(2);
|
|
});
|
|
});
|
|
|
|
myEmitter.on("foo", () => {
|
|
order.push(3);
|
|
});
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([1, 3]);
|
|
|
|
myEmitter.emit("foo");
|
|
|
|
expect(order).toEqual([1, 3, 1, 3, 2]);
|
|
});
|
|
|
|
test("listeners", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const fn = () => {};
|
|
myEmitter.on("foo", fn);
|
|
expect(myEmitter.listeners("foo")).toEqual([fn]);
|
|
const fn2 = () => {};
|
|
myEmitter.on("foo", fn2);
|
|
expect(myEmitter.listeners("foo")).toEqual([fn, fn2]);
|
|
myEmitter.off("foo", fn2);
|
|
expect(myEmitter.listeners("foo")).toEqual([fn]);
|
|
const fn3 = () => {};
|
|
myEmitter.once("foo", fn3);
|
|
expect(myEmitter.listeners("foo")).toEqual([fn, fn3]);
|
|
});
|
|
|
|
test("rawListeners", () => {
|
|
const myEmitter = new EventEmitter();
|
|
const fn = () => {};
|
|
myEmitter.on("foo", fn);
|
|
expect(myEmitter.rawListeners("foo")).toEqual([fn]);
|
|
const fn2 = () => {};
|
|
myEmitter.on("foo", fn2);
|
|
expect(myEmitter.rawListeners("foo")).toEqual([fn, fn2]);
|
|
myEmitter.off("foo", fn2);
|
|
expect(myEmitter.rawListeners("foo")).toEqual([fn]);
|
|
const fn3 = () => {};
|
|
myEmitter.once("foo", fn3);
|
|
const rawListeners: (Function & { listener?: Function })[] = myEmitter.rawListeners("foo");
|
|
// rawListeners() returns onceWrappers as well
|
|
expect([rawListeners[0], rawListeners[1].listener]).toEqual([fn, fn3]);
|
|
});
|
|
|
|
test("eventNames", () => {
|
|
const myEmitter = new EventEmitter();
|
|
expect(myEmitter.eventNames()).toEqual([]);
|
|
const fn = () => {};
|
|
myEmitter.on("foo", fn);
|
|
expect(myEmitter.eventNames()).toEqual(["foo"]);
|
|
myEmitter.on("bar", () => {});
|
|
expect(myEmitter.eventNames()).toEqual(["foo", "bar"]);
|
|
myEmitter.off("foo", fn);
|
|
expect(myEmitter.eventNames()).toEqual(["bar"]);
|
|
});
|
|
|
|
test("_eventsCount", () => {
|
|
const myEmitter = new EventEmitter() as EventEmitter & {
|
|
_eventsCount: number;
|
|
};
|
|
expect(myEmitter._eventsCount).toBe(0);
|
|
myEmitter.on("foo", () => {});
|
|
expect(myEmitter._eventsCount).toBe(1);
|
|
myEmitter.on("foo", () => {});
|
|
expect(myEmitter._eventsCount).toBe(1);
|
|
myEmitter.on("bar", () => {});
|
|
expect(myEmitter._eventsCount).toBe(2);
|
|
myEmitter.on("foo", () => {});
|
|
expect(myEmitter._eventsCount).toBe(2);
|
|
myEmitter.on("bar", () => {});
|
|
expect(myEmitter._eventsCount).toBe(2);
|
|
myEmitter.removeAllListeners("foo");
|
|
expect(myEmitter._eventsCount).toBe(1);
|
|
});
|
|
|
|
test("events.init", () => {
|
|
// init is a undocumented property that is identical to the constructor except it doesn't return the instance
|
|
// in node, EventEmitter just calls init()
|
|
let instance = Object.create(EventEmitter.prototype);
|
|
(EventEmitter as any).init.call(instance);
|
|
expect(instance._eventsCount).toBe(0);
|
|
expect(instance._maxListeners).toBeUndefined();
|
|
expect(instance._events).toEqual({});
|
|
expect(instance instanceof EventEmitter).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("EventEmitter.on", () => {
|
|
test("Basic test", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey");
|
|
|
|
expect(asyncIterator.next).toBeDefined();
|
|
expect(asyncIterator[Symbol.asyncIterator]).toBeDefined();
|
|
|
|
process.nextTick(() => {
|
|
emitter.emit("hey", 1);
|
|
});
|
|
|
|
const { value } = await asyncIterator.next();
|
|
expect(value).toEqual([1]);
|
|
});
|
|
|
|
test("Basic test with for await...of", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey", { close: ["close"] } as any);
|
|
|
|
process.nextTick(() => {
|
|
emitter.emit("hey", 1);
|
|
emitter.emit("hey", 2);
|
|
emitter.emit("hey", 3);
|
|
emitter.emit("hey", 4);
|
|
emitter.emit("close");
|
|
});
|
|
|
|
const result = [];
|
|
for await (const ev of asyncIterator) {
|
|
result.push(ev);
|
|
}
|
|
|
|
expect(result).toEqual([[1], [2], [3], [4]]);
|
|
});
|
|
|
|
test("Stop reading events after 'close' event is emitted", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey", { close: ["close"] } as any);
|
|
|
|
process.nextTick(() => {
|
|
emitter.emit("hey", 1);
|
|
emitter.emit("hey", 2);
|
|
emitter.emit("close");
|
|
emitter.emit("hey", 3);
|
|
});
|
|
|
|
const result = [];
|
|
for await (const ev of asyncIterator) {
|
|
result.push(ev);
|
|
}
|
|
|
|
expect(result).toEqual([[1], [2]]);
|
|
});
|
|
|
|
test("Queue events before first next() call", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey");
|
|
|
|
emitter.emit("hey", 1);
|
|
emitter.emit("hey", 2);
|
|
emitter.emit("hey", 3);
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
expect((await asyncIterator.next()).value).toEqual([1]);
|
|
expect((await asyncIterator.next()).value).toEqual([2]);
|
|
expect((await asyncIterator.next()).value).toEqual([3]);
|
|
});
|
|
|
|
test("Emit multiple values", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey");
|
|
|
|
emitter.emit("hey", 1, 2, 3);
|
|
|
|
const { value } = await asyncIterator.next();
|
|
expect(value).toEqual([1, 2, 3]);
|
|
});
|
|
|
|
test("kFirstEventParam", async () => {
|
|
const kFirstEventParam = Symbol.for("nodejs.kFirstEventParam");
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey", { [kFirstEventParam]: true } as any);
|
|
|
|
emitter.emit("hey", 1, 2, 3);
|
|
emitter.emit("hey", [4, 5, 6]);
|
|
|
|
expect((await asyncIterator.next()).value).toBe(1);
|
|
expect((await asyncIterator.next()).value).toEqual([4, 5, 6]);
|
|
});
|
|
|
|
test("Cancel via error event", async () => {
|
|
const { on, EventEmitter } = require("node:events");
|
|
const process = require("node:process");
|
|
|
|
const ee = new EventEmitter();
|
|
const output = [];
|
|
|
|
// Emit later on
|
|
process.nextTick(() => {
|
|
ee.emit("foo", "bar");
|
|
ee.emit("foo", 42);
|
|
ee.emit("foo", "baz");
|
|
});
|
|
|
|
setTimeout(() => {
|
|
ee.emit("error", "DONE");
|
|
}, 1_000);
|
|
|
|
try {
|
|
for await (const event of on(ee, "foo")) {
|
|
output.push([1, event]);
|
|
}
|
|
} catch (error) {
|
|
output.push([2, error]);
|
|
}
|
|
|
|
expect(output).toEqual([
|
|
[1, ["bar"]],
|
|
[1, [42]],
|
|
[1, ["baz"]],
|
|
[2, "DONE"],
|
|
]);
|
|
});
|
|
|
|
test("AbortController", () => {
|
|
const { on, EventEmitter } = require("node:events");
|
|
|
|
const ac = new AbortController();
|
|
const ee = new EventEmitter();
|
|
const output = [];
|
|
|
|
process.nextTick(() => {
|
|
ee.emit("foo", "bar");
|
|
ee.emit("foo", 42);
|
|
ee.emit("foo", "baz");
|
|
});
|
|
(async () => {
|
|
try {
|
|
for await (const event of on(ee, "foo", { signal: ac.signal })) {
|
|
output.push([1, event]);
|
|
}
|
|
console.log("unreachable");
|
|
} catch (error: any) {
|
|
const { code, message } = error;
|
|
output.push([2, { code, message }]);
|
|
|
|
expect(output).toEqual([
|
|
[1, ["bar"]],
|
|
[1, [42]],
|
|
[1, ["baz"]],
|
|
[
|
|
2,
|
|
{
|
|
code: "ABORT_ERR",
|
|
message: "The operation was aborted.",
|
|
},
|
|
],
|
|
]);
|
|
}
|
|
})();
|
|
|
|
process.nextTick(() => ac.abort());
|
|
});
|
|
|
|
// Checks for potential issues with FixedQueue size
|
|
test("Queue many events", async () => {
|
|
const emitter = new EventEmitter();
|
|
const asyncIterator = EventEmitter.on(emitter, "hey");
|
|
|
|
for (let i = 0; i < 2500; i += 1) {
|
|
emitter.emit("hey", i);
|
|
}
|
|
|
|
expect((await asyncIterator.next()).value).toEqual([0]);
|
|
});
|
|
|
|
test("readline.createInterface", async () => {
|
|
const { createInterface } = require("node:readline");
|
|
const { createReadStream } = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const fpath = path.join(__filename, "..", "..", "child_process", "fixtures", "child-process-echo-options.js");
|
|
const text = await Bun.file(fpath).text();
|
|
const interfaced = createInterface(createReadStream(fpath));
|
|
const output = [];
|
|
|
|
try {
|
|
for await (const line of interfaced) {
|
|
output.push(line);
|
|
}
|
|
} catch (e) {}
|
|
const out = text.replaceAll("\r\n", "\n").trim().split("\n");
|
|
expect(output).toEqual(out);
|
|
});
|
|
});
|
|
|
|
describe("EventEmitter error handling", () => {
|
|
test("unhandled error event throws on emit", () => {
|
|
const myEmitter = new EventEmitter();
|
|
|
|
expect(() => {
|
|
myEmitter.emit("error", "Hello!");
|
|
}).toThrow("Hello!");
|
|
});
|
|
|
|
test("unhandled error event throws on emit with no arguments", () => {
|
|
const myEmitter = new EventEmitter();
|
|
|
|
expect(() => {
|
|
myEmitter.emit("error");
|
|
}).toThrow("Unhandled error.");
|
|
});
|
|
|
|
test("handled error event", () => {
|
|
const myEmitter = new EventEmitter();
|
|
|
|
let handled = false;
|
|
myEmitter.on("error", (...args) => {
|
|
expect(args).toEqual(["Hello", "World"]);
|
|
handled = true;
|
|
});
|
|
|
|
myEmitter.emit("error", "Hello", "World");
|
|
|
|
expect(handled).toBe(true);
|
|
});
|
|
|
|
test("errorMonitor", () => {
|
|
const myEmitter = new EventEmitter();
|
|
|
|
let handled = false;
|
|
myEmitter.on(EventEmitter.errorMonitor, (...args) => {
|
|
expect(args).toEqual(["Hello", "World"]);
|
|
handled = true;
|
|
});
|
|
|
|
myEmitter.on("error", () => {});
|
|
|
|
myEmitter.emit("error", "Hello", "World");
|
|
|
|
expect(handled).toBe(true);
|
|
});
|
|
|
|
test("errorMonitor (unhandled)", () => {
|
|
const myEmitter = new EventEmitter();
|
|
|
|
let handled = false;
|
|
myEmitter.on(EventEmitter.errorMonitor, (...args) => {
|
|
expect(args).toEqual(["Hello", "World"]);
|
|
handled = true;
|
|
});
|
|
|
|
expect(() => {
|
|
myEmitter.emit("error", "Hello", "World");
|
|
}).toThrow("Hello");
|
|
|
|
expect(handled).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("EventEmitter captureRejections", () => {
|
|
// Can't catch the unhandled rejection because we do not have process.on("unhandledRejection")
|
|
// test("captureRejections off will not capture rejections", async () => {
|
|
// const myEmitter = new EventEmitter();
|
|
|
|
// let handled = false;
|
|
// myEmitter.on("error", (...args) => {
|
|
// handled = true;
|
|
// });
|
|
|
|
// myEmitter.on("action", async () => {
|
|
// throw new Error("Hello World");
|
|
// });
|
|
|
|
// myEmitter.emit("action");
|
|
|
|
// await sleep(1);
|
|
|
|
// expect(handled).toBe(false);
|
|
// });
|
|
test("it captures rejections", async () => {
|
|
const myEmitter = new EventEmitter({ captureRejections: true });
|
|
|
|
let handled: any = null;
|
|
myEmitter.on("error", (...args) => {
|
|
handled = args;
|
|
});
|
|
|
|
myEmitter.on("action", async () => {
|
|
throw 123;
|
|
});
|
|
|
|
myEmitter.emit("action");
|
|
|
|
await sleep(5);
|
|
|
|
expect(handled).toEqual([123]);
|
|
});
|
|
test("it does not capture successful promises", async () => {
|
|
const myEmitter = new EventEmitter({ captureRejections: true });
|
|
|
|
let handled: any = null;
|
|
myEmitter.on("error", () => {
|
|
handled = true;
|
|
});
|
|
|
|
myEmitter.on("action", async () => {
|
|
return 123;
|
|
});
|
|
|
|
myEmitter.emit("action");
|
|
|
|
await sleep(5);
|
|
|
|
expect(handled).toEqual(null);
|
|
});
|
|
test("it does not capture handled rejections", async () => {
|
|
const myEmitter = new EventEmitter({ captureRejections: true });
|
|
|
|
let handled: any = null;
|
|
myEmitter.on("error", () => {
|
|
handled = true;
|
|
});
|
|
|
|
myEmitter.on("action", async () => {
|
|
return Promise.reject(123).catch(() => 234);
|
|
});
|
|
|
|
myEmitter.emit("action");
|
|
|
|
await sleep(5);
|
|
|
|
expect(handled).toEqual(null);
|
|
});
|
|
});
|
|
|
|
const waysOfCreating = [
|
|
() => Object.create(EventEmitter.prototype),
|
|
() => new EventEmitter(),
|
|
() => new (class extends EventEmitter {})(),
|
|
() => {
|
|
class MyEmitter extends EventEmitter {}
|
|
return new MyEmitter();
|
|
},
|
|
() => {
|
|
var foo = {};
|
|
Object.setPrototypeOf(foo, EventEmitter.prototype);
|
|
return foo;
|
|
},
|
|
() => {
|
|
function FakeEmitter(this: any) {
|
|
return EventEmitter.call(this);
|
|
}
|
|
Object.setPrototypeOf(FakeEmitter.prototype, EventEmitter.prototype);
|
|
Object.setPrototypeOf(FakeEmitter, EventEmitter);
|
|
return new (FakeEmitter as any)();
|
|
},
|
|
() => {
|
|
const FakeEmitter: any = function FakeEmitter(this: any) {
|
|
EventEmitter.call(this);
|
|
} as any;
|
|
Object.assign(FakeEmitter.prototype, EventEmitter.prototype);
|
|
Object.assign(FakeEmitter, EventEmitter);
|
|
return new FakeEmitter();
|
|
},
|
|
() => {
|
|
var foo = {};
|
|
Object.assign(foo, EventEmitter.prototype);
|
|
return foo;
|
|
},
|
|
];
|
|
|
|
describe("EventEmitter constructors", () => {
|
|
for (let create of waysOfCreating) {
|
|
test(`${create
|
|
.toString()
|
|
.slice(6, 52)
|
|
.replaceAll("\n", "")
|
|
.trim()
|
|
.replaceAll(/ {2,}/g, " ")
|
|
.replace(/^\{ ?/, "")} should work`, () => {
|
|
var myEmitter = create();
|
|
var called = false;
|
|
(myEmitter as EventEmitter).once("event", function () {
|
|
called = true;
|
|
// @ts-ignore
|
|
expect(this).toBe(myEmitter);
|
|
});
|
|
var firstEvents = myEmitter._events;
|
|
expect(myEmitter.listenerCount("event")).toBe(1);
|
|
|
|
expect(myEmitter.emit("event")).toBe(true);
|
|
expect(myEmitter.listenerCount("event")).toBe(0);
|
|
|
|
expect(firstEvents).toEqual({ event: firstEvents.event }); // it shouldn't mutate
|
|
expect(called).toBe(true);
|
|
});
|
|
}
|
|
|
|
test("with createRequire, events is callable", () => {
|
|
const req = createRequire(import.meta.path);
|
|
const events = req("events");
|
|
new events();
|
|
});
|
|
|
|
test("in cjs, events is callable", () => {
|
|
const EventEmitter = require("events");
|
|
new EventEmitter();
|
|
});
|
|
});
|
|
|
|
test("addAbortListener", async () => {
|
|
const emitter = new EventEmitter();
|
|
const controller = new AbortController();
|
|
const promise = EventEmitter.once(emitter, "hey", { signal: controller.signal });
|
|
const mocked = mock();
|
|
EventEmitter.addAbortListener(controller.signal, mocked);
|
|
controller.abort();
|
|
expect(promise).rejects.toThrow("aborted");
|
|
expect(mocked).toHaveBeenCalled();
|
|
});
|
|
|
|
test("using addAbortListener", async () => {
|
|
const emitter = new EventEmitter();
|
|
const controller = new AbortController();
|
|
const promise = EventEmitter.once(emitter, "hey", { signal: controller.signal });
|
|
const mocked = mock();
|
|
{
|
|
using aborty = EventEmitter.addAbortListener(controller.signal, mocked);
|
|
}
|
|
controller.abort();
|
|
expect(promise).rejects.toThrow("aborted");
|
|
expect(mocked).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("getMaxListeners", () => {
|
|
const emitter = new EventEmitter();
|
|
expect(emitter.getMaxListeners()).toBe(10);
|
|
emitter.setMaxListeners(20);
|
|
expect(emitter.getMaxListeners()).toBe(20);
|
|
});
|
|
|
|
test("setMaxListeners", () => {
|
|
const emitter = new EventEmitter();
|
|
expect(emitter.getMaxListeners()).toBe(10);
|
|
emitter.setMaxListeners(20);
|
|
expect(emitter.getMaxListeners()).toBe(20);
|
|
|
|
setMaxListeners(30, emitter);
|
|
expect(emitter.getMaxListeners()).toBe(30);
|
|
|
|
const eventTarget = new EventTarget();
|
|
setMaxListeners(1, eventTarget);
|
|
expect(getMaxListeners(eventTarget)).toBe(1);
|
|
|
|
setMaxListeners(99, eventTarget);
|
|
expect(getMaxListeners(eventTarget)).toBe(99);
|
|
});
|
|
|
|
test("getEventListeners", () => {
|
|
const target = new EventTarget();
|
|
expect(getEventListeners(target, "hey").length).toBe(0);
|
|
target.addEventListener("hey", () => {}, { once: true });
|
|
expect(getEventListeners(target, "hey").length).toBe(1);
|
|
target.dispatchEvent(new Event("hey"));
|
|
expect(getEventListeners(target, "hey").length).toBe(0);
|
|
});
|
|
|
|
test("EventEmitter.name", () => {
|
|
expect(EventEmitter.name).toBe("EventEmitter");
|
|
});
|