diff --git a/packages/browser/src/node/providers/playwright.ts b/packages/browser/src/node/providers/playwright.ts
index 2a08cef23695..54a5ef84dcd6 100644
--- a/packages/browser/src/node/providers/playwright.ts
+++ b/packages/browser/src/node/providers/playwright.ts
@@ -185,6 +185,12 @@ export class PlaywrightBrowserProvider implements BrowserProvider {
})
}
+ // unhandled page crashes will hang vitest process
+ page.on('crash', () => {
+ const session = this.project.vitest._browserSessions.getSession(sessionId)
+ session?.reject(new Error('Page crashed when executing tests'))
+ })
+
return page
}
diff --git a/packages/utils/src/display.ts b/packages/utils/src/display.ts
index f61b425787d6..69d7bef51322 100644
--- a/packages/utils/src/display.ts
+++ b/packages/utils/src/display.ts
@@ -73,8 +73,9 @@ export function stringify(
})
}
+ // Prevents infinite loop https://github.com/vitest-dev/vitest/issues/7249
return result.length >= MAX_LENGTH && maxDepth > 1
- ? stringify(object, Math.floor(maxDepth / 2))
+ ? stringify(object, Math.floor(Math.min(maxDepth, Number.MAX_SAFE_INTEGER) / 2), { maxLength, ...options })
: result
}
diff --git a/test/browser/fixtures/browser-crash/browser-crash.test.ts b/test/browser/fixtures/browser-crash/browser-crash.test.ts
new file mode 100644
index 000000000000..eccc9f375616
--- /dev/null
+++ b/test/browser/fixtures/browser-crash/browser-crash.test.ts
@@ -0,0 +1,12 @@
+import { commands } from '@vitest/browser/context'
+import { it } from 'vitest'
+
+declare module '@vitest/browser/context' {
+ interface BrowserCommands {
+ forceCrash: () => Promise
+ }
+}
+
+it('fails gracefully when browser crashes', async () => {
+ await commands.forceCrash()
+})
diff --git a/test/browser/fixtures/browser-crash/vitest.config.ts b/test/browser/fixtures/browser-crash/vitest.config.ts
new file mode 100644
index 000000000000..3f23dfde5b97
--- /dev/null
+++ b/test/browser/fixtures/browser-crash/vitest.config.ts
@@ -0,0 +1,39 @@
+import { fileURLToPath } from 'node:url'
+import { defineConfig } from 'vitest/config'
+import { instances, provider } from '../../settings'
+import { BrowserCommand } from 'vitest/node'
+
+const forceCrash: BrowserCommand<[]> = async (context) => {
+ const browser = context.context.browser().browserType().name()
+ if (browser === 'chromium') {
+ await context.page.goto('chrome://crash')
+ }
+
+ if (browser === 'firefox') {
+ await context.page.goto('about:crashcontent')
+ }
+
+ throw new Error(`Browser crash not supported for ${browser}`)
+}
+
+export default defineConfig({
+ cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
+ test: {
+ browser: {
+ commands: { forceCrash },
+ enabled: true,
+ provider,
+ instances: instances.map(instance => ({
+ ...instance,
+ context: {
+ actionTimeout: 500,
+ },
+ })),
+ },
+ expect: {
+ poll: {
+ timeout: 500,
+ },
+ },
+ },
+})
diff --git a/test/browser/specs/browser-crash.test.ts b/test/browser/specs/browser-crash.test.ts
new file mode 100644
index 000000000000..76428b0e23f5
--- /dev/null
+++ b/test/browser/specs/browser-crash.test.ts
@@ -0,0 +1,17 @@
+import { expect, test } from 'vitest'
+import { instances, provider, runBrowserTests } from './utils'
+
+// TODO handle webdriverio. Currently they
+// expose no trustable way to detect browser crashes.
+test.runIf(provider === 'playwright')('fails gracefully when browser crashes', async () => {
+ const { stderr } = await runBrowserTests({
+ root: './fixtures/browser-crash',
+ reporters: [['verbose', { isTTY: false }]],
+ browser: {
+ // webkit has no support for simulating browser crash
+ instances: instances.filter(item => item.name !== 'webkit'),
+ },
+ })
+
+ expect(stderr).contains('Page crashed when executing tests')
+})
diff --git a/test/browser/test/__snapshots__/utils.test.ts.snap b/test/browser/test/__snapshots__/utils.test.ts.snap
index c0ab66b34255..60fb56d24d77 100644
--- a/test/browser/test/__snapshots__/utils.test.ts.snap
+++ b/test/browser/test/__snapshots__/utils.test.ts.snap
@@ -35,3 +35,13 @@ exports[`prints the element with attributes 1`] = `
+
+