Skip to content

Commit efa0bd8

Browse files
committed
test: refactor common.expectsError
This completely refactors the `expectsError` behavior: so far it's almost identical to `assert.throws(fn, object)` in case it was used with a function as first argument. It had a magical property check that allowed to verify a functions `type` in case `type` was passed used in the validation object. This pattern is now completely removed and `assert.throws()` should be used instead. The main intent for `common.expectsError()` is to verify error cases for callback based APIs. This is now more flexible by accepting all validation possibilites that `assert.throws()` accepts as well. No magical properties exist anymore. This reduces surprising behavior for developers who are not used to the Node.js core code base. This has the side effect that `common` is used significantly less frequent. PR-URL: #31092 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]>
1 parent 10f7169 commit efa0bd8

File tree

395 files changed

+2124
-2174
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

395 files changed

+2124
-2174
lines changed

test/.eslintrc.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ rules:
1414
# Custom rules in tools/eslint-rules
1515
node-core/prefer-assert-iferror: error
1616
node-core/prefer-assert-methods: error
17-
node-core/prefer-common-expectserror: error
1817
node-core/prefer-common-mustnotcall: error
1918
node-core/crypto-check: error
2019
node-core/eslint-check: error

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const skipMessage = 'intensive toString tests due to memory confinements';
55
if (!common.enoughTestMem)
66
common.skip(skipMessage);
77

8+
const assert = require('assert');
89
const binding = require(`./build/${common.buildType}/binding`);
910

1011
// v8 fails silently if string length > v8::String::kMaxLength
@@ -25,11 +26,11 @@ if (!binding.ensureAllocation(2 * kStringMaxLength))
2526
common.skip(skipMessage);
2627

