Skip to content

Commit

Permalink
feat(testing/asserts): return error from assertRejects and `assertT…
Browse files Browse the repository at this point in the history
…hrows` (#2226)

Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
  • Loading branch information
mrkldshv and kt3k committed May 25, 2022
1 parent fae2224 commit 662f27c
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 160 deletions.
2 changes: 1 addition & 1 deletion collections/sliding_windows_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function slidingWindowsThrowsTest<T>(
size: number,
config?: { step?: number; partial?: boolean },
],
ErrorClass?: ErrorConstructor | undefined,
ErrorClass: ErrorConstructor,
msgIncludes?: string,
msg?: string | undefined,
) {
Expand Down
2 changes: 1 addition & 1 deletion encoding/jsonc_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function assertValidParse(
function assertInvalidParse(
text: string,
// deno-lint-ignore no-explicit-any
ErrorClass?: (new (...args: any[]) => Error),
ErrorClass: (new (...args: any[]) => Error),
msgIncludes?: string,
options?: JSONC.ParseOptions,
) {
Expand Down
2 changes: 1 addition & 1 deletion encoding/testdata/jsonc/node-jsonc-parser/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function assertValidParse(
function assertInvalidParse(
text: string,
// deno-lint-ignore no-explicit-any
ErrorClass?: (new (...args: any[]) => Error),
ErrorClass: (new (...args: any[]) => Error),
msgIncludes?: string,
options?: JSONC.ParseOptions,
) {
Expand Down
2 changes: 1 addition & 1 deletion node/_crypto/crypto_browserify/evp_bytes_to_key_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Deno.test("salt buffer length is 7", function () {
function () {
EVP_BytesToKey(Buffer.alloc(5), Buffer.alloc(7), 1, 1);
},
undefined,
Error,
"salt should be Buffer with 8 byte length",
);
});
2 changes: 1 addition & 1 deletion node/module_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Deno.test("EventEmitter is exported correctly", () => {
Deno.test("Require .mjs", () => {
assertThrows(
() => require("./testdata/inspect.mjs"),
undefined,
Error,
"Importing ESM module",
);
});
Expand Down
5 changes: 3 additions & 2 deletions testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ pretty-printed diff of failing assertion.
- `assertThrows()` - Expects the passed `fn` to throw. If `fn` does not throw,
this function does. Also compares any errors thrown to an optional expected
`Error` class and checks that the error `.message` includes an optional
string.
string. If there is caught error, it gets returned.
- `assertRejects()` - Expects the passed `fn` to be async and throw and return a
`Promise` that rejects. If the `fn` does not throw or reject, this function
will reject _(⚠️ you should normally await this assertion)_. Also optionally
accepts an Error class which the expected error must be an instance of, and a
string which must be a substring of the error's `.message`.
string which must be a substring of the error's `.message`. If there is caught
error, it gets returned.
- `unimplemented()` - Use this to stub out methods that will throw when invoked.
- `unreachable()` - Used to assert unreachable code.

Expand Down
2 changes: 1 addition & 1 deletion testing/_format_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Deno.test("assert diff formatting (strings)", () => {
() => {
assertEquals([..."abcd"].join("\n"), [..."abxde"].join("\n"));
},
undefined,
Error,
`
a\\n
b\\n
Expand Down
149 changes: 90 additions & 59 deletions testing/asserts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,66 +601,82 @@ export function assertIsError<E extends Error = Error>(
* callback which will be passed the error, usually to apply some custom
* assertions on it.
*/
export function assertThrows(
fn: () => unknown,
msg?: string,
): unknown;
export function assertThrows<E extends Error = Error>(
fn: () => unknown,
// deno-lint-ignore no-explicit-any
ErrorClass?: new (...args: any[]) => E,
ErrorClass: new (...args: any[]) => E,
msgIncludes?: string,
msg?: string,
): void;
): E;
/** @deprecated */
export function assertThrows(
fn: () => unknown,
errorCallback: (e: Error) => unknown,
msg?: string,
): void;
): Error;
export function assertThrows<E extends Error = Error>(
fn: () => unknown,
errorClassOrCallback?:
errorClassOrCallbackOrMsg?:
// deno-lint-ignore no-explicit-any
| (new (...args: any[]) => E)
| ((e: Error) => unknown),
| ((e: Error) => unknown)
| string,
msgIncludesOrMsg?: string,
msg?: string,
): void {
): E | Error | unknown {
// deno-lint-ignore no-explicit-any
let ErrorClass: (new (...args: any[]) => E) | undefined = undefined;
let msgIncludes: string | undefined = undefined;
let errorCallback;
if (
errorClassOrCallback == null ||
errorClassOrCallback.prototype instanceof Error ||
errorClassOrCallback.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrCallback as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
errorCallback = null;
let errorCallback: ((e: Error) => unknown) | undefined = undefined;
let err;

if (typeof errorClassOrCallbackOrMsg !== "string") {
if (
errorClassOrCallbackOrMsg === undefined ||
errorClassOrCallbackOrMsg.prototype instanceof Error ||
errorClassOrCallbackOrMsg.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrCallbackOrMsg as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
} else {
errorCallback = errorClassOrCallbackOrMsg as (e: Error) => unknown;
msg = msgIncludesOrMsg;
}
} else {
errorCallback = errorClassOrCallback as (e: Error) => unknown;
msg = msgIncludesOrMsg;
msg = errorClassOrCallbackOrMsg;
}
let doesThrow = false;
const msgToAppendToError = msg ? `: ${msg}` : ".";
try {
fn();
} catch (error) {
if (error instanceof Error === false) {
throw new AssertionError("A non-Error object was thrown.");
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
if (typeof errorCallback == "function") {
errorCallback(error);
if (ErrorClass || errorCallback) {
if (error instanceof Error === false) {
throw new AssertionError("A non-Error object was thrown.");
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
if (typeof errorCallback === "function") {
errorCallback(error);
}
}
err = error;
doesThrow = true;
}
if (!doesThrow) {
msg = `Expected function to throw${msg ? `: ${msg}` : "."}`;
msg = `Expected function to throw${msgToAppendToError}`;
throw new AssertionError(msg);
}
return err;
}

/**
Expand All @@ -670,43 +686,54 @@ export function assertThrows<E extends Error = Error>(
* callback which will be passed the error, usually to apply some custom
* assertions on it.
*/
export function assertRejects(
fn: () => Promise<unknown>,
msg?: string,
): Promise<unknown>;
export function assertRejects<E extends Error = Error>(
fn: () => Promise<unknown>,
// deno-lint-ignore no-explicit-any
ErrorClass?: new (...args: any[]) => E,
ErrorClass: new (...args: any[]) => E,
msgIncludes?: string,
msg?: string,
): Promise<void>;
): Promise<E>;
/** @deprecated */
export function assertRejects(
fn: () => Promise<unknown>,
errorCallback: (e: Error) => unknown,
msg?: string,
): Promise<void>;
): Promise<Error>;
export async function assertRejects<E extends Error = Error>(
fn: () => Promise<unknown>,
errorClassOrCallback?:
errorClassOrCallbackOrMsg?:
// deno-lint-ignore no-explicit-any
| (new (...args: any[]) => E)
| ((e: Error) => unknown),
| ((e: Error) => unknown)
| string,
msgIncludesOrMsg?: string,
msg?: string,
): Promise<void> {
): Promise<E | Error | unknown> {
// deno-lint-ignore no-explicit-any
let ErrorClass: (new (...args: any[]) => E) | undefined = undefined;
let msgIncludes: string | undefined = undefined;
let errorCallback;
if (
errorClassOrCallback == null ||
errorClassOrCallback.prototype instanceof Error ||
errorClassOrCallback.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrCallback as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
errorCallback = null;
let errorCallback: ((e: Error) => unknown) | undefined = undefined;
let err;

if (typeof errorClassOrCallbackOrMsg !== "string") {
if (
errorClassOrCallbackOrMsg === undefined ||
errorClassOrCallbackOrMsg.prototype instanceof Error ||
errorClassOrCallbackOrMsg.prototype === Error.prototype
) {
// deno-lint-ignore no-explicit-any
ErrorClass = errorClassOrCallbackOrMsg as new (...args: any[]) => E;
msgIncludes = msgIncludesOrMsg;
} else {
errorCallback = errorClassOrCallbackOrMsg as (e: Error) => unknown;
msg = msgIncludesOrMsg;
}
} else {
errorCallback = errorClassOrCallback as (e: Error) => unknown;
msg = msgIncludesOrMsg;
msg = errorClassOrCallbackOrMsg;
}
let doesThrow = false;
let isPromiseReturned = false;
Expand All @@ -723,25 +750,29 @@ export async function assertRejects<E extends Error = Error>(
`Function throws when expected to reject${msgToAppendToError}`,
);
}
if (error instanceof Error === false) {
throw new AssertionError("A non-Error object was rejected.");
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
if (typeof errorCallback == "function") {
errorCallback(error);
if (ErrorClass || errorCallback) {
if (error instanceof Error === false) {
throw new AssertionError("A non-Error object was rejected.");
}
assertIsError(
error,
ErrorClass,
msgIncludes,
msg,
);
if (typeof errorCallback == "function") {
errorCallback(error);
}
}
err = error;
doesThrow = true;
}
if (!doesThrow) {
throw new AssertionError(
`Expected function to reject${msgToAppendToError}`,
);
}
return err;
}

/** Use this to stub out methods that will throw when invoked. */
Expand Down
Loading

0 comments on commit 662f27c

Please sign in to comment.