mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-10-12 00:25:14 +03:00
chore: parse response in tests (#796)
This commit is contained in:
1
package-lock.json
generated
1
package-lock.json
generated
@@ -17,6 +17,7 @@
|
||||
"playwright": "1.55.0-alpha-1752701791000",
|
||||
"playwright-core": "1.55.0-alpha-1752701791000",
|
||||
"ws": "^8.18.1",
|
||||
"zod": "^3.24.1",
|
||||
"zod-to-json-schema": "^3.24.4"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"playwright": "1.55.0-alpha-1752701791000",
|
||||
"playwright-core": "1.55.0-alpha-1752701791000",
|
||||
"ws": "^8.18.1",
|
||||
"zod": "^3.24.1",
|
||||
"zod-to-json-schema": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -93,8 +93,8 @@ export class Context {
|
||||
|
||||
if (!this._tabs.length) {
|
||||
return [
|
||||
'### No open tabs',
|
||||
'Use the "browser_navigate" tool to navigate to a page first.',
|
||||
'### Open tabs',
|
||||
'No open tabs. Use the "browser_navigate" tool to navigate to a page first.',
|
||||
'',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ export function createServer(backend: ServerBackend, runHeartbeat: boolean): Ser
|
||||
}
|
||||
|
||||
const errorResult = (...messages: string[]) => ({
|
||||
content: [{ type: 'text', text: messages.join('\n') }],
|
||||
content: [{ type: 'text', text: '### Result\n' + messages.join('\n') }],
|
||||
isError: true,
|
||||
});
|
||||
const tool = tools.find(tool => tool.name === request.params.name) as ToolSchema<any>;
|
||||
if (!tool)
|
||||
return errorResult(`Tool "${request.params.name}" not found`);
|
||||
return errorResult(`Error: Tool "${request.params.name}" not found`);
|
||||
|
||||
try {
|
||||
return await backend.callTool(tool, tool.inputSchema.parse(request.params.arguments || {}));
|
||||
|
||||
@@ -28,6 +28,7 @@ export class Response {
|
||||
|
||||
readonly toolName: string;
|
||||
readonly toolArgs: Record<string, any>;
|
||||
private _isError: boolean | undefined;
|
||||
|
||||
constructor(context: Context, toolName: string, toolArgs: Record<string, any>) {
|
||||
this._context = context;
|
||||
@@ -39,6 +40,11 @@ export class Response {
|
||||
this._result.push(result);
|
||||
}
|
||||
|
||||
addError(error: string) {
|
||||
this._result.push(`Error: ${error}`);
|
||||
this._isError = true;
|
||||
}
|
||||
|
||||
result() {
|
||||
return this._result.join('\n');
|
||||
}
|
||||
@@ -77,7 +83,7 @@ export class Response {
|
||||
return this._snapshot;
|
||||
}
|
||||
|
||||
async serialize(): Promise<{ content: (TextContent | ImageContent)[] }> {
|
||||
async serialize(): Promise<{ content: (TextContent | ImageContent)[], isError?: boolean }> {
|
||||
const response: string[] = [];
|
||||
|
||||
// Start with command result.
|
||||
@@ -116,6 +122,6 @@ ${this._code.join('\n')}
|
||||
content.push({ type: 'image', data: image.data.toString('base64'), mimeType: image.contentType });
|
||||
}
|
||||
|
||||
return { content };
|
||||
return { content, isError: this._isError };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ const resize = defineTabTool({
|
||||
},
|
||||
|
||||
handle: async (tab, params, response) => {
|
||||
response.addCode(`// Resize browser window to ${params.width}x${params.height}`);
|
||||
response.addCode(`await page.setViewportSize({ width: ${params.width}, height: ${params.height} });`);
|
||||
|
||||
await tab.waitForCompletion(async () => {
|
||||
|
||||
@@ -67,11 +67,9 @@ const type = defineTabTool({
|
||||
await tab.waitForCompletion(async () => {
|
||||
if (params.slowly) {
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`// Press "${params.text}" sequentially into "${params.element}"`);
|
||||
response.addCode(`await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(params.text)});`);
|
||||
await locator.pressSequentially(params.text);
|
||||
} else {
|
||||
response.addCode(`// Fill "${params.text}" into "${params.element}"`);
|
||||
response.addCode(`await page.${await generateLocator(locator)}.fill(${javascript.quote(params.text)});`);
|
||||
await locator.fill(params.text);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ const navigate = defineTool({
|
||||
await tab.navigate(params.url);
|
||||
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`// Navigate to ${params.url}`);
|
||||
response.addCode(`await page.goto('${params.url}');`);
|
||||
},
|
||||
});
|
||||
@@ -53,7 +52,6 @@ const goBack = defineTabTool({
|
||||
handle: async (tab, params, response) => {
|
||||
await tab.page.goBack();
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`// Navigate back`);
|
||||
response.addCode(`await page.goBack();`);
|
||||
},
|
||||
});
|
||||
@@ -70,7 +68,6 @@ const goForward = defineTabTool({
|
||||
handle: async (tab, params, response) => {
|
||||
await tab.page.goForward();
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`// Navigate forward`);
|
||||
response.addCode(`await page.goForward();`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,7 +37,6 @@ const pdf = defineTabTool({
|
||||
|
||||
handle: async (tab, params, response) => {
|
||||
const fileName = await outputFile(tab.context.config, params.filename ?? `page-${new Date().toISOString()}.pdf`);
|
||||
response.addCode(`// Save page as ${fileName}`);
|
||||
response.addCode(`await page.pdf(${javascript.formatObject({ path: fileName })});`);
|
||||
response.addResult(`Saved page as ${fileName}`);
|
||||
await tab.page.pdf({ path: fileName });
|
||||
|
||||
@@ -63,13 +63,11 @@ const click = defineTabTool({
|
||||
const button = params.button;
|
||||
const buttonAttr = button ? `{ button: '${button}' }` : '';
|
||||
|
||||
if (params.doubleClick) {
|
||||
response.addCode(`// Double click ${params.element}`);
|
||||
if (params.doubleClick)
|
||||
response.addCode(`await page.${await generateLocator(locator)}.dblclick(${buttonAttr});`);
|
||||
} else {
|
||||
response.addCode(`// Click ${params.element}`);
|
||||
else
|
||||
response.addCode(`await page.${await generateLocator(locator)}.click(${buttonAttr});`);
|
||||
}
|
||||
|
||||
|
||||
await tab.waitForCompletion(async () => {
|
||||
if (params.doubleClick)
|
||||
@@ -151,7 +149,6 @@ const selectOption = defineTabTool({
|
||||
response.setIncludeSnapshot();
|
||||
|
||||
const locator = await tab.refLocator(params);
|
||||
response.addCode(`// Select options [${params.values.join(', ')}] in ${params.element}`);
|
||||
response.addCode(`await page.${await generateLocator(locator)}.selectOption(${javascript.formatObject(params.values)});`);
|
||||
|
||||
await tab.waitForCompletion(async () => {
|
||||
|
||||
@@ -60,10 +60,11 @@ export function defineTabTool<Input extends z.Schema>(tool: TabTool<Input>): Too
|
||||
const tab = context.currentTabOrDie();
|
||||
const modalStates = tab.modalStates().map(state => state.type);
|
||||
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
|
||||
throw new Error(`The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` + tab.modalStatesMarkdown().join('\n'));
|
||||
if (!tool.clearsModalState && modalStates.length)
|
||||
throw new Error(`Tool "${tool.schema.name}" does not handle the modal state.\n` + tab.modalStatesMarkdown().join('\n'));
|
||||
return tool.handle(tab, params, response);
|
||||
response.addError(`The tool "${tool.schema.name}" can only be used when there is related modal state present.\n` + tab.modalStatesMarkdown().join('\n'));
|
||||
else if (!tool.clearsModalState && modalStates.length)
|
||||
response.addError(`Tool "${tool.schema.name}" does not handle the modal state.\n` + tab.modalStatesMarkdown().join('\n'));
|
||||
else
|
||||
return tool.handle(tab, params, response);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ test('cdp server', async ({ cdpServer, startClient, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- generic [active] [ref=e1]: Hello, world!`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
});
|
||||
});
|
||||
|
||||
test('cdp server reuse tab', async ({ cdpServer, startClient, server }) => {
|
||||
@@ -41,18 +43,21 @@ test('cdp server reuse tab', async ({ cdpServer, startClient, server }) => {
|
||||
element: 'Hello, world!',
|
||||
ref: 'f0',
|
||||
},
|
||||
})).toHaveTextContent(`Error: No open pages available. Use the \"browser_navigate\" tool to navigate to a page first.`);
|
||||
})).toHaveResponse({
|
||||
result: `Error: No open pages available. Use the "browser_navigate" tool to navigate to a page first.`,
|
||||
isError: true,
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_snapshot',
|
||||
})).toHaveTextContent(`### Page state
|
||||
- Page URL: ${server.HELLO_WORLD}
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- Page URL: ${server.HELLO_WORLD}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Hello, world!
|
||||
\`\`\`
|
||||
`);
|
||||
\`\`\``),
|
||||
});
|
||||
});
|
||||
|
||||
test('should throw connection error and allow re-connecting', async ({ cdpServer, startClient, server }) => {
|
||||
@@ -66,12 +71,17 @@ test('should throw connection error and allow re-connecting', async ({ cdpServer
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent(`Error: browserType.connectOverCDP: connect ECONNREFUSED`);
|
||||
})).toHaveResponse({
|
||||
result: expect.stringContaining(`Error: browserType.connectOverCDP: connect ECONNREFUSED`),
|
||||
isError: true,
|
||||
});
|
||||
await cdpServer.start();
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent(`- generic [active] [ref=e1]: Hello, world!`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
});
|
||||
});
|
||||
|
||||
// NOTE: Can be removed when we drop Node.js 18 support and changed to import.meta.filename.
|
||||
|
||||
@@ -33,21 +33,10 @@ test('browser_click', async ({ client, server, mcpBrowser }) => {
|
||||
element: 'Submit button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toHaveTextContent(`
|
||||
### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Click Submit button
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
\`\`\`
|
||||
|
||||
### Page state
|
||||
- Page URL: ${server.PREFIX}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- button "Submit" ${mcpBrowser !== 'webkit' || process.platform === 'linux' ? '[active] ' : ''}[ref=e2]
|
||||
\`\`\`
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('button', { name: 'Submit' }).click();`,
|
||||
pageState: expect.stringContaining(`- button "Submit" ${mcpBrowser !== 'webkit' || process.platform === 'linux' ? '[active] ' : ''}[ref=e2]`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_click (double)', async ({ client, server }) => {
|
||||
@@ -73,21 +62,10 @@ test('browser_click (double)', async ({ client, server }) => {
|
||||
ref: 'e2',
|
||||
doubleClick: true,
|
||||
},
|
||||
})).toHaveTextContent(`
|
||||
### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Double click Click me
|
||||
await page.getByRole('heading', { name: 'Click me' }).dblclick();
|
||||
\`\`\`
|
||||
|
||||
### Page state
|
||||
- Page URL: ${server.PREFIX}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- heading "Double clicked" [level=1] [ref=e3]
|
||||
\`\`\`
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('heading', { name: 'Click me' }).dblclick();`,
|
||||
pageState: expect.stringContaining(`- heading "Double clicked" [level=1] [ref=e3]`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_click (right)', async ({ client, server }) => {
|
||||
@@ -114,6 +92,8 @@ test('browser_click (right)', async ({ client, server }) => {
|
||||
button: 'right',
|
||||
},
|
||||
});
|
||||
expect(result).toContainTextContent(`await page.getByRole('button', { name: 'Menu' }).click({ button: 'right' });`);
|
||||
expect(result).toContainTextContent(`- button "Right clicked"`);
|
||||
expect(result).toHaveResponse({
|
||||
code: `await page.getByRole('button', { name: 'Menu' }).click({ button: 'right' });`,
|
||||
pageState: expect.stringContaining(`- button "Right clicked"`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -37,7 +37,9 @@ test('config user data dir', async ({ startClient, server, mcpMode }, testInfo)
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent(`Hello, world!`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Hello, world!`),
|
||||
});
|
||||
|
||||
const files = await fs.promises.readdir(config.browser!.userDataDir!);
|
||||
expect(files.length).toBeGreaterThan(0);
|
||||
@@ -58,7 +60,9 @@ test.describe(() => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: 'data:text/html,<script>document.title = navigator.userAgent</script>' },
|
||||
})).toContainTextContent(`Firefox`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Firefox`),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -37,11 +37,10 @@ test('browser_console_messages', async ({ client, server }) => {
|
||||
const resource = await client.callTool({
|
||||
name: 'browser_console_messages',
|
||||
});
|
||||
expect(resource).toHaveTextContent([
|
||||
'### Result',
|
||||
`[LOG] Hello, world! @ ${server.PREFIX}:4`,
|
||||
`[ERROR] Error @ ${server.PREFIX}:5`,
|
||||
].join('\n'));
|
||||
expect(resource).toHaveResponse({
|
||||
result: `[LOG] Hello, world! @ ${server.PREFIX}:4
|
||||
[ERROR] Error @ ${server.PREFIX}:5`,
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_console_messages (page error)', async ({ client, server }) => {
|
||||
@@ -64,8 +63,12 @@ test('browser_console_messages (page error)', async ({ client, server }) => {
|
||||
const resource = await client.callTool({
|
||||
name: 'browser_console_messages',
|
||||
});
|
||||
expect(resource).toHaveTextContent(/Error: Error in script/);
|
||||
expect(resource).toHaveTextContent(new RegExp(server.PREFIX));
|
||||
expect(resource).toHaveResponse({
|
||||
result: expect.stringContaining(`Error: Error in script`),
|
||||
});
|
||||
expect(resource).toHaveResponse({
|
||||
result: expect.stringContaining(server.PREFIX),
|
||||
});
|
||||
});
|
||||
|
||||
test('recent console messages', async ({ client, server }) => {
|
||||
@@ -91,7 +94,7 @@ test('recent console messages', async ({ client, server }) => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).toContainTextContent(`
|
||||
### New console messages
|
||||
- [LOG] Hello, world! @`);
|
||||
expect(response).toHaveResponse({
|
||||
consoleMessages: expect.stringContaining(`- [LOG] Hello, world! @`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,22 +20,15 @@ test('browser_navigate', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toHaveTextContent(`
|
||||
### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Navigate to ${server.HELLO_WORLD}
|
||||
await page.goto('${server.HELLO_WORLD}');
|
||||
\`\`\`
|
||||
|
||||
### Page state
|
||||
- Page URL: ${server.HELLO_WORLD}
|
||||
})).toHaveResponse({
|
||||
code: `await page.goto('${server.HELLO_WORLD}');`,
|
||||
pageState: `- Page URL: ${server.HELLO_WORLD}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Hello, world!
|
||||
\`\`\`
|
||||
`
|
||||
);
|
||||
\`\`\``,
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_select_option', async ({ client, server }) => {
|
||||
@@ -59,23 +52,17 @@ test('browser_select_option', async ({ client, server }) => {
|
||||
ref: 'e2',
|
||||
values: ['bar'],
|
||||
},
|
||||
})).toHaveTextContent(`
|
||||
### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Select options [bar] in Select
|
||||
await page.getByRole('combobox').selectOption(['bar']);
|
||||
\`\`\`
|
||||
|
||||
### Page state
|
||||
- Page URL: ${server.PREFIX}
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('combobox').selectOption(['bar']);`,
|
||||
pageState: `- Page URL: ${server.PREFIX}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- combobox [ref=e2]:
|
||||
- option "Foo"
|
||||
- option "Bar" [selected]
|
||||
\`\`\`
|
||||
`);
|
||||
\`\`\``,
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_select_option (multiple)', async ({ client, server }) => {
|
||||
@@ -100,24 +87,14 @@ test('browser_select_option (multiple)', async ({ client, server }) => {
|
||||
ref: 'e2',
|
||||
values: ['bar', 'baz'],
|
||||
},
|
||||
})).toHaveTextContent(`
|
||||
### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Select options [bar, baz] in Select
|
||||
await page.getByRole('listbox').selectOption(['bar', 'baz']);
|
||||
\`\`\`
|
||||
|
||||
### Page state
|
||||
- Page URL: ${server.PREFIX}
|
||||
- Page Title: Title
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('listbox').selectOption(['bar', 'baz']);`,
|
||||
pageState: expect.stringContaining(`
|
||||
- listbox [ref=e2]:
|
||||
- option "Foo" [ref=e3]
|
||||
- option "Bar" [selected] [ref=e4]
|
||||
- option "Baz" [selected] [ref=e5]
|
||||
\`\`\`
|
||||
`);
|
||||
- option "Baz" [selected] [ref=e5]`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_resize', async ({ client, server }) => {
|
||||
@@ -141,12 +118,12 @@ test('browser_resize', async ({ client, server }) => {
|
||||
height: 780,
|
||||
},
|
||||
});
|
||||
expect(response).toContainTextContent(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Resize browser window to 390x780
|
||||
await page.setViewportSize({ width: 390, height: 780 });
|
||||
\`\`\``);
|
||||
await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toContainTextContent('Window size: 390x780');
|
||||
expect(response).toHaveResponse({
|
||||
code: `await page.setViewportSize({ width: 390, height: 780 });`,
|
||||
});
|
||||
await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Window size: 390x780`),
|
||||
});
|
||||
});
|
||||
|
||||
test('old locator error message', async ({ client, server }) => {
|
||||
@@ -165,10 +142,11 @@ test('old locator error message', async ({ client, server }) => {
|
||||
arguments: {
|
||||
url: server.PREFIX,
|
||||
},
|
||||
})).toContainTextContent(`
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`
|
||||
- button "Button 1" [ref=e2]
|
||||
- button "Button 2" [ref=e3]
|
||||
`.trim());
|
||||
- button "Button 2" [ref=e3]`),
|
||||
});
|
||||
|
||||
await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -184,7 +162,10 @@ test('old locator error message', async ({ client, server }) => {
|
||||
element: 'Button 2',
|
||||
ref: 'e3',
|
||||
},
|
||||
})).toContainTextContent('Ref e3 not found in the current page snapshot. Try capturing new snapshot.');
|
||||
})).toHaveResponse({
|
||||
result: expect.stringContaining(`Ref e3 not found in the current page snapshot. Try capturing new snapshot.`),
|
||||
isError: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('visibility: hidden > visible should be shown', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright-mcp/issues/535' } }, async ({ client, server }) => {
|
||||
@@ -203,5 +184,7 @@ test('visibility: hidden > visible should be shown', { annotation: { type: 'issu
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_snapshot'
|
||||
})).toContainTextContent('- button "Button"');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button"`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,5 +39,7 @@ test('--device should work', async ({ startClient, server, mcpMode }) => {
|
||||
arguments: {
|
||||
url: server.PREFIX,
|
||||
},
|
||||
})).toContainTextContent(`393x659`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`393x659`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,9 @@ test('alert dialog', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -29,25 +31,31 @@ test('alert dialog', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toHaveTextContent(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Click Button
|
||||
await page.getByRole('button', { name: 'Button' }).click();
|
||||
\`\`\`
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('button', { name: 'Button' }).click();`,
|
||||
modalState: `- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`,
|
||||
});
|
||||
|
||||
### Modal state
|
||||
- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
arguments: {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toHaveResponse({
|
||||
code: undefined,
|
||||
modalState: `- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`,
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
arguments: {
|
||||
accept: true,
|
||||
},
|
||||
})).toHaveResponse({
|
||||
modalState: undefined,
|
||||
pageState: expect.stringContaining(`- button "Button"`),
|
||||
});
|
||||
|
||||
expect(result).not.toContainTextContent('### Modal state');
|
||||
expect(result).toContainTextContent(`Page Snapshot:`);
|
||||
});
|
||||
|
||||
test('two alert dialogs', async ({ client, server }) => {
|
||||
@@ -61,7 +69,9 @@ test('two alert dialogs', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -69,15 +79,10 @@ test('two alert dialogs', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toHaveTextContent(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Click Button
|
||||
await page.getByRole('button', { name: 'Button' }).click();
|
||||
\`\`\`
|
||||
|
||||
### Modal state
|
||||
- ["alert" dialog with message "Alert 1"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('button', { name: 'Button' }).click();`,
|
||||
modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 1"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
@@ -86,9 +91,9 @@ await page.getByRole('button', { name: 'Button' }).click();
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toContainTextContent(`### Modal state
|
||||
- ["alert" dialog with message "Alert 2"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
expect(result).toHaveResponse({
|
||||
modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 2"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result2 = await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
@@ -97,7 +102,9 @@ await page.getByRole('button', { name: 'Button' }).click();
|
||||
},
|
||||
});
|
||||
|
||||
expect(result2).not.toContainTextContent('### Modal state');
|
||||
expect(result2).not.toHaveResponse({
|
||||
modalState: expect.stringContaining(`- ["alert" dialog with message "Alert 2"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
});
|
||||
|
||||
test('confirm dialog (true)', async ({ client, server }) => {
|
||||
@@ -111,7 +118,9 @@ test('confirm dialog (true)', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -119,21 +128,19 @@ test('confirm dialog (true)', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toContainTextContent(`### Modal state
|
||||
- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool`);
|
||||
})).toHaveResponse({
|
||||
modalState: expect.stringContaining(`- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
arguments: {
|
||||
accept: true,
|
||||
},
|
||||
})).toHaveResponse({
|
||||
modalState: undefined,
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: "true"`),
|
||||
});
|
||||
|
||||
expect(result).not.toContainTextContent('### Modal state');
|
||||
expect(result).toContainTextContent(`- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: "true"
|
||||
\`\`\``);
|
||||
});
|
||||
|
||||
test('confirm dialog (false)', async ({ client, server }) => {
|
||||
@@ -147,7 +154,9 @@ test('confirm dialog (false)', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -155,21 +164,19 @@ test('confirm dialog (false)', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toContainTextContent(`### Modal state
|
||||
- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
modalState: expect.stringContaining(`- ["confirm" dialog with message "Confirm"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
arguments: {
|
||||
accept: false,
|
||||
},
|
||||
})).toHaveResponse({
|
||||
modalState: undefined,
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: "false"`),
|
||||
});
|
||||
|
||||
expect(result).toContainTextContent(`- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: "false"
|
||||
\`\`\``);
|
||||
});
|
||||
|
||||
test('prompt dialog', async ({ client, server }) => {
|
||||
@@ -183,7 +190,9 @@ test('prompt dialog', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -191,9 +200,9 @@ test('prompt dialog', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toContainTextContent(`### Modal state
|
||||
- ["prompt" dialog with message "Prompt"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
modalState: expect.stringContaining(`- ["prompt" dialog with message "Prompt"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
@@ -203,10 +212,9 @@ test('prompt dialog', async ({ client, server }) => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toContainTextContent(`- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Answer
|
||||
\`\`\``);
|
||||
expect(result).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Answer`),
|
||||
});
|
||||
});
|
||||
|
||||
test('alert dialog w/ race', async ({ client, server }) => {
|
||||
@@ -214,7 +222,9 @@ test('alert dialog w/ race', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- button "Button" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- button "Button" [ref=e2]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -222,15 +232,10 @@ test('alert dialog w/ race', async ({ client, server }) => {
|
||||
element: 'Button',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toHaveTextContent(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
// Click Button
|
||||
await page.getByRole('button', { name: 'Button' }).click();
|
||||
\`\`\`
|
||||
|
||||
### Modal state
|
||||
- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool
|
||||
`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.getByRole('button', { name: 'Button' }).click();`,
|
||||
modalState: expect.stringContaining(`- ["alert" dialog with message "Alert"]: can be handled by the "browser_handle_dialog" tool`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'browser_handle_dialog',
|
||||
@@ -239,11 +244,12 @@ await page.getByRole('button', { name: 'Button' }).click();
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).not.toContainTextContent('### Modal state');
|
||||
expect(result).toContainTextContent(`### Page state
|
||||
- Page URL: ${server.PREFIX}
|
||||
expect(result).toHaveResponse({
|
||||
modalState: undefined,
|
||||
pageState: expect.stringContaining(`- Page URL: ${server.PREFIX}
|
||||
- Page Title:
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- button "Button"`);
|
||||
- button "Button"`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,15 +20,19 @@ test('browser_evaluate', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- Page Title: Title`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- Page Title: Title`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_evaluate',
|
||||
arguments: {
|
||||
function: '() => document.title',
|
||||
},
|
||||
})).toHaveResponse({
|
||||
result: `"Title"`,
|
||||
code: `await page.evaluate('() => document.title');`,
|
||||
});
|
||||
expect(result).toContainTextContent(`"Title"`);
|
||||
});
|
||||
|
||||
test('browser_evaluate (element)', async ({ client, server }) => {
|
||||
@@ -47,15 +51,19 @@ test('browser_evaluate (element)', async ({ client, server }) => {
|
||||
element: 'body',
|
||||
ref: 'e1',
|
||||
},
|
||||
})).toContainTextContent(`### Result
|
||||
"red"`);
|
||||
})).toHaveResponse({
|
||||
result: `"red"`,
|
||||
code: `await page.getByText('Hello, world!').evaluate('element => element.style.backgroundColor');`,
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_evaluate (error)', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- Page Title: Title`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- Page Title: Title`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'browser_evaluate',
|
||||
|
||||
@@ -26,22 +26,21 @@ test('browser_file_upload', async ({ client, server }, testInfo) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent(`
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]:
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]:
|
||||
- button "Choose File" [ref=e2]
|
||||
- button "Button" [ref=e3]
|
||||
\`\`\``);
|
||||
- button "Button" [ref=e3]`),
|
||||
});
|
||||
|
||||
{
|
||||
expect(await client.callTool({
|
||||
name: 'browser_file_upload',
|
||||
arguments: { paths: [] },
|
||||
})).toHaveTextContent(`
|
||||
Error: The tool "browser_file_upload" can only be used when there is related modal state present.
|
||||
### Modal state
|
||||
- There is no modal state present
|
||||
`.trim());
|
||||
})).toHaveResponse({
|
||||
isError: true,
|
||||
result: expect.stringContaining(`The tool "browser_file_upload" can only be used when there is related modal state present.`),
|
||||
modalState: expect.stringContaining(`- There is no modal state present`),
|
||||
});
|
||||
}
|
||||
|
||||
expect(await client.callTool({
|
||||
@@ -50,8 +49,9 @@ Error: The tool "browser_file_upload" can only be used when there is related mod
|
||||
element: 'Textbox',
|
||||
ref: 'e2',
|
||||
},
|
||||
})).toContainTextContent(`### Modal state
|
||||
- [File chooser]: can be handled by the "browser_file_upload" tool`);
|
||||
})).toHaveResponse({
|
||||
modalState: expect.stringContaining(`- [File chooser]: can be handled by the "browser_file_upload" tool`),
|
||||
});
|
||||
|
||||
const filePath = testInfo.outputPath('test.txt');
|
||||
await fs.writeFile(filePath, 'Hello, world!');
|
||||
@@ -64,7 +64,10 @@ Error: The tool "browser_file_upload" can only be used when there is related mod
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).not.toContainTextContent('### Modal state');
|
||||
expect(response).toHaveResponse({
|
||||
code: expect.stringContaining(`await fileChooser.setFiles(`),
|
||||
modalState: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -76,7 +79,9 @@ Error: The tool "browser_file_upload" can only be used when there is related mod
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).toContainTextContent('- [File chooser]: can be handled by the \"browser_file_upload\" tool');
|
||||
expect(response).toHaveResponse({
|
||||
modalState: `- [File chooser]: can be handled by the "browser_file_upload" tool`,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
@@ -88,9 +93,10 @@ Error: The tool "browser_file_upload" can only be used when there is related mod
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).toContainTextContent(`Error: Tool "browser_click" does not handle the modal state.
|
||||
### Modal state
|
||||
- [File chooser]: can be handled by the "browser_file_upload" tool`);
|
||||
expect(response).toHaveResponse({
|
||||
result: `Error: Tool "browser_click" does not handle the modal state.`,
|
||||
modalState: expect.stringContaining(`- [File chooser]: can be handled by the "browser_file_upload" tool`),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,7 +111,9 @@ test('clicking on download link emits download', async ({ startClient, server, m
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent('- link "Download" [ref=e2]');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- link "Download" [ref=e2]`),
|
||||
});
|
||||
await client.callTool({
|
||||
name: 'browser_click',
|
||||
arguments: {
|
||||
@@ -113,8 +121,9 @@ test('clicking on download link emits download', async ({ startClient, server, m
|
||||
ref: 'e2',
|
||||
},
|
||||
});
|
||||
await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toContainTextContent(`### Downloads
|
||||
- Downloaded file test.txt to ${testInfo.outputPath('output', 'test.txt')}`);
|
||||
await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toHaveResponse({
|
||||
downloads: `- Downloaded file test.txt to ${testInfo.outputPath('output', 'test.txt')}`,
|
||||
});
|
||||
});
|
||||
|
||||
test('navigating to download link emits download', async ({ startClient, server, mcpBrowser, mcpMode }, testInfo) => {
|
||||
@@ -136,5 +145,7 @@ test('navigating to download link emits download', async ({ startClient, server,
|
||||
arguments: {
|
||||
url: server.PREFIX + 'download',
|
||||
},
|
||||
})).toContainTextContent('### Downloads');
|
||||
})).toHaveResponse({
|
||||
downloads: expect.stringContaining(`- Downloaded file test.txt to`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -199,41 +199,14 @@ async function createTransport(args: string[], mcpMode: TestOptions['mcpMode']):
|
||||
type Response = Awaited<ReturnType<Client['callTool']>>;
|
||||
|
||||
export const expect = baseExpect.extend({
|
||||
toHaveTextContent(response: Response, content: string | RegExp) {
|
||||
toHaveResponse(response: Response, object: any) {
|
||||
const parsed = parseResponse(response);
|
||||
const isNot = this.isNot;
|
||||
try {
|
||||
const text = (response.content as any)[0].text;
|
||||
if (typeof content === 'string') {
|
||||
if (isNot)
|
||||
baseExpect(text.trim()).not.toBe(content.trim());
|
||||
else
|
||||
baseExpect(text.trim()).toBe(content.trim());
|
||||
} else {
|
||||
if (isNot)
|
||||
baseExpect(text).not.toMatch(content);
|
||||
else
|
||||
baseExpect(text).toMatch(content);
|
||||
}
|
||||
} catch (e) {
|
||||
return {
|
||||
pass: isNot,
|
||||
message: () => e.message,
|
||||
};
|
||||
}
|
||||
return {
|
||||
pass: !isNot,
|
||||
message: () => ``,
|
||||
};
|
||||
},
|
||||
|
||||
toContainTextContent(response: Response, content: string) {
|
||||
const isNot = this.isNot;
|
||||
try {
|
||||
const texts = (response.content as any).map(c => c.text).join('\n');
|
||||
if (isNot)
|
||||
expect(texts).not.toContain(content);
|
||||
expect(parsed).not.toEqual(expect.objectContaining(object));
|
||||
else
|
||||
expect(texts).toContain(content);
|
||||
expect(parsed).toEqual(expect.objectContaining(object));
|
||||
} catch (e) {
|
||||
return {
|
||||
pass: isNot,
|
||||
@@ -250,3 +223,46 @@ export const expect = baseExpect.extend({
|
||||
export function formatOutput(output: string): string[] {
|
||||
return output.split('\n').map(line => line.replace(/^pw:mcp:test /, '').replace(/user data dir.*/, 'user data dir').trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function parseResponse(response: any) {
|
||||
const text = (response as any).content[0].text;
|
||||
const sections = parseSections(text);
|
||||
|
||||
const result = sections.get('Result');
|
||||
const code = sections.get('Ran Playwright code');
|
||||
const tabs = sections.get('Open tabs');
|
||||
const pageState = sections.get('Page state');
|
||||
const consoleMessages = sections.get('New console messages');
|
||||
const modalState = sections.get('Modal state');
|
||||
const downloads = sections.get('Downloads');
|
||||
const codeNoFrame = code?.replace(/^```js\n/, '').replace(/\n```$/, '');
|
||||
const isError = response.isError;
|
||||
|
||||
return {
|
||||
result,
|
||||
code: codeNoFrame,
|
||||
tabs,
|
||||
pageState,
|
||||
consoleMessages,
|
||||
modalState,
|
||||
downloads,
|
||||
isError,
|
||||
};
|
||||
}
|
||||
|
||||
function parseSections(text: string): Map<string, string> {
|
||||
const sections = new Map<string, string>();
|
||||
const sectionHeaders = text.split(/^### /m).slice(1); // Remove empty first element
|
||||
|
||||
for (const section of sectionHeaders) {
|
||||
const firstNewlineIndex = section.indexOf('\n');
|
||||
if (firstNewlineIndex === -1)
|
||||
continue;
|
||||
|
||||
const sectionName = section.substring(0, firstNewlineIndex);
|
||||
const sectionContent = section.substring(firstNewlineIndex + 1).trim();
|
||||
sections.set(sectionName, sectionContent);
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ for (const mcpHeadless of [false, true]) {
|
||||
test.use({ mcpHeadless });
|
||||
test.skip(process.platform === 'linux', 'Auto-detection wont let this test run on linux');
|
||||
test.skip(({ mcpMode, mcpHeadless }) => mcpMode === 'docker' && !mcpHeadless, 'Headed mode is not supported in docker');
|
||||
|
||||
test('browser', async ({ client, server, mcpBrowser }) => {
|
||||
test.skip(!['chrome', 'msedge', 'chromium'].includes(mcpBrowser ?? ''), 'Only chrome is supported for this test');
|
||||
server.route('/', (req, res) => {
|
||||
@@ -40,11 +41,9 @@ for (const mcpHeadless of [false, true]) {
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).toContainTextContent(`Mozilla/5.0`);
|
||||
if (mcpHeadless)
|
||||
expect(response).toContainTextContent(`HeadlessChrome`);
|
||||
else
|
||||
expect(response).not.toContainTextContent(`HeadlessChrome`);
|
||||
expect(response).toHaveResponse({
|
||||
pageState: (mcpHeadless ? expect : expect.not).stringContaining(`HeadlessChrome`),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,9 +22,8 @@ test('stitched aria frames', async ({ client }) => {
|
||||
arguments: {
|
||||
url: `data:text/html,<h1>Hello</h1><iframe src="data:text/html,<button>World</button><main><iframe src='data:text/html,<p>Nested</p>'></iframe></main>"></iframe><iframe src="data:text/html,<h1>Should be invisible</h1>" style="display: none;"></iframe>`,
|
||||
},
|
||||
})).toContainTextContent(`
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]:
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]:
|
||||
- heading "Hello" [level=1] [ref=e2]
|
||||
- iframe [ref=e3]:
|
||||
- generic [active] [ref=f1e1]:
|
||||
@@ -32,7 +31,8 @@ test('stitched aria frames', async ({ client }) => {
|
||||
- main [ref=f1e3]:
|
||||
- iframe [ref=f1e4]:
|
||||
- paragraph [ref=f2e2]: Nested
|
||||
\`\`\``);
|
||||
\`\`\``),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_click',
|
||||
@@ -40,5 +40,7 @@ test('stitched aria frames', async ({ client }) => {
|
||||
element: 'World',
|
||||
ref: 'f1e2',
|
||||
},
|
||||
})).toContainTextContent(`// Click World`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.locator('iframe').first().contentFrame().getByRole('button', { name: 'World' }).click();`,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,5 +20,7 @@ test('browser_install', async ({ client, mcpBrowser }) => {
|
||||
test.skip(mcpBrowser !== 'chromium', 'Test only chromium');
|
||||
expect(await client.callTool({
|
||||
name: 'browser_install',
|
||||
})).toContainTextContent(`### No open tabs`);
|
||||
})).toHaveResponse({
|
||||
tabs: expect.stringContaining(`No open tabs`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,18 +27,17 @@ test('test reopen browser', async ({ startClient, server, mcpMode }) => {
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_close',
|
||||
})).toContainTextContent(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
await page.close()
|
||||
\`\`\`
|
||||
|
||||
### No open tabs
|
||||
Use the "browser_navigate" tool to navigate to a page first.`);
|
||||
})).toHaveResponse({
|
||||
code: `await page.close()`,
|
||||
tabs: `No open tabs. Use the "browser_navigate" tool to navigate to a page first.`,
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- generic [active] [ref=e1]: Hello, world!`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
});
|
||||
|
||||
await client.close();
|
||||
|
||||
@@ -68,7 +67,10 @@ test('executable path', async ({ startClient, server }) => {
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
});
|
||||
expect(response).toContainTextContent(`executable doesn't exist`);
|
||||
expect(response).toHaveResponse({
|
||||
result: expect.stringContaining(`executable doesn't exist`),
|
||||
isError: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('persistent context', async ({ startClient, server }) => {
|
||||
@@ -82,11 +84,12 @@ test('persistent context', async ({ startClient, server }) => {
|
||||
`, 'text/html');
|
||||
|
||||
const { client } = await startClient();
|
||||
const response = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Storage: NO`),
|
||||
});
|
||||
expect(response).toContainTextContent(`Storage: NO`);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
@@ -95,12 +98,12 @@ test('persistent context', async ({ startClient, server }) => {
|
||||
});
|
||||
|
||||
const { client: client2 } = await startClient();
|
||||
const response2 = await client2.callTool({
|
||||
expect(await client2.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Storage: YES`),
|
||||
});
|
||||
|
||||
expect(response2).toContainTextContent(`Storage: YES`);
|
||||
});
|
||||
|
||||
test('isolated context', async ({ startClient, server }) => {
|
||||
@@ -114,22 +117,24 @@ test('isolated context', async ({ startClient, server }) => {
|
||||
`, 'text/html');
|
||||
|
||||
const { client: client1 } = await startClient({ args: [`--isolated`] });
|
||||
const response = await client1.callTool({
|
||||
expect(await client1.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Storage: NO`),
|
||||
});
|
||||
expect(response).toContainTextContent(`Storage: NO`);
|
||||
|
||||
await client1.callTool({
|
||||
name: 'browser_close',
|
||||
});
|
||||
|
||||
const { client: client2 } = await startClient({ args: [`--isolated`] });
|
||||
const response2 = await client2.callTool({
|
||||
expect(await client2.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Storage: NO`),
|
||||
});
|
||||
expect(response2).toContainTextContent(`Storage: NO`);
|
||||
});
|
||||
|
||||
test('isolated context with storage state', async ({ startClient, server }, testInfo) => {
|
||||
@@ -155,9 +160,10 @@ test('isolated context with storage state', async ({ startClient, server }, test
|
||||
`--isolated`,
|
||||
`--storage-state=${storageStatePath}`,
|
||||
] });
|
||||
const response = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`Storage: session-value`),
|
||||
});
|
||||
expect(response).toContainTextContent(`Storage: session-value`);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,8 @@ test('browser_network_requests', async ({ client, server }) => {
|
||||
|
||||
await expect.poll(() => client.callTool({
|
||||
name: 'browser_network_requests',
|
||||
})).toHaveTextContent(`### Result
|
||||
[GET] ${`${server.PREFIX}`} => [200] OK
|
||||
[GET] ${`${server.PREFIX}json`} => [200] OK`);
|
||||
})).toHaveResponse({
|
||||
result: expect.stringContaining(`[GET] ${`${server.PREFIX}`} => [200] OK
|
||||
[GET] ${`${server.PREFIX}json`} => [200] OK`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,7 +27,10 @@ test('save as pdf unavailable', async ({ startClient, server }) => {
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_pdf_save',
|
||||
})).toHaveTextContent(/Tool \"browser_pdf_save\" not found/);
|
||||
})).toHaveResponse({
|
||||
result: 'Error: Tool "browser_pdf_save" not found',
|
||||
isError: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('save as pdf', async ({ startClient, mcpBrowser, server }, testInfo) => {
|
||||
@@ -40,12 +43,16 @@ test('save as pdf', async ({ startClient, mcpBrowser, server }, testInfo) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- generic [active] [ref=e1]: Hello, world!`);
|
||||
|
||||
const response = await client.callTool({
|
||||
name: 'browser_pdf_save',
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_pdf_save',
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`await page.pdf(`),
|
||||
result: expect.stringMatching(/Saved page as.*page-[^:]+.pdf/),
|
||||
});
|
||||
expect(response).toHaveTextContent(/Save page as.*page-[^:]+.pdf/);
|
||||
});
|
||||
|
||||
test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser, server }, testInfo) => {
|
||||
@@ -58,14 +65,19 @@ test('save as pdf (filename: output.pdf)', async ({ startClient, mcpBrowser, ser
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`- generic [active] [ref=e1]: Hello, world!`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [active] [ref=e1]: Hello, world!`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_pdf_save',
|
||||
arguments: {
|
||||
filename: 'output.pdf',
|
||||
},
|
||||
})).toContainTextContent(`output.pdf`);
|
||||
})).toHaveResponse({
|
||||
result: expect.stringContaining(`output.pdf`),
|
||||
code: expect.stringContaining(`await page.pdf(`),
|
||||
});
|
||||
|
||||
const files = [...fs.readdirSync(outputDir)];
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ test('browser_take_screenshot (viewport)', async ({ startClient, server }, testI
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -51,7 +53,9 @@ test('browser_take_screenshot (element)', async ({ startClient, server }, testIn
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`[ref=e1]`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`[ref=e1]`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -82,7 +86,9 @@ test('--output-dir should work', async ({ startClient, server }, testInfo) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -104,7 +110,9 @@ for (const raw of [undefined, true]) {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.PREFIX },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -144,7 +152,9 @@ test('browser_take_screenshot (filename: "output.jpeg")', async ({ startClient,
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -184,7 +194,9 @@ test('browser_take_screenshot (imageResponses=omit)', async ({ startClient, serv
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -209,7 +221,9 @@ test('browser_take_screenshot (fullPage: true)', async ({ startClient, server },
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -236,7 +250,9 @@ test('browser_take_screenshot (fullPage with element should error)', async ({ st
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`[ref=e1]`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`[ref=e1]`),
|
||||
});
|
||||
|
||||
const result = await client.callTool({
|
||||
name: 'browser_take_screenshot',
|
||||
@@ -259,7 +275,9 @@ test('browser_take_screenshot (viewport without snapshot)', async ({ startClient
|
||||
// Ensure we have a tab but don't navigate anywhere (no snapshot captured)
|
||||
expect(await client.callTool({
|
||||
name: 'browser_tab_list',
|
||||
})).toContainTextContent('about:blank');
|
||||
})).toHaveResponse({
|
||||
tabs: `- 0: (current) [] (about:blank)`,
|
||||
});
|
||||
|
||||
// This should work without requiring a snapshot since it's a viewport screenshot
|
||||
expect(await client.callTool({
|
||||
|
||||
@@ -30,99 +30,87 @@ async function createTab(client: Client, title: string, body: string) {
|
||||
test('list initial tabs', async ({ client }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_tab_list',
|
||||
})).toHaveTextContent(`### Open tabs
|
||||
- 0: (current) [] (about:blank)`);
|
||||
})).toHaveResponse({
|
||||
tabs: `- 0: (current) [] (about:blank)`,
|
||||
});
|
||||
});
|
||||
|
||||
test('list first tab', async ({ client }) => {
|
||||
await createTab(client, 'Tab one', 'Body one');
|
||||
expect(await client.callTool({
|
||||
name: 'browser_tab_list',
|
||||
})).toHaveTextContent(`### Open tabs
|
||||
- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`);
|
||||
})).toHaveResponse({
|
||||
tabs: `- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`,
|
||||
});
|
||||
});
|
||||
|
||||
test('create new tab', async ({ client }) => {
|
||||
const result = await createTab(client, 'Tab one', 'Body one');
|
||||
expect(result).toContainTextContent(`### Open tabs
|
||||
- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||
`);
|
||||
|
||||
expect(result).toContainTextContent(`
|
||||
### Page state
|
||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
expect(await createTab(client, 'Tab one', 'Body one')).toHaveResponse({
|
||||
tabs: `- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`,
|
||||
pageState: expect.stringContaining(`- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
- Page Title: Tab one
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Body one
|
||||
\`\`\``);
|
||||
\`\`\``),
|
||||
});
|
||||
|
||||
const result2 = await createTab(client, 'Tab two', 'Body two');
|
||||
expect(result2).toContainTextContent(`### Open tabs
|
||||
- 0: [] (about:blank)
|
||||
expect(await createTab(client, 'Tab two', 'Body two')).toHaveResponse({
|
||||
tabs: `- 0: [] (about:blank)
|
||||
- 1: [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||
- 2: (current) [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)
|
||||
`);
|
||||
|
||||
expect(result2).toContainTextContent(`
|
||||
### Page state
|
||||
- Page URL: data:text/html,<title>Tab two</title><body>Body two</body>
|
||||
- 2: (current) [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)`,
|
||||
pageState: expect.stringContaining(`- Page URL: data:text/html,<title>Tab two</title><body>Body two</body>
|
||||
- Page Title: Tab two
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Body two
|
||||
\`\`\``);
|
||||
\`\`\``),
|
||||
});
|
||||
});
|
||||
|
||||
test('select tab', async ({ client }) => {
|
||||
await createTab(client, 'Tab one', 'Body one');
|
||||
await createTab(client, 'Tab two', 'Body two');
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_tab_select',
|
||||
arguments: {
|
||||
index: 1,
|
||||
},
|
||||
});
|
||||
expect(result).toContainTextContent(`### Open tabs
|
||||
- 0: [] (about:blank)
|
||||
})).toHaveResponse({
|
||||
tabs: `- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||
- 2: [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)`);
|
||||
|
||||
expect(result).toContainTextContent(`
|
||||
### Page state
|
||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
- 2: [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)`,
|
||||
pageState: expect.stringContaining(`- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
- Page Title: Tab one
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Body one
|
||||
\`\`\``);
|
||||
\`\`\``),
|
||||
});
|
||||
});
|
||||
|
||||
test('close tab', async ({ client }) => {
|
||||
await createTab(client, 'Tab one', 'Body one');
|
||||
await createTab(client, 'Tab two', 'Body two');
|
||||
|
||||
const result = await client.callTool({
|
||||
expect(await client.callTool({
|
||||
name: 'browser_tab_close',
|
||||
arguments: {
|
||||
index: 2,
|
||||
},
|
||||
});
|
||||
expect(result).toContainTextContent(`### Open tabs
|
||||
- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`);
|
||||
|
||||
expect(result).toContainTextContent(`
|
||||
### Page state
|
||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
})).toHaveResponse({
|
||||
tabs: `- 0: [] (about:blank)
|
||||
- 1: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)`,
|
||||
pageState: expect.stringContaining(`- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||
- Page Title: Tab one
|
||||
- Page Snapshot:
|
||||
\`\`\`yaml
|
||||
- generic [active] [ref=e1]: Body one
|
||||
\`\`\``);
|
||||
\`\`\``),
|
||||
});
|
||||
});
|
||||
|
||||
test('reuse first tab when navigating', async ({ startClient, cdpServer, server }) => {
|
||||
|
||||
@@ -29,7 +29,9 @@ test('check that trace is saved', async ({ startClient, server, mcpMode }, testI
|
||||
expect(await client.callTool({
|
||||
name: 'browser_navigate',
|
||||
arguments: { url: server.HELLO_WORLD },
|
||||
})).toContainTextContent(`Navigate to http://localhost`);
|
||||
})).toHaveResponse({
|
||||
code: expect.stringContaining(`page.goto('http://localhost`),
|
||||
});
|
||||
|
||||
expect(fs.existsSync(path.join(outputDir, 'traces', 'trace.trace'))).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -41,13 +41,18 @@ test('browser_type', async ({ client, server }) => {
|
||||
submit: true,
|
||||
},
|
||||
});
|
||||
expect(response).toContainTextContent(`fill('Hi!');`);
|
||||
expect(response).toContainTextContent(`- textbox`);
|
||||
expect(response).toHaveResponse({
|
||||
code: `await page.getByRole('textbox').fill('Hi!');
|
||||
await page.getByRole('textbox').press('Enter');`,
|
||||
pageState: expect.stringContaining(`- textbox`),
|
||||
});
|
||||
}
|
||||
|
||||
expect(await client.callTool({
|
||||
name: 'browser_console_messages',
|
||||
})).toHaveTextContent(/\[LOG\] Key pressed: Enter , Text: Hi!/);
|
||||
})).toHaveResponse({
|
||||
result: expect.stringContaining(`[LOG] Key pressed: Enter , Text: Hi!`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_type (slowly)', async ({ client, server }) => {
|
||||
@@ -72,15 +77,23 @@ test('browser_type (slowly)', async ({ client, server }) => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(response).toContainTextContent(`pressSequentially('Hi!');`);
|
||||
expect(response).toContainTextContent(`- textbox`);
|
||||
expect(response).toHaveResponse({
|
||||
code: `await page.getByRole('textbox').pressSequentially('Hi!');`,
|
||||
pageState: expect.stringContaining(`- textbox`),
|
||||
});
|
||||
}
|
||||
const response = await client.callTool({
|
||||
name: 'browser_console_messages',
|
||||
});
|
||||
expect(response).toHaveTextContent(/\[LOG\] Key pressed: H Text: /);
|
||||
expect(response).toHaveTextContent(/\[LOG\] Key pressed: i Text: H/);
|
||||
expect(response).toHaveTextContent(/\[LOG\] Key pressed: ! Text: Hi/);
|
||||
expect(response).toHaveResponse({
|
||||
result: expect.stringContaining(`[LOG] Key pressed: H Text: `),
|
||||
});
|
||||
expect(response).toHaveResponse({
|
||||
result: expect.stringContaining(`[LOG] Key pressed: i Text: H`),
|
||||
});
|
||||
expect(response).toHaveResponse({
|
||||
result: expect.stringContaining(`[LOG] Key pressed: ! Text: Hi`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_type (no submit)', async ({ client, server }) => {
|
||||
@@ -95,7 +108,9 @@ test('browser_type (no submit)', async ({ client, server }) => {
|
||||
url: server.PREFIX,
|
||||
},
|
||||
});
|
||||
expect(response).toContainTextContent(`- textbox`);
|
||||
expect(response).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- textbox`),
|
||||
});
|
||||
}
|
||||
{
|
||||
const response = await client.callTool({
|
||||
@@ -106,14 +121,18 @@ test('browser_type (no submit)', async ({ client, server }) => {
|
||||
text: 'Hi!',
|
||||
},
|
||||
});
|
||||
expect(response).toContainTextContent(`fill('Hi!');`);
|
||||
// Should yield no snapshot.
|
||||
expect(response).not.toContainTextContent(`- textbox`);
|
||||
expect(response).toHaveResponse({
|
||||
code: expect.stringContaining(`fill('Hi!')`),
|
||||
// Should yield no snapshot.
|
||||
pageState: expect.not.stringContaining(`- textbox`),
|
||||
});
|
||||
}
|
||||
{
|
||||
const response = await client.callTool({
|
||||
name: 'browser_console_messages',
|
||||
});
|
||||
expect(response).toHaveTextContent(/\[LOG\] New value: Hi!/);
|
||||
expect(response).toHaveResponse({
|
||||
result: expect.stringContaining(`[LOG] New value: Hi!`),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -47,7 +47,9 @@ test('browser_wait_for(text)', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_wait_for',
|
||||
arguments: { text: 'Text to appear' },
|
||||
})).toContainTextContent(`- generic [ref=e3]: Text to appear`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [ref=e3]: Text to appear`),
|
||||
});
|
||||
});
|
||||
|
||||
test('browser_wait_for(textGone)', async ({ client, server }) => {
|
||||
@@ -81,5 +83,7 @@ test('browser_wait_for(textGone)', async ({ client, server }) => {
|
||||
expect(await client.callTool({
|
||||
name: 'browser_wait_for',
|
||||
arguments: { textGone: 'Text to disappear' },
|
||||
})).toContainTextContent(`- generic [ref=e3]: Text to appear`);
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`- generic [ref=e3]: Text to appear`),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,5 +34,7 @@ test('do not falsely advertise user agent as a test driver', async ({ client, se
|
||||
arguments: {
|
||||
url: server.PREFIX,
|
||||
},
|
||||
})).toContainTextContent('webdriver: false');
|
||||
})).toHaveResponse({
|
||||
pageState: expect.stringContaining(`webdriver: false`),
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user