mirror of
https://github.com/nodejs/node.git
synced 2025-05-01 17:03:34 +00:00

ERR_INVALID_ARG_TYPE is the most common error used throughout the code base. This improves the error message by providing more details to the user and by indicating more precisely which values are allowed ones and which ones are not. It adds the actual input to the error message in case it's a primitive. If it's a class instance, it'll print the class name instead of "object" and "falsy" or similar entries are not named "type" anymore. PR-URL: https://github.com/nodejs/node/pull/29675 Reviewed-By: Rich Trott <rtrott@gmail.com>
290 lines
7.5 KiB
JavaScript
290 lines
7.5 KiB
JavaScript
'use strict';
|
|
const common = require('../common');
|
|
|
|
// This test checks that the semantics of `util.callbackify` are as described in
|
|
// the API docs
|
|
|
|
const assert = require('assert');
|
|
const { callbackify } = require('util');
|
|
const { execFile } = require('child_process');
|
|
const fixtures = require('../common/fixtures');
|
|
|
|
const values = [
|
|
'hello world',
|
|
null,
|
|
undefined,
|
|
false,
|
|
0,
|
|
{},
|
|
{ key: 'value' },
|
|
Symbol('I am a symbol'),
|
|
function ok() {},
|
|
['array', 'with', 4, 'values'],
|
|
new Error('boo')
|
|
];
|
|
|
|
{
|
|
// Test that the resolution value is passed as second argument to callback
|
|
for (const value of values) {
|
|
// Test and `async function`
|
|
async function asyncFn() {
|
|
return value;
|
|
}
|
|
|
|
const cbAsyncFn = callbackify(asyncFn);
|
|
cbAsyncFn(common.mustCall((err, ret) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
}));
|
|
|
|
// Test Promise factory
|
|
function promiseFn() {
|
|
return Promise.resolve(value);
|
|
}
|
|
|
|
const cbPromiseFn = callbackify(promiseFn);
|
|
cbPromiseFn(common.mustCall((err, ret) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
}));
|
|
|
|
// Test Thenable
|
|
function thenableFn() {
|
|
return {
|
|
then(onRes, onRej) {
|
|
onRes(value);
|
|
}
|
|
};
|
|
}
|
|
|
|
const cbThenableFn = callbackify(thenableFn);
|
|
cbThenableFn(common.mustCall((err, ret) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
}));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Test that rejection reason is passed as first argument to callback
|
|
for (const value of values) {
|
|
// Test an `async function`
|
|
async function asyncFn() {
|
|
return Promise.reject(value);
|
|
}
|
|
|
|
const cbAsyncFn = callbackify(asyncFn);
|
|
assert.strictEqual(cbAsyncFn.length, 1);
|
|
assert.strictEqual(cbAsyncFn.name, 'asyncFnCallbackified');
|
|
cbAsyncFn(common.mustCall((err, ret) => {
|
|
assert.strictEqual(ret, undefined);
|
|
if (err instanceof Error) {
|
|
if ('reason' in err) {
|
|
assert(!value);
|
|
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
|
|
assert.strictEqual(err.reason, value);
|
|
} else {
|
|
assert.strictEqual(String(value).endsWith(err.message), true);
|
|
}
|
|
} else {
|
|
assert.strictEqual(err, value);
|
|
}
|
|
}));
|
|
|
|
// Test a Promise factory
|
|
function promiseFn() {
|
|
return Promise.reject(value);
|
|
}
|
|
const obj = {};
|
|
Object.defineProperty(promiseFn, 'name', {
|
|
value: obj,
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
|
|
const cbPromiseFn = callbackify(promiseFn);
|
|
assert.strictEqual(promiseFn.name, obj);
|
|
cbPromiseFn(common.mustCall((err, ret) => {
|
|
assert.strictEqual(ret, undefined);
|
|
if (err instanceof Error) {
|
|
if ('reason' in err) {
|
|
assert(!value);
|
|
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
|
|
assert.strictEqual(err.reason, value);
|
|
} else {
|
|
assert.strictEqual(String(value).endsWith(err.message), true);
|
|
}
|
|
} else {
|
|
assert.strictEqual(err, value);
|
|
}
|
|
}));
|
|
|
|
// Test Thenable
|
|
function thenableFn() {
|
|
return {
|
|
then(onRes, onRej) {
|
|
onRej(value);
|
|
}
|
|
};
|
|
}
|
|
|
|
const cbThenableFn = callbackify(thenableFn);
|
|
cbThenableFn(common.mustCall((err, ret) => {
|
|
assert.strictEqual(ret, undefined);
|
|
if (err instanceof Error) {
|
|
if ('reason' in err) {
|
|
assert(!value);
|
|
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
|
|
assert.strictEqual(err.reason, value);
|
|
} else {
|
|
assert.strictEqual(String(value).endsWith(err.message), true);
|
|
}
|
|
} else {
|
|
assert.strictEqual(err, value);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Test that arguments passed to callbackified function are passed to original
|
|
for (const value of values) {
|
|
async function asyncFn(arg) {
|
|
assert.strictEqual(arg, value);
|
|
return arg;
|
|
}
|
|
|
|
const cbAsyncFn = callbackify(asyncFn);
|
|
assert.strictEqual(cbAsyncFn.length, 2);
|
|
assert.notStrictEqual(
|
|
Object.getPrototypeOf(cbAsyncFn),
|
|
Object.getPrototypeOf(asyncFn)
|
|
);
|
|
assert.strictEqual(Object.getPrototypeOf(cbAsyncFn), Function.prototype);
|
|
cbAsyncFn(value, common.mustCall((err, ret) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
}));
|
|
|
|
function promiseFn(arg) {
|
|
assert.strictEqual(arg, value);
|
|
return Promise.resolve(arg);
|
|
}
|
|
const obj = {};
|
|
Object.defineProperty(promiseFn, 'length', {
|
|
value: obj,
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
|
|
const cbPromiseFn = callbackify(promiseFn);
|
|
assert.strictEqual(promiseFn.length, obj);
|
|
cbPromiseFn(value, common.mustCall((err, ret) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
}));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Test that `this` binding is the same for callbackified and original
|
|
for (const value of values) {
|
|
const iAmThis = {
|
|
fn(arg) {
|
|
assert.strictEqual(this, iAmThis);
|
|
return Promise.resolve(arg);
|
|
},
|
|
};
|
|
iAmThis.cbFn = callbackify(iAmThis.fn);
|
|
iAmThis.cbFn(value, common.mustCall(function(err, ret) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
assert.strictEqual(this, iAmThis);
|
|
}));
|
|
|
|
const iAmThat = {
|
|
async fn(arg) {
|
|
assert.strictEqual(this, iAmThat);
|
|
return arg;
|
|
},
|
|
};
|
|
iAmThat.cbFn = callbackify(iAmThat.fn);
|
|
iAmThat.cbFn(value, common.mustCall(function(err, ret) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(ret, value);
|
|
assert.strictEqual(this, iAmThat);
|
|
}));
|
|
}
|
|
}
|
|
|
|
{
|
|
// Test that callback that throws emits an `uncaughtException` event
|
|
const fixture = fixtures.path('uncaught-exceptions', 'callbackify1.js');
|
|
execFile(
|
|
process.execPath,
|
|
[fixture],
|
|
common.mustCall((err, stdout, stderr) => {
|
|
assert.strictEqual(err.code, 1);
|
|
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error');
|
|
assert.strictEqual(stdout, '');
|
|
const errLines = stderr.trim().split(/[\r\n]+/);
|
|
const errLine = errLines.find((l) => /^Error/.exec(l));
|
|
assert.strictEqual(errLine, `Error: ${fixture}`);
|
|
})
|
|
);
|
|
}
|
|
|
|
{
|
|
// Test that handled `uncaughtException` works and passes rejection reason
|
|
const fixture = fixtures.path('uncaught-exceptions', 'callbackify2.js');
|
|
execFile(
|
|
process.execPath,
|
|
[fixture],
|
|
common.mustCall((err, stdout, stderr) => {
|
|
assert.ifError(err);
|
|
assert.strictEqual(
|
|
stdout.trim(),
|
|
`ifError got unwanted exception: ${fixture}`);
|
|
assert.strictEqual(stderr, '');
|
|
})
|
|
);
|
|
}
|
|
|
|
{
|
|
// Verify that non-function inputs throw.
|
|
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
|
|
common.expectsError(() => {
|
|
callbackify(value);
|
|
}, {
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
type: TypeError,
|
|
message: 'The "original" argument must be of type function.' +
|
|
common.invalidArgTypeHelper(value)
|
|
});
|
|
});
|
|
}
|
|
|
|
{
|
|
async function asyncFn() {
|
|
return 42;
|
|
}
|
|
|
|
const cb = callbackify(asyncFn);
|
|
const args = [];
|
|
|
|
// Verify that the last argument to the callbackified function is a function.
|
|
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
|
|
args.push(value);
|
|
common.expectsError(() => {
|
|
cb(...args);
|
|
}, {
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
type: TypeError,
|
|
message: 'The last argument must be of type function.' +
|
|
common.invalidArgTypeHelper(value)
|
|
});
|
|
});
|
|
}
|