diff --git a/extension/src/background.ts b/extension/src/background.ts index 50cc074..3fbf024 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -339,6 +339,17 @@ async function ensureConnection(): Promise { } logger.debug('Removed tab from _connectedTabs map') }, + onTabAttached: (tabId, targetId) => { + // logger.debug('=== Tab attached callback for tab:', tabId, '===') + useExtensionStore.setState((state) => { + const newTabs = new Map(state.connectedTabs) + newTabs.set(tabId, { + targetId, + state: 'connected', + }) + return { connectedTabs: newTabs, connectionState: 'connected' } + }) + }, }) useExtensionStore.setState({ connection: newConnection }) diff --git a/extension/src/relayConnection.ts b/extension/src/relayConnection.ts index 5a98892..a664d1b 100644 --- a/extension/src/relayConnection.ts +++ b/extension/src/relayConnection.ts @@ -95,15 +95,18 @@ export class RelayConnection { private _closed = false; private _onCloseCallback?: (reason: string, code: number) => void; private _onTabDetachedCallback?: (tabId: number, reason: `${chrome.debugger.DetachReason}`) => void + private _onTabAttachedCallback?: (tabId: number, targetId: string) => void - constructor({ ws, onClose, onTabDetached }: { + constructor({ ws, onClose, onTabDetached, onTabAttached }: { ws: WebSocket; onClose?: (reason: string, code: number) => void; onTabDetached?: (tabId: number, reason: `${chrome.debugger.DetachReason}`) => void; + onTabAttached?: (tabId: number, targetId: string) => void; }) { this._ws = ws; this._onCloseCallback = onClose; this._onTabDetachedCallback = onTabDetached; + this._onTabAttachedCallback = onTabAttached; this._ws.onmessage = async (event: MessageEvent) => { let message: ExtensionCommandMessage; try { @@ -208,6 +211,7 @@ export class RelayConnection { }); logger.debug('Tab attached successfully:', tabId, 'sessionId:', sessionId, 'targetId:', targetInfo.targetId); + this._onTabAttachedCallback?.(tabId, targetInfo.targetId); return targetInfo; } diff --git a/package.json b/package.json index 6aec61e..c7f162f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "root", "private": true, "scripts": { - "test": "NODE_ENV=test vitest", + "test": "pnpm -r test", "watch": "pnpm -r watch", "build": "pnpm -r build", "agents.md": "agentsdotmd ./PLAYWRITER_AGENTS.md core.md typescript.md pnpm.md vitest.md changelog.md docs-writing.md github.md playwright.md zod.md gitchamber.md", diff --git a/playwriter/src/mcp.test.ts b/playwriter/src/mcp.test.ts index 63c237e..d063a9f 100644 --- a/playwriter/src/mcp.test.ts +++ b/playwriter/src/mcp.test.ts @@ -203,6 +203,55 @@ describe('MCP Server Tests', () => { expect(result.content).toBeDefined() }, 30000) + it('should show extension as connected for pages created via newPage()', async () => { + if (!browserContext) throw new Error('Browser not initialized') + const serviceWorker = await getExtensionServiceWorker(browserContext) + + // Create a page via MCP (which uses context.newPage()) + await client.callTool({ + name: 'execute', + arguments: { + code: js` + const newPage = await context.newPage(); + state.testPage = newPage; + await newPage.goto('https://example.com/mcp-test'); + return newPage.url(); + `, + }, + }) + + // Get extension state to verify the page is marked as connected + const extensionState = await serviceWorker.evaluate(async () => { + const state = globalThis.getExtensionState() + const chrome = globalThis.chrome + const tabs = await chrome.tabs.query({}) + const testTab = tabs.find((t: any) => t.url?.includes('mcp-test')) + return { + connected: !!testTab && state.connectedTabs.has(testTab.id), + tabId: testTab?.id, + tabInfo: testTab ? state.connectedTabs.get(testTab.id) : null, + connectionState: state.connectionState + } + }) + + expect(extensionState.connected).toBe(true) + expect(extensionState.tabInfo?.state).toBe('connected') + expect(extensionState.connectionState).toBe('connected') + + // Clean up + await client.callTool({ + name: 'execute', + arguments: { + code: js` + if (state.testPage) { + await state.testPage.close(); + delete state.testPage; + } + `, + }, + }) + }, 30000) + it('should get accessibility snapshot of hacker news', async () => { await client.callTool({ name: 'execute',