Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion expect/_extend_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExtendMatchResult>;
}
}

Expand Down Expand Up @@ -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<void>((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", () => {
Expand Down Expand Up @@ -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<void>();
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)",
);
});
2 changes: 1 addition & 1 deletion expect/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface MatcherContext {
export type Matcher = (
context: MatcherContext,
...args: any[]
) => MatchResult | ExtendMatchResult;
) => MatchResult | ExtendMatchResult | Promise<ExtendMatchResult>;

export type Matchers = {
[key: string]: Matcher;
Expand Down
26 changes: 21 additions & 5 deletions expect/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,29 @@ export function expect<T extends Expected = Expected>(
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<ExtendMatchResult>;

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);
Expand Down
Loading