Skip to main content
Module

std/node/assert.ts

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.// deno-lint-ignore-file ban-typesimport { AssertionError, AssertionErrorConstructorOptions,} from "./assertion_error.ts";import * as asserts from "../testing/asserts.ts";import { inspect } from "./util.ts";import { ERR_AMBIGUOUS_ARGUMENT, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_RETURN_VALUE, ERR_MISSING_ARGS,} from "./internal/errors.ts";import { isDeepEqual } from "./internal/util/comparisons.ts";
function innerFail(obj: { actual?: unknown; expected?: unknown; message?: string | Error; operator?: string;}) { if (obj.message instanceof Error) { throw obj.message; }
throw new AssertionError({ actual: obj.actual, expected: obj.expected, message: obj.message, operator: obj.operator, });}
interface ExtendedAssertionErrorConstructorOptions extends AssertionErrorConstructorOptions { generatedMessage?: boolean;}
// TODO(uki00a): This function is a workaround for setting the `generatedMessage` property flexibly.function createAssertionError( options: ExtendedAssertionErrorConstructorOptions,): AssertionError { const error = new AssertionError(options); if (options.generatedMessage) { error.generatedMessage = true; } return error;}
/** Converts the std assertion error to node.js assertion error */function toNode( fn: () => void, opts?: { actual: unknown; expected: unknown; message?: string | Error; operator?: string; },) { const { operator, message, actual, expected } = opts || {}; try { fn(); } catch (e) { if (e instanceof asserts.AssertionError) { if (typeof message === "string") { throw new AssertionError({ operator, message, actual, expected, }); } else if (message instanceof Error) { throw message; } else { throw new AssertionError({ operator, message: e.message, actual, expected, }); } } throw e; }}
function assert(actual: unknown, message?: string | Error): asserts actual { if (arguments.length === 0) { throw new AssertionError({ message: "No value argument passed to `assert.ok()`", }); } toNode( () => asserts.assert(actual), { message, actual, expected: true }, );}const ok = assert;
function throws( fn: () => void, error?: RegExp | Function | Error, message?: string,) { // Check arg types if (typeof fn !== "function") { throw new ERR_INVALID_ARG_TYPE("fn", "function", fn); } if ( typeof error === "object" && error !== null && Object.getPrototypeOf(error) === Object.prototype && Object.keys(error).length === 0 ) { // error is an empty object throw new ERR_INVALID_ARG_VALUE( "error", error, "may not be an empty object", ); } if (typeof message === "string") { if ( !(error instanceof RegExp) && typeof error !== "function" && !(error instanceof Error) && typeof error !== "object" ) { throw new ERR_INVALID_ARG_TYPE("error", [ "Function", "Error", "RegExp", "Object", ], error); } } else { if ( typeof error !== "undefined" && typeof error !== "string" && !(error instanceof RegExp) && typeof error !== "function" && !(error instanceof Error) && typeof error !== "object" ) { throw new ERR_INVALID_ARG_TYPE("error", [ "Function", "Error", "RegExp", "Object", ], error); } }
// Checks test function try { fn(); } catch (e) { if ( validateThrownError(e, error, message, { operator: throws, }) ) { return; } } if (message) { let msg = `Missing expected exception: ${message}`; if (typeof error === "function" && error?.name) { msg = `Missing expected exception (${error.name}): ${message}`; } throw new AssertionError({ message: msg, operator: "throws", actual: undefined, expected: error, }); } else if (typeof error === "string") { // Use case of throws(fn, message) throw new AssertionError({ message: `Missing expected exception: ${error}`, operator: "throws", actual: undefined, expected: undefined, }); } else if (typeof error === "function" && error?.prototype !== undefined) { throw new AssertionError({ message: `Missing expected exception (${error.name}).`, operator: "throws", actual: undefined, expected: error, }); } else { throw new AssertionError({ message: "Missing expected exception.", operator: "throws", actual: undefined, expected: error, }); }}
function doesNotThrow( fn: () => void, message?: string,): void;function doesNotThrow( fn: () => void, error?: Function, message?: string | Error,): void;function doesNotThrow( fn: () => void, error?: RegExp, message?: string,): void;function doesNotThrow( fn: () => void, expected?: Function | RegExp | string, message?: string | Error,) { // Check arg type if (typeof fn !== "function") { throw new ERR_INVALID_ARG_TYPE("fn", "function", fn); } else if ( !(expected instanceof RegExp) && typeof expected !== "function" && typeof expected !== "string" && typeof expected !== "undefined" ) { throw new ERR_INVALID_ARG_TYPE("expected", ["Function", "RegExp"], fn); }
// Checks test function try { fn(); } catch (e) { gotUnwantedException(e, expected, message, doesNotThrow); } return;}
function equal( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
if (actual == expected) { return; }
if (Number.isNaN(actual) && Number.isNaN(expected)) { return; }
if (typeof message === "string") { throw new AssertionError({ message, }); } else if (message instanceof Error) { throw message; }
toNode( () => asserts.assertStrictEquals(actual, expected), { message: message || `${actual} == ${expected}`, operator: "==", actual, expected, }, );}function notEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
if (Number.isNaN(actual) && Number.isNaN(expected)) { throw new AssertionError({ message: `${actual} != ${expected}`, operator: "!=", actual, expected, }); } if (actual != expected) { return; }
if (typeof message === "string") { throw new AssertionError({ message, }); } else if (message instanceof Error) { throw message; }
toNode( () => asserts.assertNotStrictEquals(actual, expected), { message: message || `${actual} != ${expected}`, operator: "!=", actual, expected, }, );}function strictEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
toNode( () => asserts.assertStrictEquals(actual, expected), { message, operator: "strictEqual", actual, expected }, );}function notStrictEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
toNode( () => asserts.assertNotStrictEquals(actual, expected), { message, actual, expected, operator: "notStrictEqual" }, );}
function deepEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
if (!isDeepEqual(actual, expected)) { innerFail({ actual, expected, message, operator: "deepEqual" }); }}function notDeepEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
if (isDeepEqual(actual, expected)) { innerFail({ actual, expected, message, operator: "notDeepEqual" }); }}function deepStrictEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
toNode( () => asserts.assertEquals(actual, expected), { message, actual, expected, operator: "deepStrictEqual" }, );}function notDeepStrictEqual( actual: unknown, expected: unknown, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); }
toNode( () => asserts.assertNotEquals(actual, expected), { message, actual, expected, operator: "deepNotStrictEqual" }, );}
function fail(message?: string | Error): never { if (typeof message === "string" || message == null) { throw createAssertionError({ message: message ?? "Failed", operator: "fail", generatedMessage: message == null, }); } else { throw message; }}function match(actual: string, regexp: RegExp, message?: string | Error) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "regexp"); } if (!(regexp instanceof RegExp)) { throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp); }
toNode( () => asserts.assertMatch(actual, regexp), { message, actual, expected: regexp, operator: "match" }, );}
function doesNotMatch( string: string, regexp: RegExp, message?: string | Error,) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("string", "regexp"); } if (!(regexp instanceof RegExp)) { throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp); } if (typeof string !== "string") { if (message instanceof Error) { throw message; } throw new AssertionError({ message: message || `The "string" argument must be of type string. Received type ${typeof string} (${ inspect(string) })`, actual: string, expected: regexp, operator: "doesNotMatch", }); }
toNode( () => asserts.assertNotMatch(string, regexp), { message, actual: string, expected: regexp, operator: "doesNotMatch" }, );}
function strict(actual: unknown, message?: string | Error): asserts actual { if (arguments.length === 0) { throw new AssertionError({ message: "No value argument passed to `assert.ok()`", }); } assert(actual, message);}
function rejects( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), error?: RegExp | Function | Error,): Promise<void>;
function rejects( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), message?: string,): Promise<void>;
// Intentionally avoid using async/await because test-assert-async.js requires itfunction rejects( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), error?: RegExp | Function | Error | string, message?: string,) { let promise: Promise<void>; if (typeof asyncFn === "function") { try { promise = asyncFn(); } catch (err) { // If `asyncFn` throws an error synchronously, this function returns a rejected promise. return Promise.reject(err); }
if (!isValidThenable(promise)) { return Promise.reject( new ERR_INVALID_RETURN_VALUE( "instance of Promise", "promiseFn", promise, ), ); } } else if (!isValidThenable(asyncFn)) { return Promise.reject( new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn), ); } else { promise = asyncFn; }
function onFulfilled() { let message = "Missing expected rejection"; if (typeof error === "string") { message += `: ${error}`; } else if (typeof error === "function" && error.prototype !== undefined) { message += ` (${error.name}).`; } else { message += "."; } return Promise.reject(createAssertionError({ message, operator: "rejects", generatedMessage: true, })); }
// deno-lint-ignore camelcase function rejects_onRejected(e: Error) { // TODO(uki00a): In order to `test-assert-async.js` pass, intentionally adds `rejects_` as a prefix. if ( validateThrownError(e, error, message, { operator: rejects, validationFunctionName: "validate", }) ) { return; } }
return promise.then(onFulfilled, rejects_onRejected);}
function doesNotReject( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), error?: RegExp | Function,): Promise<void>;
function doesNotReject( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), message?: string,): Promise<void>;
// Intentionally avoid using async/await because test-assert-async.js requires itfunction doesNotReject( // deno-lint-ignore no-explicit-any asyncFn: Promise<any> | (() => Promise<any>), error?: RegExp | Function | string, message?: string,) { // deno-lint-ignore no-explicit-any let promise: Promise<any>; if (typeof asyncFn === "function") { try { const value = asyncFn(); if (!isValidThenable(value)) { return Promise.reject( new ERR_INVALID_RETURN_VALUE( "instance of Promise", "promiseFn", value, ), ); } promise = value; } catch (e) { // If `asyncFn` throws an error synchronously, this function returns a rejected promise. return Promise.reject(e); } } else if (!isValidThenable(asyncFn)) { return Promise.reject( new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn), ); } else { promise = asyncFn; }
return promise.then( () => {}, (e) => gotUnwantedException(e, error, message, doesNotReject), );}
function gotUnwantedException( // deno-lint-ignore no-explicit-any e: any, expected: RegExp | Function | string | null | undefined, message: string | Error | null | undefined, operator: Function,): never { if (typeof expected === "string") { // The use case of doesNotThrow(fn, message); throw new AssertionError({ message: `Got unwanted exception: ${expected}\nActual message: "${e.message}"`, operator: operator.name, }); } else if ( typeof expected === "function" && expected.prototype !== undefined ) { // The use case of doesNotThrow(fn, Error, message); if (e instanceof expected) { let msg = `Got unwanted exception: ${e.constructor?.name}`; if (message) { msg += ` ${String(message)}`; } throw new AssertionError({ message: msg, operator: operator.name, }); } else if (expected.prototype instanceof Error) { throw e; } else { const result = expected(e); if (result === true) { let msg = `Got unwanted rejection.\nActual message: "${e.message}"`; if (message) { msg += ` ${String(message)}`; } throw new AssertionError({ message: msg, operator: operator.name, }); } } throw e; } else { if (message) { throw new AssertionError({ message: `Got unwanted exception: ${message}\nActual message: "${ e ? e.message : String(e) }"`, operator: operator.name, }); } throw new AssertionError({ message: `Got unwanted exception.\nActual message: "${ e ? e.message : String(e) }"`, operator: operator.name, }); }}
/** * Throws `value` if the value is not `null` or `undefined`. * * @param err */// deno-lint-ignore no-explicit-anyfunction ifError(err: any) { if (err !== null && err !== undefined) { let message = "ifError got unwanted exception: ";
if (typeof err === "object" && typeof err.message === "string") { if (err.message.length === 0 && err.constructor) { message += err.constructor.name; } else { message += err.message; } } else { message += inspect(err); }
const newErr = new AssertionError({ actual: err, expected: null, operator: "ifError", message, stackStartFn: ifError, });
// Make sure we actually have a stack trace! const origStack = err.stack;
if (typeof origStack === "string") { // This will remove any duplicated frames from the error frames taken // from within `ifError` and add the original error frames to the newly // created ones. const tmp2 = origStack.split("\n"); tmp2.shift();
// Filter all frames existing in err.stack. let tmp1 = newErr!.stack?.split("\n");
for (const errFrame of tmp2) { // Find the first occurrence of the frame. const pos = tmp1?.indexOf(errFrame);
if (pos !== -1) { // Only keep new frames. tmp1 = tmp1?.slice(0, pos);
break; } }
newErr.stack = `${tmp1?.join("\n")}\n${tmp2.join("\n")}`; }
throw newErr; }}
interface ValidateThrownErrorOptions { operator: Function; validationFunctionName?: string;}
function validateThrownError( // deno-lint-ignore no-explicit-any e: any, error: RegExp | Function | Error | string | null | undefined, message: string | undefined | null, options: ValidateThrownErrorOptions,): boolean { if (typeof error === "string") { if (message != null) { throw new ERR_INVALID_ARG_TYPE( "error", ["Object", "Error", "Function", "RegExp"], error, ); } else if (typeof e === "object" && e !== null) { if (e.message === error) { throw new ERR_AMBIGUOUS_ARGUMENT( "error/message", `The error message "${e.message}" is identical to the message.`, ); } } else if (e === error) { throw new ERR_AMBIGUOUS_ARGUMENT( "error/message", `The error "${e}" is identical to the message.`, ); } message = error; error = undefined; } if ( error instanceof Function && error.prototype !== undefined && error.prototype instanceof Error ) { // error is a constructor if (e instanceof error) { return true; } throw createAssertionError({ message: `The error is expected to be an instance of "${error.name}". Received "${e?.constructor?.name}"\n\nError message:\n\n${e?.message}`, actual: e, expected: error, operator: options.operator.name, generatedMessage: true, }); } if (error instanceof Function) { const received = error(e); if (received === true) { return true; } throw createAssertionError({ message: `The ${ options.validationFunctionName ? `"${options.validationFunctionName}" validation` : "validation" } function is expected to return "true". Received ${ inspect(received) }\n\nCaught error:\n\n${e}`, actual: e, expected: error, operator: options.operator.name, generatedMessage: true, }); } if (error instanceof RegExp) { if (error.test(String(e))) { return true; } throw createAssertionError({ message: `The input did not match the regular expression ${error.toString()}. Input:\n\n'${ String(e) }'\n`, actual: e, expected: error, operator: options.operator.name, generatedMessage: true, }); } if (typeof error === "object" && error !== null) { const keys = Object.keys(error); if (error instanceof Error) { keys.push("name", "message"); } for (const k of keys) { if (e == null) { throw createAssertionError({ message: message || "object is expected to thrown, but got null", actual: e, expected: error, operator: options.operator.name, generatedMessage: message == null, }); }
if (typeof e === "string") { throw createAssertionError({ message: message || `object is expected to thrown, but got string: ${e}`, actual: e, expected: error, operator: options.operator.name, generatedMessage: message == null, }); } if (typeof e === "number") { throw createAssertionError({ message: message || `object is expected to thrown, but got number: ${e}`, actual: e, expected: error, operator: options.operator.name, generatedMessage: message == null, }); } if (!(k in e)) { throw createAssertionError({ message: message || `A key in the expected object is missing: ${k}`, actual: e, expected: error, operator: options.operator.name, generatedMessage: message == null, }); } const actual = e[k]; // deno-lint-ignore no-explicit-any const expected = (error as any)[k]; if (typeof actual === "string" && expected instanceof RegExp) { match(actual, expected); } else { deepStrictEqual(actual, expected); } } return true; } if (typeof error === "undefined") { return true; } throw createAssertionError({ message: `Invalid expectation: ${error}`, operator: options.operator.name, generatedMessage: true, });}
// deno-lint-ignore no-explicit-anyfunction isValidThenable(maybeThennable: any): boolean { if (!maybeThennable) { return false; }
if (maybeThennable instanceof Promise) { return true; }
const isThenable = typeof maybeThennable.then === "function" && typeof maybeThennable.catch === "function";
return isThenable && typeof maybeThennable !== "function";}
Object.assign(strict, { AssertionError, deepEqual: deepStrictEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal: strictEqual, fail, ifError, match, notDeepEqual: notDeepStrictEqual, notDeepStrictEqual, notEqual: notStrictEqual, notStrictEqual, ok, rejects, strict, strictEqual, throws,});
export default Object.assign(assert, { AssertionError, deepEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal, fail, ifError, match, notDeepEqual, notDeepStrictEqual, notEqual, notStrictEqual, ok, rejects, strict, strictEqual, throws,});
export { AssertionError, deepEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal, fail, ifError, match, notDeepEqual, notDeepStrictEqual, notEqual, notStrictEqual, ok, rejects, strict, strictEqual, throws,};