-
Notifications
You must be signed in to change notification settings - Fork 680
Feat async extended matchers #7213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ export interface MatcherContext { | |
| export type Matcher = ( | ||
| context: MatcherContext, | ||
| ...args: any[] | ||
| ) => MatchResult | ExtendMatchResult; | ||
| ) => MatchResult | ExtendMatchResult | Promise<ExtendMatchResult>; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the missing-await hazard originates, and I think it needs addressing before the feature ships.
The type system doesn't help here either: the call-site signature comes entirely from the user's own At minimum, |
||
|
|
||
| export type Matchers = { | ||
| [key: string]: Matcher; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,13 +219,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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A non-native thenable — a userland promise implementation, a This module already has |
||
| return result.then((result) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Worth extracting the shared body rather than duplicating it. This block and the sync branch below are verbatim identical — same Something like a local Separately, note that moving |
||
| 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); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test name is byte-identical to the existing one at line 83, so the two are indistinguishable in test output — and
deno test --filterwill match both. Something like"expect.extend() supports async matchers"would describe what it actually covers.While you're here, three cases worth adding: an async matcher under
.resolves/.rejects(theisPromisedpath should flatten it, but nothing pins that), theexpect.assertions()interaction noted above, and a matcher whose promise rejects rather than resolving topass: false.