diff --git a/tests/capabilities.spec.ts b/tests/capabilities.spec.ts index 6daf467..83af532 100644 --- a/tests/capabilities.spec.ts +++ b/tests/capabilities.spec.ts @@ -77,7 +77,7 @@ test('test vision tool list', async ({ visionClient }) => { }); test('test capabilities', async ({ startClient }) => { - const client = await startClient({ + const { client } = await startClient({ args: ['--caps="core"'], }); const { tools } = await client.listTools(); diff --git a/tests/cdp.spec.ts b/tests/cdp.spec.ts index 23f2cea..a5a58c2 100644 --- a/tests/cdp.spec.ts +++ b/tests/cdp.spec.ts @@ -18,7 +18,7 @@ import { test, expect } from './fixtures.js'; test('cdp server', async ({ cdpServer, startClient, server }) => { await cdpServer.start(); - const client = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); expect(await client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, @@ -27,7 +27,7 @@ test('cdp server', async ({ cdpServer, startClient, server }) => { test('cdp server reuse tab', async ({ cdpServer, startClient, server }) => { const browserContext = await cdpServer.start(); - const client = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); const [page] = browserContext.pages(); await page.goto(server.HELLO_WORLD); @@ -58,7 +58,7 @@ test('cdp server reuse tab', async ({ cdpServer, startClient, server }) => { }); test('should throw connection error and allow re-connecting', async ({ cdpServer, startClient, server }) => { - const client = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); server.setContent('/', ` Title diff --git a/tests/config.spec.ts b/tests/config.spec.ts index 03c3d81..4478347 100644 --- a/tests/config.spec.ts +++ b/tests/config.spec.ts @@ -33,7 +33,7 @@ test('config user data dir', async ({ startClient, server }, testInfo) => { const configPath = testInfo.outputPath('config.json'); await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); - const client = await startClient({ args: ['--config', configPath] }); + const { client } = await startClient({ args: ['--config', configPath] }); expect(await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, @@ -54,7 +54,7 @@ test.describe(() => { const configPath = testInfo.outputPath('config.json'); await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2)); - const client = await startClient({ args: ['--config', configPath] }); + const { client } = await startClient({ args: ['--config', configPath] }); expect(await client.callTool({ name: 'browser_navigate', arguments: { url: 'data:text/html,' }, diff --git a/tests/device.spec.ts b/tests/device.spec.ts index 3fe2933..32ceecb 100644 --- a/tests/device.spec.ts +++ b/tests/device.spec.ts @@ -17,7 +17,7 @@ import { test, expect } from './fixtures.js'; test('--device should work', async ({ startClient, server }) => { - const client = await startClient({ + const { client } = await startClient({ args: ['--device', 'iPhone 15'], }); diff --git a/tests/files.spec.ts b/tests/files.spec.ts index 600d54f..3653bca 100644 --- a/tests/files.spec.ts +++ b/tests/files.spec.ts @@ -101,7 +101,7 @@ The tool "browser_file_upload" can only be used when there is related modal stat }); test('clicking on download link emits download', async ({ startClient, server }, testInfo) => { - const client = await startClient({ + const { client } = await startClient({ config: { outputDir: testInfo.outputPath('output') }, }); @@ -125,7 +125,7 @@ test('clicking on download link emits download', async ({ startClient, server }, }); test('navigating to download link emits download', async ({ startClient, server, mcpBrowser }, testInfo) => { - const client = await startClient({ + const { client } = await startClient({ config: { outputDir: testInfo.outputPath('output') }, }); diff --git a/tests/fixtures.ts b/tests/fixtures.ts index 598af17..e207d7e 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -40,7 +40,7 @@ type CDPServer = { type TestFixtures = { client: Client; visionClient: Client; - startClient: (options?: { clientName?: string, args?: string[], config?: Config }) => Promise; + startClient: (options?: { clientName?: string, args?: string[], config?: Config }) => Promise<{ client: Client, stderr: () => string }>; wsEndpoint: string; cdpServer: CDPServer; server: TestServer; @@ -55,11 +55,13 @@ type WorkerFixtures = { export const test = baseTest.extend({ client: async ({ startClient }, use) => { - await use(await startClient()); + const { client } = await startClient(); + await use(client); }, visionClient: async ({ startClient }, use) => { - await use(await startClient({ args: ['--vision'] })); + const { client } = await startClient({ args: ['--vision'] }); + await use(client); }, startClient: async ({ mcpHeadless, mcpBrowser, mcpMode }, use, testInfo) => { @@ -85,9 +87,13 @@ export const test = baseTest.extend( client = new Client({ name: options?.clientName ?? 'test', version: '1.0.0' }); const transport = createTransport(args, mcpMode); + let stderr = ''; + transport.stderr?.on('data', data => { + stderr += data.toString(); + }); await client.connect(transport); await client.ping(); - return client; + return { client, stderr: () => stderr }; }); await client?.close(); @@ -168,7 +174,13 @@ function createTransport(args: string[], mcpMode: TestOptions['mcpMode']) { command: 'node', args: [path.join(path.dirname(__filename), '../cli.js'), ...args], cwd: path.join(path.dirname(__filename), '..'), - env: process.env as Record, + stderr: 'pipe', + env: { + ...process.env, + DEBUG: 'pw:mcp:test', + DEBUG_COLORS: '0', + DEBUG_HIDE_DATE: '1', + }, }); } @@ -225,3 +237,7 @@ export const expect = baseExpect.extend({ }; }, }); + +export function formatOutput(output: string): string[] { + return output.split('\n').map(line => line.replace(/^pw:mcp:test /, '').replace(/test-results.*/, '').trim()).filter(Boolean); +} diff --git a/tests/launch.spec.ts b/tests/launch.spec.ts index 713e412..f0ad4b2 100644 --- a/tests/launch.spec.ts +++ b/tests/launch.spec.ts @@ -16,9 +16,10 @@ import fs from 'fs'; -import { test, expect } from './fixtures.js'; +import { test, expect, formatOutput } from './fixtures.js'; -test('test reopen browser', async ({ client, server }) => { +test('test reopen browser', async ({ startClient, server }) => { + const { client, stderr } = await startClient(); await client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, @@ -32,10 +33,31 @@ test('test reopen browser', async ({ client, server }) => { name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, })).toContainTextContent(`- generic [ref=e1]: Hello, world!`); + + await client.close(); + + if (process.platform === 'win32') + return; + + await expect.poll(() => formatOutput(stderr()), { timeout: 0 }).toEqual([ + 'create context', + 'create browser context (persistent)', + 'lock user data dir', + 'close context', + 'close browser context (persistent)', + 'release user data dir', + 'close browser context complete (persistent)', + 'create browser context (persistent)', + 'lock user data dir', + 'close context', + 'close browser context (persistent)', + 'release user data dir', + 'close browser context complete (persistent)', + ]); }); test('executable path', async ({ startClient, server }) => { - const client = await startClient({ args: [`--executable-path=bogus`] }); + const { client } = await startClient({ args: [`--executable-path=bogus`] }); const response = await client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, @@ -53,7 +75,7 @@ test('persistent context', async ({ startClient, server }) => { `, 'text/html'); - const client = await startClient(); + const { client } = await startClient(); const response = await client.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, @@ -66,7 +88,7 @@ test('persistent context', async ({ startClient, server }) => { name: 'browser_close', }); - const client2 = await startClient(); + const { client: client2 } = await startClient(); const response2 = await client2.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, @@ -85,18 +107,18 @@ test('isolated context', async ({ startClient, server }) => { `, 'text/html'); - const client = await startClient({ args: [`--isolated`] }); - const response = await client.callTool({ + const { client: client1 } = await startClient({ args: [`--isolated`] }); + const response = await client1.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, }); expect(response).toContainTextContent(`Storage: NO`); - await client.callTool({ + await client1.callTool({ name: 'browser_close', }); - const client2 = await startClient({ args: [`--isolated`] }); + const { client: client2 } = await startClient({ args: [`--isolated`] }); const response2 = await client2.callTool({ name: 'browser_navigate', arguments: { url: server.PREFIX }, @@ -123,7 +145,7 @@ test('isolated context with storage state', async ({ startClient, server }, test `, 'text/html'); - const client = await startClient({ args: [ + const { client } = await startClient({ args: [ `--isolated`, `--storage-state=${storageStatePath}`, ] }); diff --git a/tests/pdf.spec.ts b/tests/pdf.spec.ts index 7b599c8..8af4667 100644 --- a/tests/pdf.spec.ts +++ b/tests/pdf.spec.ts @@ -19,7 +19,7 @@ import fs from 'fs'; import { test, expect } from './fixtures.js'; test('save as pdf unavailable', async ({ startClient, server }) => { - const client = await startClient({ args: ['--caps="no-pdf"'] }); + const { client } = await startClient({ args: ['--caps="no-pdf"'] }); await client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, @@ -31,7 +31,7 @@ test('save as pdf unavailable', async ({ startClient, server }) => { }); test('save as pdf', async ({ startClient, mcpBrowser, server }, testInfo) => { - const client = await startClient({ + const { client } = await startClient({ config: { outputDir: testInfo.outputPath('output') }, }); @@ -51,7 +51,7 @@ test('save as pdf', async ({ startClient, mcpBrowser, server }, testInfo) => { test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); test.skip(!!mcpBrowser && !['chromium', 'chrome', 'msedge'].includes(mcpBrowser), 'Save as PDF is only supported in Chromium.'); - const client = await startClient({ + const { client } = await startClient({ config: { outputDir }, }); diff --git a/tests/request-blocking.spec.ts b/tests/request-blocking.spec.ts index 7d0a016..21a7185 100644 --- a/tests/request-blocking.spec.ts +++ b/tests/request-blocking.spec.ts @@ -37,7 +37,7 @@ test('default to allow all', async ({ server, client }) => { }); test('blocked works', async ({ startClient }) => { - const client = await startClient({ + const { client } = await startClient({ args: ['--blocked-origins', 'microsoft.com;example.com;playwright.dev'] }); const result = await fetchPage(client, 'https://example.com/'); @@ -46,7 +46,7 @@ test('blocked works', async ({ startClient }) => { test('allowed works', async ({ server, startClient }) => { server.setContent('/ppp', 'content:PPP', 'text/html'); - const client = await startClient({ + const { client } = await startClient({ args: ['--allowed-origins', `microsoft.com;${new URL(server.PREFIX).host};playwright.dev`] }); const result = await fetchPage(client, server.PREFIX + 'ppp'); @@ -54,7 +54,7 @@ test('allowed works', async ({ server, startClient }) => { }); test('blocked takes precedence', async ({ startClient }) => { - const client = await startClient({ + const { client } = await startClient({ args: [ '--blocked-origins', 'example.com', '--allowed-origins', 'example.com', @@ -65,7 +65,7 @@ test('blocked takes precedence', async ({ startClient }) => { }); test('allowed without blocked blocks all non-explicitly specified origins', async ({ startClient }) => { - const client = await startClient({ + const { client } = await startClient({ args: ['--allowed-origins', 'playwright.dev'], }); const result = await fetchPage(client, 'https://example.com/'); @@ -74,7 +74,7 @@ test('allowed without blocked blocks all non-explicitly specified origins', asyn test('blocked without allowed allows non-explicitly specified origins', async ({ server, startClient }) => { server.setContent('/ppp', 'content:PPP', 'text/html'); - const client = await startClient({ + const { client } = await startClient({ args: ['--blocked-origins', 'example.com'], }); const result = await fetchPage(client, server.PREFIX + 'ppp'); diff --git a/tests/screenshot.spec.ts b/tests/screenshot.spec.ts index d771359..b83e10a 100644 --- a/tests/screenshot.spec.ts +++ b/tests/screenshot.spec.ts @@ -19,7 +19,7 @@ import fs from 'fs'; import { test, expect } from './fixtures.js'; test('browser_take_screenshot (viewport)', async ({ startClient, server }, testInfo) => { - const client = await startClient({ + const { client } = await startClient({ config: { outputDir: testInfo.outputPath('output') }, }); expect(await client.callTool({ @@ -45,7 +45,7 @@ test('browser_take_screenshot (viewport)', async ({ startClient, server }, testI }); test('browser_take_screenshot (element)', async ({ startClient, server }, testInfo) => { - const client = await startClient({ + const { client } = await startClient({ config: { outputDir: testInfo.outputPath('output') }, }); expect(await client.callTool({ @@ -76,7 +76,7 @@ test('browser_take_screenshot (element)', async ({ startClient, server }, testIn test('--output-dir should work', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); - const client = await startClient({ + const { client } = await startClient({ config: { outputDir }, }); expect(await client.callTool({ @@ -98,7 +98,7 @@ for (const raw of [undefined, true]) { test(`browser_take_screenshot (raw: ${raw})`, async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); const ext = raw ? 'png' : 'jpeg'; - const client = await startClient({ + const { client } = await startClient({ config: { outputDir }, }); expect(await client.callTool({ @@ -138,7 +138,7 @@ for (const raw of [undefined, true]) { test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); - const client = await startClient({ + const { client } = await startClient({ config: { outputDir }, }); expect(await client.callTool({ @@ -174,7 +174,7 @@ test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient, test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); - const client = await startClient({ + const { client } = await startClient({ config: { outputDir, imageResponses: 'omit', @@ -205,7 +205,7 @@ test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, serv test('browser_take_screenshot (cursor)', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); - const client = await startClient({ + const { client } = await startClient({ clientName: 'cursor:vscode', config: { outputDir }, }); diff --git a/tests/tabs.spec.ts b/tests/tabs.spec.ts index 3242aa4..08afd63 100644 --- a/tests/tabs.spec.ts +++ b/tests/tabs.spec.ts @@ -141,7 +141,7 @@ test('reuse first tab when navigating', async ({ startClient, cdpServer, server const browserContext = await cdpServer.start(); const pages = browserContext.pages(); - const client = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); + const { client } = await startClient({ args: [`--cdp-endpoint=${cdpServer.endpoint}`] }); await client.callTool({ name: 'browser_navigate', arguments: { url: server.HELLO_WORLD }, diff --git a/tests/trace.spec.ts b/tests/trace.spec.ts index b3e78b7..13e9d4f 100644 --- a/tests/trace.spec.ts +++ b/tests/trace.spec.ts @@ -22,7 +22,7 @@ import { test, expect } from './fixtures.js'; test('check that trace is saved', async ({ startClient, server }, testInfo) => { const outputDir = testInfo.outputPath('output'); - const client = await startClient({ + const { client } = await startClient({ args: ['--save-trace', `--output-dir=${outputDir}`], });