0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/test/parallel/test-event-capture-rejections.js
Adrian Estrada cd4052c9df
test: better error validations for event-capture
PR-URL: https://github.com/nodejs/node/pull/32771
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
2020-04-24 16:36:31 +02:00

306 lines
7.6 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const { EventEmitter, captureRejectionSymbol } = require('events');
const { inherits } = require('util');
// Inherits from EE without a call to the
// parent constructor.
function NoConstructor() {
}
inherits(NoConstructor, EventEmitter);
function captureRejections() {
const ee = new EventEmitter({ captureRejections: true });
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
process.nextTick(captureRejectionsTwoHandlers);
}));
ee.emit('something');
}
function captureRejectionsTwoHandlers() {
const ee = new EventEmitter({ captureRejections: true });
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
// throw twice
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
let count = 0;
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
if (++count === 2) {
process.nextTick(defaultValue);
}
}, 2));
ee.emit('something');
}
function defaultValue() {
const ee = new EventEmitter();
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
process.removeAllListeners('unhandledRejection');
process.once('unhandledRejection', common.mustCall((err) => {
// restore default
process.on('unhandledRejection', (err) => { throw err; });
assert.strictEqual(err, _err);
process.nextTick(globalSetting);
}));
ee.emit('something');
}
function globalSetting() {
assert.strictEqual(EventEmitter.captureRejections, false);
EventEmitter.captureRejections = true;
const ee = new EventEmitter();
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
// restore default
EventEmitter.captureRejections = false;
process.nextTick(configurable);
}));
ee.emit('something');
}
// We need to be able to configure this for streams, as we would
// like to call destro(err) there.
function configurable() {
const ee = new EventEmitter({ captureRejections: true });
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (...args) => {
assert.deepStrictEqual(args, [42, 'foobar']);
throw _err;
}));
assert.strictEqual(captureRejectionSymbol, Symbol.for('nodejs.rejection'));
ee[captureRejectionSymbol] = common.mustCall((err, type, ...args) => {
assert.strictEqual(err, _err);
assert.strictEqual(type, 'something');
assert.deepStrictEqual(args, [42, 'foobar']);
process.nextTick(globalSettingNoConstructor);
});
ee.emit('something', 42, 'foobar');
}
function globalSettingNoConstructor() {
assert.strictEqual(EventEmitter.captureRejections, false);
EventEmitter.captureRejections = true;
const ee = new NoConstructor();
const _err = new Error('kaboom');
ee.on('something', common.mustCall(async (value) => {
throw _err;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
// restore default
EventEmitter.captureRejections = false;
process.nextTick(thenable);
}));
ee.emit('something');
}
function thenable() {
const ee = new EventEmitter({ captureRejections: true });
const _err = new Error('kaboom');
ee.on('something', common.mustCall((value) => {
const obj = {};
Object.defineProperty(obj, 'then', {
get: common.mustCall(() => {
return common.mustCall((resolved, rejected) => {
assert.strictEqual(resolved, undefined);
rejected(_err);
});
}, 1) // Only 1 call for Promises/A+ compat.
});
return obj;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
process.nextTick(avoidLoopOnRejection);
}));
ee.emit('something');
}
function avoidLoopOnRejection() {
const ee = new EventEmitter({ captureRejections: true });
const _err1 = new Error('kaboom');
const _err2 = new Error('kaboom2');
ee.on('something', common.mustCall(async (value) => {
throw _err1;
}));
ee[captureRejectionSymbol] = common.mustCall(async (err) => {
assert.strictEqual(err, _err1);
throw _err2;
});
process.removeAllListeners('unhandledRejection');
process.once('unhandledRejection', common.mustCall((err) => {
// restore default
process.on('unhandledRejection', (err) => { throw err; });
assert.strictEqual(err, _err2);
process.nextTick(avoidLoopOnError);
}));
ee.emit('something');
}
function avoidLoopOnError() {
const ee = new EventEmitter({ captureRejections: true });
const _err1 = new Error('kaboom');
const _err2 = new Error('kaboom2');
ee.on('something', common.mustCall(async (value) => {
throw _err1;
}));
ee.on('error', common.mustCall(async (err) => {
assert.strictEqual(err, _err1);
throw _err2;
}));
process.removeAllListeners('unhandledRejection');
process.once('unhandledRejection', common.mustCall((err) => {
// restore default
process.on('unhandledRejection', (err) => { throw err; });
assert.strictEqual(err, _err2);
process.nextTick(thenableThatThrows);
}));
ee.emit('something');
}
function thenableThatThrows() {
const ee = new EventEmitter({ captureRejections: true });
const _err = new Error('kaboom');
ee.on('something', common.mustCall((value) => {
const obj = {};
Object.defineProperty(obj, 'then', {
get: common.mustCall(() => {
throw _err;
}, 1) // Only 1 call for Promises/A+ compat.
});
return obj;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
process.nextTick(resetCaptureOnThrowInError);
}));
ee.emit('something');
}
function resetCaptureOnThrowInError() {
const ee = new EventEmitter({ captureRejections: true });
ee.on('something', common.mustCall(async (value) => {
throw new Error('kaboom');
}));
ee.once('error', common.mustCall((err) => {
throw err;
}));
process.removeAllListeners('uncaughtException');
process.once('uncaughtException', common.mustCall((err) => {
process.nextTick(next);
}));
ee.emit('something');
function next() {
process.on('uncaughtException', common.mustNotCall());
const _err = new Error('kaboom2');
ee.on('something2', common.mustCall(async (value) => {
throw _err;
}));
ee.on('error', common.mustCall((err) => {
assert.strictEqual(err, _err);
process.removeAllListeners('uncaughtException');
// restore default
process.on('uncaughtException', (err) => { throw err; });
process.nextTick(argValidation);
}));
ee.emit('something2');
}
}
function argValidation() {
function testType(obj) {
const received = obj.constructor.name !== 'Number' ?
`an instance of ${obj.constructor.name}` :
`type number (${obj})`;
assert.throws(() => new EventEmitter({ captureRejections: obj }), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "options.captureRejections" property must be of type ' +
`boolean. Received ${received}`
});
assert.throws(() => EventEmitter.captureRejections = obj, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "EventEmitter.captureRejections" property must be of ' +
`type boolean. Received ${received}`
});
}
testType([]);
testType({ hello: 42 });
testType(42);
}
captureRejections();