1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
import { test, describe, expect, it } from "bun:test";
import fs from "node:fs";
// 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, { getEventListeners, captureRejectionSymbol } from "node:events";
import { heapStats } from "bun:jsc";
describe("EventEmitter", () => {
it("captureRejectionSymbol", () => {
expect(EventEmitter.captureRejectionSymbol).toBeDefined();
expect(captureRejectionSymbol).toBeDefined();
});
test("getEventListeners", () => {
expect(getEventListeners(new EventEmitter(), "hey").length).toBe(0);
});
test("EventEmitter constructor", () => {
var emitter = new EventEmitter();
emitter.setMaxListeners(100);
expect(emitter.getMaxListeners()).toBe(100);
});
test("EventEmitter.removeAllListeners()", () => {
var emitter = new EventEmitter();
var ran = false;
emitter.on("hey", () => {
ran = true;
});
emitter.removeAllListeners();
expect(emitter.listenerCount("hey")).toBe(0);
emitter.emit("hey");
expect(ran).toBe(false);
emitter.on("hey", () => {
ran = true;
});
emitter.emit("hey");
expect(ran).toBe(true);
expect(emitter.listenerCount("hey")).toBe(1);
});
// These are also tests for the done() function in the test runner.
test("EventEmitter emit (different tick)", done => {
var emitter = new EventEmitter();
emitter.on("wow", () => done());
queueMicrotask(() => {
emitter.emit("wow");
});
});
// Unlike Jest, bun supports async and done
test("async EventEmitter emit (microtask)", async done => {
await 1;
var emitter = new EventEmitter();
emitter.on("wow", () => done());
emitter.emit("wow");
});
test("async EventEmitter emit (microtask) after", async done => {
var emitter = new EventEmitter();
emitter.on("wow", () => done());
await 1;
emitter.emit("wow");
});
test("EventEmitter emit (same tick)", done => {
var emitter = new EventEmitter();
emitter.on("wow", () => done());
emitter.emit("wow");
});
test("EventEmitter emit (setTimeout task)", done => {
var emitter = new EventEmitter();
emitter.on("wow", () => done());
setTimeout(() => emitter.emit("wow"), 1);
});
});
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;
},
() => {
const FakeEmitter = function FakeEmitter() {
return EventEmitter.call(this);
};
Object.setPrototypeOf(FakeEmitter.prototype, EventEmitter.prototype);
Object.setPrototypeOf(FakeEmitter, EventEmitter);
return new FakeEmitter();
},
() => {
const FakeEmitter = function FakeEmitter() {
EventEmitter.call(this);
};
Object.assign(FakeEmitter.prototype, EventEmitter.prototype);
Object.assign(FakeEmitter, EventEmitter);
return new FakeEmitter();
},
() => {
var foo = {};
Object.assign(foo, EventEmitter.prototype);
return foo;
},
];
for (let create of waysOfCreating) {
it(`${create.toString().slice(10, 40).replaceAll("\n", "\\n").trim()} should work`, () => {
var myEmitter = create();
var called = false;
myEmitter.once("event", function () {
called = true;
expect(this as any).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).toBe(myEmitter._events);
expect(called).toBe(true);
});
}
test("EventEmitter.on", () => {
var myEmitter = new EventEmitter();
expect(myEmitter.on("foo", () => {})).toBe(myEmitter);
});
test("EventEmitter.off", () => {
var myEmitter = new EventEmitter();
expect(myEmitter.off("foo", () => {})).toBe(myEmitter);
});
// Internally, EventEmitter has a JSC::Weak with the thisValue of the listener
test("EventEmitter GCs", () => {
Bun.gc(true);
const startCount = heapStats().objectTypeCounts["EventEmitter"] || 0;
(function () {
Bun.gc(true);
function EventEmitterSubclass() {
EventEmitter.call(this);
}
Object.setPrototypeOf(EventEmitterSubclass.prototype, EventEmitter.prototype);
Object.setPrototypeOf(EventEmitterSubclass, EventEmitter);
var myEmitter = new EventEmitterSubclass();
myEmitter.on("foo", () => {});
myEmitter.emit("foo");
Bun.gc(true);
})();
Bun.gc(true);
const endCount = heapStats().objectTypeCounts["EventEmitter"] || 0;
expect(endCount).toBe(startCount);
});
|