Skip to content

Commit

Permalink
Check for visibility not just existence of Redbox
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Feb 12, 2025
1 parent 5346a9b commit b19aafc
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 56 deletions.
7 changes: 7 additions & 0 deletions test/lib/browsers/playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Page,
ElementHandle,
devices,
Locator,
} from 'playwright'
import path from 'path'

Expand Down Expand Up @@ -516,4 +517,10 @@ export class Playwright extends BrowserInterface {
return page.waitForLoadState('networkidle')
})
}

locateRedbox(): Locator {
return page.locator(
'nextjs-portal [aria-labelledby="nextjs__container_errors_label"]'
)
}
}
93 changes: 37 additions & 56 deletions test/lib/next-test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type { SpawnOptions, ChildProcess } from 'child_process'
import type { RequestInit, Response } from 'node-fetch'
import type { NextServer } from 'next/dist/server/next'
import { BrowserInterface } from './browsers/base'
import { Playwright } from './browsers/playwright'

import { getTurbopackFlag, shouldRunTurboDevTest } from './turbo'
import stripAnsi from 'strip-ansi'
Expand Down Expand Up @@ -821,73 +822,48 @@ export async function retry<T>(
}
}

async function ensureNoSuspendedComponentsInRedBox(browser: BrowserInterface) {
await retry(async () => {
const suspended = await browser.eval(() => {
return Boolean(
[].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector('[data-nextjs-error-suspended]')
)
)
})
expect(suspended).toBe(false)
}, 10000)
}

export async function assertHasRedbox(browser: BrowserInterface) {
try {
await retry(
async () => {
const hasRedbox = await browser.eval(() => {
return Boolean(
[].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector(
'#nextjs__container_errors_label, #nextjs__container_errors_label'
)
)
)
})
expect(hasRedbox).toBe(true)
},
5000,
200
)
// TODO: Implement for other BrowserInterface implementations
const playwright = browser as Playwright

await ensureNoSuspendedComponentsInRedBox(browser)
const redbox = playwright.locateRedbox()
try {
await redbox.waitFor({ timeout: 5000 })
} catch (errorCause) {
const error = new Error('Expected Redbox but found none')
const error = new Error('Expected Redbox but found no visible one.')
Error.captureStackTrace(error, assertHasRedbox)
throw error
}

try {
await redbox
.locator('[data-nextjs-error-suspended]')
.waitFor({ state: 'detached', timeout: 10000 })
} catch (cause) {
const error = new Error('Redbox still had suspended content after 10s', {
cause,
})
Error.captureStackTrace(error, assertHasRedbox)
throw error
}
}

export async function assertNoRedbox(browser: BrowserInterface) {
// TODO: Implement for other BrowserInterface implementations
const playwright = browser as Playwright

await waitFor(5000)
const hasRedbox = await browser.eval(() => {
return Boolean(
[].slice
.call(document.querySelectorAll('nextjs-portal'))
.find((p) =>
p.shadowRoot.querySelector(
'#nextjs__container_errors_label, #nextjs__container_errors_label'
)
)
)
})
const redbox = playwright.locateRedbox()

if (hasRedbox) {
if (await redbox.isVisible()) {
const [redboxHeader, redboxDescription, redboxSource] = await Promise.all([
getRedboxHeader(browser).catch(() => '<missing>'),
getRedboxDescription(browser).catch(() => '<missing>'),
getRedboxSource(browser).catch(() => '<missing>'),
])

const error = new Error(
'Expected no Redbox but found one\n' +
'Expected no visible Redbox but found one\n' +
`header: ${redboxHeader}\n` +
`description: ${redboxDescription}\n` +
`source: ${redboxSource}`
Expand Down Expand Up @@ -934,10 +910,21 @@ export async function getToastErrorCount(
* Success implies {@link assertHasRedbox}.
*/
export async function openRedbox(browser: BrowserInterface): Promise<void> {
// TODO: Implement for other BrowserInterface implementations
const playwright = browser as Playwright
const redbox = playwright.locateRedbox()
if (await redbox.isVisible()) {
const error = new Error(
'Redbox is already open. Use `assertHasRedbox` instead.'
)
Error.captureStackTrace(error, openRedbox)
throw error
}

try {
await browser.waitForElementByCss('[data-issues]').click()
} catch (cause) {
const error = new Error('No Redbox to open.', { cause })
const error = new Error('Redbox did not open.')
Error.captureStackTrace(error, openRedbox)
throw error
}
Expand Down Expand Up @@ -1036,8 +1023,6 @@ export async function getRedboxTotalErrorCount(
}

export async function getRedboxSource(browser: BrowserInterface) {
await ensureNoSuspendedComponentsInRedBox(browser)

return browser.eval(() => {
const portal = [].slice
.call(document.querySelectorAll('nextjs-portal'))
Expand Down Expand Up @@ -1344,7 +1329,6 @@ export async function getRedboxComponentStack(
}

export async function hasRedboxCallStack(browser: BrowserInterface) {
await ensureNoSuspendedComponentsInRedBox(browser)
return browser.eval(() => {
const portal = [].slice
.call(document.querySelectorAll('nextjs-portal'))
Expand All @@ -1358,7 +1342,6 @@ export async function hasRedboxCallStack(browser: BrowserInterface) {
export async function getRedboxCallStack(
browser: BrowserInterface
): Promise<string | null> {
await ensureNoSuspendedComponentsInRedBox(browser)
const callStackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
Expand All @@ -1372,7 +1355,6 @@ export async function getRedboxCallStack(
export async function getRedboxCallStackCollapsed(
browser: BrowserInterface
): Promise<string> {
await ensureNoSuspendedComponentsInRedBox(browser)
const callStackFrameElements = await browser.elementsByCss(
'.nextjs-container-errors-body > [data-nextjs-codeframe] > :first-child, ' +
'.nextjs-container-errors-body > [data-nextjs-call-stack-frame], ' +
Expand Down Expand Up @@ -1594,7 +1576,6 @@ export const checkLink = (
) => checkMeta(browser, rel, content, 'rel', 'link', 'href')

export async function getStackFramesContent(browser) {
await ensureNoSuspendedComponentsInRedBox(browser)
const stackFrameElements = await browser.elementsByCss(
'[data-nextjs-call-stack-frame]'
)
Expand Down

0 comments on commit b19aafc

Please sign in to comment.