-
Notifications
You must be signed in to change notification settings - Fork 27.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add internal
toDisplayRedbox
and toDisplayCollapsedRedbox
inline …
…snapshot matchers These are meant to replace `assertHasRedbox` over time. We want that every redbox insertion in the future asserts on the full error (message, stack, codeframe) in both browser and terminal. We'll slowly expand usage of these matchers until all use cases are covered at which point the old, granular helpers are removed. The end goal is full confidence in our error display without sacrificing DX for people focused on the error message itself. The downside of inline snapshot matcher that we can't have fine-grained TODO comments. But that's only a concern for the few working on working on the error display infra. The goal here is to encourage using these helpers so the priorities of the few working on error infra is lowest. The most annoying fact is the need for forking assertions between Turbopack and Webpack. All the more reason for us to fix the off-by-one column issues between Turbopack and Webpack.
- Loading branch information
Showing
7 changed files
with
221 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import type { MatcherContext } from 'expect' | ||
import { toMatchInlineSnapshot } from 'jest-snapshot' | ||
import { | ||
assertHasRedbox, | ||
getRedboxCallStack, | ||
getRedboxDescription, | ||
getRedboxSource, | ||
openRedbox, | ||
} from './next-test-utils' | ||
import type { BrowserInterface } from './browsers/base' | ||
|
||
declare global { | ||
namespace jest { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- module augmentation needs to match generic params even if unused | ||
interface Matchers<R> { | ||
/** | ||
* Inline snapshot matcher for a Redbox that's popped up by default. | ||
* When a Redbox is hidden at first and requires manual display by clicking the toast, | ||
* use {@link toDisplayCollapsedRedbox} instead. | ||
* @param inlineSnapshot - The snapshot to compare against. | ||
*/ | ||
toDisplayRedbox(inlineSnapshot?: string): Promise<void> | ||
|
||
/** | ||
* Inline snapshot matcher for a Redbox that's collapsed by default. | ||
* When a Redbox is immediately displayed , | ||
* use {@link toDisplayRedbox} instead. | ||
* @param inlineSnapshot - The snapshot to compare against. | ||
*/ | ||
toDisplayCollapsedRedbox(inlineSnapshot?: string): Promise<void> | ||
} | ||
} | ||
} | ||
|
||
interface RedboxSnapshot { | ||
stack: string | ||
description: string | ||
} | ||
|
||
async function createRedboxSnaspshot( | ||
browser: BrowserInterface | ||
): Promise<RedboxSnapshot> { | ||
const redbox = { | ||
description: await getRedboxDescription(browser).catch(() => '<empty>'), | ||
source: await getRedboxSource(browser).catch(() => '<empty>'), | ||
stack: await getRedboxCallStack(browser).catch(() => '<empty>'), | ||
// TODO: message, etc. | ||
} | ||
|
||
return redbox | ||
} | ||
|
||
expect.extend({ | ||
async toDisplayRedbox( | ||
this: MatcherContext, | ||
browser: BrowserInterface, | ||
expectedRedboxSnapshot?: string | ||
) { | ||
// Otherwise jest uses the async stack trace which makes it impossible to know the actual callsite of `toMatchSpeechInlineSnapshot`. | ||
// @ts-expect-error -- Not readonly | ||
this.error = new Error() | ||
// Abort test on first mismatch. | ||
// Subsequent actions will be based on an incorrect state otherwise and almost always fail as well. | ||
// TODO: Actually, we may want to proceed. Kinda nice to also do more assertions later. | ||
this.dontThrow = () => {} | ||
|
||
try { | ||
await assertHasRedbox(browser) | ||
} catch { | ||
// argument length is relevant. | ||
// Jest will update absent snapshots but fail if you specify a snapshot even if undefined. | ||
if (expectedRedboxSnapshot === undefined) { | ||
return toMatchInlineSnapshot.call(this, '<no redbox found>') | ||
} else { | ||
return toMatchInlineSnapshot.call( | ||
this, | ||
'<no redbox found>', | ||
expectedRedboxSnapshot | ||
) | ||
} | ||
} | ||
|
||
const redbox = await createRedboxSnaspshot(browser) | ||
|
||
// argument length is relevant. | ||
// Jest will update absent snapshots but fail if you specify a snapshot even if undefined. | ||
if (expectedRedboxSnapshot === undefined) { | ||
return toMatchInlineSnapshot.call(this, redbox) | ||
} else { | ||
return toMatchInlineSnapshot.call(this, redbox, expectedRedboxSnapshot) | ||
} | ||
}, | ||
async toDisplayCollapsedRedbox( | ||
this: MatcherContext, | ||
browser: BrowserInterface, | ||
expectedRedboxSnapshot?: string | ||
) { | ||
// Otherwise jest uses the async stack trace which makes it impossible to know the actual callsite of `toMatchSpeechInlineSnapshot`. | ||
// @ts-expect-error -- Not readonly | ||
this.error = new Error() | ||
// Abort test on first mismatch. | ||
// Subsequent actions will be based on an incorrect state otherwise and almost always fail as well. | ||
// TODO: Actually, we may want to proceed. Kinda nice to also do more assertions later. | ||
this.dontThrow = () => {} | ||
|
||
try { | ||
await openRedbox(browser) | ||
} catch { | ||
// argument length is relevant. | ||
// Jest will update absent snapshots but fail if you specify a snapshot even if undefined. | ||
if (expectedRedboxSnapshot === undefined) { | ||
return toMatchInlineSnapshot.call(this, '<no redbox to open>') | ||
} else { | ||
return toMatchInlineSnapshot.call( | ||
this, | ||
'<no redbox to open>', | ||
expectedRedboxSnapshot | ||
) | ||
} | ||
} | ||
|
||
const redbox = await createRedboxSnaspshot(browser) | ||
|
||
// argument length is relevant. | ||
// Jest will update absent snapshots but fail if you specify a snapshot even if undefined. | ||
if (expectedRedboxSnapshot === undefined) { | ||
return toMatchInlineSnapshot.call(this, redbox) | ||
} else { | ||
return toMatchInlineSnapshot.call(this, redbox, expectedRedboxSnapshot) | ||
} | ||
}, | ||
}) |
Oops, something went wrong.