Skip to content
Closed
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
4 changes: 4 additions & 0 deletions packages/playwright-core/src/remote/playwrightServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ export class PlaywrightServer {
return {
preLaunchedBrowser: browser,
denyLaunch: true,
// The browser is shared between sequential connections, so that
// pages created by one connection can be kept alive after it drops
// (e.g. to debug or record them) and picked up by the next one.
sharedBrowser: true,
dispose: async () => {
// Don't close the pages so that user could debug them,
// but close all the empty contexts to clean up.
Expand Down
32 changes: 32 additions & 0 deletions tests/library/debug-controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,38 @@ test('should reset routes before reuse', async ({ server, connectedBrowserFactor
await browser2.close();
});

test('should keep pages alive when the test connection drops', async ({ backend, connectedBrowserFactory }, testInfo) => {
testInfo.annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/37822' });

const pageCounts: number[] = [];
backend.on('stateChanged', params => pageCounts.push(params.pageCount));
await backend.setReportStateChanged({ enabled: true }, undefined);

// Emulates "Debug Test": the test process creates its own context and page.
const browser1 = await connectedBrowserFactory();
const context1 = await browser1.newContext();
const page1 = await context1.newPage();
await page1.setContent('<button>Submit</button>');
const contextEmpty = await browser1.newContext();
expect(contextEmpty.pages()).toHaveLength(0);
await expect.poll(() => pageCounts[pageCounts.length - 1]).toBe(1);

// Emulates the user hitting "Stop" during debugging: the test process is
// killed and its connection drops without a graceful close.
await browser1.close();

// The next connection (e.g. "Record at cursor") still sees the page alive:
// contexts with pages are kept around for debugging, empty ones are cleaned up.
const browser2 = await connectedBrowserFactory();
const contexts = browser2.contexts();
expect(contexts).toHaveLength(1);
const page = contexts[0].pages()[0];
await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible();

// The backend never reported the debugged page as closed.
expect(pageCounts).not.toContain(0);
});

test('should highlight inside iframe', async ({ backend, connectedBrowser }, testInfo) => {
testInfo.annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/33146' });

Expand Down