Skip to content

Commit a3a46a5

Browse files
authored
fix: prevent infinite loop on prettyDOM calls (#7250)
1 parent 84287fc commit a3a46a5

File tree

7 files changed

+101
-1
lines changed

7 files changed

+101
-1
lines changed

packages/browser/src/node/providers/playwright.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ export class PlaywrightBrowserProvider implements BrowserProvider {
185185
})
186186
}
187187

188+
// unhandled page crashes will hang vitest process
189+
page.on('crash', () => {
190+
const session = this.project.vitest._browserSessions.getSession(sessionId)
191+
session?.reject(new Error('Page crashed when executing tests'))
192+
})
193+
188194
return page
189195
}
190196

packages/utils/src/display.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ export function stringify(
7373
})
7474
}
7575

76+
// Prevents infinite loop https://github.com/vitest-dev/vitest/issues/7249
7677
return result.length >= MAX_LENGTH && maxDepth > 1
77-
? stringify(object, Math.floor(maxDepth / 2))
78+
? stringify(object, Math.floor(Math.min(maxDepth, Number.MAX_SAFE_INTEGER) / 2), { maxLength, ...options })
7879
: result
7980
}
8081

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { commands } from '@vitest/browser/context'
2+
import { it } from 'vitest'
3+
4+
declare module '@vitest/browser/context' {
5+
interface BrowserCommands {
6+
forceCrash: () => Promise<void>
7+
}
8+
}
9+
10+
it('fails gracefully when browser crashes', async () => {
11+
await commands.forceCrash()
12+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fileURLToPath } from 'node:url'
2+
import { defineConfig } from 'vitest/config'
3+
import { instances, provider } from '../../settings'
4+
import { BrowserCommand } from 'vitest/node'
5+
6+
const forceCrash: BrowserCommand<[]> = async (context) => {
7+
const browser = context.context.browser().browserType().name()
8+
if (browser === 'chromium') {
9+
await context.page.goto('chrome://crash')
10+
}
11+
12+
if (browser === 'firefox') {
13+
await context.page.goto('about:crashcontent')
14+
}
15+
16+
throw new Error(`Browser crash not supported for ${browser}`)
17+
}
18+
19+
export default defineConfig({
20+
cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
21+
test: {
22+
browser: {
23+
commands: { forceCrash },
24+
enabled: true,
25+
provider,
26+
instances: instances.map(instance => ({
27+
...instance,
28+
context: {
29+
actionTimeout: 500,
30+
},
31+
})),
32+
},
33+
expect: {
34+
poll: {
35+
timeout: 500,
36+
},
37+
},
38+
},
39+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { expect, test } from 'vitest'
2+
import { instances, provider, runBrowserTests } from './utils'
3+
4+
// TODO handle webdriverio. Currently they
5+
// expose no trustable way to detect browser crashes.
6+
test.runIf(provider === 'playwright')('fails gracefully when browser crashes', async () => {
7+
const { stderr } = await runBrowserTests({
8+
root: './fixtures/browser-crash',
9+
reporters: [['verbose', { isTTY: false }]],
10+
browser: {
11+
// webkit has no support for simulating browser crash
12+
instances: instances.filter(item => item.name !== 'webkit'),
13+
},
14+
})
15+
16+
expect(stderr).contains('Page crashed when executing tests')
17+
})

test/browser/test/__snapshots__/utils.test.ts.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,13 @@ exports[`prints the element with attributes 1`] = `
3535
</div>
3636
</body>"
3737
`;
38+
39+
exports[`should handle DOM content bigger than maxLength 1`] = `
40+
"<body>
41+
<div>
42+
<div>
43+
<div />
44+
</div>
45+
</div>
46+
</body>..."
47+
`;

test/browser/test/utils.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,18 @@ test('prints the element with attributes', async () => {
3737

3838
expect(await commands.stripVTControlCharacters(prettyDOM())).toMatchSnapshot()
3939
})
40+
41+
test('should handle DOM content bigger than maxLength', async () => {
42+
const depth = 200
43+
const maxContent = 150
44+
45+
const openingTags = '<div>'.repeat(depth)
46+
const closingTags = '</div>'.repeat(depth)
47+
const domString = `${openingTags}${closingTags}`
48+
49+
const parentDiv = document.createElement('div')
50+
parentDiv.innerHTML = domString
51+
52+
document.body.appendChild(parentDiv)
53+
expect(await commands.stripVTControlCharacters(prettyDOM(undefined, maxContent))).toMatchSnapshot()
54+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy