From 27172cf7caf5af6d8f2ee6f1cf6cfb0922325147 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Sat, 22 Nov 2025 12:43:52 +0100 Subject: [PATCH] concurrent tabs test works --- PLAYWRITER_AGENTS.md | 12 ++ playwriter/relay-server.log | 225 ------------------------------------ playwriter/src/mcp.test.ts | 77 ++++++------ playwriter/src/utils.ts | 2 +- 4 files changed, 57 insertions(+), 259 deletions(-) delete mode 100644 playwriter/relay-server.log diff --git a/PLAYWRITER_AGENTS.md b/PLAYWRITER_AGENTS.md index bc18067..cb32a04 100644 --- a/PLAYWRITER_AGENTS.md +++ b/PLAYWRITER_AGENTS.md @@ -37,3 +37,15 @@ curl -sL https://raw.githubusercontent.com/ChromeDevTools/devtools-protocol/mast ``` you can list other files in that folder on github to read more if you need to control things like DOM, performance, etc + +## testing + +run `cd playwriter && pnpm test` to test the extension and mcp and CDP directly in a chrome instance automated. with the extension loaded too. + +the test script will also pass -u to update some inline snapshots used + +you can run singular tests with `-t "testname"` + +each test() block should reset the extension connection to make sure tests are independent. + +NEVER call browser.close() in the tests diff --git a/playwriter/relay-server.log b/playwriter/relay-server.log deleted file mode 100644 index 648c2c3..0000000 --- a/playwriter/relay-server.log +++ /dev/null @@ -1,225 +0,0 @@ -CDP relay server started -Host: 127.0.0.1 -Port: 19988 -Extension endpoint: ws://127.0.0.1:19988/extension -CDP endpoint: ws://127.0.0.1:19988/cdp -Extension connected with clean state -← Extension: Target.attachedToTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 -→ Playwright [ALL]: Target.attachedToTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 -← Extension: Target.attachedToTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 -→ Playwright [ALL]: Target.attachedToTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 -← Extension: Target.attachedToTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 -→ Playwright [ALL]: Target.attachedToTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 -Playwright client connected: E3E15D82C13B4A15D3CF71E44B260036 (1 total) -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Browser.getVersion id=1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.setAutoAttach id=2 -→ Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.attachedToTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 (server-generated) -→ Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.attachedToTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 (server-generated) -→ Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.attachedToTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 (server-generated) -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Browser.setDownloadBehavior id=3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.enable id=4, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.getFrameTree id=5, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Log.enable id=6, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.setLifecycleEventsEnabled id=7, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.enable id=8, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.addScriptToEvaluateOnNewDocument id=9, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Network.enable id=10, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.setAutoAttach id=11, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setFocusEmulationEnabled id=12, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setEmulatedMedia id=13, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.runIfWaitingForDebugger id=14, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.enable id=15, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.getFrameTree id=16, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Log.enable id=17, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.setLifecycleEventsEnabled id=18, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.enable id=19, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.addScriptToEvaluateOnNewDocument id=20, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Network.enable id=21, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.setAutoAttach id=22, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setFocusEmulationEnabled id=23, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setEmulatedMedia id=24, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.runIfWaitingForDebugger id=25, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.enable id=26, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.getFrameTree id=27, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Log.enable id=28, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.setLifecycleEventsEnabled id=29, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.enable id=30, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.addScriptToEvaluateOnNewDocument id=31, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Network.enable id=32, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.setAutoAttach id=33, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setFocusEmulationEnabled id=34, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Emulation.setEmulatedMedia id=35, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.runIfWaitingForDebugger id=36, sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Target.getTargetInfo id=37 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Log.entryAdded sessionId=pw-tab-2 -→ Playwright [ALL]: Log.entryAdded sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.createIsolatedWorld id=38, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.createIsolatedWorld id=39, sessionId=pw-tab-2 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Page.createIsolatedWorld id=40, sessionId=pw-tab-3 -Playwright client connected: 16E435409CFBF47529751B04E9F9021D (2 total) -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Browser.getVersion id=1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Target.setAutoAttach id=2 -→ Playwright [16E435409CFBF47529751B04E9F9021D]: Target.attachedToTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 (server-generated) -→ Playwright [16E435409CFBF47529751B04E9F9021D]: Target.attachedToTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 (server-generated) -→ Playwright [16E435409CFBF47529751B04E9F9021D]: Target.attachedToTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 (server-generated) -← Playwright [16E435409CFBF47529751B04E9F9021D]: Browser.setDownloadBehavior id=3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.enable id=4, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.getFrameTree id=5, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Log.enable id=6, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.setLifecycleEventsEnabled id=7, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.enable id=8, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.addScriptToEvaluateOnNewDocument id=9, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Network.enable id=10, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Target.setAutoAttach id=11, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setFocusEmulationEnabled id=12, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setEmulatedMedia id=13, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.runIfWaitingForDebugger id=14, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.enable id=15, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.getFrameTree id=16, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Log.enable id=17, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.setLifecycleEventsEnabled id=18, sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.enable id=19, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.addScriptToEvaluateOnNewDocument id=20, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Network.enable id=21, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Target.setAutoAttach id=22, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setFocusEmulationEnabled id=23, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setEmulatedMedia id=24, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.runIfWaitingForDebugger id=25, sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.enable id=26, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.getFrameTree id=27, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Log.enable id=28, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.setLifecycleEventsEnabled id=29, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.enable id=30, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.addScriptToEvaluateOnNewDocument id=31, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Network.enable id=32, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Target.setAutoAttach id=33, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setFocusEmulationEnabled id=34, sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-1 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Emulation.setEmulatedMedia id=35, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.runIfWaitingForDebugger id=36, sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Target.getTargetInfo id=37 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.createIsolatedWorld id=38, sessionId=pw-tab-1 -← Extension: Page.lifecycleEvent sessionId=pw-tab-2 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.createIsolatedWorld id=39, sessionId=pw-tab-2 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Page.lifecycleEvent sessionId=pw-tab-3 -→ Playwright [ALL]: Page.lifecycleEvent sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-1 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Page.createIsolatedWorld id=40, sessionId=pw-tab-3 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-2 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-2 -← Extension: Runtime.executionContextCreated sessionId=pw-tab-3 -→ Playwright [ALL]: Runtime.executionContextCreated sessionId=pw-tab-3 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.evaluate id=41, sessionId=pw-tab-1 -← Playwright [E3E15D82C13B4A15D3CF71E44B260036]: Runtime.callFunctionOn id=42, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.evaluate id=41, sessionId=pw-tab-1 -← Playwright [16E435409CFBF47529751B04E9F9021D]: Runtime.callFunctionOn id=42, sessionId=pw-tab-1 -← Extension: Inspector.detached sessionId=pw-tab-3 -→ Playwright [ALL]: Inspector.detached sessionId=pw-tab-3 -← Extension: Inspector.detached sessionId=pw-tab-2 -→ Playwright [ALL]: Inspector.detached sessionId=pw-tab-2 -← Extension: Inspector.detached sessionId=pw-tab-1 -→ Playwright [ALL]: Inspector.detached sessionId=pw-tab-1 -← Extension: Target.detachedFromTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 -→ Playwright [ALL]: Target.detachedFromTarget targetId=16E435409CFBF47529751B04E9F9021D, sessionId=pw-tab-3 -← Extension: Target.detachedFromTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 -→ Playwright [ALL]: Target.detachedFromTarget targetId=E3E15D82C13B4A15D3CF71E44B260036, sessionId=pw-tab-2 -← Extension: Target.detachedFromTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 -→ Playwright [ALL]: Target.detachedFromTarget targetId=37BE179F93AF8207BD1E2372B9C515BA, sessionId=pw-tab-1 -Extension disconnected -Playwright client disconnected: E3E15D82C13B4A15D3CF71E44B260036 (0 remaining) -Playwright client disconnected: 16E435409CFBF47529751B04E9F9021D (0 remaining) diff --git a/playwriter/src/mcp.test.ts b/playwriter/src/mcp.test.ts index 42be7af..cc735f2 100644 --- a/playwriter/src/mcp.test.ts +++ b/playwriter/src/mcp.test.ts @@ -45,6 +45,12 @@ async function killProcessOnPort(port: number): Promise { } } +declare global { + var toggleExtensionForActiveTab: () => Promise<{ isConnected: boolean; state: any }>; + var getExtensionState: () => { connectedTabs: Map }; + var chrome: any; +} + describe('MCP Server Tests', () => { let client: Awaited>['client'] let cleanup: (() => Promise) | null = null @@ -114,7 +120,6 @@ describe('MCP Server Tests', () => { // Connect the tab await serviceWorker.evaluate(async () => { - // @ts-ignore await globalThis.toggleExtensionForActiveTab() }) @@ -289,7 +294,6 @@ describe('MCP Server Tests', () => { // 2. Enable extension on this new tab // Since it's a new page, extension is not connected yet const result = await serviceWorker.evaluate(async () => { - // @ts-ignore return await globalThis.toggleExtensionForActiveTab() }) expect(result.isConnected).toBe(true) @@ -305,11 +309,10 @@ describe('MCP Server Tests', () => { expect(foundPage).toBeDefined() expect(foundPage?.url()).toBe(testUrl) - await directBrowser.close() + // 4. Disable extension on this tab const resultDisabled = await serviceWorker.evaluate(async () => { - // @ts-ignore return await globalThis.toggleExtensionForActiveTab() }) expect(resultDisabled.isConnected).toBe(false) @@ -325,11 +328,10 @@ describe('MCP Server Tests', () => { foundPage = pages.find(p => p.url() === testUrl) expect(foundPage).toBeUndefined() - await directBrowser.close() + // 6. Re-enable extension const resultEnabled = await serviceWorker.evaluate(async () => { - // @ts-ignore return await globalThis.toggleExtensionForActiveTab() }) expect(resultEnabled.isConnected).toBe(true) @@ -351,7 +353,7 @@ describe('MCP Server Tests', () => { expect(foundPage).toBeDefined() expect(foundPage?.url()).toBe(testUrl) - await directBrowser.close() + await page.close() }) it('should maintain connection across reloads and navigation', async () => { @@ -366,7 +368,6 @@ describe('MCP Server Tests', () => { // 2. Enable extension await serviceWorker.evaluate(async () => { - // @ts-ignore await globalThis.toggleExtensionForActiveTab() }) @@ -391,7 +392,7 @@ describe('MCP Server Tests', () => { expect(connectedPage?.url()).toBe(newUrl) expect(await connectedPage?.title()).toContain('Hacker News') - await directBrowser.close() + await page.close() }) @@ -406,7 +407,6 @@ describe('MCP Server Tests', () => { await pageA.bringToFront() await new Promise(resolve => setTimeout(resolve, 500)) await serviceWorker.evaluate(async () => { - // @ts-ignore await globalThis.toggleExtensionForActiveTab() }) @@ -416,49 +416,60 @@ describe('MCP Server Tests', () => { await pageB.bringToFront() await new Promise(resolve => setTimeout(resolve, 500)) await serviceWorker.evaluate(async () => { - // @ts-ignore await globalThis.toggleExtensionForActiveTab() }) // Get target IDs for both const targetIds = await serviceWorker.evaluate(async () => { - // @ts-ignore const state = globalThis.getExtensionState() - // @ts-ignore + const chrome = globalThis.chrome const tabs = await chrome.tabs.query({}) - const tabA = tabs.find(t => t.url?.includes('tab-a')) - const tabB = tabs.find(t => t.url?.includes('tab-b')) + const tabA = tabs.find((t: any) => t.url?.includes('tab-a')) + const tabB = tabs.find((t: any) => t.url?.includes('tab-b')) return { - // @ts-ignore idA: state.connectedTabs.get(tabA?.id)?.targetId, - // @ts-ignore idB: state.connectedTabs.get(tabB?.id)?.targetId } }) - expect(targetIds.idA).toBeTruthy() - expect(targetIds.idB).toBeTruthy() + expect(targetIds).toMatchInlineSnapshot({ + idA: expect.any(String), + idB: expect.any(String) + }, ` + { + "idA": Any, + "idB": Any, + } + `) expect(targetIds.idA).not.toBe(targetIds.idB) // Verify independent connections - const browserA = await chromium.connectOverCDP(getCdpUrl({ clientId: targetIds.idA })) - const browserB = await chromium.connectOverCDP(getCdpUrl({ clientId: targetIds.idB })) + const browser = await chromium.connectOverCDP(getCdpUrl()) - const titleA = await browserA.contexts()[0].pages()[0].title() - const titleB = await browserB.contexts()[0].pages()[0].title() + const pages = browser.contexts()[0].pages() - // Both are "Example Domain" but we verified they are distinct pages via target ID - expect(titleA).toBe('Example Domain') - expect(titleB).toBe('Example Domain') + const results = await Promise.all(pages.map(async (p) => ({ + url: p.url(), + title: await p.title() + }))) - // Verify URL to be sure - const urlA = browserA.contexts()[0].pages()[0].url() - const urlB = browserB.contexts()[0].pages()[0].url() - expect(urlA).toContain('tab-a') - expect(urlB).toContain('tab-b') + expect(results).toMatchInlineSnapshot(` + [ + { + "title": "", + "url": "about:blank", + }, + { + "title": "Example Domain", + "url": "https://example.com/tab-a", + }, + { + "title": "Example Domain", + "url": "https://example.com/tab-b", + }, + ] + `) - await browserA.close() - await browserB.close() await pageA.close() await pageB.close() }) diff --git a/playwriter/src/utils.ts b/playwriter/src/utils.ts index dfbf33e..079e23b 100644 --- a/playwriter/src/utils.ts +++ b/playwriter/src/utils.ts @@ -1,4 +1,4 @@ export function getCdpUrl({ port = 19988, host = '127.0.0.1', clientId }: { port?: number; host?: string; clientId?: string } = {}) { - const id = clientId || Date.now().toString() + const id = clientId || Math.random().toString(36).substring(2, 15) return `ws://${host}:${port}/cdp/${id}` }