From 6b6ac216f2a7d8412d96411e2b80191551e7329c Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Sat, 22 Nov 2025 23:49:40 +0100 Subject: [PATCH] add disconnectEverything function. add new test where playwright sees existing page tab --- extension/src/background.ts | 25 ++++++++++++++++ playwriter/src/mcp.test.ts | 58 +++++++++++++++++++++++++++++++++---- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/extension/src/background.ts b/extension/src/background.ts index 7b1bf5f..4082539 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -68,6 +68,30 @@ async function toggleExtensionForActiveTab(): Promise<{ isConnected: boolean; st return { isConnected, state } } +// @ts-ignore +globalThis.disconnectEverything = disconnectEverything + +async function disconnectEverything() { + const { connectedTabs, connection } = useExtensionStore.getState() + + // Disconnect all tabs + for (const tabId of connectedTabs.keys()) { + await disconnectTab(tabId) + } + + // Force close connection if it still exists + const state = useExtensionStore.getState() + if (state.connection) { + state.connection.close('Manual full disconnect') + useExtensionStore.setState({ + connection: undefined, + connectionState: 'disconnected', + connectedTabs: new Map(), + errorText: undefined + }) + } +} + // @ts-ignore globalThis.getExtensionState = () => useExtensionStore.getState() @@ -75,6 +99,7 @@ declare global { var state: typeof useExtensionStore var toggleExtensionForActiveTab: () => Promise<{ isConnected: boolean; state: ExtensionState }> var getExtensionState: () => ExtensionState + var disconnectEverything: () => Promise } async function resetDebugger() { diff --git a/playwriter/src/mcp.test.ts b/playwriter/src/mcp.test.ts index c3b03a9..8c2e6b8 100644 --- a/playwriter/src/mcp.test.ts +++ b/playwriter/src/mcp.test.ts @@ -39,11 +39,11 @@ function js(strings: TemplateStringsArray, ...values: any[]): string { async function killProcessOnPort(port: number): Promise { try { const { stdout } = await execAsync(`lsof -ti:${port}`) - const pid = stdout.trim() - if (pid) { - await execAsync(`kill -9 ${pid}`) - console.log(`Killed process ${pid} on port ${port}`) - await new Promise((resolve) => setTimeout(resolve, 500)) + const pids = stdout.trim().split('\n').filter(Boolean) + if (pids.length > 0) { + await execAsync(`kill -9 ${pids.join(' ')}`) + console.log(`Killed processes ${pids.join(', ')} on port ${port}`) + await new Promise((resolve) => setTimeout(resolve, 1000)) } } catch (error) { // No process running on port or already killed @@ -53,6 +53,7 @@ async function killProcessOnPort(port: number): Promise { declare global { var toggleExtensionForActiveTab: () => Promise<{ isConnected: boolean; state: any }>; var getExtensionState: () => { connectedTabs: Map }; + var disconnectEverything: () => Promise; var chrome: any; } @@ -116,6 +117,16 @@ describe('MCP Server Tests', () => { // Wait for service worker and connect const serviceWorker = await getExtensionServiceWorker(browserContext) + // Wait for extension to initialize global functions + for (let i = 0; i < 50; i++) { + const isReady = await serviceWorker.evaluate(() => { + // @ts-ignore + return typeof globalThis.toggleExtensionForActiveTab === 'function' + }) + if (isReady) break + await new Promise(r => setTimeout(r, 100)) + } + // Create a page to attach to const page = await browserContext.newPage() await page.goto('about:blank') @@ -662,6 +673,43 @@ describe('MCP Server Tests', () => { await pageB.close() }) + it('should show correct url when enabling extension after navigation', async () => { + if (!browserContext) throw new Error('Browser not initialized') + const serviceWorker = await getExtensionServiceWorker(browserContext) + + // 1. Ensure clean state (disconnected) + await serviceWorker.evaluate(async () => { + await globalThis.disconnectEverything() + }) + + // 2. Open page and navigate + const page = await browserContext.newPage() + const targetUrl = 'https://example.com/late-enable' + await page.goto(targetUrl) + await page.bringToFront() + + // Wait for load + await page.waitForLoadState('networkidle') + + // 3. Enable extension + await serviceWorker.evaluate(async () => { + await globalThis.toggleExtensionForActiveTab() + }) + + // 4. Verify via CDP + const browser = await chromium.connectOverCDP(getCdpUrl()) + // Wait for sync + await new Promise(r => setTimeout(r, 1000)) + + const cdpPage = browser.contexts()[0].pages().find(p => p.url() === targetUrl) + + expect(cdpPage).toBeDefined() + expect(cdpPage?.url()).toBe(targetUrl) + + await browser.close() + await page.close() + }) + })