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`] = ` " `; + +exports[`should handle DOM content bigger than maxLength 1`] = ` +" +
+
+
+
+
+..." +`; diff --git a/test/browser/test/utils.test.ts b/test/browser/test/utils.test.ts index c66fcf0da811..f6de6683ebe9 100644 --- a/test/browser/test/utils.test.ts +++ b/test/browser/test/utils.test.ts @@ -37,3 +37,18 @@ test('prints the element with attributes', async () => { expect(await commands.stripVTControlCharacters(prettyDOM())).toMatchSnapshot() }) + +test('should handle DOM content bigger than maxLength', async () => { + const depth = 200 + const maxContent = 150 + + const openingTags = '
'.repeat(depth) + const closingTags = '
'.repeat(depth) + const domString = `${openingTags}${closingTags}` + + const parentDiv = document.createElement('div') + parentDiv.innerHTML = domString + + document.body.appendChild(parentDiv) + expect(await commands.stripVTControlCharacters(prettyDOM(undefined, maxContent))).toMatchSnapshot() +})