diff --git a/expect/_extend_test.ts b/expect/_extend_test.ts index 98352b437fba..080b5b6993ed 100644 --- a/expect/_extend_test.ts +++ b/expect/_extend_test.ts @@ -2,11 +2,12 @@ import { expect } from "./expect.ts"; import type { Async, Expected, MatcherContext, Tester } from "./_types.ts"; -import { AssertionError, assertThrows } from "@std/assert"; +import { AssertionError, assertRejects, assertThrows } from "@std/assert"; declare module "./_types.ts" { interface Expected { toEqualBook: (expected: unknown) => ExtendMatchResult; + toBeResolved: () => Promise; } } @@ -78,6 +79,24 @@ expect.extend({ pass: result, }; }, + async toBeResolved(context) { + if (!(context.value instanceof Promise)) { + throw new TypeError("Expected value to be a promise"); + } + const test = new Promise((resolve) => setTimeout(resolve, 0)); + const status = await Promise.race([ + context.value.then(() => "resolved"), + test.then(() => "pending"), + ]); + await test; + return { + message: () => + `Expected promise to be ${ + { pending: "resolved", resolved: "pending" }[status] + } (got ${status})`, + pass: status === "resolved", + }; + }, }); Deno.test("expect.extend() api test case", () => { @@ -158,3 +177,20 @@ Deno.test("expect.extend() example is valid", async () => { myexpect("foo").not.toBeNull(); myexpect.anything; }); + +Deno.test("expect.extend() api test case", async () => { + const { promise, resolve } = Promise.withResolvers(); + await expect(promise).not.toBeResolved(); + await assertRejects( + () => expect(promise).toBeResolved(), + AssertionError, + "Expected promise to be resolved (got pending)", + ); + resolve(); + await expect(promise).toBeResolved(); + await assertRejects( + () => expect(promise).not.toBeResolved(), + AssertionError, + "Expected promise to be pending (got resolved)", + ); +}); diff --git a/expect/_types.ts b/expect/_types.ts index 3988d69fb9bb..9e0a4f231829 100644 --- a/expect/_types.ts +++ b/expect/_types.ts @@ -12,7 +12,7 @@ export interface MatcherContext { export type Matcher = ( context: MatcherContext, ...args: any[] -) => MatchResult | ExtendMatchResult; +) => MatchResult | ExtendMatchResult | Promise; export type Matchers = { [key: string]: Matcher; diff --git a/expect/expect.ts b/expect/expect.ts index cd9d24a0ff2c..04b84d82f6d7 100644 --- a/expect/expect.ts +++ b/expect/expect.ts @@ -211,13 +211,29 @@ export function expect( context.isNot = true; } if (name in extendMatchers) { - const result = matcher(context, ...args) as ExtendMatchResult; - if (context.isNot) { - if (result.pass) { + const result = matcher(context, ...args) as + | ExtendMatchResult + | Promise; + + if (result instanceof Promise) { + return result.then((result) => { + if (context.isNot) { + if (result.pass) { + throw new AssertionError(result.message()); + } + } else if (!result.pass) { + throw new AssertionError(result.message()); + } + emitAssertionTrigger(); + }); + } else { + if (context.isNot) { + if (result.pass) { + throw new AssertionError(result.message()); + } + } else if (!result.pass) { throw new AssertionError(result.message()); } - } else if (!result.pass) { - throw new AssertionError(result.message()); } } else { matcher(context, ...args);