1'use strict';
2// Flags: --expose-internals --no-warnings
3
4const common = require('../common');
5const { once, EventEmitter } = require('events');
6const {
7  strictEqual,
8  deepStrictEqual,
9  fail,
10  rejects,
11} = require('assert');
12const { kEvents } = require('internal/event_target');
13
14async function onceAnEvent() {
15  const ee = new EventEmitter();
16
17  process.nextTick(() => {
18    ee.emit('myevent', 42);
19  });
20
21  const [value] = await once(ee, 'myevent');
22  strictEqual(value, 42);
23  strictEqual(ee.listenerCount('error'), 0);
24  strictEqual(ee.listenerCount('myevent'), 0);
25}
26
27async function onceAnEventWithNullOptions() {
28  const ee = new EventEmitter();
29
30  process.nextTick(() => {
31    ee.emit('myevent', 42);
32  });
33
34  const [value] = await once(ee, 'myevent', null);
35  strictEqual(value, 42);
36}
37
38
39async function onceAnEventWithTwoArgs() {
40  const ee = new EventEmitter();
41
42  process.nextTick(() => {
43    ee.emit('myevent', 42, 24);
44  });
45
46  const value = await once(ee, 'myevent');
47  deepStrictEqual(value, [42, 24]);
48}
49
50async function catchesErrors() {
51  const ee = new EventEmitter();
52
53  const expected = new Error('kaboom');
54  let err;
55  process.nextTick(() => {
56    ee.emit('error', expected);
57  });
58
59  try {
60    await once(ee, 'myevent');
61  } catch (_e) {
62    err = _e;
63  }
64  strictEqual(err, expected);
65  strictEqual(ee.listenerCount('error'), 0);
66  strictEqual(ee.listenerCount('myevent'), 0);
67}
68
69async function catchesErrorsWithAbortSignal() {
70  const ee = new EventEmitter();
71  const ac = new AbortController();
72  const signal = ac.signal;
73
74  const expected = new Error('boom');
75  let err;
76  process.nextTick(() => {
77    ee.emit('error', expected);
78  });
79
80  try {
81    const promise = once(ee, 'myevent', { signal });
82    strictEqual(ee.listenerCount('error'), 1);
83    strictEqual(signal[kEvents].size, 1);
84
85    await promise;
86  } catch (e) {
87    err = e;
88  }
89  strictEqual(err, expected);
90  strictEqual(ee.listenerCount('error'), 0);
91  strictEqual(ee.listenerCount('myevent'), 0);
92  strictEqual(signal[kEvents].size, 0);
93}
94
95async function stopListeningAfterCatchingError() {
96  const ee = new EventEmitter();
97
98  const expected = new Error('kaboom');
99  let err;
100  process.nextTick(() => {
101    ee.emit('error', expected);
102    ee.emit('myevent', 42, 24);
103  });
104
105  try {
106    await once(ee, 'myevent');
107  } catch (_e) {
108    err = _e;
109  }
110  process.removeAllListeners('multipleResolves');
111  strictEqual(err, expected);
112  strictEqual(ee.listenerCount('error'), 0);
113  strictEqual(ee.listenerCount('myevent'), 0);
114}
115
116async function onceError() {
117  const ee = new EventEmitter();
118
119  const expected = new Error('kaboom');
120  process.nextTick(() => {
121    ee.emit('error', expected);
122  });
123
124  const promise = once(ee, 'error');
125  strictEqual(ee.listenerCount('error'), 1);
126  const [ err ] = await promise;
127  strictEqual(err, expected);
128  strictEqual(ee.listenerCount('error'), 0);
129  strictEqual(ee.listenerCount('myevent'), 0);
130}
131
132async function onceWithEventTarget() {
133  const et = new EventTarget();
134  const event = new Event('myevent');
135  process.nextTick(() => {
136    et.dispatchEvent(event);
137  });
138  const [ value ] = await once(et, 'myevent');
139  strictEqual(value, event);
140}
141
142async function onceWithEventTargetError() {
143  const et = new EventTarget();
144  const error = new Event('error');
145  process.nextTick(() => {
146    et.dispatchEvent(error);
147  });
148
149  const [ err ] = await once(et, 'error');
150  strictEqual(err, error);
151}
152
153async function onceWithInvalidEventEmmiter() {
154  const ac = new AbortController();
155  return rejects(once(ac, 'myevent'), {
156    code: 'ERR_INVALID_ARG_TYPE',
157  });
158}
159
160async function prioritizesEventEmitter() {
161  const ee = new EventEmitter();
162  ee.addEventListener = fail;
163  ee.removeAllListeners = fail;
164  process.nextTick(() => ee.emit('foo'));
165  await once(ee, 'foo');
166}
167
168async function abortSignalBefore() {
169  const ee = new EventEmitter();
170  ee.on('error', common.mustNotCall());
171  const abortedSignal = AbortSignal.abort();
172
173  await Promise.all([1, {}, 'hi', null, false].map((signal) => {
174    return rejects(once(ee, 'foo', { signal }), {
175      code: 'ERR_INVALID_ARG_TYPE',
176    });
177  }));
178
179  return rejects(once(ee, 'foo', { signal: abortedSignal }), {
180    name: 'AbortError',
181  });
182}
183
184async function abortSignalAfter() {
185  const ee = new EventEmitter();
186  const ac = new AbortController();
187  ee.on('error', common.mustNotCall());
188  const r = rejects(once(ee, 'foo', { signal: ac.signal }), {
189    name: 'AbortError',
190  });
191  process.nextTick(() => ac.abort());
192  return r;
193}
194
195async function abortSignalAfterEvent() {
196  const ee = new EventEmitter();
197  const ac = new AbortController();
198  process.nextTick(() => {
199    ee.emit('foo');
200    ac.abort();
201  });
202  const promise = once(ee, 'foo', { signal: ac.signal });
203  strictEqual(ac.signal[kEvents].size, 1);
204  await promise;
205  strictEqual(ac.signal[kEvents].size, 0);
206}
207
208async function abortSignalRemoveListener() {
209  const ee = new EventEmitter();
210  const ac = new AbortController();
211
212  try {
213    process.nextTick(() => ac.abort());
214    await once(ee, 'test', { signal: ac.signal });
215  } catch {
216    strictEqual(ee.listeners('test').length, 0);
217    strictEqual(ee.listeners('error').length, 0);
218  }
219}
220
221async function eventTargetAbortSignalBefore() {
222  const et = new EventTarget();
223  const abortedSignal = AbortSignal.abort();
224
225  await Promise.all([1, {}, 'hi', null, false].map((signal) => {
226    return rejects(once(et, 'foo', { signal }), {
227      code: 'ERR_INVALID_ARG_TYPE',
228    });
229  }));
230
231  return rejects(once(et, 'foo', { signal: abortedSignal }), {
232    name: 'AbortError',
233  });
234}
235
236async function eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped() {
237  const et = new EventTarget();
238  const ac = new AbortController();
239  const { signal } = ac;
240  signal.addEventListener('abort', (e) => e.stopImmediatePropagation(), { once: true });
241
242  process.nextTick(() => ac.abort());
243  return rejects(once(et, 'foo', { signal }), {
244    name: 'AbortError',
245  });
246}
247
248async function eventTargetAbortSignalAfter() {
249  const et = new EventTarget();
250  const ac = new AbortController();
251  const r = rejects(once(et, 'foo', { signal: ac.signal }), {
252    name: 'AbortError',
253  });
254  process.nextTick(() => ac.abort());
255  return r;
256}
257
258async function eventTargetAbortSignalAfterEvent() {
259  const et = new EventTarget();
260  const ac = new AbortController();
261  process.nextTick(() => {
262    et.dispatchEvent(new Event('foo'));
263    ac.abort();
264  });
265  await once(et, 'foo', { signal: ac.signal });
266}
267
268Promise.all([
269  onceAnEvent(),
270  onceAnEventWithNullOptions(),
271  onceAnEventWithTwoArgs(),
272  catchesErrors(),
273  catchesErrorsWithAbortSignal(),
274  stopListeningAfterCatchingError(),
275  onceError(),
276  onceWithEventTarget(),
277  onceWithEventTargetError(),
278  onceWithInvalidEventEmmiter(),
279  prioritizesEventEmitter(),
280  abortSignalBefore(),
281  abortSignalAfter(),
282  abortSignalAfterEvent(),
283  abortSignalRemoveListener(),
284  eventTargetAbortSignalBefore(),
285  eventTargetAbortSignalBeforeEvenWhenSignalPropagationStopped(),
286  eventTargetAbortSignalAfter(),
287  eventTargetAbortSignalAfterEvent(),
288]).then(common.mustCall());
289