2728
const stringLengthHex = kStringMaxLength.toString(16);
28-
common.expectsError(() => {
29+
assert.throws(() => {
2930
buf.toString('ascii');
3031
}, {
3132
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3233
'characters',
3334
code: 'ERR_STRING_TOO_LONG',
34-
type: Error
35+
name: 'Error'
3536
});

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const skipMessage = 'intensive toString tests due to memory confinements';
55
if (!common.enoughTestMem)
66
common.skip(skipMessage);
77

8+
const assert = require('assert');
89
const binding = require(`./build/${common.buildType}/binding`);
910

1011
// v8 fails silently if string length > v8::String::kMaxLength
@@ -25,11 +26,11 @@ if (!binding.ensureAllocation(2 * kStringMaxLength))
2526
common.skip(skipMessage);
2627

2728
const stringLengthHex = kStringMaxLength.toString(16);
28-
common.expectsError(() => {
29+
assert.throws(() => {
2930
buf.toString('base64');
3031
}, {
3132
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3233
'characters',
3334
code: 'ERR_STRING_TOO_LONG',
34-
type: Error
35+
name: 'Error'
3536
});

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ if (!binding.ensureAllocation(2 * kStringMaxLength))
2727
common.skip(skipMessage);
2828

2929
const stringLengthHex = kStringMaxLength.toString(16);
30-
common.expectsError(() => {
30+
assert.throws(() => {
3131
buf.toString('latin1');
3232
}, {
3333
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3434
'characters',
3535
code: 'ERR_STRING_TOO_LONG',
36-
type: Error
36+
name: 'Error'
3737
});
3838

3939
// FIXME: Free the memory early to avoid OOM.

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const skipMessage = 'intensive toString tests due to memory confinements';
55
if (!common.enoughTestMem)
66
common.skip(skipMessage);
77

8+
const assert = require('assert');
89
const binding = require(`./build/${common.buildType}/binding`);
910

1011
// v8 fails silently if string length > v8::String::kMaxLength
@@ -25,11 +26,11 @@ if (!binding.ensureAllocation(2 * kStringMaxLength))
2526
common.skip(skipMessage);
2627

2728
const stringLengthHex = kStringMaxLength.toString(16);
28-
common.expectsError(() => {
29+
assert.throws(() => {
2930
buf.toString('hex');
3031
}, {
3132
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3233
'characters',
3334
code: 'ERR_STRING_TOO_LONG',
34-
type: Error
35+
name: 'Error'
3536
});

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ assert.throws(() => {
3535
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3636
'characters',
3737
code: 'ERR_STRING_TOO_LONG',
38-
type: Error
38+
name: 'Error'
3939
})(e);
4040
return true;
4141
} else {
4242
return true;
4343
}
4444
});
4545

46-
common.expectsError(() => {
46+
assert.throws(() => {
4747
buf.toString('utf8');
4848
}, {
4949
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
5050
'characters',
5151
code: 'ERR_STRING_TOO_LONG',
52-
type: Error
52+
name: 'Error'
5353
});

test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const skipMessage = 'intensive toString tests due to memory confinements';
55
if (!common.enoughTestMem)
66
common.skip(skipMessage);
77

8+
const assert = require('assert');
89
const binding = require(`./build/${common.buildType}/binding`);
910

1011
// v8 fails silently if string length > v8::String::kMaxLength
@@ -26,11 +27,11 @@ if (!binding.ensureAllocation(2 * kStringMaxLength))
2627

2728
const stringLengthHex = kStringMaxLength.toString(16);
2829

29-
common.expectsError(() => {
30+
assert.throws(() => {
3031
buf.toString('utf16le');
3132
}, {
3233
message: `Cannot create a string longer than 0x${stringLengthHex} ` +
3334
'characters',
3435
code: 'ERR_STRING_TOO_LONG',
35-
type: Error
36+
name: 'Error'
3637
});

test/async-hooks/test-embedder.api.async-resource-no-type.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ if (process.argv[2] === 'child') {
1818
}
1919

2020
[null, undefined, 1, Date, {}, []].forEach((i) => {
21-
common.expectsError(() => new Foo(i), {
21+
assert.throws(() => new Foo(i), {
2222
code: 'ERR_INVALID_ARG_TYPE',
23-
type: TypeError
23+
name: 'TypeError'
2424
});
2525
});
2626

test/async-hooks/test-embedder.api.async-resource.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ const { checkInvocations } = require('./hook-checks');
1212
const hooks = initHooks();
1313
hooks.enable();
1414

15-
common.expectsError(
15+
assert.throws(
1616
() => new AsyncResource(), {
1717
code: 'ERR_INVALID_ARG_TYPE',
18-
type: TypeError,
18+
name: 'TypeError',
1919
});
20-
common.expectsError(() => {
20+
assert.throws(() => {
2121
new AsyncResource('invalid_trigger_id', { triggerAsyncId: null });
2222
}, {
2323
code: 'ERR_INVALID_ASYNC_ID',
24-
type: RangeError,
24+
name: 'RangeError',
2525
});
2626

2727
assert.strictEqual(

test/common/README.md

+10-36
Original file line numberDiff line numberDiff line change
@@ -71,44 +71,17 @@ the `unhandledRejection` hook is directly used by the test.
7171

7272
Indicates if there is more than 1gb of total memory.
7373

74-
### expectsError(\[fn, \]settings\[, exact\])
75-
* `fn` [&lt;Function>][] a function that should throw.
76-
* `settings` [&lt;Object>][]
77-
that must contain the `code` property plus any of the other following
78-
properties (some properties only apply for `AssertionError`):
79-
* `code` [&lt;string>][]
80-
expected error must have this value for its `code` property.
81-
* `type` [&lt;Function>][]
82-
expected error must be an instance of `type` and must be an Error subclass.
83-
* `message` [&lt;string>][] or [&lt;RegExp>][]
84-
if a string is provided for `message`, expected error must have it for its
85-
`message` property; if a regular expression is provided for `message`, the
86-
regular expression must match the `message` property of the expected error.
87-
* `name` [&lt;string>][]
88-
expected error must have this value for its `name` property.
89-
* `info` &lt;Object> expected error must have the same `info` property
90-
that is deeply equal to this value.
91-
* `generatedMessage` [&lt;string>][]
92-
(`AssertionError` only) expected error must have this value for its
93-
`generatedMessage` property.
94-
* `actual` &lt;any>
95-
(`AssertionError` only) expected error must have this value for its
96-
`actual` property.
97-
* `expected` &lt;any>
98-
(`AssertionError` only) expected error must have this value for its
99-
`expected` property.
100-
* `operator` &lt;any>
101-
(`AssertionError` only) expected error must have this value for its
102-
`operator` property.
74+
### expectsError(validator\[, exact\])
75+
* `validator` [&lt;Object>][] | [&lt;RegExp>][] | [&lt;Function>][] |
76+
[&lt;Error>][] The validator behaves identical to
77+
`assert.throws(fn, validator)`.
10378
* `exact` [&lt;number>][] default = 1
104-
* return [&lt;Function>][]
79+
* return [&lt;Function>][] A callback function that expects an error.
10580

106-
If `fn` is provided, it will be passed to `assert.throws` as first argument
107-
and `undefined` will be returned.
108-
Otherwise a function suitable as callback or for use as a validation function
109-
passed as the second argument to `assert.throws()` will be returned. If the
110-
returned function has not been called exactly `exact` number of times when the
111-
test is complete, then the test will fail.
81+
A function suitable as callback to validate callback based errors. The error is
82+
validated using `assert.throws(() => { throw error; }, validator)`. If the
83+
returned function has not been called exactly `exact` number of times when the
84+
test is complete, then the test will fail.
11285

11386
### expectWarning(name\[, expected\[, code\]\])
11487
* `name` [&lt;string>][] | [&lt;Object>][]
@@ -929,6 +902,7 @@ See [the WPT tests README][] for details.
929902
[&lt;ArrayBufferView>]: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
930903
[&lt;Buffer>]: https://nodejs.org/api/buffer.html#buffer_class_buffer
931904
[&lt;BufferSource>]: https://developer.mozilla.org/en-US/docs/Web/API/BufferSource
905+
[&lt;Error>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
932906
[&lt;Function>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
933907
[&lt;Object>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
934908
[&lt;RegExp>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

test/common/index.js

+7-77
Original file line numberDiff line numberDiff line change
@@ -525,92 +525,22 @@ function expectWarning(nameOrMap, expected, code) {
525525
}
526526
}
527527

528-
class Comparison {
529-
constructor(obj, keys) {
530-
for (const key of keys) {
531-
if (key in obj)
532-
this[key] = obj[key];
533-
}
534-
}
535-
}
536-
537528
// Useful for testing expected internal/error objects
538-
function expectsError(fn, settings, exact) {
539-
if (typeof fn !== 'function') {
540-
exact = settings;
541-
settings = fn;
542-
fn = undefined;
543-
}
544-
545-
function innerFn(error) {
546-
if (arguments.length !== 1) {
529+
function expectsError(validator, exact) {
530+
return mustCall((...args) => {
531+
if (args.length !== 1) {
547532
// Do not use `assert.strictEqual()` to prevent `util.inspect` from
548533
// always being called.
549-
assert.fail(`Expected one argument, got ${util.inspect(arguments)}`);
534+
assert.fail(`Expected one argument, got ${util.inspect(args)}`);
550535
}
536+
const error = args.pop();
551537
const descriptor = Object.getOwnPropertyDescriptor(error, 'message');
552538
// The error message should be non-enumerable
553539
assert.strictEqual(descriptor.enumerable, false);
554540

555-
let innerSettings = settings;
556-
if ('type' in settings) {
557-
const type = settings.type;
558-
if (type !== Error && !Error.isPrototypeOf(type)) {
559-
throw new TypeError('`settings.type` must inherit from `Error`');
560-
}
561-
let constructor = error.constructor;
562-
if (constructor.name === 'NodeError' && type.name !== 'NodeError') {
563-
constructor = Object.getPrototypeOf(error.constructor);
564-
}
565-
// Add the `type` to the error to properly compare and visualize it.
566-
if (!('type' in error))
567-
error.type = constructor;
568-
}
569-
570-
if ('message' in settings &&
571-
typeof settings.message === 'object' &&
572-
settings.message.test(error.message)) {
573-
// Make a copy so we are able to modify the settings.
574-
innerSettings = Object.create(
575-
settings, Object.getOwnPropertyDescriptors(settings));
576-
// Visualize the message as identical in case of other errors.
577-
innerSettings.message = error.message;
578-
}
579-
580-
// Check all error properties.
581-
const keys = Object.keys(settings);
582-
for (const key of keys) {
583-
if (!util.isDeepStrictEqual(error[key], innerSettings[key])) {
584-
// Create placeholder objects to create a nice output.
585-
const a = new Comparison(error, keys);
586-
const b = new Comparison(innerSettings, keys);
587-
588-
const tmpLimit = Error.stackTraceLimit;
589-
Error.stackTraceLimit = 0;
590-
const err = new assert.AssertionError({
591-
actual: a,
592-
expected: b,
593-
operator: 'strictEqual',
594-
stackStartFn: assert.throws
595-
});
596-
Error.stackTraceLimit = tmpLimit;
597-
598-
throw new assert.AssertionError({
599-
actual: error,
600-
expected: settings,
601-
operator: 'common.expectsError',
602-
message: err.message
603-
});
604-
}
605-
606-
}
541+
assert.throws(() => { throw error; }, validator);
607542
return true;
608-
}
609-
if (fn) {
610-
assert.throws(fn, innerFn);
611-
return;
612-
}
613-
return mustCall(innerFn, exact);
543+
}, exact);
614544
}
615545

616546
const suffix = 'This is caused by either a bug in Node.js ' +

test/es-module/test-esm-loader-modulemap.js

+10-9
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
// This test ensures that the type checking of ModuleMap throws
55
// errors appropriately
66

7-
const common = require('../common');
7+
require('../common');
88

9+
const assert = require('assert');
910
const { URL } = require('url');
1011
const { Loader } = require('internal/modules/esm/loader');
1112
const ModuleMap = require('internal/modules/esm/module_map');
@@ -20,41 +21,41 @@ const moduleMap = new ModuleMap();
2021
const moduleJob = new ModuleJob(loader, stubModule.module,
2122
() => new Promise(() => {}));
2223

23-
common.expectsError(
24+
assert.throws(
2425
() => moduleMap.get(1),
2526
{
2627
code: 'ERR_INVALID_ARG_TYPE',
27-
type: TypeError,
28+
name: 'TypeError',
2829
message: 'The "url" argument must be of type string. Received type number' +
2930
' (1)'
3031
}
3132
);
3233

33-
common.expectsError(
34+
assert.throws(
3435
() => moduleMap.set(1, moduleJob),
3536
{
3637
code: 'ERR_INVALID_ARG_TYPE',
37-
type: TypeError,
38+
name: 'TypeError',
3839
message: 'The "url" argument must be of type string. Received type number' +
3940
' (1)'
4041
}
4142
);
4243

43-
common.expectsError(
44+
assert.throws(
4445
() => moduleMap.set('somestring', 'notamodulejob'),
4546
{
4647
code: 'ERR_INVALID_ARG_TYPE',
47-
type: TypeError,
48+
name: 'TypeError',
4849
message: 'The "job" argument must be an instance of ModuleJob. ' +
4950
"Received type string ('notamodulejob')"
5051
}
5152
);
5253

53-
common.expectsError(
54+
assert.throws(
5455
() => moduleMap.has(1),
5556
{
5657
code: 'ERR_INVALID_ARG_TYPE',
57-
type: TypeError,
58+
name: 'TypeError',
5859
message: 'The "url" argument must be of type string. Received type number' +
5960
' (1)'
6061
}

0 commit comments

Comments
 (0)