Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(playwright): teardown when global quit force terminates browser #59548

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,8 @@ ${ENDGROUP}`)
// If environment is CI and if this test execution is failed after retry, preserve test traces
// to upload into github actions artifacts for debugging purpose
const shouldPreserveTracesOutput =
process.env.CI && isRetry && isChildExitWithNonZero
(process.env.CI && isRetry && isChildExitWithNonZero) ||
process.env.PRESERVE_TRACES_OUTPUT
if (!shouldPreserveTracesOutput) {
await fsp
.rm(
Expand Down
25 changes: 16 additions & 9 deletions test/lib/browsers/playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,25 @@ let websocketFrames: Array<{ payload: string | Buffer }> = []

const tracePlaywright = process.env.TRACE_PLAYWRIGHT

// loose global to register teardown functions before quitting the browser instance.
// This is due to `quit` can be called anytime outside of BrowserInterface's lifecycle,
// which can create corrupted state by terminating the context.
// [TODO] global `quit` might need to be removed, instead should introduce per-instance teardown
const pendingTeardown = []
export async function quit() {
await Promise.all(pendingTeardown.map((fn) => fn()))
await context?.close()
await browser?.close()
context = undefined
browser = undefined
}

async function teardown(tearDownFn: () => Promise<void>) {
pendingTeardown.push(tearDownFn)
await tearDownFn()
pendingTeardown.splice(pendingTeardown.indexOf(tearDownFn), 1)
}

interface ElementHandleExt extends ElementHandle {
getComputedCss(prop: string): Promise<string>
text(): Promise<string>
Expand All @@ -38,7 +50,6 @@ export class Playwright extends BrowserInterface {
private eventCallbacks: Record<Event, Set<(...args: any[]) => void>> = {
request: new Set(),
}

private async initContextTracing(
url: string,
page: Page,
Expand All @@ -50,7 +61,7 @@ export class Playwright extends BrowserInterface {

try {
// Clean up if any previous traces are still active
await this.teardownTracing()
await teardown(this.teardownTracing.bind(this))

await context.tracing.start({
screenshots: true,
Expand All @@ -60,7 +71,7 @@ export class Playwright extends BrowserInterface {
this.activeTrace = encodeURIComponent(url)

page.on('close', async () => {
await this.teardownTracing()
await teardown(this.teardownTracing.bind(this))
})
} catch (e) {
this.activeTrace = undefined
Expand All @@ -87,6 +98,7 @@ export class Playwright extends BrowserInterface {
path: traceOutputPath,
})
} catch (e) {
require('console').warn('Failed to teardown playwright tracing', e)
} finally {
this.activeTrace = undefined
}
Expand Down Expand Up @@ -128,16 +140,14 @@ export class Playwright extends BrowserInterface {
if (browser) {
if (contextHasJSEnabled !== javaScriptEnabled) {
// If we have switched from having JS enable/disabled we need to recreate the context.
await teardown(this.teardownTracing.bind(this))
await context?.close()
context = await browser.newContext({
locale,
javaScriptEnabled,
ignoreHTTPSErrors,
...device,
})
context.once('close', async () => {
await this.teardownTracing()
})
contextHasJSEnabled = javaScriptEnabled
}
return
Expand All @@ -150,9 +160,6 @@ export class Playwright extends BrowserInterface {
ignoreHTTPSErrors,
...device,
})
context.once('close', async () => {
await this.teardownTracing()
})
contextHasJSEnabled = javaScriptEnabled
}

Expand Down