• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2const common = require('../common');
3const assert = require('assert');
4const { EventEmitter, captureRejectionSymbol } = require('events');
5const { inherits } = require('util');
6
7// Inherits from EE without a call to the
8// parent constructor.
9function NoConstructor() {
10}
11
12inherits(NoConstructor, EventEmitter);
13
14function captureRejections() {
15  const ee = new EventEmitter({ captureRejections: true });
16  const _err = new Error('kaboom');
17  ee.on('something', common.mustCall(async (value) => {
18    throw _err;
19  }));
20
21  ee.on('error', common.mustCall((err) => {
22    assert.strictEqual(err, _err);
23    process.nextTick(captureRejectionsTwoHandlers);
24  }));
25
26  ee.emit('something');
27}
28
29function captureRejectionsTwoHandlers() {
30  const ee = new EventEmitter({ captureRejections: true });
31  const _err = new Error('kaboom');
32
33  ee.on('something', common.mustCall(async (value) => {
34    throw _err;
35  }));
36
37  // throw twice
38  ee.on('something', common.mustCall(async (value) => {
39    throw _err;
40  }));
41
42  let count = 0;
43
44  ee.on('error', common.mustCall((err) => {
45    assert.strictEqual(err, _err);
46    if (++count === 2) {
47      process.nextTick(defaultValue);
48    }
49  }, 2));
50
51  ee.emit('something');
52}
53
54function defaultValue() {
55  const ee = new EventEmitter();
56  const _err = new Error('kaboom');
57  ee.on('something', common.mustCall(async (value) => {
58    throw _err;
59  }));
60
61  process.removeAllListeners('unhandledRejection');
62
63  process.once('unhandledRejection', common.mustCall((err) => {
64    // restore default
65    process.on('unhandledRejection', (err) => { throw err; });
66
67    assert.strictEqual(err, _err);
68    process.nextTick(globalSetting);
69  }));
70
71  ee.emit('something');
72}
73
74function globalSetting() {
75  assert.strictEqual(EventEmitter.captureRejections, false);
76  EventEmitter.captureRejections = true;
77  const ee = new EventEmitter();
78  const _err = new Error('kaboom');
79  ee.on('something', common.mustCall(async (value) => {
80    throw _err;
81  }));
82
83  ee.on('error', common.mustCall((err) => {
84    assert.strictEqual(err, _err);
85
86    // restore default
87    EventEmitter.captureRejections = false;
88    process.nextTick(configurable);
89  }));
90
91  ee.emit('something');
92}
93
94// We need to be able to configure this for streams, as we would
95// like to call destro(err) there.
96function configurable() {
97  const ee = new EventEmitter({ captureRejections: true });
98  const _err = new Error('kaboom');
99  ee.on('something', common.mustCall(async (...args) => {
100    assert.deepStrictEqual(args, [42, 'foobar']);
101    throw _err;
102  }));
103
104  assert.strictEqual(captureRejectionSymbol, Symbol.for('nodejs.rejection'));
105
106  ee[captureRejectionSymbol] = common.mustCall((err, type, ...args) => {
107    assert.strictEqual(err, _err);
108    assert.strictEqual(type, 'something');
109    assert.deepStrictEqual(args, [42, 'foobar']);
110    process.nextTick(globalSettingNoConstructor);
111  });
112
113  ee.emit('something', 42, 'foobar');
114}
115
116function globalSettingNoConstructor() {
117  assert.strictEqual(EventEmitter.captureRejections, false);
118  EventEmitter.captureRejections = true;
119  const ee = new NoConstructor();
120  const _err = new Error('kaboom');
121  ee.on('something', common.mustCall(async (value) => {
122    throw _err;
123  }));
124
125  ee.on('error', common.mustCall((err) => {
126    assert.strictEqual(err, _err);
127
128    // restore default
129    EventEmitter.captureRejections = false;
130    process.nextTick(thenable);
131  }));
132
133  ee.emit('something');
134}
135
136function thenable() {
137  const ee = new EventEmitter({ captureRejections: true });
138  const _err = new Error('kaboom');
139  ee.on('something', common.mustCall((value) => {
140    const obj = {};
141
142    Object.defineProperty(obj, 'then', {
143      get: common.mustCall(() => {
144        return common.mustCall((resolved, rejected) => {
145          assert.strictEqual(resolved, undefined);
146          rejected(_err);
147        });
148      }, 1) // Only 1 call for Promises/A+ compat.
149    });
150
151    return obj;
152  }));
153
154  ee.on('error', common.mustCall((err) => {
155    assert.strictEqual(err, _err);
156    process.nextTick(avoidLoopOnRejection);
157  }));
158
159  ee.emit('something');
160}
161
162function avoidLoopOnRejection() {
163  const ee = new EventEmitter({ captureRejections: true });
164  const _err1 = new Error('kaboom');
165  const _err2 = new Error('kaboom2');
166  ee.on('something', common.mustCall(async (value) => {
167    throw _err1;
168  }));
169
170  ee[captureRejectionSymbol] = common.mustCall(async (err) => {
171    assert.strictEqual(err, _err1);
172    throw _err2;
173  });
174
175  process.removeAllListeners('unhandledRejection');
176
177  process.once('unhandledRejection', common.mustCall((err) => {
178    // restore default
179    process.on('unhandledRejection', (err) => { throw err; });
180
181    assert.strictEqual(err, _err2);
182    process.nextTick(avoidLoopOnError);
183  }));
184
185  ee.emit('something');
186}
187
188function avoidLoopOnError() {
189  const ee = new EventEmitter({ captureRejections: true });
190  const _err1 = new Error('kaboom');
191  const _err2 = new Error('kaboom2');
192  ee.on('something', common.mustCall(async (value) => {
193    throw _err1;
194  }));
195
196  ee.on('error', common.mustCall(async (err) => {
197    assert.strictEqual(err, _err1);
198    throw _err2;
199  }));
200
201  process.removeAllListeners('unhandledRejection');
202
203  process.once('unhandledRejection', common.mustCall((err) => {
204    // restore default
205    process.on('unhandledRejection', (err) => { throw err; });
206
207    assert.strictEqual(err, _err2);
208    process.nextTick(thenableThatThrows);
209  }));
210
211  ee.emit('something');
212}
213
214function thenableThatThrows() {
215  const ee = new EventEmitter({ captureRejections: true });
216  const _err = new Error('kaboom');
217  ee.on('something', common.mustCall((value) => {
218    const obj = {};
219
220    Object.defineProperty(obj, 'then', {
221      get: common.mustCall(() => {
222        throw _err;
223      }, 1) // Only 1 call for Promises/A+ compat.
224    });
225
226    return obj;
227  }));
228
229  ee.on('error', common.mustCall((err) => {
230    assert.strictEqual(err, _err);
231    process.nextTick(resetCaptureOnThrowInError);
232  }));
233
234  ee.emit('something');
235}
236
237function resetCaptureOnThrowInError() {
238  const ee = new EventEmitter({ captureRejections: true });
239  ee.on('something', common.mustCall(async (value) => {
240    throw new Error('kaboom');
241  }));
242
243  ee.once('error', common.mustCall((err) => {
244    throw err;
245  }));
246
247  process.removeAllListeners('uncaughtException');
248
249  process.once('uncaughtException', common.mustCall((err) => {
250    process.nextTick(next);
251  }));
252
253  ee.emit('something');
254
255  function next() {
256    process.on('uncaughtException', common.mustNotCall());
257
258    const _err = new Error('kaboom2');
259    ee.on('something2', common.mustCall(async (value) => {
260      throw _err;
261    }));
262
263    ee.on('error', common.mustCall((err) => {
264      assert.strictEqual(err, _err);
265
266      process.removeAllListeners('uncaughtException');
267
268      // restore default
269      process.on('uncaughtException', (err) => { throw err; });
270
271      process.nextTick(argValidation);
272    }));
273
274    ee.emit('something2');
275  }
276}
277
278function argValidation() {
279
280  function testType(obj) {
281    const received = obj.constructor.name !== 'Number' ?
282      `an instance of ${obj.constructor.name}` :
283      `type number (${obj})`;
284
285    assert.throws(() => new EventEmitter({ captureRejections: obj }), {
286      code: 'ERR_INVALID_ARG_TYPE',
287      name: 'TypeError',
288      message: 'The "options.captureRejections" property must be of type ' +
289               `boolean. Received ${received}`
290    });
291
292    assert.throws(() => EventEmitter.captureRejections = obj, {
293      code: 'ERR_INVALID_ARG_TYPE',
294      name: 'TypeError',
295      message: 'The "EventEmitter.captureRejections" property must be of ' +
296               `type boolean. Received ${received}`
297    });
298  }
299
300  testType([]);
301  testType({ hello: 42 });
302  testType(42);
303}
304
305captureRejections();
306