mirror of
https://github.com/microsoft/playwright-mcp.git
synced 2025-07-27 00:52:27 +08:00
chore: generalize status & action as code (#188)
This commit is contained in:
parent
4a19e18999
commit
795a9d578a
@ -33,7 +33,6 @@ type PageOrFrameLocator = playwright.Page | playwright.FrameLocator;
|
|||||||
type RunOptions = {
|
type RunOptions = {
|
||||||
captureSnapshot?: boolean;
|
captureSnapshot?: boolean;
|
||||||
waitForCompletion?: boolean;
|
waitForCompletion?: boolean;
|
||||||
status?: string;
|
|
||||||
noClearFileChooser?: boolean;
|
noClearFileChooser?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,8 +78,8 @@ export class Context {
|
|||||||
|
|
||||||
async listTabs(): Promise<string> {
|
async listTabs(): Promise<string> {
|
||||||
if (!this._tabs.length)
|
if (!this._tabs.length)
|
||||||
return 'No tabs open';
|
return '### No tabs open';
|
||||||
const lines: string[] = ['Open tabs:'];
|
const lines: string[] = ['### Open tabs'];
|
||||||
for (let i = 0; i < this._tabs.length; i++) {
|
for (let i = 0; i < this._tabs.length; i++) {
|
||||||
const tab = this._tabs[i];
|
const tab = this._tabs[i];
|
||||||
const title = await tab.page.title();
|
const title = await tab.page.title();
|
||||||
@ -172,6 +171,10 @@ export class Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RunResult = {
|
||||||
|
code: string[];
|
||||||
|
};
|
||||||
|
|
||||||
class Tab {
|
class Tab {
|
||||||
readonly context: Context;
|
readonly context: Context;
|
||||||
readonly page: playwright.Page;
|
readonly page: playwright.Page;
|
||||||
@ -207,33 +210,33 @@ class Tab {
|
|||||||
await this.page.waitForLoadState('load', { timeout: 5000 }).catch(() => {});
|
await this.page.waitForLoadState('load', { timeout: 5000 }).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(callback: (tab: Tab) => Promise<void | string>, options?: RunOptions): Promise<ToolResult> {
|
async run(callback: (tab: Tab) => Promise<RunResult>, options?: RunOptions): Promise<ToolResult> {
|
||||||
let actionCode: string | undefined;
|
let runResult: RunResult | undefined;
|
||||||
try {
|
try {
|
||||||
if (!options?.noClearFileChooser)
|
if (!options?.noClearFileChooser)
|
||||||
this._fileChooser = undefined;
|
this._fileChooser = undefined;
|
||||||
if (options?.waitForCompletion)
|
if (options?.waitForCompletion)
|
||||||
actionCode = await waitForCompletion(this.page, () => callback(this)) ?? undefined;
|
runResult = await waitForCompletion(this.page, () => callback(this)) ?? undefined;
|
||||||
else
|
else
|
||||||
actionCode = await callback(this) ?? undefined;
|
runResult = await callback(this) ?? undefined;
|
||||||
} finally {
|
} finally {
|
||||||
if (options?.captureSnapshot)
|
if (options?.captureSnapshot)
|
||||||
this._snapshot = await PageSnapshot.create(this.page);
|
this._snapshot = await PageSnapshot.create(this.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
if (options?.status)
|
result.push(`- Ran code:
|
||||||
result.push(options.status, '');
|
\`\`\`js
|
||||||
|
${runResult.code.join('\n')}
|
||||||
|
\`\`\`
|
||||||
|
`);
|
||||||
|
|
||||||
if (this.context.tabs().length > 1)
|
if (this.context.tabs().length > 1)
|
||||||
result.push(await this.context.listTabs(), '');
|
result.push(await this.context.listTabs(), '');
|
||||||
|
|
||||||
if (actionCode)
|
|
||||||
result.push('- Action: ' + actionCode, '');
|
|
||||||
|
|
||||||
if (this._snapshot) {
|
if (this._snapshot) {
|
||||||
if (this.context.tabs().length > 1)
|
if (this.context.tabs().length > 1)
|
||||||
result.push('Current tab:');
|
result.push('### Current tab');
|
||||||
result.push(this._snapshot.text({ hasFileChooser: !!this._fileChooser }));
|
result.push(this._snapshot.text({ hasFileChooser: !!this._fileChooser }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,14 +248,14 @@ class Tab {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async runAndWait(callback: (tab: Tab) => Promise<void | string>, options?: RunOptions): Promise<ToolResult> {
|
async runAndWait(callback: (tab: Tab) => Promise<RunResult>, options?: RunOptions): Promise<ToolResult> {
|
||||||
return await this.run(callback, {
|
return await this.run(callback, {
|
||||||
waitForCompletion: true,
|
waitForCompletion: true,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async runAndWaitWithSnapshot(callback: (snapshot: PageSnapshot) => Promise<void | string>, options?: RunOptions): Promise<ToolResult> {
|
async runAndWaitWithSnapshot(callback: (snapshot: PageSnapshot) => Promise<RunResult>, options?: RunOptions): Promise<ToolResult> {
|
||||||
return await this.run(tab => callback(tab.lastSnapshot()), {
|
return await this.run(tab => callback(tab.lastSnapshot()), {
|
||||||
captureSnapshot: true,
|
captureSnapshot: true,
|
||||||
waitForCompletion: true,
|
waitForCompletion: true,
|
||||||
|
@ -78,13 +78,16 @@ const resize: ToolFactory = captureSnapshot => ({
|
|||||||
const validatedParams = resizeSchema.parse(params);
|
const validatedParams = resizeSchema.parse(params);
|
||||||
|
|
||||||
const tab = context.currentTab();
|
const tab = context.currentTab();
|
||||||
return await tab.run(
|
return await tab.run(async tab => {
|
||||||
tab => tab.page.setViewportSize({ width: validatedParams.width, height: validatedParams.height }),
|
await tab.page.setViewportSize({ width: validatedParams.width, height: validatedParams.height });
|
||||||
{
|
const code = [
|
||||||
status: `Resized browser window`,
|
`// Resize browser window to ${validatedParams.width}x${validatedParams.height}`,
|
||||||
|
`await page.setViewportSize({ width: ${validatedParams.width}, height: ${validatedParams.height} });`
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
|
}, {
|
||||||
captureSnapshot,
|
captureSnapshot,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,8 +35,11 @@ const uploadFile: ToolFactory = captureSnapshot => ({
|
|||||||
const tab = context.currentTab();
|
const tab = context.currentTab();
|
||||||
return await tab.runAndWait(async () => {
|
return await tab.runAndWait(async () => {
|
||||||
await tab.submitFileChooser(validatedParams.paths);
|
await tab.submitFileChooser(validatedParams.paths);
|
||||||
|
const code = [
|
||||||
|
`// <internal code to chose files ${validatedParams.paths.join(', ')}`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
}, {
|
}, {
|
||||||
status: `Chose files ${validatedParams.paths.join(', ')}`,
|
|
||||||
captureSnapshot,
|
captureSnapshot,
|
||||||
noClearFileChooser: true,
|
noClearFileChooser: true,
|
||||||
});
|
});
|
||||||
|
@ -34,9 +34,12 @@ const pressKey: ToolFactory = captureSnapshot => ({
|
|||||||
const validatedParams = pressKeySchema.parse(params);
|
const validatedParams = pressKeySchema.parse(params);
|
||||||
return await context.currentTab().runAndWait(async tab => {
|
return await context.currentTab().runAndWait(async tab => {
|
||||||
await tab.page.keyboard.press(validatedParams.key);
|
await tab.page.keyboard.press(validatedParams.key);
|
||||||
return `await page.keyboard.press('${validatedParams.key}');`;
|
const code = [
|
||||||
|
`// Press ${validatedParams.key}`,
|
||||||
|
`await page.keyboard.press('${validatedParams.key}');`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
}, {
|
}, {
|
||||||
status: `Pressed key ${validatedParams.key}`,
|
|
||||||
captureSnapshot,
|
captureSnapshot,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -35,9 +35,12 @@ const navigate: ToolFactory = captureSnapshot => ({
|
|||||||
const currentTab = await context.ensureTab();
|
const currentTab = await context.ensureTab();
|
||||||
return await currentTab.run(async tab => {
|
return await currentTab.run(async tab => {
|
||||||
await tab.navigate(validatedParams.url);
|
await tab.navigate(validatedParams.url);
|
||||||
return `await page.goto('${validatedParams.url}');`;
|
const code = [
|
||||||
|
`// Navigate to ${validatedParams.url}`,
|
||||||
|
`await page.goto('${validatedParams.url}');`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
}, {
|
}, {
|
||||||
status: `Navigated to ${validatedParams.url}`,
|
|
||||||
captureSnapshot,
|
captureSnapshot,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -55,9 +58,12 @@ const goBack: ToolFactory = snapshot => ({
|
|||||||
handle: async context => {
|
handle: async context => {
|
||||||
return await context.currentTab().runAndWait(async tab => {
|
return await context.currentTab().runAndWait(async tab => {
|
||||||
await tab.page.goBack();
|
await tab.page.goBack();
|
||||||
return `await page.goBack();`;
|
const code = [
|
||||||
|
`// Navigate back`,
|
||||||
|
`await page.goBack();`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
}, {
|
}, {
|
||||||
status: 'Navigated back',
|
|
||||||
captureSnapshot: snapshot,
|
captureSnapshot: snapshot,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -75,9 +81,12 @@ const goForward: ToolFactory = snapshot => ({
|
|||||||
handle: async context => {
|
handle: async context => {
|
||||||
return await context.currentTab().runAndWait(async tab => {
|
return await context.currentTab().runAndWait(async tab => {
|
||||||
await tab.page.goForward();
|
await tab.page.goForward();
|
||||||
return `await page.goForward();`;
|
const code = [
|
||||||
|
`// Navigate forward`,
|
||||||
|
`await page.goForward();`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
}, {
|
}, {
|
||||||
status: 'Navigated forward',
|
|
||||||
captureSnapshot: snapshot,
|
captureSnapshot: snapshot,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -79,11 +79,16 @@ const click: Tool = {
|
|||||||
handle: async (context, params) => {
|
handle: async (context, params) => {
|
||||||
return await context.currentTab().runAndWait(async tab => {
|
return await context.currentTab().runAndWait(async tab => {
|
||||||
const validatedParams = clickSchema.parse(params);
|
const validatedParams = clickSchema.parse(params);
|
||||||
|
const code = [
|
||||||
|
`// Click mouse at coordinates (${validatedParams.x}, ${validatedParams.y})`,
|
||||||
|
`await page.mouse.move(${validatedParams.x}, ${validatedParams.y});`,
|
||||||
|
`await page.mouse.down();`,
|
||||||
|
`await page.mouse.up();`,
|
||||||
|
];
|
||||||
await tab.page.mouse.move(validatedParams.x, validatedParams.y);
|
await tab.page.mouse.move(validatedParams.x, validatedParams.y);
|
||||||
await tab.page.mouse.down();
|
await tab.page.mouse.down();
|
||||||
await tab.page.mouse.up();
|
await tab.page.mouse.up();
|
||||||
}, {
|
return { code };
|
||||||
status: 'Clicked mouse',
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -110,8 +115,14 @@ const drag: Tool = {
|
|||||||
await tab.page.mouse.down();
|
await tab.page.mouse.down();
|
||||||
await tab.page.mouse.move(validatedParams.endX, validatedParams.endY);
|
await tab.page.mouse.move(validatedParams.endX, validatedParams.endY);
|
||||||
await tab.page.mouse.up();
|
await tab.page.mouse.up();
|
||||||
}, {
|
const code = [
|
||||||
status: `Dragged mouse from (${validatedParams.startX}, ${validatedParams.startY}) to (${validatedParams.endX}, ${validatedParams.endY})`,
|
`// Drag mouse from (${validatedParams.startX}, ${validatedParams.startY}) to (${validatedParams.endX}, ${validatedParams.endY})`,
|
||||||
|
`await page.mouse.move(${validatedParams.startX}, ${validatedParams.startY});`,
|
||||||
|
`await page.mouse.down();`,
|
||||||
|
`await page.mouse.move(${validatedParams.endX}, ${validatedParams.endY});`,
|
||||||
|
`await page.mouse.up();`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -132,11 +143,17 @@ const type: Tool = {
|
|||||||
handle: async (context, params) => {
|
handle: async (context, params) => {
|
||||||
const validatedParams = typeSchema.parse(params);
|
const validatedParams = typeSchema.parse(params);
|
||||||
return await context.currentTab().runAndWait(async tab => {
|
return await context.currentTab().runAndWait(async tab => {
|
||||||
|
const code = [
|
||||||
|
`// Type ${validatedParams.text}`,
|
||||||
|
`await page.keyboard.type('${validatedParams.text}');`,
|
||||||
|
];
|
||||||
await tab.page.keyboard.type(validatedParams.text);
|
await tab.page.keyboard.type(validatedParams.text);
|
||||||
if (validatedParams.submit)
|
if (validatedParams.submit) {
|
||||||
|
code.push(`// Submit text`);
|
||||||
|
code.push(`await page.keyboard.press('Enter');`);
|
||||||
await tab.page.keyboard.press('Enter');
|
await tab.page.keyboard.press('Enter');
|
||||||
}, {
|
}
|
||||||
status: `Typed text "${validatedParams.text}"`,
|
return { code };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,10 @@ const snapshot: Tool = {
|
|||||||
|
|
||||||
handle: async context => {
|
handle: async context => {
|
||||||
const tab = await context.ensureTab();
|
const tab = await context.ensureTab();
|
||||||
return await tab.run(async () => {}, { captureSnapshot: true });
|
return await tab.run(async () => {
|
||||||
|
const code = [`// <internal code to capture accessibility snapshot>`];
|
||||||
|
return { code };
|
||||||
|
}, { captureSnapshot: true });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,11 +56,12 @@ const click: Tool = {
|
|||||||
const validatedParams = elementSchema.parse(params);
|
const validatedParams = elementSchema.parse(params);
|
||||||
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
||||||
const locator = snapshot.refLocator(validatedParams.ref);
|
const locator = snapshot.refLocator(validatedParams.ref);
|
||||||
const action = `await page.${await generateLocator(locator)}.click();`;
|
const code = [
|
||||||
|
`// Click ${validatedParams.element}`,
|
||||||
|
`await page.${await generateLocator(locator)}.click();`
|
||||||
|
];
|
||||||
await locator.click();
|
await locator.click();
|
||||||
return action;
|
return { code };
|
||||||
}, {
|
|
||||||
status: `Clicked "${validatedParams.element}"`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -82,11 +86,12 @@ const drag: Tool = {
|
|||||||
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
||||||
const startLocator = snapshot.refLocator(validatedParams.startRef);
|
const startLocator = snapshot.refLocator(validatedParams.startRef);
|
||||||
const endLocator = snapshot.refLocator(validatedParams.endRef);
|
const endLocator = snapshot.refLocator(validatedParams.endRef);
|
||||||
const action = `await page.${await generateLocator(startLocator)}.dragTo(page.${await generateLocator(endLocator)});`;
|
const code = [
|
||||||
|
`// Drag ${validatedParams.startElement} to ${validatedParams.endElement}`,
|
||||||
|
`await page.${await generateLocator(startLocator)}.dragTo(page.${await generateLocator(endLocator)});`
|
||||||
|
];
|
||||||
await startLocator.dragTo(endLocator);
|
await startLocator.dragTo(endLocator);
|
||||||
return action;
|
return { code };
|
||||||
}, {
|
|
||||||
status: `Dragged "${validatedParams.startElement}" to "${validatedParams.endElement}"`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -103,11 +108,12 @@ const hover: Tool = {
|
|||||||
const validatedParams = elementSchema.parse(params);
|
const validatedParams = elementSchema.parse(params);
|
||||||
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
||||||
const locator = snapshot.refLocator(validatedParams.ref);
|
const locator = snapshot.refLocator(validatedParams.ref);
|
||||||
const action = `await page.${await generateLocator(locator)}.hover();`;
|
const code = [
|
||||||
|
`// Hover over ${validatedParams.element}`,
|
||||||
|
`await page.${await generateLocator(locator)}.hover();`
|
||||||
|
];
|
||||||
await locator.hover();
|
await locator.hover();
|
||||||
return action;
|
return { code };
|
||||||
}, {
|
|
||||||
status: `Hovered over "${validatedParams.element}"`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -131,21 +137,22 @@ const type: Tool = {
|
|||||||
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
||||||
const locator = snapshot.refLocator(validatedParams.ref);
|
const locator = snapshot.refLocator(validatedParams.ref);
|
||||||
|
|
||||||
let action = '';
|
const code: string[] = [];
|
||||||
if (validatedParams.slowly) {
|
if (validatedParams.slowly) {
|
||||||
action = `await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(validatedParams.text)});`;
|
code.push(`// Press "${validatedParams.text}" sequentially into "${validatedParams.element}"`);
|
||||||
|
code.push(`await page.${await generateLocator(locator)}.pressSequentially(${javascript.quote(validatedParams.text)});`);
|
||||||
await locator.pressSequentially(validatedParams.text);
|
await locator.pressSequentially(validatedParams.text);
|
||||||
} else {
|
} else {
|
||||||
action = `await page.${await generateLocator(locator)}.fill(${javascript.quote(validatedParams.text)});`;
|
code.push(`// Fill "${validatedParams.text}" into "${validatedParams.element}"`);
|
||||||
|
code.push(`await page.${await generateLocator(locator)}.fill(${javascript.quote(validatedParams.text)});`);
|
||||||
await locator.fill(validatedParams.text);
|
await locator.fill(validatedParams.text);
|
||||||
}
|
}
|
||||||
if (validatedParams.submit) {
|
if (validatedParams.submit) {
|
||||||
action += `\nawait page.${await generateLocator(locator)}.press('Enter');`;
|
code.push(`// Submit text`);
|
||||||
|
code.push(`await page.${await generateLocator(locator)}.press('Enter');`);
|
||||||
await locator.press('Enter');
|
await locator.press('Enter');
|
||||||
}
|
}
|
||||||
return action;
|
return { code };
|
||||||
}, {
|
|
||||||
status: `Typed "${validatedParams.text}" into "${validatedParams.element}"`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -166,11 +173,12 @@ const selectOption: Tool = {
|
|||||||
const validatedParams = selectOptionSchema.parse(params);
|
const validatedParams = selectOptionSchema.parse(params);
|
||||||
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
return await context.currentTab().runAndWaitWithSnapshot(async snapshot => {
|
||||||
const locator = snapshot.refLocator(validatedParams.ref);
|
const locator = snapshot.refLocator(validatedParams.ref);
|
||||||
const action = `await page.${await generateLocator(locator)}.selectOption(${javascript.formatObject(validatedParams.values)});`;
|
const code = [
|
||||||
|
`// Select options [${validatedParams.values.join(', ')}] in ${validatedParams.element}`,
|
||||||
|
`await page.${await generateLocator(locator)}.selectOption(${javascript.formatObject(validatedParams.values)});`
|
||||||
|
];
|
||||||
await locator.selectOption(validatedParams.values);
|
await locator.selectOption(validatedParams.values);
|
||||||
return action;
|
return { code };
|
||||||
}, {
|
|
||||||
status: `Selected option in "${validatedParams.element}"`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -51,7 +51,12 @@ const selectTab: ToolFactory = captureSnapshot => ({
|
|||||||
const validatedParams = selectTabSchema.parse(params);
|
const validatedParams = selectTabSchema.parse(params);
|
||||||
await context.selectTab(validatedParams.index);
|
await context.selectTab(validatedParams.index);
|
||||||
const currentTab = await context.ensureTab();
|
const currentTab = await context.ensureTab();
|
||||||
return await currentTab.run(async () => {}, { captureSnapshot });
|
return await currentTab.run(async () => {
|
||||||
|
const code = [
|
||||||
|
`// <internal code to select tab ${validatedParams.index}>`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
|
}, { captureSnapshot });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -71,7 +76,12 @@ const newTab: Tool = {
|
|||||||
await context.newTab();
|
await context.newTab();
|
||||||
if (validatedParams.url)
|
if (validatedParams.url)
|
||||||
await context.currentTab().navigate(validatedParams.url);
|
await context.currentTab().navigate(validatedParams.url);
|
||||||
return await context.currentTab().run(async () => {}, { captureSnapshot: true });
|
return await context.currentTab().run(async () => {
|
||||||
|
const code = [
|
||||||
|
`// <internal code to open a new tab>`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
|
}, { captureSnapshot: true });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,8 +100,14 @@ const closeTab: ToolFactory = captureSnapshot => ({
|
|||||||
const validatedParams = closeTabSchema.parse(params);
|
const validatedParams = closeTabSchema.parse(params);
|
||||||
await context.closeTab(validatedParams.index);
|
await context.closeTab(validatedParams.index);
|
||||||
const currentTab = context.currentTab();
|
const currentTab = context.currentTab();
|
||||||
if (currentTab)
|
if (currentTab) {
|
||||||
return await currentTab.run(async () => {}, { captureSnapshot });
|
return await currentTab.run(async () => {
|
||||||
|
const code = [
|
||||||
|
`// <internal code to close tab ${validatedParams.index}>`,
|
||||||
|
];
|
||||||
|
return { code };
|
||||||
|
}, { captureSnapshot });
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
content: [{
|
content: [{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
@ -24,9 +24,11 @@ test('browser_navigate', async ({ client }) => {
|
|||||||
url: 'data:text/html,<html><title>Title</title><body>Hello, world!</body></html>',
|
url: 'data:text/html,<html><title>Title</title><body>Hello, world!</body></html>',
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Navigated to data:text/html,<html><title>Title</title><body>Hello, world!</body></html>
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
- Action: await page.goto('data:text/html,<html><title>Title</title><body>Hello, world!</body></html>');
|
// Navigate to data:text/html,<html><title>Title</title><body>Hello, world!</body></html>
|
||||||
|
await page.goto('data:text/html,<html><title>Title</title><body>Hello, world!</body></html>');
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
- Page URL: data:text/html,<html><title>Title</title><body>Hello, world!</body></html>
|
- Page URL: data:text/html,<html><title>Title</title><body>Hello, world!</body></html>
|
||||||
- Page Title: Title
|
- Page Title: Title
|
||||||
@ -53,9 +55,11 @@ test('browser_click', async ({ client }) => {
|
|||||||
ref: 's1e3',
|
ref: 's1e3',
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Clicked "Submit button"
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
- Action: await page.getByRole('button', { name: 'Submit' }).click();
|
// Click Submit button
|
||||||
|
await page.getByRole('button', { name: 'Submit' }).click();
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
- Page URL: data:text/html,<html><title>Title</title><button>Submit</button></html>
|
- Page URL: data:text/html,<html><title>Title</title><button>Submit</button></html>
|
||||||
- Page Title: Title
|
- Page Title: Title
|
||||||
@ -83,9 +87,11 @@ test('browser_select_option', async ({ client }) => {
|
|||||||
values: ['bar'],
|
values: ['bar'],
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Selected option in "Select"
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
- Action: await page.getByRole('combobox').selectOption(['bar']);
|
// Select options [bar] in Select
|
||||||
|
await page.getByRole('combobox').selectOption(['bar']);
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
- Page URL: data:text/html,<html><title>Title</title><select><option value="foo">Foo</option><option value="bar">Bar</option></select></html>
|
- Page URL: data:text/html,<html><title>Title</title><select><option value="foo">Foo</option><option value="bar">Bar</option></select></html>
|
||||||
- Page Title: Title
|
- Page Title: Title
|
||||||
@ -114,9 +120,11 @@ test('browser_select_option (multiple)', async ({ client }) => {
|
|||||||
values: ['bar', 'baz'],
|
values: ['bar', 'baz'],
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Selected option in "Select"
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
- Action: await page.getByRole('listbox').selectOption(['bar', 'baz']);
|
// Select options [bar, baz] in Select
|
||||||
|
await page.getByRole('listbox').selectOption(['bar', 'baz']);
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
- Page URL: data:text/html,<html><title>Title</title><select multiple><option value="foo">Foo</option><option value="bar">Bar</option><option value="baz">Baz</option></select></html>
|
- Page URL: data:text/html,<html><title>Title</title><select multiple><option value="foo">Foo</option><option value="bar">Bar</option><option value="baz">Baz</option></select></html>
|
||||||
- Page Title: Title
|
- Page Title: Title
|
||||||
@ -260,6 +268,10 @@ test('browser_resize', async ({ client }) => {
|
|||||||
height: 780,
|
height: 780,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(response).toContainTextContent('Resized browser window');
|
expect(response).toContainTextContent(`- Ran 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');
|
await expect.poll(() => client.callTool({ name: 'browser_snapshot' })).toContainTextContent('Window size: 390x780');
|
||||||
});
|
});
|
||||||
|
@ -41,6 +41,11 @@ test('cdp server reuse tab', async ({ cdpEndpoint, startClient }) => {
|
|||||||
name: 'browser_snapshot',
|
name: 'browser_snapshot',
|
||||||
arguments: {},
|
arguments: {},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
|
// <internal code to capture accessibility snapshot>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
- Page URL: data:text/html,hello world
|
- Page URL: data:text/html,hello world
|
||||||
- Page Title:
|
- Page Title:
|
||||||
- Page Snapshot
|
- Page Snapshot
|
||||||
|
@ -39,5 +39,5 @@ test('stitched aria frames', async ({ client }) => {
|
|||||||
element: 'World',
|
element: 'World',
|
||||||
ref: 'f1s1e3',
|
ref: 'f1s1e3',
|
||||||
},
|
},
|
||||||
})).toContainTextContent('Clicked "World"');
|
})).toContainTextContent(`// Click World`);
|
||||||
});
|
});
|
||||||
|
@ -31,11 +31,16 @@ async function createTab(client: Client, title: string, body: string) {
|
|||||||
|
|
||||||
test('create new tab', async ({ client }) => {
|
test('create new tab', async ({ client }) => {
|
||||||
expect(await createTab(client, 'Tab one', 'Body one')).toHaveTextContent(`
|
expect(await createTab(client, 'Tab one', 'Body one')).toHaveTextContent(`
|
||||||
Open tabs:
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
|
// <internal code to open a new tab>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Open tabs
|
||||||
- 1: [] (about:blank)
|
- 1: [] (about:blank)
|
||||||
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||||
|
|
||||||
Current tab:
|
### Current tab
|
||||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||||
- Page Title: Tab one
|
- Page Title: Tab one
|
||||||
- Page Snapshot
|
- Page Snapshot
|
||||||
@ -44,12 +49,17 @@ Current tab:
|
|||||||
\`\`\``);
|
\`\`\``);
|
||||||
|
|
||||||
expect(await createTab(client, 'Tab two', 'Body two')).toHaveTextContent(`
|
expect(await createTab(client, 'Tab two', 'Body two')).toHaveTextContent(`
|
||||||
Open tabs:
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
|
// <internal code to open a new tab>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Open tabs
|
||||||
- 1: [] (about:blank)
|
- 1: [] (about:blank)
|
||||||
- 2: [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
- 2: [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||||
- 3: (current) [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)
|
- 3: (current) [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)
|
||||||
|
|
||||||
Current tab:
|
### Current tab
|
||||||
- Page URL: data:text/html,<title>Tab two</title><body>Body two</body>
|
- Page URL: data:text/html,<title>Tab two</title><body>Body two</body>
|
||||||
- Page Title: Tab two
|
- Page Title: Tab two
|
||||||
- Page Snapshot
|
- Page Snapshot
|
||||||
@ -67,12 +77,17 @@ test('select tab', async ({ client }) => {
|
|||||||
index: 2,
|
index: 2,
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Open tabs:
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
|
// <internal code to select tab 2>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Open tabs
|
||||||
- 1: [] (about:blank)
|
- 1: [] (about:blank)
|
||||||
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||||
- 3: [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)
|
- 3: [Tab two] (data:text/html,<title>Tab two</title><body>Body two</body>)
|
||||||
|
|
||||||
Current tab:
|
### Current tab
|
||||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||||
- Page Title: Tab one
|
- Page Title: Tab one
|
||||||
- Page Snapshot
|
- Page Snapshot
|
||||||
@ -90,11 +105,16 @@ test('close tab', async ({ client }) => {
|
|||||||
index: 3,
|
index: 3,
|
||||||
},
|
},
|
||||||
})).toHaveTextContent(`
|
})).toHaveTextContent(`
|
||||||
Open tabs:
|
- Ran code:
|
||||||
|
\`\`\`js
|
||||||
|
// <internal code to close tab 3>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Open tabs
|
||||||
- 1: [] (about:blank)
|
- 1: [] (about:blank)
|
||||||
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
- 2: (current) [Tab one] (data:text/html,<title>Tab one</title><body>Body one</body>)
|
||||||
|
|
||||||
Current tab:
|
### Current tab
|
||||||
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
- Page URL: data:text/html,<title>Tab one</title><body>Body one</body>
|
||||||
- Page Title: Tab one
|
- Page Title: Tab one
|
||||||
- Page Snapshot
|
- Page Snapshot
|
||||||
|
Loading…
x
Reference in New Issue
Block a user