|
|
|
'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 { join } = require('path');
|
|
|
|
const { execFile } = require('child_process');
|
|
|
|
const fixtureDir = join(common.fixturesDir, 'uncaught-exceptions');
|
|
|
|
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 await Promise.resolve(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 await Promise.reject(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
const cbAsyncFn = callbackify(asyncFn);
|
|
|
|
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 cbPromiseFn = callbackify(promiseFn);
|
|
|
|
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 await Promise.resolve(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
const cbAsyncFn = callbackify(asyncFn);
|
|
|
|
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 cbPromiseFn = callbackify(promiseFn);
|
|
|
|
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 await Promise.resolve(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 = join(fixtureDir, '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 = join(fixtureDir, 'callbackify2.js');
|
|
|
|
execFile(
|
|
|
|
process.execPath,
|
|
|
|
[fixture],
|
|
|
|
common.mustCall((err, stdout, stderr) => {
|
|
|
|
assert.ifError(err);
|
|
|
|
assert.strictEqual(stdout.trim(), fixture);
|
|
|
|
assert.strictEqual(stderr, '');
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Verify that non-function inputs throw.
|
|
|
|
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
|
|
|
|
assert.throws(() => {
|
|
|
|
callbackify(value);
|
|
|
|
}, common.expectsError({
|
|
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
|
|
type: TypeError,
|
|
|
|
message: 'The "original" argument must be of type function'
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
async function asyncFn() {
|
|
|
|
return await Promise.resolve(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);
|
|
|
|
assert.throws(() => {
|
|
|
|
cb(...args);
|
|
|
|
}, common.expectsError({
|
|
|
|
code: 'ERR_INVALID_ARG_TYPE',
|
|
|
|
type: TypeError,
|
|
|
|
message: 'The last argument must be of type function'
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|