@supercat1337/event-emitter 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +38 -12
- package/src/event-emitter-lite.d.ts +13 -13
- package/src/event-emitter-lite.js +7 -7
- package/src/event-emitter.d.ts +8 -8
- package/src/event-emitter.js +8 -8
- package/.prettierrc +0 -10
- package/jsconfig.json +0 -12
- package/my.tsconfig.types.json +0 -19
- package/tests/event-emitter-lite.test.js +0 -256
- package/tests/event-emitter.test.js +0 -471
package/package.json
CHANGED
|
@@ -1,31 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supercat1337/event-emitter",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Lightweight typed event emitter with Symbol support, one-time listeners, and zero dependencies. Works in Node.js and browsers.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"types": "./index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"index.js",
|
|
14
|
+
"index.d.ts",
|
|
15
|
+
"src/**/*.js",
|
|
16
|
+
"src/**/*.d.ts"
|
|
17
|
+
],
|
|
6
18
|
"scripts": {
|
|
7
19
|
"test": "c8 ava",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
20
|
+
"clean": "shx rm -f *.d.ts *.d.ts.map && shx rm -rf src/**/*.d.ts src/**/*.d.ts.map",
|
|
21
|
+
"build:types": "npm run clean && tsc --project my.tsconfig.types.json"
|
|
10
22
|
},
|
|
11
|
-
"
|
|
23
|
+
"keywords": [
|
|
24
|
+
"event",
|
|
25
|
+
"emitter",
|
|
26
|
+
"eventemitter",
|
|
27
|
+
"event-emitter",
|
|
28
|
+
"typed",
|
|
29
|
+
"typescript",
|
|
30
|
+
"symbols",
|
|
31
|
+
"lightweight",
|
|
32
|
+
"tiny",
|
|
33
|
+
"zero-dependencies",
|
|
34
|
+
"browser",
|
|
35
|
+
"node",
|
|
36
|
+
"events",
|
|
37
|
+
"pubsub",
|
|
38
|
+
"observer"
|
|
39
|
+
],
|
|
40
|
+
"author": "Albert Bazaleev",
|
|
12
41
|
"license": "MIT",
|
|
13
42
|
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.12.2",
|
|
14
44
|
"ava": "^6.1.2",
|
|
15
45
|
"c8": "^9.1.0",
|
|
16
|
-
"
|
|
46
|
+
"shx": "^0.4.0",
|
|
47
|
+
"typescript": "^5.4.3"
|
|
17
48
|
},
|
|
18
|
-
"type": "module",
|
|
19
|
-
"keywords": [
|
|
20
|
-
"typed event emitter",
|
|
21
|
-
"typed event bus"
|
|
22
|
-
],
|
|
23
49
|
"publishConfig": {
|
|
24
50
|
"registry": "https://registry.npmjs.org"
|
|
25
51
|
},
|
|
26
52
|
"homepage": "https://github.com/supercat1337/event-emitter",
|
|
27
53
|
"repository": {
|
|
28
54
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/supercat1337/event-emitter.git"
|
|
55
|
+
"url": "git+https://github.com/supercat1337/event-emitter.git"
|
|
30
56
|
}
|
|
31
57
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const ORIGINAL: unique symbol;
|
|
2
2
|
/**
|
|
3
|
-
* @template {string | Record<string, any[]>} [Events=string]
|
|
3
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
4
4
|
*/
|
|
5
|
-
export class EventEmitterLite<Events extends string | Record<string, any[]> = string> {
|
|
5
|
+
export class EventEmitterLite<Events extends string | symbol | Record<string | symbol, any[]> = string> {
|
|
6
6
|
/**
|
|
7
|
-
* @type {Object.<Events extends string ? Events : keyof Events, Function[]>}
|
|
7
|
+
* @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
|
|
8
8
|
*/
|
|
9
9
|
events: any;
|
|
10
10
|
/**
|
|
@@ -14,39 +14,39 @@ export class EventEmitterLite<Events extends string | Record<string, any[]> = st
|
|
|
14
14
|
logErrors: boolean;
|
|
15
15
|
/**
|
|
16
16
|
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
17
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
17
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
18
18
|
* @param {K} event
|
|
19
19
|
* @param {Function} listener
|
|
20
20
|
* @returns {() => void}
|
|
21
21
|
*/
|
|
22
|
-
on<K extends Events extends string ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
22
|
+
on<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
23
23
|
/**
|
|
24
24
|
* Add a one-time listener
|
|
25
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
25
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
26
26
|
* @param {K} event
|
|
27
27
|
* @param {Function} listener
|
|
28
28
|
* @returns {()=>void}
|
|
29
29
|
*/
|
|
30
|
-
once<K extends Events extends string ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
30
|
+
once<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): () => void;
|
|
31
31
|
/**
|
|
32
32
|
* off is an alias for removeListener
|
|
33
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
33
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
34
34
|
* @param {K} event
|
|
35
35
|
* @param {Function} listener
|
|
36
36
|
*/
|
|
37
|
-
off<K extends Events extends string ? Events : keyof Events>(event: K, listener: Function): void;
|
|
37
|
+
off<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
|
|
38
38
|
/**
|
|
39
39
|
* Remove an event listener from an event
|
|
40
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
40
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
41
41
|
* @param {K} event
|
|
42
42
|
* @param {Function} listener
|
|
43
43
|
*/
|
|
44
|
-
removeListener<K extends Events extends string ? Events : keyof Events>(event: K, listener: Function): void;
|
|
44
|
+
removeListener<K extends Events extends string | symbol ? Events : keyof Events>(event: K, listener: Function): void;
|
|
45
45
|
/**
|
|
46
46
|
* emit is used to trigger an event
|
|
47
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
47
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
48
48
|
* @param {K} event
|
|
49
49
|
* @param {...any} args
|
|
50
50
|
*/
|
|
51
|
-
emit<K extends Events extends string ? Events : keyof Events>(event: K, ...args: any[]): void;
|
|
51
|
+
emit<K extends Events extends string | symbol ? Events : keyof Events>(event: K, ...args: any[]): void;
|
|
52
52
|
}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
export const ORIGINAL = Symbol('original');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* @template {string | Record<string, any[]>} [Events=string]
|
|
6
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
7
7
|
*/
|
|
8
8
|
export class EventEmitterLite {
|
|
9
9
|
/**
|
|
10
|
-
* @type {Object.<Events extends string ? Events : keyof Events, Function[]>}
|
|
10
|
+
* @type {Object.<Events extends string | symbol ? Events : keyof Events, Function[]>}
|
|
11
11
|
*/
|
|
12
12
|
events = Object.create(null);
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ export class EventEmitterLite {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
22
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
22
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
23
23
|
* @param {K} event
|
|
24
24
|
* @param {Function} listener
|
|
25
25
|
* @returns {() => void}
|
|
@@ -34,7 +34,7 @@ export class EventEmitterLite {
|
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Add a one-time listener
|
|
37
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
37
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
38
38
|
* @param {K} event
|
|
39
39
|
* @param {Function} listener
|
|
40
40
|
* @returns {()=>void}
|
|
@@ -50,7 +50,7 @@ export class EventEmitterLite {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* off is an alias for removeListener
|
|
53
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
53
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
54
54
|
* @param {K} event
|
|
55
55
|
* @param {Function} listener
|
|
56
56
|
*/
|
|
@@ -60,7 +60,7 @@ export class EventEmitterLite {
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Remove an event listener from an event
|
|
63
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
63
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
64
64
|
* @param {K} event
|
|
65
65
|
* @param {Function} listener
|
|
66
66
|
*/
|
|
@@ -80,7 +80,7 @@ export class EventEmitterLite {
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* emit is used to trigger an event
|
|
83
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
83
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
84
84
|
* @param {K} event
|
|
85
85
|
* @param {...any} args
|
|
86
86
|
*/
|
package/src/event-emitter.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @template {string | Record<string, any[]>} [Events=string]
|
|
2
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
3
3
|
* @extends {EventEmitterLite<Events>}
|
|
4
4
|
*/
|
|
5
|
-
export class EventEmitter<Events extends string | Record<string, any[]> = string> extends EventEmitterLite<Events> {
|
|
5
|
+
export class EventEmitter<Events extends string | symbol | Record<string | symbol, any[]> = string> extends EventEmitterLite<Events> {
|
|
6
6
|
/**
|
|
7
7
|
* Is the event emitter destroyed?
|
|
8
8
|
* @type {boolean}
|
|
@@ -10,20 +10,20 @@ export class EventEmitter<Events extends string | Record<string, any[]> = string
|
|
|
10
10
|
get isDestroyed(): boolean;
|
|
11
11
|
/**
|
|
12
12
|
* Wait for a specific event to be emitted.
|
|
13
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
13
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
14
14
|
* @param {K} event - The event to wait for.
|
|
15
15
|
* @param {number} [max_wait_ms=0] - Maximum time to wait in ms. If 0, waits indefinitely.
|
|
16
16
|
* @returns {Promise<boolean>} - Resolves with true if event emitted, false on timeout.
|
|
17
17
|
*/
|
|
18
|
-
waitForEvent<K extends Events extends string ? Events : keyof Events>(event: K, max_wait_ms?: number): Promise<boolean>;
|
|
18
|
+
waitForEvent<K extends Events extends string | symbol ? Events : keyof Events>(event: K, max_wait_ms?: number): Promise<boolean>;
|
|
19
19
|
/**
|
|
20
20
|
* Wait for any of the specified events to be emitted.
|
|
21
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
21
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
22
22
|
* @param {K[]} events - Array of event names.
|
|
23
23
|
* @param {number} [max_wait_ms=0] - Maximum time to wait in ms.
|
|
24
24
|
* @returns {Promise<boolean>} - Resolves with true if any event emitted, false on timeout.
|
|
25
25
|
*/
|
|
26
|
-
waitForAnyEvent<K extends Events extends string ? Events : keyof Events>(events: K[], max_wait_ms?: number): Promise<boolean>;
|
|
26
|
+
waitForAnyEvent<K extends Events extends string | symbol ? Events : keyof Events>(events: K[], max_wait_ms?: number): Promise<boolean>;
|
|
27
27
|
/**
|
|
28
28
|
* Clear all events
|
|
29
29
|
*/
|
|
@@ -34,10 +34,10 @@ export class EventEmitter<Events extends string | Record<string, any[]> = string
|
|
|
34
34
|
destroy(): void;
|
|
35
35
|
/**
|
|
36
36
|
* Clears all listeners for a specified event.
|
|
37
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
37
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
38
38
|
* @param {K} event
|
|
39
39
|
*/
|
|
40
|
-
clearEventListeners<K extends Events extends string ? Events : keyof Events>(event: K): void;
|
|
40
|
+
clearEventListeners<K extends Events extends string | symbol ? Events : keyof Events>(event: K): void;
|
|
41
41
|
/**
|
|
42
42
|
* onHasEventListeners() is used to subscribe to the "#has-listeners" event. This event is emitted when the number of listeners for any event (except "#has-listeners" and "#no-listeners") goes from 0 to 1.
|
|
43
43
|
* @param {Function} callback
|
package/src/event-emitter.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { EventEmitterLite } from './event-emitter-lite.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @template {string | Record<string, any[]>} [Events=string]
|
|
5
|
+
* @template {string | symbol | Record<string|symbol, any[]>} [Events=string]
|
|
6
6
|
* @extends {EventEmitterLite<Events>}
|
|
7
7
|
*/
|
|
8
8
|
export class EventEmitter extends EventEmitterLite {
|
|
@@ -26,7 +26,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* on is used to add a callback function that's going to be executed when the event is triggered
|
|
29
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
29
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
30
30
|
* @param {K} event
|
|
31
31
|
* @param {Function} listener
|
|
32
32
|
*/
|
|
@@ -47,7 +47,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Remove an event listener from an event
|
|
50
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
50
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
51
51
|
* @param {K} event
|
|
52
52
|
* @param {Function} listener
|
|
53
53
|
*/
|
|
@@ -96,7 +96,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
98
|
* emit is used to trigger an event
|
|
99
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
99
|
+
* @template {Events extends string | symbol ? Events : keyof Events} K
|
|
100
100
|
* @param {K} event
|
|
101
101
|
* @param {...any} args
|
|
102
102
|
*/
|
|
@@ -157,7 +157,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
159
|
* Wait for a specific event to be emitted.
|
|
160
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
160
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
161
161
|
* @param {K} event - The event to wait for.
|
|
162
162
|
* @param {number} [max_wait_ms=0] - Maximum time to wait in ms. If 0, waits indefinitely.
|
|
163
163
|
* @returns {Promise<boolean>} - Resolves with true if event emitted, false on timeout.
|
|
@@ -168,7 +168,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
170
|
* Wait for any of the specified events to be emitted.
|
|
171
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
171
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
172
172
|
* @param {K[]} events - Array of event names.
|
|
173
173
|
* @param {number} [max_wait_ms=0] - Maximum time to wait in ms.
|
|
174
174
|
* @returns {Promise<boolean>} - Resolves with true if any event emitted, false on timeout.
|
|
@@ -212,7 +212,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
212
212
|
clear() {
|
|
213
213
|
if (this.#isDestroyed) return;
|
|
214
214
|
|
|
215
|
-
/** @type {(Events extends string ? Events : keyof Events)[]} */
|
|
215
|
+
/** @type {(Events extends string | symbol? Events : keyof Events)[]} */
|
|
216
216
|
// @ts-ignore
|
|
217
217
|
const eventNames = Object.keys(this.events);
|
|
218
218
|
|
|
@@ -238,7 +238,7 @@ export class EventEmitter extends EventEmitterLite {
|
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
240
|
* Clears all listeners for a specified event.
|
|
241
|
-
* @template {Events extends string ? Events : keyof Events} K
|
|
241
|
+
* @template {Events extends string | symbol? Events : keyof Events} K
|
|
242
242
|
* @param {K} event
|
|
243
243
|
*/
|
|
244
244
|
clearEventListeners(event) {
|
package/.prettierrc
DELETED
package/jsconfig.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"baseUrl": ".",
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"module": "NodeNext",
|
|
6
|
-
"noEmit": true,
|
|
7
|
-
"lib": ["ES2020", "es5", "es6", "dom", "dom.iterable"],
|
|
8
|
-
"moduleResolution": "NodeNext"
|
|
9
|
-
},
|
|
10
|
-
"exclude": ["node_modules"],
|
|
11
|
-
"include": ["index.js", "modules/**/*.d.ts"]
|
|
12
|
-
}
|
package/my.tsconfig.types.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"allowJs": true,
|
|
4
|
-
"target": "ESNext",
|
|
5
|
-
"module": "NodeNext",
|
|
6
|
-
"emitDeclarationOnly": true,
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"outDir": "./",
|
|
9
|
-
"rootDir": ".",
|
|
10
|
-
"declarationMap": false,
|
|
11
|
-
"lib": ["ESNext", "DOM", "ES2023"],
|
|
12
|
-
"moduleResolution": "NodeNext",
|
|
13
|
-
"stripInternal": true
|
|
14
|
-
},
|
|
15
|
-
"include": [
|
|
16
|
-
"./index.js"
|
|
17
|
-
],
|
|
18
|
-
"exclude": []
|
|
19
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
import ava from 'ava';
|
|
4
|
-
import { EventEmitterLite } from '../src/event-emitter-lite.js';
|
|
5
|
-
|
|
6
|
-
/** @typedef {{ emitter: EventEmitterLite }} MyContext */
|
|
7
|
-
/** @typedef {import('ava').TestFn<MyContext>} TestFn */
|
|
8
|
-
|
|
9
|
-
const test = /** @type {TestFn} */ (ava);
|
|
10
|
-
|
|
11
|
-
test.beforeEach(t => {
|
|
12
|
-
t.context.emitter = new EventEmitterLite();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('on() should add listener and emit should call it', t => {
|
|
16
|
-
const { emitter } = t.context;
|
|
17
|
-
let called = false;
|
|
18
|
-
emitter.on('test', () => {
|
|
19
|
-
called = true;
|
|
20
|
-
});
|
|
21
|
-
emitter.emit('test');
|
|
22
|
-
t.true(called);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('emit() should pass arguments to listener', t => {
|
|
26
|
-
const { emitter } = t.context;
|
|
27
|
-
emitter.on(
|
|
28
|
-
'test',
|
|
29
|
-
/** @param {number} a @param {string} b */ (a, b) => {
|
|
30
|
-
t.is(a, 1);
|
|
31
|
-
t.is(b, 'two');
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
emitter.emit('test', 1, 'two');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('once() should call listener only once', t => {
|
|
38
|
-
const { emitter } = t.context;
|
|
39
|
-
let count = 0;
|
|
40
|
-
emitter.once('test', () => count++);
|
|
41
|
-
emitter.emit('test');
|
|
42
|
-
emitter.emit('test');
|
|
43
|
-
t.is(count, 1);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('once() should be removable via off() with original listener', t => {
|
|
47
|
-
const { emitter } = t.context;
|
|
48
|
-
let count = 0;
|
|
49
|
-
function listener() {
|
|
50
|
-
count++;
|
|
51
|
-
}
|
|
52
|
-
emitter.once('test', listener);
|
|
53
|
-
emitter.off('test', listener); // remove listener
|
|
54
|
-
emitter.emit('test');
|
|
55
|
-
t.is(count, 0);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('off() alias for removeListener should remove listener', t => {
|
|
59
|
-
const { emitter } = t.context;
|
|
60
|
-
let called = false;
|
|
61
|
-
const fn = () => {
|
|
62
|
-
called = true;
|
|
63
|
-
};
|
|
64
|
-
emitter.on('test', fn);
|
|
65
|
-
emitter.off('test', fn);
|
|
66
|
-
emitter.emit('test');
|
|
67
|
-
t.false(called);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('removeListener() should remove listener added via on()', t => {
|
|
71
|
-
const { emitter } = t.context;
|
|
72
|
-
let called = false;
|
|
73
|
-
const fn = () => {
|
|
74
|
-
called = true;
|
|
75
|
-
};
|
|
76
|
-
emitter.on('test', fn);
|
|
77
|
-
emitter.removeListener('test', fn);
|
|
78
|
-
emitter.emit('test');
|
|
79
|
-
t.false(called);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('unsubscriber returned by on() should remove listener', t => {
|
|
83
|
-
const { emitter } = t.context;
|
|
84
|
-
let called = false;
|
|
85
|
-
const unsub = emitter.on('test', () => {
|
|
86
|
-
called = true;
|
|
87
|
-
});
|
|
88
|
-
unsub();
|
|
89
|
-
emitter.emit('test');
|
|
90
|
-
t.false(called);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('removeListener() should do nothing if listener not found', t => {
|
|
94
|
-
const { emitter } = t.context;
|
|
95
|
-
t.notThrows(() => {
|
|
96
|
-
emitter.removeListener('test', () => {});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test('emit() with no listeners should do nothing', t => {
|
|
101
|
-
const { emitter } = t.context;
|
|
102
|
-
t.notThrows(() => {
|
|
103
|
-
emitter.emit('test');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('multiple listeners should all be called', t => {
|
|
108
|
-
const { emitter } = t.context;
|
|
109
|
-
let count = 0;
|
|
110
|
-
const inc = () => count++;
|
|
111
|
-
emitter.on('test', inc);
|
|
112
|
-
emitter.on('test', inc);
|
|
113
|
-
emitter.emit('test');
|
|
114
|
-
t.is(count, 2);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test('listeners should be called in order of addition', t => {
|
|
118
|
-
const { emitter } = t.context;
|
|
119
|
-
/** @type {number[]} */
|
|
120
|
-
const order = [];
|
|
121
|
-
emitter.on('test', () => order.push(1));
|
|
122
|
-
emitter.on('test', () => order.push(2));
|
|
123
|
-
emitter.emit('test');
|
|
124
|
-
t.deepEqual(order, [1, 2]);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test('removing listener during emit does not affect current iteration', t => {
|
|
128
|
-
const { emitter } = t.context;
|
|
129
|
-
let called = 0;
|
|
130
|
-
const fn1 = () => {
|
|
131
|
-
called++;
|
|
132
|
-
emitter.removeListener('test', fn2); // remove fn2
|
|
133
|
-
};
|
|
134
|
-
const fn2 = () => {
|
|
135
|
-
called++;
|
|
136
|
-
};
|
|
137
|
-
emitter.on('test', fn1);
|
|
138
|
-
emitter.on('test', fn2);
|
|
139
|
-
emitter.emit('test');
|
|
140
|
-
t.is(called, 2); // fn2 called twice
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test('adding listener during emit does not affect current iteration', t => {
|
|
144
|
-
const { emitter } = t.context;
|
|
145
|
-
let called = 0;
|
|
146
|
-
const fn1 = () => {
|
|
147
|
-
emitter.on('test', () => called++);
|
|
148
|
-
};
|
|
149
|
-
emitter.on('test', fn1);
|
|
150
|
-
emitter.emit('test');
|
|
151
|
-
t.is(called, 0); // fn1 not called
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('logErrors: true should log to console', t => {
|
|
155
|
-
const { emitter } = t.context;
|
|
156
|
-
const originalError = console.error;
|
|
157
|
-
let logged = false;
|
|
158
|
-
console.error = (...args) => {
|
|
159
|
-
logged = true;
|
|
160
|
-
};
|
|
161
|
-
t.teardown(() => {
|
|
162
|
-
console.error = originalError;
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
emitter.logErrors = true;
|
|
166
|
-
emitter.on('test', () => {
|
|
167
|
-
throw new Error('boom');
|
|
168
|
-
});
|
|
169
|
-
t.notThrows(() => emitter.emit('test'));
|
|
170
|
-
t.true(logged);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('logErrors: false should not log', t => {
|
|
174
|
-
const { emitter } = t.context;
|
|
175
|
-
const originalError = console.error;
|
|
176
|
-
let logged = false;
|
|
177
|
-
console.error = (...args) => {
|
|
178
|
-
logged = true;
|
|
179
|
-
};
|
|
180
|
-
t.teardown(() => {
|
|
181
|
-
console.error = originalError;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
emitter.logErrors = false;
|
|
185
|
-
emitter.on('test', () => {
|
|
186
|
-
throw new Error('boom');
|
|
187
|
-
});
|
|
188
|
-
t.notThrows(() => emitter.emit('test'));
|
|
189
|
-
t.false(logged);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('error in one listener does not prevent others', t => {
|
|
193
|
-
const { emitter } = t.context;
|
|
194
|
-
let called = false;
|
|
195
|
-
emitter.on('test', () => {
|
|
196
|
-
throw new Error('err');
|
|
197
|
-
});
|
|
198
|
-
emitter.on('test', () => {
|
|
199
|
-
called = true;
|
|
200
|
-
});
|
|
201
|
-
t.notThrows(() => emitter.emit('test'));
|
|
202
|
-
t.true(called);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
test('this context in listener should be the emitter', t => {
|
|
206
|
-
const { emitter } = t.context;
|
|
207
|
-
emitter.on('test', function () {
|
|
208
|
-
// @ts-ignore
|
|
209
|
-
t.is(this, emitter);
|
|
210
|
-
});
|
|
211
|
-
emitter.emit('test');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test('event key is removed from events object when last listener removed', t => {
|
|
215
|
-
const { emitter } = t.context;
|
|
216
|
-
const fn = () => {};
|
|
217
|
-
emitter.on('test', fn);
|
|
218
|
-
t.true('test' in emitter.events); // or Object.prototype.hasOwnProperty.call(emitter.events, 'test')
|
|
219
|
-
emitter.removeListener('test', fn);
|
|
220
|
-
t.false('test' in emitter.events);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test('off with non-function listener should not throw', t => {
|
|
224
|
-
const { emitter } = t.context;
|
|
225
|
-
t.notThrows(() => {
|
|
226
|
-
// @ts-expect-error
|
|
227
|
-
emitter.off('test', 'not a function');
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test('same listener added twice should be called twice', t => {
|
|
232
|
-
const { emitter } = t.context;
|
|
233
|
-
let count = 0;
|
|
234
|
-
function fn() {
|
|
235
|
-
count++;
|
|
236
|
-
}
|
|
237
|
-
emitter.on('test', fn);
|
|
238
|
-
emitter.on('test', fn);
|
|
239
|
-
emitter.emit('test');
|
|
240
|
-
t.is(count, 2);
|
|
241
|
-
emitter.off('test', fn);
|
|
242
|
-
emitter.emit('test');
|
|
243
|
-
t.is(count, 3);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test('once listener should be removable via off with original', t => {
|
|
247
|
-
const { emitter } = t.context;
|
|
248
|
-
let called = false;
|
|
249
|
-
function listener() {
|
|
250
|
-
called = true;
|
|
251
|
-
}
|
|
252
|
-
emitter.once('test', listener);
|
|
253
|
-
emitter.off('test', listener);
|
|
254
|
-
emitter.emit('test');
|
|
255
|
-
t.false(called);
|
|
256
|
-
});
|
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import ava from 'ava';
|
|
3
|
-
import { EventEmitter } from '../src/event-emitter.js';
|
|
4
|
-
|
|
5
|
-
/** @typedef {{ emitter: EventEmitter<any> }} MyContext */
|
|
6
|
-
/** @typedef {import('ava').TestFn<MyContext>} TestFn */
|
|
7
|
-
|
|
8
|
-
const test = /** @type {TestFn} */ (ava);
|
|
9
|
-
|
|
10
|
-
test.beforeEach(t => {
|
|
11
|
-
t.context = { emitter: new EventEmitter() };
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// ----------------------------------------------------------------------
|
|
15
|
-
// Basic inheritance (quick check that Lite works)
|
|
16
|
-
// ----------------------------------------------------------------------
|
|
17
|
-
test('should inherit from EventEmitterLite: on and emit work', t => {
|
|
18
|
-
const { emitter } = t.context;
|
|
19
|
-
let called = false;
|
|
20
|
-
emitter.on('test', () => {
|
|
21
|
-
called = true;
|
|
22
|
-
});
|
|
23
|
-
emitter.emit('test');
|
|
24
|
-
t.true(called);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('once works as expected', t => {
|
|
28
|
-
const { emitter } = t.context;
|
|
29
|
-
let count = 0;
|
|
30
|
-
emitter.once('test', () => count++);
|
|
31
|
-
emitter.emit('test');
|
|
32
|
-
emitter.emit('test');
|
|
33
|
-
t.is(count, 1);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('off removes listener', t => {
|
|
37
|
-
const { emitter } = t.context;
|
|
38
|
-
let called = false;
|
|
39
|
-
const fn = () => {
|
|
40
|
-
called = true;
|
|
41
|
-
};
|
|
42
|
-
emitter.on('test', fn);
|
|
43
|
-
emitter.off('test', fn);
|
|
44
|
-
emitter.emit('test');
|
|
45
|
-
t.false(called);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// ----------------------------------------------------------------------
|
|
49
|
-
// Internal events (#has-listeners, #no-listeners, #listener-error)
|
|
50
|
-
// ----------------------------------------------------------------------
|
|
51
|
-
test('#has-listeners emitted when first listener added', t => {
|
|
52
|
-
const { emitter } = t.context;
|
|
53
|
-
/** @type {string[]} */
|
|
54
|
-
const events = [];
|
|
55
|
-
|
|
56
|
-
emitter.onHasEventListeners(
|
|
57
|
-
/** @param {string} event */ event => {
|
|
58
|
-
events.push(event);
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
emitter.on('foo', () => {});
|
|
63
|
-
emitter.on('foo', () => {}); // second should not trigger
|
|
64
|
-
emitter.on('bar', () => {}); // should trigger for bar
|
|
65
|
-
|
|
66
|
-
t.deepEqual(events, ['foo', 'bar']);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test('#no-listeners emitted when last listener removed', t => {
|
|
70
|
-
const { emitter } = t.context;
|
|
71
|
-
/** @type {string[]} */
|
|
72
|
-
const events = [];
|
|
73
|
-
|
|
74
|
-
emitter.onNoEventListeners(
|
|
75
|
-
/** @param {string} event */ event => {
|
|
76
|
-
events.push(event);
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const fn1 = () => {};
|
|
81
|
-
const fn2 = () => {};
|
|
82
|
-
const fnBar = () => {}; // keep reference for bar
|
|
83
|
-
|
|
84
|
-
emitter.on('foo', fn1);
|
|
85
|
-
emitter.on('foo', fn2);
|
|
86
|
-
emitter.on('bar', fnBar);
|
|
87
|
-
|
|
88
|
-
emitter.off('foo', fn1); // not last for foo yet
|
|
89
|
-
emitter.off('foo', fn2); // last for foo -> #no-listeners('foo')
|
|
90
|
-
emitter.off('bar', fnBar); // last for bar -> #no-listeners('bar')
|
|
91
|
-
|
|
92
|
-
t.deepEqual(events, ['foo', 'bar']);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('#listener-error emitted when a listener throws', t => {
|
|
96
|
-
const { emitter } = t.context;
|
|
97
|
-
/** @type {Array<{ error: Error; event: string; args: any[] }>} */
|
|
98
|
-
const errors = [];
|
|
99
|
-
|
|
100
|
-
emitter.onListenerError(
|
|
101
|
-
/** @type {(error: Error, event: string, ...args: any[]) => void} */ (
|
|
102
|
-
error,
|
|
103
|
-
event,
|
|
104
|
-
...args
|
|
105
|
-
) => {
|
|
106
|
-
errors.push({ error, event, args });
|
|
107
|
-
}
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
emitter.on('fail', () => {
|
|
111
|
-
throw new Error('boom 1');
|
|
112
|
-
});
|
|
113
|
-
emitter.on(
|
|
114
|
-
'fail',
|
|
115
|
-
/** @type {(x: number) => void} */ x => {
|
|
116
|
-
throw new Error(`boom ${x}`);
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
emitter.emit('fail', 42);
|
|
120
|
-
|
|
121
|
-
t.is(errors.length, 2);
|
|
122
|
-
t.is(errors[0].error.message, 'boom 1');
|
|
123
|
-
t.is(errors[0].event, 'fail');
|
|
124
|
-
t.deepEqual(errors[0].args, [42]);
|
|
125
|
-
|
|
126
|
-
t.is(errors[1].error.message, 'boom 42');
|
|
127
|
-
t.is(errors[1].event, 'fail');
|
|
128
|
-
t.deepEqual(errors[1].args, [42]);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('internal events do not trigger #has-listeners/#no-listeners for themselves', t => {
|
|
132
|
-
const { emitter } = t.context;
|
|
133
|
-
let hasCount = 0;
|
|
134
|
-
let noCount = 0;
|
|
135
|
-
|
|
136
|
-
emitter.onHasEventListeners(() => hasCount++);
|
|
137
|
-
emitter.onNoEventListeners(() => noCount++);
|
|
138
|
-
|
|
139
|
-
// Subscribing to internal events should not increase counters for 'has-listeners' etc.
|
|
140
|
-
emitter.onHasEventListeners(() => {});
|
|
141
|
-
emitter.onNoEventListeners(() => {});
|
|
142
|
-
emitter.onListenerError(() => {});
|
|
143
|
-
|
|
144
|
-
t.is(hasCount, 0);
|
|
145
|
-
t.is(noCount, 0);
|
|
146
|
-
|
|
147
|
-
// Adding a regular event should trigger
|
|
148
|
-
emitter.on('test', () => {});
|
|
149
|
-
t.is(hasCount, 1);
|
|
150
|
-
|
|
151
|
-
// Removing the last one should trigger no
|
|
152
|
-
emitter.off('test', () => {}); // but need to remove the same listener that was added
|
|
153
|
-
// For test purity, better to keep a reference
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// ----------------------------------------------------------------------
|
|
157
|
-
// waitForEvent / waitForAnyEvent
|
|
158
|
-
// ----------------------------------------------------------------------
|
|
159
|
-
test('waitForEvent resolves true when event emitted', async t => {
|
|
160
|
-
const { emitter } = t.context;
|
|
161
|
-
const promise = emitter.waitForEvent('ready');
|
|
162
|
-
emitter.emit('ready');
|
|
163
|
-
const result = await promise;
|
|
164
|
-
t.true(result);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test('waitForEvent resolves false on timeout', async t => {
|
|
168
|
-
const { emitter } = t.context;
|
|
169
|
-
const promise = emitter.waitForEvent('ready', 10);
|
|
170
|
-
const result = await promise;
|
|
171
|
-
t.false(result);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('waitForAnyEvent resolves true when any event emitted', async t => {
|
|
175
|
-
const { emitter } = t.context;
|
|
176
|
-
const promise = emitter.waitForAnyEvent(['start', 'stop'], 100);
|
|
177
|
-
emitter.emit('stop');
|
|
178
|
-
const result = await promise;
|
|
179
|
-
t.true(result);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('waitForAnyEvent resolves false on timeout', async t => {
|
|
183
|
-
const { emitter } = t.context;
|
|
184
|
-
const promise = emitter.waitForAnyEvent(['start', 'stop'], 10);
|
|
185
|
-
const result = await promise;
|
|
186
|
-
t.false(result);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
test('waitForAnyEvent unsubscribes after first event', async t => {
|
|
190
|
-
const { emitter } = t.context;
|
|
191
|
-
let count = 0;
|
|
192
|
-
emitter.on('event1', () => count++);
|
|
193
|
-
emitter.on('event2', () => count++);
|
|
194
|
-
|
|
195
|
-
await emitter.waitForAnyEvent(['event1', 'event2'], 100);
|
|
196
|
-
emitter.emit('event1');
|
|
197
|
-
emitter.emit('event2');
|
|
198
|
-
|
|
199
|
-
t.is(count, 2); // both events should fire after waiting
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test('waitForEvent throws if emitter destroyed', t => {
|
|
203
|
-
const { emitter } = t.context;
|
|
204
|
-
emitter.destroy();
|
|
205
|
-
t.throws(() => emitter.waitForEvent('test'), { message: /destroyed/ });
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('waitForAnyEvent throws if emitter destroyed', t => {
|
|
209
|
-
const { emitter } = t.context;
|
|
210
|
-
emitter.destroy();
|
|
211
|
-
t.throws(() => emitter.waitForAnyEvent(['test']), { message: /destroyed/ });
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// ----------------------------------------------------------------------
|
|
215
|
-
// destroy
|
|
216
|
-
// ----------------------------------------------------------------------
|
|
217
|
-
test('destroy sets isDestroyed = true and clears events', t => {
|
|
218
|
-
const { emitter } = t.context;
|
|
219
|
-
emitter.on('test', () => {});
|
|
220
|
-
t.false(emitter.isDestroyed);
|
|
221
|
-
emitter.destroy();
|
|
222
|
-
t.true(emitter.isDestroyed);
|
|
223
|
-
t.is(Object.keys(emitter.events).length, 0);
|
|
224
|
-
|
|
225
|
-
// After destroy, trying to subscribe to internal events should throw
|
|
226
|
-
t.throws(() => emitter.onHasEventListeners(() => {}), { message: /destroyed/ });
|
|
227
|
-
t.throws(() => emitter.onNoEventListeners(() => {}), { message: /destroyed/ });
|
|
228
|
-
t.throws(() => emitter.onListenerError(() => {}), { message: /destroyed/ });
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test('after destroy, on throws', t => {
|
|
232
|
-
const { emitter } = t.context;
|
|
233
|
-
emitter.destroy();
|
|
234
|
-
t.throws(() => emitter.on('test', () => {}), { message: /destroyed/ });
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('after destroy, emit does nothing (no throw)', t => {
|
|
238
|
-
const { emitter } = t.context;
|
|
239
|
-
emitter.destroy();
|
|
240
|
-
t.notThrows(() => emitter.emit('test'));
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test('after destroy, removeListener does nothing', t => {
|
|
244
|
-
const { emitter } = t.context;
|
|
245
|
-
emitter.destroy();
|
|
246
|
-
t.notThrows(() => emitter.removeListener('test', () => {}));
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('destroy can be called multiple times without error', t => {
|
|
250
|
-
const { emitter } = t.context;
|
|
251
|
-
emitter.destroy();
|
|
252
|
-
t.notThrows(() => emitter.destroy());
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// ----------------------------------------------------------------------
|
|
256
|
-
// clear and clearEventListeners
|
|
257
|
-
// ----------------------------------------------------------------------
|
|
258
|
-
test('clear removes all listeners and emits #no-listeners for each', t => {
|
|
259
|
-
const { emitter } = t.context;
|
|
260
|
-
/** @type {string[]} */
|
|
261
|
-
const noEvents = [];
|
|
262
|
-
|
|
263
|
-
emitter.onNoEventListeners(/** @param {string} event */ event => noEvents.push(event));
|
|
264
|
-
emitter.on('a', () => {});
|
|
265
|
-
emitter.on('b', () => {});
|
|
266
|
-
emitter.on('c', () => {});
|
|
267
|
-
|
|
268
|
-
emitter.clear();
|
|
269
|
-
|
|
270
|
-
t.deepEqual(noEvents.sort(), ['a', 'b', 'c']);
|
|
271
|
-
t.is(Object.keys(emitter.events).length, 0);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
test('clearEventListeners removes all listeners for one event', t => {
|
|
275
|
-
const { emitter } = t.context;
|
|
276
|
-
let noCalled = false;
|
|
277
|
-
|
|
278
|
-
emitter.onNoEventListeners(
|
|
279
|
-
/** @param {string} event */ event => {
|
|
280
|
-
if (event === 'test') noCalled = true;
|
|
281
|
-
}
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
emitter.on('test', () => {});
|
|
285
|
-
emitter.on('test', () => {});
|
|
286
|
-
emitter.on('other', () => {});
|
|
287
|
-
|
|
288
|
-
emitter.clearEventListeners('test');
|
|
289
|
-
|
|
290
|
-
t.true(noCalled);
|
|
291
|
-
t.false('test' in emitter.events); // key removed
|
|
292
|
-
t.true('other' in emitter.events); // other remains
|
|
293
|
-
t.is(emitter.events['other'].length, 1);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
// ----------------------------------------------------------------------
|
|
297
|
-
// logErrors and interaction with #listener-error
|
|
298
|
-
// ----------------------------------------------------------------------
|
|
299
|
-
test('when logErrors is true, errors are logged and #listener-error emitted', t => {
|
|
300
|
-
const { emitter } = t.context;
|
|
301
|
-
const originalError = console.error;
|
|
302
|
-
/** @type {any[][]} */
|
|
303
|
-
const logged = [];
|
|
304
|
-
console.error = (...args) => logged.push(args);
|
|
305
|
-
t.teardown(() => {
|
|
306
|
-
console.error = originalError;
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
let internalErrorCaught = false;
|
|
310
|
-
emitter.onListenerError(() => {
|
|
311
|
-
internalErrorCaught = true;
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
emitter.logErrors = true;
|
|
315
|
-
emitter.on('boom', () => {
|
|
316
|
-
throw new Error('kaboom');
|
|
317
|
-
});
|
|
318
|
-
emitter.emit('boom');
|
|
319
|
-
|
|
320
|
-
t.true(internalErrorCaught);
|
|
321
|
-
t.true(logged.length > 0);
|
|
322
|
-
t.regex(logged[0][0], /Error in listener for event "boom"/);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test('when logErrors is false, errors are not logged but #listener-error still emitted', t => {
|
|
326
|
-
const { emitter } = t.context;
|
|
327
|
-
const originalError = console.error;
|
|
328
|
-
let logged = false;
|
|
329
|
-
console.error = () => (logged = true);
|
|
330
|
-
t.teardown(() => {
|
|
331
|
-
console.error = originalError;
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
let internalErrorCaught = false;
|
|
335
|
-
emitter.onListenerError(() => {
|
|
336
|
-
internalErrorCaught = true;
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
emitter.logErrors = false;
|
|
340
|
-
emitter.on('boom', () => {
|
|
341
|
-
throw new Error('kaboom');
|
|
342
|
-
});
|
|
343
|
-
emitter.emit('boom');
|
|
344
|
-
|
|
345
|
-
t.true(internalErrorCaught);
|
|
346
|
-
t.false(logged);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// ----------------------------------------------------------------------
|
|
350
|
-
// Event key removal when no listeners (inherited from Lite)
|
|
351
|
-
// ----------------------------------------------------------------------
|
|
352
|
-
test('event key removed when last listener removed (inherited)', t => {
|
|
353
|
-
const { emitter } = t.context;
|
|
354
|
-
const fn = () => {};
|
|
355
|
-
emitter.on('test', fn);
|
|
356
|
-
t.true('test' in emitter.events);
|
|
357
|
-
emitter.removeListener('test', fn);
|
|
358
|
-
t.false('test' in emitter.events);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// ----------------------------------------------------------------------
|
|
362
|
-
// Getter isDestroyed
|
|
363
|
-
// ----------------------------------------------------------------------
|
|
364
|
-
test('isDestroyed reflects destroyed state', t => {
|
|
365
|
-
const { emitter } = t.context;
|
|
366
|
-
t.false(emitter.isDestroyed);
|
|
367
|
-
emitter.destroy();
|
|
368
|
-
t.true(emitter.isDestroyed);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
// test/event-emitter-extra.test.js (or add to existing file)
|
|
372
|
-
|
|
373
|
-
// ----------------------------------------------------------------------
|
|
374
|
-
// Additional tests to cover uncovered lines
|
|
375
|
-
// ----------------------------------------------------------------------
|
|
376
|
-
|
|
377
|
-
test('emit with no listeners should not throw and not call internal events', t => {
|
|
378
|
-
const { emitter } = t.context;
|
|
379
|
-
let internalErrorCalled = false;
|
|
380
|
-
emitter.onListenerError(() => {
|
|
381
|
-
internalErrorCalled = true;
|
|
382
|
-
});
|
|
383
|
-
t.notThrows(() => emitter.emit('nonexistent'));
|
|
384
|
-
t.false(internalErrorCalled); // covers line where listeners existence is checked
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
test('error in #listener-error handler does not cause infinite loop', t => {
|
|
388
|
-
const { emitter } = t.context;
|
|
389
|
-
const originalConsoleError = console.error;
|
|
390
|
-
let consoleCalled = false;
|
|
391
|
-
console.error = () => {
|
|
392
|
-
consoleCalled = true;
|
|
393
|
-
};
|
|
394
|
-
t.teardown(() => {
|
|
395
|
-
console.error = originalConsoleError;
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// Subscriber to #listener-error itself throws
|
|
399
|
-
emitter.onListenerError(() => {
|
|
400
|
-
throw new Error('error in error handler');
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
// Regular listener that triggers #listener-error
|
|
404
|
-
emitter.on('boom', () => {
|
|
405
|
-
throw new Error('original error');
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
t.notThrows(() => emitter.emit('boom'));
|
|
409
|
-
t.true(consoleCalled); // critical error should be logged
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
test('waitForEvent with zero timeout waits indefinitely', async t => {
|
|
413
|
-
const { emitter } = t.context;
|
|
414
|
-
let resolved = false;
|
|
415
|
-
const promise = emitter.waitForEvent('ready', 0).then(() => {
|
|
416
|
-
resolved = true;
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
await new Promise(r => setTimeout(r, 50));
|
|
420
|
-
t.false(resolved); // not resolved yet
|
|
421
|
-
|
|
422
|
-
emitter.emit('ready');
|
|
423
|
-
await promise;
|
|
424
|
-
t.true(resolved);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test('waitForEvent with negative timeout waits indefinitely until event', async t => {
|
|
428
|
-
const { emitter } = t.context;
|
|
429
|
-
const promise = emitter.waitForEvent('ready', -10);
|
|
430
|
-
setTimeout(() => emitter.emit('ready'), 20);
|
|
431
|
-
const result = await promise;
|
|
432
|
-
t.true(result);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test('clearEventListeners on non-existent event does nothing', t => {
|
|
436
|
-
const { emitter } = t.context;
|
|
437
|
-
t.notThrows(() => emitter.clearEventListeners('absent'));
|
|
438
|
-
t.false('absent' in emitter.events);
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
test('calling destroy multiple times does nothing', t => {
|
|
442
|
-
const { emitter } = t.context;
|
|
443
|
-
emitter.destroy();
|
|
444
|
-
t.notThrows(() => emitter.destroy());
|
|
445
|
-
t.true(emitter.isDestroyed);
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
test('after destroy, onHasEventListeners throws', t => {
|
|
449
|
-
const { emitter } = t.context;
|
|
450
|
-
emitter.destroy();
|
|
451
|
-
t.throws(() => emitter.onHasEventListeners(() => {}), { message: /destroyed/ });
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
test('error in #listener-error handler does not cause infinite loop and flag resets', t => {
|
|
455
|
-
const { emitter } = t.context;
|
|
456
|
-
let calls = 0;
|
|
457
|
-
|
|
458
|
-
// Subscribe to #listener-error and throw
|
|
459
|
-
emitter.onListenerError(() => {
|
|
460
|
-
calls++;
|
|
461
|
-
throw new Error('error in error handler');
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
// Regular listener that triggers the first error
|
|
465
|
-
emitter.on('boom', () => {
|
|
466
|
-
throw new Error('original error');
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
t.notThrows(() => emitter.emit('boom'));
|
|
470
|
-
t.is(calls, 1); // #listener-error handler called once
|
|
471
|
-
});
|