From 24db52924767b806aefe8738f167450c5a2e4f9d Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Thu, 12 Mar 2026 16:30:44 +0100 Subject: [PATCH] test/docs: add cookie workaround for issue #66 and skip failing repro Add a focused regression test that reads page cookies via Network.getCookies through a page CDP session in extension mode. Keep the Storage.getCookies issue #66 reproduction test skipped with an explicit note that Storage.getCookies is unsupported in this relay path and document the required workaround in skill.md. --- playwriter/src/relay-core.test.ts | 110 ++++++++++++++++++++++++++++++ playwriter/src/skill.md | 10 +++ 2 files changed, 120 insertions(+) diff --git a/playwriter/src/relay-core.test.ts b/playwriter/src/relay-core.test.ts index 26248a6..94c4b90 100644 --- a/playwriter/src/relay-core.test.ts +++ b/playwriter/src/relay-core.test.ts @@ -312,6 +312,116 @@ describe('Relay Core Tests', () => { expect(result.content).toBeDefined() }, 30000) + // Repro test for https://github.com/remorses/playwriter/issues/66. + // Current limitation: extension-mode routing does not support root-session + // Storage.getCookies in playwriter. MUST use Network.getCookies via page CDP + // session instead (see test below), so this repro stays skipped. + it.skip('should reproduce page.route failure in MCP execute path (issue #66)', async () => { + const server = await createSimpleServer({ + routes: { + '/': 'route issue repro', + '/api/data': '{"ok":true}', + }, + }) + + try { + const result = await client.callTool({ + name: 'execute', + arguments: { + code: js` + const newPage = await context.newPage(); + state.issue66Page = newPage; + await newPage.goto('${server.baseUrl}', { waitUntil: 'domcontentloaded' }); + + let routeFetchError = null; + await newPage.route('**/api/**', async (route) => { + try { + const response = await route.fetch(); + await route.fulfill({ response }); + } catch (error) { + routeFetchError = error instanceof Error ? error.message : String(error); + await route.abort(); + } + }); + + await newPage.evaluate(async () => { + await fetch('/api/data').catch(() => null); + }); + + return { routeFetchError }; + `, + }, + }) + + const resultWithContent = result as { content?: unknown } + const content = Array.isArray(resultWithContent.content) ? resultWithContent.content : [] + const firstContent = content[0] + const output = + typeof firstContent === 'object' && firstContent !== null && 'text' in firstContent + ? String((firstContent as { text?: unknown }).text ?? '') + : '' + expect(output).toContain('routeFetchError') + expect(output).toContain('Storage.getCookies') + expect(output).toContain('No tab found for method Storage.getCookies') + } finally { + try { + await client.callTool({ + name: 'execute', + arguments: { + code: js` + if (state.issue66Page && !state.issue66Page.isClosed()) { + await state.issue66Page.close(); + } + delete state.issue66Page; + `, + }, + }) + } catch { + // Ignore cleanup failure if MCP disconnected due to the repro. + } + await server.close() + } + }, 30000) + + it('should read cookies via Network.getCookies through page CDP session', async () => { + const browserContext = getBrowserContext() + const serviceWorker = await getExtensionServiceWorker(browserContext) + + const server = await createSimpleServer({ + routes: { + '/': 'cookies test', + }, + }) + + const page = await browserContext.newPage() + try { + await page.goto(server.baseUrl, { waitUntil: 'domcontentloaded' }) + await page.bringToFront() + + await serviceWorker.evaluate(async () => { + await globalThis.toggleExtensionForActiveTab() + }) + + await new Promise((r) => { + setTimeout(r, 200) + }) + + await page.evaluate(() => { + document.cookie = 'issue66=ok; path=/' + }) + + const cdpSession = await getCDPSessionForPage({ page }) + const cookiesResult = await cdpSession.send('Network.getCookies', { urls: [page.url()] }) + const cookie = cookiesResult.cookies.find((value) => { + return value.name === 'issue66' + }) + expect(cookie?.value).toBe('ok') + } finally { + await page.close() + await server.close() + } + }, 30000) + it('should show extension as connected for pages created via newPage()', async () => { const browserContext = getBrowserContext() const serviceWorker = await getExtensionServiceWorker(browserContext) diff --git a/playwriter/src/skill.md b/playwriter/src/skill.md index 0f288de..944e8cb 100644 --- a/playwriter/src/skill.md +++ b/playwriter/src/skill.md @@ -533,6 +533,16 @@ const data = await state.page.evaluate(async (url) => { }, 'https://example.com/protected/resource') ``` +**Read page cookies via CDP** - use `Network.getCookies` on the page CDP session: + +```js +const cdp = await getCDPSession({ page: state.page }) +const { cookies } = await cdp.send('Network.getCookies', { urls: [state.page.url()] }) +console.log(cookies) +``` + +MUST use this for page-scoped cookies in extension mode. `Storage.getCookies` is a root-session command and will fail in playwriter. + **Downloading large data** - console output truncates large strings. Trigger a browser download instead: ```js