deno.land / std@0.166.0 / node / _util / _util_callbackify_test.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.//// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.//// Permission is hereby granted, free of charge, to any person obtaining a// copy of this software and associated documentation files (the// "Software"), to deal in the Software without restriction, including// without limitation the rights to use, copy, modify, merge, publish,// distribute, sublicense, and/or sell copies of the Software, and to permit// persons to whom the Software is furnished to do so, subject to the// following conditions://// The above copyright notice and this permission notice shall be included// in all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE// USE OR OTHER DEALINGS IN THE SOFTWARE.import { assert, assertStrictEquals } from "../../testing/asserts.ts";import { callbackify } from "./_util_callbackify.ts";
const values = [ "hello world", null, undefined, false, 0, {}, { key: "value" }, Symbol("I am a symbol"), function ok() {}, ["array", "with", 4, "values"], new Error("boo"),];
class TestQueue { #waitingPromise: Promise<void>; #resolve?: () => void; #reject?: (err: unknown) => void; #queueSize = 0;
constructor() { this.#waitingPromise = new Promise((resolve, reject) => { this.#resolve = resolve; this.#reject = reject; }); }
enqueue(fn: (done: () => void) => void) { this.#queueSize++; try { fn(() => { this.#queueSize--; if (this.#queueSize === 0) { assert( this.#resolve, "Test setup error; async queue is missing #resolve", ); this.#resolve(); } }); } catch (err) { assert(this.#reject, "Test setup error; async queue is missing #reject"); this.#reject(err); } }
waitForCompletion() { return this.#waitingPromise; }}
Deno.test( "callbackify passes the resolution value as the second argument to the callback", async () => { const testQueue = new TestQueue();
for (const value of values) { // deno-lint-ignore require-await const asyncFn = async (): Promise<typeof value> => { return value; }; const cbAsyncFn = callbackify(asyncFn); testQueue.enqueue((done) => { cbAsyncFn((err: unknown, ret: unknown) => { assertStrictEquals(err, null); assertStrictEquals(ret, value); done(); }); });
const promiseFn = (): Promise<typeof value> => { return Promise.resolve(value); }; const cbPromiseFn = callbackify(promiseFn); testQueue.enqueue((done) => { cbPromiseFn((err: unknown, ret: unknown) => { assertStrictEquals(err, null); assertStrictEquals(ret, value); done(); }); });
// deno-lint-ignore no-explicit-any const thenableFn = (): PromiseLike<any> => { return { // deno-lint-ignore no-explicit-any then(onfulfilled): PromiseLike<any> { assert(onfulfilled); onfulfilled(value); return this; }, }; }; const cbThenableFn = callbackify(thenableFn); testQueue.enqueue((done) => { cbThenableFn((err: unknown, ret: unknown) => { assertStrictEquals(err, null); assertStrictEquals(ret, value); done(); }); }); }
await testQueue.waitForCompletion(); },);
Deno.test( "callbackify passes the rejection value as the first argument to the callback", async () => { const testQueue = new TestQueue();
for (const value of values) { // deno-lint-ignore require-await const asyncFn = async (): Promise<never> => { return Promise.reject(value); }; const cbAsyncFn = callbackify(asyncFn); assertStrictEquals(cbAsyncFn.length, 1); assertStrictEquals(cbAsyncFn.name, "asyncFnCallbackified"); testQueue.enqueue((done) => { cbAsyncFn((err: unknown, ret: unknown) => { assertStrictEquals(ret, undefined); if (err instanceof Error) { if ("reason" in err) { assert(!value); assertStrictEquals( // deno-lint-ignore no-explicit-any (err as any).code, "ERR_FALSY_VALUE_REJECTION", ); // deno-lint-ignore no-explicit-any assertStrictEquals((err as any).reason, value); } else { assertStrictEquals(String(value).endsWith(err.message), true); } } else { assertStrictEquals(err, value); } done(); }); });
const promiseFn = (): Promise<never> => { return Promise.reject(value); }; const obj = {}; Object.defineProperty(promiseFn, "name", { value: obj, writable: false, enumerable: false, configurable: true, });
const cbPromiseFn = callbackify(promiseFn); assertStrictEquals(promiseFn.name, obj); testQueue.enqueue((done) => { cbPromiseFn((err: unknown, ret: unknown) => { assertStrictEquals(ret, undefined); if (err instanceof Error) { if ("reason" in err) { assert(!value); assertStrictEquals( // deno-lint-ignore no-explicit-any (err as any).code, "ERR_FALSY_VALUE_REJECTION", ); // deno-lint-ignore no-explicit-any assertStrictEquals((err as any).reason, value); } else { assertStrictEquals(String(value).endsWith(err.message), true); } } else { assertStrictEquals(err, value); } done(); }); });
const thenableFn = (): PromiseLike<never> => { return { then(_onfulfilled, onrejected): PromiseLike<never> { assert(onrejected); onrejected(value); return this; }, }; };
const cbThenableFn = callbackify(thenableFn); testQueue.enqueue((done) => { cbThenableFn((err: unknown, ret: unknown) => { assertStrictEquals(ret, undefined); if (err instanceof Error) { if ("reason" in err) { assert(!value); assertStrictEquals( // deno-lint-ignore no-explicit-any (err as any).code, "ERR_FALSY_VALUE_REJECTION", ); // deno-lint-ignore no-explicit-any assertStrictEquals((err as any).reason, value); } else { assertStrictEquals(String(value).endsWith(err.message), true); } } else { assertStrictEquals(err, value); } done(); }); }); }
await testQueue.waitForCompletion(); },);
Deno.test("callbackify passes arguments to the original", async () => { const testQueue = new TestQueue();
for (const value of values) { // deno-lint-ignore require-await const asyncFn = async (arg: typeof value): Promise<typeof value> => { assertStrictEquals(arg, value); return arg; };
const cbAsyncFn = callbackify(asyncFn); assertStrictEquals(cbAsyncFn.length, 2); assert(Object.getPrototypeOf(cbAsyncFn) !== Object.getPrototypeOf(asyncFn)); assertStrictEquals(Object.getPrototypeOf(cbAsyncFn), Function.prototype); testQueue.enqueue((done) => { cbAsyncFn(value, (err: unknown, ret: unknown) => { assertStrictEquals(err, null); assertStrictEquals(ret, value); done(); }); });
const promiseFn = <T>(arg: typeof value): Promise<typeof value> => { assertStrictEquals(arg, value); return Promise.resolve(arg); }; const obj = {}; Object.defineProperty(promiseFn, "length", { value: obj, writable: false, enumerable: false, configurable: true, });
const cbPromiseFn = callbackify(promiseFn); assertStrictEquals(promiseFn.length, obj); testQueue.enqueue((done) => { cbPromiseFn(value, (err: unknown, ret: unknown) => { assertStrictEquals(err, null); assertStrictEquals(ret, value); done(); }); }); }
await testQueue.waitForCompletion();});
Deno.test("callbackify preserves the `this` binding", async () => { const testQueue = new TestQueue();
for (const value of values) { const objectWithSyncFunction = { fn(this: unknown, arg: typeof value): Promise<typeof value> { assertStrictEquals(this, objectWithSyncFunction); return Promise.resolve(arg); }, }; const cbSyncFunction = callbackify(objectWithSyncFunction.fn); testQueue.enqueue((done) => { cbSyncFunction.call(objectWithSyncFunction, value, function ( this: unknown, err: unknown, ret: unknown, ) { assertStrictEquals(err, null); assertStrictEquals(ret, value); assertStrictEquals(this, objectWithSyncFunction); done(); }); });
const objectWithAsyncFunction = { // deno-lint-ignore require-await async fn(this: unknown, arg: typeof value): Promise<typeof value> { assertStrictEquals(this, objectWithAsyncFunction); return arg; }, }; const cbAsyncFunction = callbackify(objectWithAsyncFunction.fn); testQueue.enqueue((done) => { cbAsyncFunction.call(objectWithAsyncFunction, value, function ( this: unknown, err: unknown, ret: unknown, ) { assertStrictEquals(err, null); assertStrictEquals(ret, value); assertStrictEquals(this, objectWithAsyncFunction); done(); }); }); }
await testQueue.waitForCompletion();});
Deno.test("callbackify throws with non-function inputs", () => { ["foo", null, undefined, false, 0, {}, Symbol(), []].forEach((value) => { try { // deno-lint-ignore no-explicit-any callbackify(value as any); throw Error("We should never reach this error"); } catch (err) { assert(err instanceof TypeError); // deno-lint-ignore no-explicit-any assertStrictEquals((err as any).code, "ERR_INVALID_ARG_TYPE"); assertStrictEquals(err.name, "TypeError"); assertStrictEquals( err.message, 'The "original" argument must be of type function.', ); } });});
Deno.test( "callbackify returns a function that throws if the last argument is not a function", () => { // deno-lint-ignore require-await async function asyncFn(): Promise<number> { return 42; }
// deno-lint-ignore no-explicit-any const cb = callbackify(asyncFn) as any; const args: unknown[] = [];
["foo", null, undefined, false, 0, {}, Symbol(), []].forEach((value) => { args.push(value);
try { cb(...args); throw Error("We should never reach this error"); } catch (err) { assert(err instanceof TypeError); // deno-lint-ignore no-explicit-any assertStrictEquals((err as any).code, "ERR_INVALID_ARG_TYPE"); assertStrictEquals(err.name, "TypeError"); assertStrictEquals( err.message, "The last argument must be of type function.", ); } }); },);
Version Info