Skip to content
Draft
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
47 changes: 46 additions & 1 deletion e2e/utils/localTestFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,33 @@ export const test = base.extend<ScenarioFixtures & { localConfig: LocalConfig },
{ scope: 'test' },
],

page: async ({ page, localConfig, scenario }, use) => {
page: async ({ page, localConfig, scenario }, use, testInfo) => {
// Watch the browser console + page errors for `ZodError` strings.
// `@gusto/embedded-api` validates every response with Zod; when the
// backend ships a shape that disagrees with the published schema the
// request rejects and surfaces as a `ZodError` in the browser console
// (either as an uncaught page error or via React's error-boundary
// `console.error`). Tests can otherwise pass while the SDK is silently
// crashing into an error boundary mid-flow, so we fail the test if any
// such error fires during its lifetime.
const zodErrors: string[] = []

const recordIfZodError = (source: 'console' | 'pageerror', text: string) => {
if (text.includes('ZodError')) {
zodErrors.push(`[${source}] ${text}`)
}
}

page.on('console', msg => {
if (msg.type() !== 'error' && msg.type() !== 'warning') return
recordIfZodError('console', msg.text())
})

page.on('pageerror', err => {
const text = err.stack ?? `${err.name}: ${err.message}`
recordIfZodError('pageerror', text)
})

const originalGoto = page.goto.bind(page)

page.goto = async (url: string, options?: Parameters<typeof page.goto>[1]) => {
Expand Down Expand Up @@ -236,6 +262,25 @@ export const test = base.extend<ScenarioFixtures & { localConfig: LocalConfig },
}

await use(page)

if (zodErrors.length > 0) {
const detail = zodErrors.join('\n\n---\n\n')
await testInfo.attach('zod-errors.txt', {
body: detail,
contentType: 'text/plain',
})
// Only fail the test if it would otherwise pass — if it already
// failed for another reason, surface the Zod errors as an
// attachment but don't overwrite the existing failure.
if (testInfo.status === 'passed' || testInfo.status === undefined) {
throw new Error(
`Detected ${zodErrors.length} ZodError(s) in the browser console during this test. ` +
`This means the backend returned a response shape that disagrees with the ` +
`@gusto/embedded-api Zod schema. See the zod-errors.txt attachment for the full text.\n\n` +
detail,
)
}
}
},
})

Expand Down
Loading