• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4const { once, EventEmitter } = require('events');
5const { strictEqual, deepStrictEqual } = require('assert');
6
7class EventTargetMock {
8  constructor() {
9    this.events = {};
10  }
11
12  addEventListener = common.mustCall(function(name, listener, options) {
13    if (!(name in this.events)) {
14      this.events[name] = { listeners: [], options };
15    }
16    this.events[name].listeners.push(listener);
17  });
18
19  removeEventListener = common.mustCall(function(name, callback) {
20    if (!(name in this.events)) {
21      return;
22    }
23    const event = this.events[name];
24    const stack = event.listeners;
25
26    for (let i = 0, l = stack.length; i < l; i++) {
27      if (stack[i] === callback) {
28        stack.splice(i, 1);
29        if (stack.length === 0) {
30          Reflect.deleteProperty(this.events, name);
31        }
32        return;
33      }
34    }
35  });
36
37  dispatchEvent = function(name, ...arg) {
38    if (!(name in this.events)) {
39      return true;
40    }
41    const event = this.events[name];
42    const stack = event.listeners.slice();
43
44    for (let i = 0, l = stack.length; i < l; i++) {
45      stack[i].apply(this, arg);
46      if (event.options.once) {
47        this.removeEventListener(name, stack[i]);
48      }
49    }
50    return !name.defaultPrevented;
51  };
52}
53
54async function onceAnEvent() {
55  const ee = new EventEmitter();
56
57  process.nextTick(() => {
58    ee.emit('myevent', 42);
59  });
60
61  const [value] = await once(ee, 'myevent');
62  strictEqual(value, 42);
63  strictEqual(ee.listenerCount('error'), 0);
64  strictEqual(ee.listenerCount('myevent'), 0);
65}
66
67async function onceAnEventWithTwoArgs() {
68  const ee = new EventEmitter();
69
70  process.nextTick(() => {
71    ee.emit('myevent', 42, 24);
72  });
73
74  const value = await once(ee, 'myevent');
75  deepStrictEqual(value, [42, 24]);
76}
77
78async function catchesErrors() {
79  const ee = new EventEmitter();
80
81  const expected = new Error('kaboom');
82  let err;
83  process.nextTick(() => {
84    ee.emit('error', expected);
85  });
86
87  try {
88    await once(ee, 'myevent');
89  } catch (_e) {
90    err = _e;
91  }
92  strictEqual(err, expected);
93  strictEqual(ee.listenerCount('error'), 0);
94  strictEqual(ee.listenerCount('myevent'), 0);
95}
96
97async function stopListeningAfterCatchingError() {
98  const ee = new EventEmitter();
99
100  const expected = new Error('kaboom');
101  let err;
102  process.nextTick(() => {
103    ee.emit('error', expected);
104    ee.emit('myevent', 42, 24);
105  });
106
107  process.on('multipleResolves', common.mustNotCall());
108
109  try {
110    await once(ee, 'myevent');
111  } catch (_e) {
112    err = _e;
113  }
114  process.removeAllListeners('multipleResolves');
115  strictEqual(err, expected);
116  strictEqual(ee.listenerCount('error'), 0);
117  strictEqual(ee.listenerCount('myevent'), 0);
118}
119
120async function onceError() {
121  const ee = new EventEmitter();
122
123  const expected = new Error('kaboom');
124  process.nextTick(() => {
125    ee.emit('error', expected);
126  });
127
128  const [err] = await once(ee, 'error');
129  strictEqual(err, expected);
130  strictEqual(ee.listenerCount('error'), 0);
131  strictEqual(ee.listenerCount('myevent'), 0);
132}
133
134async function onceWithEventTarget() {
135  const et = new EventTargetMock();
136
137  process.nextTick(() => {
138    et.dispatchEvent('myevent', 42);
139  });
140  const [ value ] = await once(et, 'myevent');
141  strictEqual(value, 42);
142  strictEqual(Reflect.has(et.events, 'myevent'), false);
143}
144
145async function onceWithEventTargetTwoArgs() {
146  const et = new EventTargetMock();
147
148  process.nextTick(() => {
149    et.dispatchEvent('myevent', 42, 24);
150  });
151
152  const value = await once(et, 'myevent');
153  deepStrictEqual(value, [42, 24]);
154}
155
156async function onceWithEventTargetError() {
157  const et = new EventTargetMock();
158
159  const expected = new Error('kaboom');
160  process.nextTick(() => {
161    et.dispatchEvent('error', expected);
162  });
163
164  const [err] = await once(et, 'error');
165  strictEqual(err, expected);
166  strictEqual(Reflect.has(et.events, 'error'), false);
167}
168
169async function assumesEventEmitterIfOnIsAFunction() {
170  const ee = new EventEmitter();
171  ee.addEventListener = () => {};
172
173  const expected = new Error('kaboom');
174  let err;
175  process.nextTick(() => {
176    ee.emit('error', expected);
177  });
178
179  try {
180    await once(ee, 'myevent');
181  } catch (_e) {
182    err = _e;
183  }
184
185  strictEqual(err, expected);
186  strictEqual(ee.listenerCount('error'), 0);
187  strictEqual(ee.listenerCount('myevent'), 0);
188}
189
190Promise.all([
191  onceAnEvent(),
192  onceAnEventWithTwoArgs(),
193  catchesErrors(),
194  stopListeningAfterCatchingError(),
195  onceError(),
196  onceWithEventTarget(),
197  onceWithEventTargetTwoArgs(),
198  onceWithEventTargetError(),
199  assumesEventEmitterIfOnIsAFunction(),
200]).then(common.mustCall());
201