Skip to content

Commit d568efc

Browse files
BridgeARMylesBorins
authored andcommitted
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. Backport-PR-URL: #31449 PR-URL: #31092 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]>
1 parent e4f9360 commit d568efc

File tree

392 files changed

+2110
-2152
lines changed

Some content is hidden

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

392 files changed

+2110
-2152
lines changed

test/.eslintrc.yaml

Lines changed: 0 additions & 1 deletion
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 3 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -81,44 +81,17 @@ least 1 GHz.
8181

8282
Indicates if there is more than 1gb of total memory.
8383

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

116-
If `fn` is provided, it will be passed to `assert.throws` as first argument
117-
and `undefined` will be returned.
118-
Otherwise a function suitable as callback or for use as a validation function
119-
passed as the second argument to `assert.throws()` will be returned. If the
120-
returned function has not been called exactly `exact` number of times when the
121-
test is complete, then the test will fail.
91+
A function suitable as callback to validate callback based errors. The error is
92+
validated using `assert.throws(() => { throw error; }, validator)`. If the
93+
returned function has not been called exactly `exact` number of times when the
94+
test is complete, then the test will fail.
12295

12396
### `expectWarning(name[, expected[, code]])`
12497

@@ -974,6 +947,7 @@ See [the WPT tests README][] for details.
974947
[&lt;ArrayBufferView>]: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
975948
[&lt;Buffer>]: https://nodejs.org/api/buffer.html#buffer_class_buffer
976949
[&lt;BufferSource>]: https://developer.mozilla.org/en-US/docs/Web/API/BufferSource
950+
[&lt;Error>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
977951
[&lt;Function>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
978952
[&lt;Object>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
979953
[&lt;RegExp>]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

test/common/index.js

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -526,92 +526,22 @@ function expectWarning(nameOrMap, expected, code) {
526526
}
527527
}
528528

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

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

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

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

Lines changed: 10 additions & 9 deletions
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
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const {
1111
} = require('internal/modules/esm/resolve');
1212

1313
assert.throws(
14-
() => resolve('target'),
14+
() => resolve('target', undefined),
1515
{
1616
code: 'ERR_MODULE_NOT_FOUND',
1717
name: 'Error',

0 commit comments

Comments
 (0)