From 57b3c14276a00f9e8887eb304c9ffc90b2aa5c7a Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 8 May 2025 17:02:09 -0700 Subject: [PATCH] chore: only reset network log upon explicit navigation (#377) Fixes https://github.com/microsoft/playwright-mcp/issues/376 --- src/tab.ts | 6 +-- tests/cdp.spec.ts | 22 ++++---- tests/config.spec.ts | 11 ++-- tests/console.spec.ts | 14 +++++- tests/core.spec.ts | 91 +++++++++++++++++++++++----------- tests/dialogs.spec.ts | 64 ++++++++++++++++-------- tests/files.spec.ts | 22 ++++---- tests/launch.spec.ts | 16 ++---- tests/network.spec.ts | 15 +++--- tests/pdf.spec.ts | 21 +++----- tests/request-blocking.spec.ts | 21 +++----- tests/screenshot.spec.ts | 68 ++++++++++--------------- tests/tabs.spec.ts | 8 +-- tests/testserver/index.ts | 28 +++++++++-- 14 files changed, 228 insertions(+), 179 deletions(-) diff --git a/src/tab.ts b/src/tab.ts index b34873d..511702f 100644 --- a/src/tab.ts +++ b/src/tab.ts @@ -35,10 +35,6 @@ export class Tab { page.on('console', event => this._consoleMessages.push(event)); page.on('request', request => this._requests.set(request, null)); page.on('response', response => this._requests.set(response.request(), response)); - page.on('framenavigated', frame => { - if (!frame.parentFrame()) - this._clearCollectedArtifacts(); - }); page.on('close', () => this._onClose()); page.on('filechooser', chooser => { this.context.setModalState({ @@ -66,6 +62,8 @@ export class Tab { } async navigate(url: string) { + this._clearCollectedArtifacts(); + const downloadEvent = this.page.waitForEvent('download').catch(() => {}); try { await this.page.goto(url, { waitUntil: 'domcontentloaded' }); diff --git a/tests/cdp.spec.ts b/tests/cdp.spec.ts index 0a047cb..860b7c5 100644 --- a/tests/cdp.spec.ts +++ b/tests/cdp.spec.ts @@ -16,13 +16,11 @@ import { test, expect } from './fixtures.js'; -test('cdp server', async ({ cdpEndpoint, startClient }) => { +test('cdp server', async ({ cdpEndpoint, startClient, server }) => { const client = await startClient({ args: [`--cdp-endpoint=${await cdpEndpoint()}`] }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, })).toContainTextContent(`- generic [ref=s1e2]: Hello, world!`); }); @@ -55,20 +53,22 @@ test('cdp server reuse tab', async ({ cdpEndpoint, startClient }) => { `); }); -test('should throw connection error and allow re-connecting', async ({ cdpEndpoint, startClient }) => { +test('should throw connection error and allow re-connecting', async ({ cdpEndpoint, startClient, server }) => { const port = 3200 + test.info().parallelIndex; const client = await startClient({ args: [`--cdp-endpoint=http://localhost:${port}`] }); + + server.setContent('/', ` + Title + Hello, world! + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent(`Error: browserType.connectOverCDP: connect ECONNREFUSED`); await cdpEndpoint(port); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent(`- generic [ref=s1e2]: Hello, world!`); }); diff --git a/tests/config.spec.ts b/tests/config.spec.ts index 2b09c0f..ed78019 100644 --- a/tests/config.spec.ts +++ b/tests/config.spec.ts @@ -19,7 +19,12 @@ import fs from 'node:fs'; import { Config } from '../config.js'; import { test, expect } from './fixtures.js'; -test('config user data dir', async ({ startClient, localOutputPath }) => { +test('config user data dir', async ({ startClient, localOutputPath, server }) => { + server.setContent('/', ` + Title + Hello, world! + `, 'text/html'); + const config: Config = { browser: { userDataDir: localOutputPath('user-data-dir'), @@ -31,9 +36,7 @@ test('config user data dir', async ({ startClient, localOutputPath }) => { const client = await startClient({ args: ['--config', configPath] }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Hello, world!', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent(`Hello, world!`); const files = await fs.promises.readdir(config.browser!.userDataDir!); diff --git a/tests/console.spec.ts b/tests/console.spec.ts index a5db3c1..781db5a 100644 --- a/tests/console.spec.ts +++ b/tests/console.spec.ts @@ -16,11 +16,21 @@ import { test, expect } from './fixtures.js'; -test('browser_console_messages', async ({ client }) => { +test('browser_console_messages', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', arguments: { - url: 'data:text/html,', + url: server.PREFIX, }, }); diff --git a/tests/core.spec.ts b/tests/core.spec.ts index 9e43933..8fc8a53 100644 --- a/tests/core.spec.ts +++ b/tests/core.spec.ts @@ -16,20 +16,18 @@ import { test, expect } from './fixtures.js'; -test('browser_navigate', async ({ client }) => { +test('browser_navigate', async ({ client, server }) => { expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, })).toHaveTextContent(` - Ran Playwright code: \`\`\`js -// Navigate to data:text/html,TitleHello, world! -await page.goto('data:text/html,TitleHello, world!'); +// Navigate to ${server.HELLO_WORLD} +await page.goto('${server.HELLO_WORLD}'); \`\`\` -- Page URL: data:text/html,TitleHello, world! +- Page URL: ${server.HELLO_WORLD} - Page Title: Title - Page Snapshot \`\`\`yaml @@ -39,12 +37,15 @@ await page.goto('data:text/html,TitleHello, world! { +test('browser_click', async ({ client, server }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, }); expect(await client.callTool({ @@ -60,7 +61,7 @@ test('browser_click', async ({ client }) => { await page.getByRole('button', { name: 'Submit' }).click(); \`\`\` -- Page URL: data:text/html,Title +- Page URL: ${server.PREFIX} - Page Title: Title - Page Snapshot \`\`\`yaml @@ -69,12 +70,18 @@ await page.getByRole('button', { name: 'Submit' }).click(); `); }); -test('browser_select_option', async ({ client }) => { +test('browser_select_option', async ({ client, server }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, }); expect(await client.callTool({ @@ -91,7 +98,7 @@ test('browser_select_option', async ({ client }) => { await page.getByRole('combobox').selectOption(['bar']); \`\`\` -- Page URL: data:text/html,Title +- Page URL: ${server.PREFIX} - Page Title: Title - Page Snapshot \`\`\`yaml @@ -102,12 +109,19 @@ await page.getByRole('combobox').selectOption(['bar']); `); }); -test('browser_select_option (multiple)', async ({ client }) => { +test('browser_select_option (multiple)', async ({ client, server }) => { + server.setContent('/', ` + Title + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, }); expect(await client.callTool({ @@ -124,7 +138,7 @@ test('browser_select_option (multiple)', async ({ client }) => { await page.getByRole('listbox').selectOption(['bar', 'baz']); \`\`\` -- Page URL: data:text/html,Title +- Page URL: ${server.PREFIX} - Page Title: Title - Page Snapshot \`\`\`yaml @@ -136,11 +150,18 @@ await page.getByRole('listbox').selectOption(['bar', 'baz']); `); }); -test('browser_type', async ({ client }) => { +test('browser_type', async ({ client, server }) => { + server.setContent('/', ` + + + + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', arguments: { - url: `data:text/html,`, + url: server.PREFIX, }, }); await client.callTool({ @@ -158,11 +179,15 @@ test('browser_type', async ({ client }) => { })).toHaveTextContent('[LOG] Key pressed: Enter , Text: Hi!'); }); -test('browser_type (slowly)', async ({ client }) => { +test('browser_type (slowly)', async ({ client, server }) => { + server.setContent('/', ` + + `, 'text/html'); + await client.callTool({ name: 'browser_navigate', arguments: { - url: `data:text/html,`, + url: server.PREFIX, }, }); await client.callTool({ @@ -186,12 +211,18 @@ test('browser_type (slowly)', async ({ client }) => { ].join('\n')); }); -test('browser_resize', async ({ client }) => { +test('browser_resize', async ({ client, server }) => { + server.setContent('/', ` + Resize Test + +
Waiting for resize...
+ + + `, 'text/html'); await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Resize Test
Waiting for resize...
', - }, + arguments: { url: server.PREFIX }, }); const response = await client.callTool({ diff --git a/tests/dialogs.spec.ts b/tests/dialogs.spec.ts index e8b3049..77da70f 100644 --- a/tests/dialogs.spec.ts +++ b/tests/dialogs.spec.ts @@ -19,12 +19,11 @@ import { test, expect } from './fixtures.js'; // https://github.com/microsoft/playwright/issues/35663 test.skip(({ mcpBrowser, mcpHeadless }) => mcpBrowser === 'webkit' && mcpHeadless); -test('alert dialog', async ({ client }) => { +test('alert dialog', async ({ client, server }) => { + server.setContent('/', ``, 'text/html'); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- button "Button" [ref=s1e3]'); expect(await client.callTool({ @@ -55,8 +54,8 @@ await page.getByRole('button', { name: 'Button' }).click(); // \`\`\` -- Page URL: data:text/html,Title -- Page Title: Title +- Page URL: ${server.PREFIX} +- Page Title: - Page Snapshot \`\`\`yaml - button "Button" [ref=s2e3] @@ -64,13 +63,19 @@ await page.getByRole('button', { name: 'Button' }).click(); `); }); -test('two alert dialogs', async ({ client }) => { +test('two alert dialogs', async ({ client, server }) => { test.fixme(true, 'Race between the dialog and ariaSnapshot'); + + server.setContent('/', ` + Title + + + + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- button "Button" [ref=s1e3]'); expect(await client.callTool({ @@ -98,12 +103,17 @@ await page.getByRole('button', { name: 'Button' }).click(); expect(result).not.toContainTextContent('### Modal state'); }); -test('confirm dialog (true)', async ({ client }) => { +test('confirm dialog (true)', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- button "Button" [ref=s1e3]'); expect(await client.callTool({ @@ -130,12 +140,17 @@ test('confirm dialog (true)', async ({ client }) => { \`\`\``); }); -test('confirm dialog (false)', async ({ client }) => { +test('confirm dialog (false)', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- button "Button" [ref=s1e3]'); expect(await client.callTool({ @@ -160,12 +175,17 @@ test('confirm dialog (false)', async ({ client }) => { \`\`\``); }); -test('prompt dialog', async ({ client }) => { +test('prompt dialog', async ({ client, server }) => { + server.setContent('/', ` + Title + + + + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- button "Button" [ref=s1e3]'); expect(await client.callTool({ diff --git a/tests/files.spec.ts b/tests/files.spec.ts index fe3f260..03d8cfe 100644 --- a/tests/files.spec.ts +++ b/tests/files.spec.ts @@ -18,12 +18,15 @@ import { test, expect } from './fixtures.js'; import fs from 'fs/promises'; import path from 'path'; -test('browser_file_upload', async ({ client, localOutputPath }) => { +test('browser_file_upload', async ({ client, localOutputPath, server }) => { + server.setContent('/', ` + + + `, 'text/html'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent(` \`\`\`yaml - button "Choose File" [ref=s1e3] @@ -96,17 +99,18 @@ The tool "browser_file_upload" can only be used when there is related modal stat } }); -test('clicking on download link emits download', async ({ startClient, localOutputPath }) => { +test('clicking on download link emits download', async ({ startClient, localOutputPath, server }) => { const outputDir = localOutputPath('output'); const client = await startClient({ config: { outputDir }, }); + server.setContent('/', `Download`, 'text/html'); + server.setContent('/download', 'Data', 'text/plain'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Download', - }, + arguments: { url: server.PREFIX }, })).toContainTextContent('- link "Download" [ref=s1e3]'); await client.callTool({ name: 'browser_click', @@ -133,7 +137,7 @@ test('navigating to download link emits download', async ({ client, server, mcpB expect(await client.callTool({ name: 'browser_navigate', arguments: { - url: server.PREFIX + '/download', + url: server.PREFIX + 'download', }, })).toContainTextContent('### Downloads'); }); diff --git a/tests/launch.spec.ts b/tests/launch.spec.ts index 1f88346..99142df 100644 --- a/tests/launch.spec.ts +++ b/tests/launch.spec.ts @@ -16,12 +16,10 @@ import { test, expect } from './fixtures.js'; -test('test reopen browser', async ({ client }) => { +test('test reopen browser', async ({ client, server }) => { await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, }); expect(await client.callTool({ @@ -31,19 +29,15 @@ test('test reopen browser', async ({ client }) => { expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, })).toContainTextContent(`- generic [ref=s1e2]: Hello, world!`); }); -test('executable path', async ({ startClient }) => { +test('executable path', async ({ startClient, server }) => { const client = await startClient({ args: [`--executable-path=bogus`] }); const response = await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, }); expect(response).toContainTextContent(`executable doesn't exist`); }); diff --git a/tests/network.spec.ts b/tests/network.spec.ts index 90f4cf5..a8555a6 100644 --- a/tests/network.spec.ts +++ b/tests/network.spec.ts @@ -17,15 +17,11 @@ import { test, expect } from './fixtures.js'; test('browser_network_requests', async ({ client, server }) => { - server.route('/', (req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end(``); - }); + server.setContent('/', ` + + `, 'text/html'); - server.route('/json', (req, res) => { - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ name: 'John Doe' })); - }); + server.setContent('/json', JSON.stringify({ name: 'John Doe' }), 'application/json'); await client.callTool({ name: 'browser_navigate', @@ -45,5 +41,6 @@ test('browser_network_requests', async ({ client, server }) => { await expect.poll(() => client.callTool({ name: 'browser_network_requests', arguments: {}, - })).toHaveTextContent(`[GET] ${`${server.PREFIX}/json`} => [200] OK`); + })).toHaveTextContent(`[GET] ${`${server.PREFIX}`} => [200] OK +[GET] ${`${server.PREFIX}json`} => [200] OK`); }); diff --git a/tests/pdf.spec.ts b/tests/pdf.spec.ts index dbccbfa..6179c96 100644 --- a/tests/pdf.spec.ts +++ b/tests/pdf.spec.ts @@ -18,13 +18,11 @@ import fs from 'fs'; import { test, expect } from './fixtures.js'; -test('save as pdf unavailable', async ({ startClient }) => { +test('save as pdf unavailable', async ({ startClient, server }) => { const client = await startClient({ args: ['--caps="no-pdf"'] }); await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, }); expect(await client.callTool({ @@ -32,13 +30,12 @@ test('save as pdf unavailable', async ({ startClient }) => { })).toHaveTextContent(/Tool \"browser_pdf_save\" not found/); }); -test('save as pdf', async ({ client, mcpBrowser }) => { +test('save as pdf', async ({ client, mcpBrowser, server }) => { test.skip(!!mcpBrowser && !['chromium', 'chrome', 'msedge'].includes(mcpBrowser), 'Save as PDF is only supported in Chromium.'); + expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, })).toContainTextContent(`- generic [ref=s1e2]: Hello, world!`); const response = await client.callTool({ @@ -48,18 +45,16 @@ test('save as pdf', async ({ client, mcpBrowser }) => { expect(response).toHaveTextContent(/Save page as.*page-[^:]+.pdf/); }); -test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser }, testInfo) => { +test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser, server, localOutputPath }) => { test.skip(!!mcpBrowser && !['chromium', 'chrome', 'msedge'].includes(mcpBrowser), 'Save as PDF is only supported in Chromium.'); - const outputDir = testInfo.outputPath('output'); + const outputDir = localOutputPath('output'); const client = await startClient({ config: { outputDir }, }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, + arguments: { url: server.HELLO_WORLD }, })).toContainTextContent(`- generic [ref=s1e2]: Hello, world!`); expect(await client.callTool({ diff --git a/tests/request-blocking.spec.ts b/tests/request-blocking.spec.ts index c65cbe5..7d0a016 100644 --- a/tests/request-blocking.spec.ts +++ b/tests/request-blocking.spec.ts @@ -31,11 +31,8 @@ const fetchPage = async (client: Client, url: string) => { }; test('default to allow all', async ({ server, client }) => { - server.route('/ppp', (_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('content:PPP'); - }); - const result = await fetchPage(client, server.PREFIX + '/ppp'); + server.setContent('/ppp', 'content:PPP', 'text/html'); + const result = await fetchPage(client, server.PREFIX + 'ppp'); expect(result).toContain('content:PPP'); }); @@ -48,14 +45,11 @@ test('blocked works', async ({ startClient }) => { }); test('allowed works', async ({ server, startClient }) => { - server.route('/ppp', (_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('content:PPP'); - }); + server.setContent('/ppp', 'content:PPP', 'text/html'); const client = await startClient({ args: ['--allowed-origins', `microsoft.com;${new URL(server.PREFIX).host};playwright.dev`] }); - const result = await fetchPage(client, server.PREFIX + '/ppp'); + const result = await fetchPage(client, server.PREFIX + 'ppp'); expect(result).toContain('content:PPP'); }); @@ -79,13 +73,10 @@ test('allowed without blocked blocks all non-explicitly specified origins', asyn }); test('blocked without allowed allows non-explicitly specified origins', async ({ server, startClient }) => { - server.route('/ppp', (_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/html' }); - res.end('content:PPP'); - }); + server.setContent('/ppp', 'content:PPP', 'text/html'); const client = await startClient({ args: ['--blocked-origins', 'example.com'], }); - const result = await fetchPage(client, server.PREFIX + '/ppp'); + const result = await fetchPage(client, server.PREFIX + 'ppp'); expect(result).toContain('content:PPP'); }); diff --git a/tests/screenshot.spec.ts b/tests/screenshot.spec.ts index 66a5e34..4fdf3e2 100644 --- a/tests/screenshot.spec.ts +++ b/tests/screenshot.spec.ts @@ -18,13 +18,11 @@ import fs from 'fs'; import { test, expect } from './fixtures.js'; -test('browser_take_screenshot (viewport)', async ({ client }) => { +test('browser_take_screenshot (viewport)', async ({ client, server }) => { expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`Navigate to http://localhost`); expect(await client.callTool({ name: 'browser_take_screenshot', @@ -44,19 +42,17 @@ test('browser_take_screenshot (viewport)', async ({ client }) => { }); }); -test('browser_take_screenshot (element)', async ({ client }) => { +test('browser_take_screenshot (element)', async ({ client, server }) => { expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,Title', - }, - })).toContainTextContent(`[ref=s1e3]`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`[ref=s1e2]`); expect(await client.callTool({ name: 'browser_take_screenshot', arguments: { element: 'hello button', - ref: 's1e3', + ref: 's1e2', }, })).toEqual({ content: [ @@ -66,24 +62,22 @@ test('browser_take_screenshot (element)', async ({ client }) => { type: 'image', }, { - text: expect.stringContaining(`page.getByRole('button', { name: 'Hello, world!' }).screenshot`), + text: expect.stringContaining(`page.getByText('Hello, world!').screenshot`), type: 'text', }, ], }); }); -test('--output-dir should work', async ({ startClient, localOutputPath }) => { +test('--output-dir should work', async ({ startClient, localOutputPath, server }) => { const outputDir = localOutputPath('output'); const client = await startClient({ args: ['--output-dir', outputDir], }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`Navigate to http://localhost`); await client.callTool({ name: 'browser_take_screenshot', @@ -95,24 +89,20 @@ test('--output-dir should work', async ({ startClient, localOutputPath }) => { }); for (const raw of [undefined, true]) { - test(`browser_take_screenshot (raw: ${raw})`, async ({ startClient }, testInfo) => { + test(`browser_take_screenshot (raw: ${raw})`, async ({ startClient, localOutputPath, server }) => { const ext = raw ? 'png' : 'jpeg'; - const outputDir = testInfo.outputPath('output'); + const outputDir = localOutputPath('output'); const client = await startClient({ config: { outputDir }, }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.PREFIX }, + })).toContainTextContent(`Navigate to http://localhost`); expect(await client.callTool({ name: 'browser_take_screenshot', - arguments: { - raw, - }, + arguments: { raw }, })).toEqual({ content: [ { @@ -140,17 +130,15 @@ for (const raw of [undefined, true]) { } -test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient }, testInfo) => { - const outputDir = testInfo.outputPath('output'); +test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient, localOutputPath, server }) => { + const outputDir = localOutputPath('output'); const client = await startClient({ config: { outputDir }, }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`Navigate to http://localhost`); expect(await client.callTool({ name: 'browser_take_screenshot', @@ -178,7 +166,7 @@ test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient } expect(files[0]).toMatch(/^output.jpeg$/); }); -test('browser_take_screenshot (noImageResponses)', async ({ startClient }) => { +test('browser_take_screenshot (noImageResponses)', async ({ startClient, server }) => { const client = await startClient({ config: { noImageResponses: true, @@ -187,10 +175,8 @@ test('browser_take_screenshot (noImageResponses)', async ({ startClient }) => { expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`Navigate to http://localhost`); await client.callTool({ name: 'browser_take_screenshot', @@ -210,15 +196,13 @@ test('browser_take_screenshot (noImageResponses)', async ({ startClient }) => { }); }); -test('browser_take_screenshot (cursor)', async ({ startClient }) => { +test('browser_take_screenshot (cursor)', async ({ startClient, server }) => { const client = await startClient({ clientName: 'cursor:vscode' }); expect(await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleHello, world!', - }, - })).toContainTextContent(`Navigate to data:text/html`); + arguments: { url: server.HELLO_WORLD }, + })).toContainTextContent(`Navigate to http://localhost`); await client.callTool({ name: 'browser_take_screenshot', diff --git a/tests/tabs.spec.ts b/tests/tabs.spec.ts index e03326e..ee989bf 100644 --- a/tests/tabs.spec.ts +++ b/tests/tabs.spec.ts @@ -141,7 +141,9 @@ test('close tab', async ({ client }) => { \`\`\``); }); -test('reuse first tab when navigating', async ({ startClient, cdpEndpoint }) => { +test('reuse first tab when navigating', async ({ startClient, cdpEndpoint, server }) => { + server.setContent('/', `TitleBody`, 'text/html'); + const browser = await chromium.connectOverCDP(await cdpEndpoint()); const [context] = browser.contexts(); const pages = context.pages(); @@ -149,9 +151,7 @@ test('reuse first tab when navigating', async ({ startClient, cdpEndpoint }) => const client = await startClient({ args: [`--cdp-endpoint=${await cdpEndpoint()}`] }); await client.callTool({ name: 'browser_navigate', - arguments: { - url: 'data:text/html,TitleBody', - }, + arguments: { url: server.PREFIX }, }); expect(pages.length).toBe(1); diff --git a/tests/testserver/index.ts b/tests/testserver/index.ts index a4f1e02..b40f7f4 100644 --- a/tests/testserver/index.ts +++ b/tests/testserver/index.ts @@ -38,6 +38,7 @@ export class TestServer { readonly PORT: number; readonly PREFIX: string; readonly CROSS_PROCESS_PREFIX: string; + readonly HELLO_WORLD: string; static async create(port: number): Promise { const server = new TestServer(port); @@ -67,8 +68,9 @@ export class TestServer { const same_origin = 'localhost'; const protocol = sslOptions ? 'https' : 'http'; this.PORT = port; - this.PREFIX = `${protocol}://${same_origin}:${port}`; - this.CROSS_PROCESS_PREFIX = `${protocol}://${cross_origin}:${port}`; + this.PREFIX = `${protocol}://${same_origin}:${port}/`; + this.CROSS_PROCESS_PREFIX = `${protocol}://${cross_origin}:${port}/`; + this.HELLO_WORLD = `${this.PREFIX}hello-world`; } setCSP(path: string, csp: string) { @@ -88,6 +90,13 @@ export class TestServer { this._routes.set(path, handler); } + setContent(path: string, content: string, mimeType: string) { + this.route(path, (req, res) => { + res.writeHead(200, { 'Content-Type': mimeType }); + res.end(mimeType === 'text/html' ? `${content}` : content); + }); + } + redirect(from: string, to: string) { this.route(from, (req, res) => { const headers = this._extraHeaders.get(req.url!) || {}; @@ -120,6 +129,15 @@ export class TestServer { for (const subscriber of this._requestSubscribers.values()) subscriber[rejectSymbol].call(null, error); this._requestSubscribers.clear(); + + this.setContent('/favicon.ico', '', 'image/x-icon'); + + this.setContent('/', ``, 'text/html'); + + this.setContent('/hello-world', ` + Title + Hello, world! + `, 'text/html'); } _onRequest(request: http.IncomingMessage, response: http.ServerResponse) { @@ -144,7 +162,11 @@ export class TestServer { this._requestSubscribers.delete(path); } const handler = this._routes.get(path); - if (handler) + if (handler) { handler.call(null, request, response); + } else { + response.writeHead(404); + response.end(); + } } }