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