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