• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --expose-internals --no-warnings --experimental-abortcontroller
2'use strict';
3
4const common = require('../common');
5const assert = require('assert');
6const { on, EventEmitter } = require('events');
7const {
8  EventTarget,
9  NodeEventTarget,
10  Event
11} = require('internal/event_target');
12
13async function basic() {
14  const ee = new EventEmitter();
15  process.nextTick(() => {
16    ee.emit('foo', 'bar');
17    // 'bar' is a spurious event, we are testing
18    // that it does not show up in the iterable
19    ee.emit('bar', 24);
20    ee.emit('foo', 42);
21  });
22
23  const iterable = on(ee, 'foo');
24
25  const expected = [['bar'], [42]];
26
27  for await (const event of iterable) {
28    const current = expected.shift();
29
30    assert.deepStrictEqual(current, event);
31
32    if (expected.length === 0) {
33      break;
34    }
35  }
36  assert.strictEqual(ee.listenerCount('foo'), 0);
37  assert.strictEqual(ee.listenerCount('error'), 0);
38}
39
40async function invalidArgType() {
41  assert.throws(() => on({}, 'foo'), common.expectsError({
42    code: 'ERR_INVALID_ARG_TYPE',
43    name: 'TypeError',
44  }));
45}
46
47async function error() {
48  const ee = new EventEmitter();
49  const _err = new Error('kaboom');
50  process.nextTick(() => {
51    ee.emit('error', _err);
52  });
53
54  const iterable = on(ee, 'foo');
55  let looped = false;
56  let thrown = false;
57
58  try {
59    // eslint-disable-next-line no-unused-vars
60    for await (const event of iterable) {
61      looped = true;
62    }
63  } catch (err) {
64    thrown = true;
65    assert.strictEqual(err, _err);
66  }
67  assert.strictEqual(thrown, true);
68  assert.strictEqual(looped, false);
69}
70
71async function errorDelayed() {
72  const ee = new EventEmitter();
73  const _err = new Error('kaboom');
74  process.nextTick(() => {
75    ee.emit('foo', 42);
76    ee.emit('error', _err);
77  });
78
79  const iterable = on(ee, 'foo');
80  const expected = [[42]];
81  let thrown = false;
82
83  try {
84    for await (const event of iterable) {
85      const current = expected.shift();
86      assert.deepStrictEqual(current, event);
87    }
88  } catch (err) {
89    thrown = true;
90    assert.strictEqual(err, _err);
91  }
92  assert.strictEqual(thrown, true);
93  assert.strictEqual(ee.listenerCount('foo'), 0);
94  assert.strictEqual(ee.listenerCount('error'), 0);
95}
96
97async function throwInLoop() {
98  const ee = new EventEmitter();
99  const _err = new Error('kaboom');
100
101  process.nextTick(() => {
102    ee.emit('foo', 42);
103  });
104
105  try {
106    for await (const event of on(ee, 'foo')) {
107      assert.deepStrictEqual(event, [42]);
108      throw _err;
109    }
110  } catch (err) {
111    assert.strictEqual(err, _err);
112  }
113
114  assert.strictEqual(ee.listenerCount('foo'), 0);
115  assert.strictEqual(ee.listenerCount('error'), 0);
116}
117
118async function next() {
119  const ee = new EventEmitter();
120  const iterable = on(ee, 'foo');
121
122  process.nextTick(function() {
123    ee.emit('foo', 'bar');
124    ee.emit('foo', 42);
125    iterable.return();
126  });
127
128  const results = await Promise.all([
129    iterable.next(),
130    iterable.next(),
131    iterable.next(),
132  ]);
133
134  assert.deepStrictEqual(results, [{
135    value: ['bar'],
136    done: false
137  }, {
138    value: [42],
139    done: false
140  }, {
141    value: undefined,
142    done: true
143  }]);
144
145  assert.deepStrictEqual(await iterable.next(), {
146    value: undefined,
147    done: true
148  });
149}
150
151async function nextError() {
152  const ee = new EventEmitter();
153  const iterable = on(ee, 'foo');
154  const _err = new Error('kaboom');
155  process.nextTick(function() {
156    ee.emit('error', _err);
157  });
158  const results = await Promise.allSettled([
159    iterable.next(),
160    iterable.next(),
161    iterable.next(),
162  ]);
163  assert.deepStrictEqual(results, [{
164    status: 'rejected',
165    reason: _err
166  }, {
167    status: 'fulfilled',
168    value: {
169      value: undefined,
170      done: true
171    }
172  }, {
173    status: 'fulfilled',
174    value: {
175      value: undefined,
176      done: true
177    }
178  }]);
179  assert.strictEqual(ee.listeners('error').length, 0);
180}
181
182async function iterableThrow() {
183  const ee = new EventEmitter();
184  const iterable = on(ee, 'foo');
185
186  process.nextTick(() => {
187    ee.emit('foo', 'bar');
188    ee.emit('foo', 42); // lost in the queue
189    iterable.throw(_err);
190  });
191
192  const _err = new Error('kaboom');
193  let thrown = false;
194
195  assert.throws(() => {
196    // No argument
197    iterable.throw();
198  }, {
199    message: 'The "EventEmitter.AsyncIterator" property must be' +
200    ' an instance of Error. Received undefined',
201    name: 'TypeError'
202  });
203
204  const expected = [['bar'], [42]];
205
206  try {
207    for await (const event of iterable) {
208      assert.deepStrictEqual(event, expected.shift());
209    }
210  } catch (err) {
211    thrown = true;
212    assert.strictEqual(err, _err);
213  }
214  assert.strictEqual(thrown, true);
215  assert.strictEqual(expected.length, 0);
216  assert.strictEqual(ee.listenerCount('foo'), 0);
217  assert.strictEqual(ee.listenerCount('error'), 0);
218}
219
220async function eventTarget() {
221  const et = new EventTarget();
222  const tick = () => et.dispatchEvent(new Event('tick'));
223  const interval = setInterval(tick, 0);
224  let count = 0;
225  for await (const [ event ] of on(et, 'tick')) {
226    count++;
227    assert.strictEqual(event.type, 'tick');
228    if (count >= 5) {
229      break;
230    }
231  }
232  assert.strictEqual(count, 5);
233  clearInterval(interval);
234}
235
236async function errorListenerCount() {
237  const et = new EventEmitter();
238  on(et, 'foo');
239  assert.strictEqual(et.listenerCount('error'), 1);
240}
241
242async function nodeEventTarget() {
243  const et = new NodeEventTarget();
244  const tick = () => et.dispatchEvent(new Event('tick'));
245  const interval = setInterval(tick, 0);
246  let count = 0;
247  for await (const [ event] of on(et, 'tick')) {
248    count++;
249    assert.strictEqual(event.type, 'tick');
250    if (count >= 5) {
251      break;
252    }
253  }
254  assert.strictEqual(count, 5);
255  clearInterval(interval);
256}
257
258async function abortableOnBefore() {
259  const ee = new EventEmitter();
260  const ac = new AbortController();
261  ac.abort();
262  [1, {}, null, false, 'hi'].forEach((signal) => {
263    assert.throws(() => on(ee, 'foo', { signal }), {
264      code: 'ERR_INVALID_ARG_TYPE'
265    });
266  });
267  assert.throws(() => on(ee, 'foo', { signal: ac.signal }), {
268    name: 'AbortError'
269  });
270}
271
272async function eventTargetAbortableOnBefore() {
273  const et = new EventTarget();
274  const ac = new AbortController();
275  ac.abort();
276  [1, {}, null, false, 'hi'].forEach((signal) => {
277    assert.throws(() => on(et, 'foo', { signal }), {
278      code: 'ERR_INVALID_ARG_TYPE'
279    });
280  });
281  assert.throws(() => on(et, 'foo', { signal: ac.signal }), {
282    name: 'AbortError'
283  });
284}
285
286async function abortableOnAfter() {
287  const ee = new EventEmitter();
288  const ac = new AbortController();
289
290  const i = setInterval(() => ee.emit('foo', 'foo'), 10);
291
292  async function foo() {
293    for await (const f of on(ee, 'foo', { signal: ac.signal })) {
294      assert.strictEqual(f, 'foo');
295    }
296  }
297
298  foo().catch(common.mustCall((error) => {
299    assert.strictEqual(error.name, 'AbortError');
300  })).finally(() => {
301    clearInterval(i);
302  });
303
304  process.nextTick(() => ac.abort());
305}
306
307async function eventTargetAbortableOnAfter() {
308  const et = new EventTarget();
309  const ac = new AbortController();
310
311  const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10);
312
313  async function foo() {
314    for await (const f of on(et, 'foo', { signal: ac.signal })) {
315      assert(f);
316    }
317  }
318
319  foo().catch(common.mustCall((error) => {
320    assert.strictEqual(error.name, 'AbortError');
321  })).finally(() => {
322    clearInterval(i);
323  });
324
325  process.nextTick(() => ac.abort());
326}
327
328async function eventTargetAbortableOnAfter2() {
329  const et = new EventTarget();
330  const ac = new AbortController();
331
332  const i = setInterval(() => et.dispatchEvent(new Event('foo')), 10);
333
334  async function foo() {
335    for await (const f of on(et, 'foo', { signal: ac.signal })) {
336      assert(f);
337      // Cancel after a single event has been triggered.
338      ac.abort();
339    }
340  }
341
342  foo().catch(common.mustCall((error) => {
343    assert.strictEqual(error.name, 'AbortError');
344  })).finally(() => {
345    clearInterval(i);
346  });
347}
348
349async function abortableOnAfterDone() {
350  const ee = new EventEmitter();
351  const ac = new AbortController();
352
353  const i = setInterval(() => ee.emit('foo', 'foo'), 1);
354  let count = 0;
355
356  async function foo() {
357    for await (const f of on(ee, 'foo', { signal: ac.signal })) {
358      assert.strictEqual(f[0], 'foo');
359      if (++count === 5)
360        break;
361    }
362    ac.abort();  // No error will occur
363  }
364
365  foo().finally(() => {
366    clearInterval(i);
367  });
368}
369
370async function run() {
371  const funcs = [
372    basic,
373    invalidArgType,
374    error,
375    errorDelayed,
376    throwInLoop,
377    next,
378    nextError,
379    iterableThrow,
380    eventTarget,
381    errorListenerCount,
382    nodeEventTarget,
383    abortableOnBefore,
384    abortableOnAfter,
385    eventTargetAbortableOnBefore,
386    eventTargetAbortableOnAfter,
387    eventTargetAbortableOnAfter2,
388    abortableOnAfterDone,
389  ];
390
391  for (const fn of funcs) {
392    await fn();
393  }
394}
395
396run().then(common.mustCall());
397