Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/playwright-core/src/mcp/browser/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const defaultConfig: FullConfig = {
timeouts: {
action: 5000,
navigation: 60000,
expect: 5000,
},
};

Expand All @@ -134,6 +135,7 @@ export type FullConfig = Config & {
timeouts: {
action: number;
navigation: number;
expect: number;
},
skillMode?: boolean;
configFile?: string;
Expand Down
10 changes: 7 additions & 3 deletions packages/playwright-core/src/mcp/browser/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ export class Tab extends EventEmitter<TabEventsInterface> {
private _recentEventEntries: EventEntry[] = [];
private _consoleLog: LogFile;
private _disposables: Disposable[];
readonly actionTimeoutOptions: { timeout: number; };
readonly navigationTimeoutOptions: { timeout: number; };
readonly expectTimeoutOptions: { timeout: number; };

constructor(context: Context, page: playwright.Page, onPageClose: (tab: Tab) => void) {
super();
Expand Down Expand Up @@ -130,12 +133,13 @@ export class Tab extends EventEmitter<TabEventsInterface> {
void this._downloadStarted(download);
}),
];
page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
page.setDefaultTimeout(this.context.config.timeouts.action);
(page as any)[tabSymbol] = this;
const wallTime = Date.now();
this._consoleLog = new LogFile(this.context, wallTime, 'console', 'Console');
this._initializedPromise = this._initialize();
this.actionTimeoutOptions = { timeout: context.config.timeouts.action };
this.navigationTimeoutOptions = { timeout: context.config.timeouts.navigation };
this.expectTimeoutOptions = { timeout: context.config.timeouts.expect };
}

dispose() {
Expand Down Expand Up @@ -300,7 +304,7 @@ export class Tab extends EventEmitter<TabEventsInterface> {

const { promise: downloadEvent, abort: abortDownloadEvent } = eventWaiter<playwright.Download>(this.page, 'download', 3000);
try {
await this.page.goto(url, { waitUntil: 'domcontentloaded' });
await this.page.goto(url, { waitUntil: 'domcontentloaded', ...this.navigationTimeoutOptions });
abortDownloadEvent();
} catch (_e: unknown) {
const e = _e as Error;
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/mcp/browser/tools/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ const fillForm = defineTabTool({
const locatorSource = `await page.${resolved}`;
if (field.type === 'textbox' || field.type === 'slider') {
const secret = tab.context.lookupSecret(field.value);
await locator.fill(secret.value);
await locator.fill(secret.value, tab.actionTimeoutOptions);
response.addCode(`${locatorSource}.fill(${secret.code});`);
} else if (field.type === 'checkbox' || field.type === 'radio') {
await locator.setChecked(field.value === 'true');
await locator.setChecked(field.value === 'true', tab.actionTimeoutOptions);
response.addCode(`${locatorSource}.setChecked(${field.value});`);
} else if (field.type === 'combobox') {
await locator.selectOption({ label: field.value });
await locator.selectOption({ label: field.value }, tab.actionTimeoutOptions);
response.addCode(`${locatorSource}.selectOption(${escapeWithQuotes(field.value)});`);
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/mcp/browser/tools/keyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,16 @@ const type = defineTabTool({
if (params.slowly) {
response.setIncludeSnapshot();
response.addCode(`await page.${resolved}.pressSequentially(${secret.code});`);
await locator.pressSequentially(secret.value);
await locator.pressSequentially(secret.value, tab.actionTimeoutOptions);
} else {
response.addCode(`await page.${resolved}.fill(${secret.code});`);
await locator.fill(secret.value);
await locator.fill(secret.value, tab.actionTimeoutOptions);
}

if (params.submit) {
response.setIncludeSnapshot();
response.addCode(`await page.${resolved}.press('Enter');`);
await locator.press('Enter');
await locator.press('Enter', tab.actionTimeoutOptions);
}
});
},
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/mcp/browser/tools/navigate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const goBack = defineTabTool({
},

handle: async (tab, params, response) => {
await tab.page.goBack();
await tab.page.goBack(tab.navigationTimeoutOptions);
response.setIncludeSnapshot();
response.addCode(`await page.goBack();`);
},
Expand All @@ -78,7 +78,7 @@ const goForward = defineTabTool({
},

handle: async (tab, params, response) => {
await tab.page.goForward();
await tab.page.goForward(tab.navigationTimeoutOptions);
response.setIncludeSnapshot();
response.addCode(`await page.goForward();`);
},
Expand All @@ -96,7 +96,7 @@ const reload = defineTabTool({
},

handle: async (tab, params, response) => {
await tab.page.reload();
await tab.page.reload(tab.navigationTimeoutOptions);
response.setIncludeSnapshot();
response.addCode(`await page.reload();`);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const screenshot = defineTabTool({
type: fileType,
quality: fileType === 'png' ? undefined : 90,
scale: 'css',
...tab.actionTimeoutOptions,
...(params.fullPage !== undefined && { fullPage: params.fullPage })
};

Expand Down
11 changes: 6 additions & 5 deletions packages/playwright-core/src/mcp/browser/tools/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const click = defineTabTool({
const options = {
button: params.button,
modifiers: params.modifiers,
...tab.actionTimeoutOptions,
};
const optionsArg = formatObjectOrVoid(options);

Expand Down Expand Up @@ -106,7 +107,7 @@ const drag = defineTabTool({
]);

await tab.waitForCompletion(async () => {
await start.locator.dragTo(end.locator);
await start.locator.dragTo(end.locator, tab.actionTimeoutOptions);
});

response.addCode(`await page.${start.resolved}.dragTo(page.${end.resolved});`);
Expand All @@ -130,7 +131,7 @@ const hover = defineTabTool({
response.addCode(`await page.${resolved}.hover();`);

await tab.waitForCompletion(async () => {
await locator.hover();
await locator.hover(tab.actionTimeoutOptions);
});
},
});
Expand All @@ -156,7 +157,7 @@ const selectOption = defineTabTool({
response.addCode(`await page.${resolved}.selectOption(${formatObject(params.values)});`);

await tab.waitForCompletion(async () => {
await locator.selectOption(params.values);
await locator.selectOption(params.values, tab.actionTimeoutOptions);
});
},
});
Expand Down Expand Up @@ -192,7 +193,7 @@ const check = defineTabTool({
handle: async (tab, params, response) => {
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.check();`);
await locator.check();
await locator.check(tab.actionTimeoutOptions);
},
});

Expand All @@ -210,7 +211,7 @@ const uncheck = defineTabTool({
handle: async (tab, params, response) => {
const { locator, resolved } = await tab.refLocator(params);
response.addCode(`await page.${resolved}.uncheck();`);
await locator.uncheck();
await locator.uncheck(tab.actionTimeoutOptions);
},
});

Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/mcp/browser/tools/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const verifyList = defineTabTool({
response.addError(`Item "${item}" not found`);
return;
}
itemTexts.push((await itemLocator.textContent())!);
itemTexts.push((await itemLocator.textContent(tab.expectTimeoutOptions))!);
}
const ariaSnapshot = `\`
- list:
Expand Down Expand Up @@ -125,14 +125,14 @@ const verifyValue = defineTabTool({
const { locator, resolved } = await tab.refLocator({ ref: params.ref, element: params.element });
const locatorSource = `page.${resolved}`;
if (params.type === 'textbox' || params.type === 'slider' || params.type === 'combobox') {
const value = await locator.inputValue();
const value = await locator.inputValue(tab.expectTimeoutOptions);
if (value !== params.value) {
response.addError(`Expected value "${params.value}", but got "${value}"`);
return;
}
response.addCode(`await expect(${locatorSource}).toHaveValue(${escapeWithQuotes(params.value)});`);
} else if (params.type === 'checkbox' || params.type === 'radio') {
const value = await locator.isChecked();
const value = await locator.isChecked(tab.expectTimeoutOptions);
if (value !== (params.value === 'true')) {
response.addError(`Expected value "${params.value}", but got "${value}"`);
return;
Expand Down
5 changes: 5 additions & 0 deletions packages/playwright-core/src/mcp/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ export type Config = {
* Configures default navigation timeout: https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout. Defaults to 60000ms.
*/
navigation?: number;

/**
* Configures default expect timeout: https://playwright.dev/docs/test-timeouts#expect-timeout. Defaults to 5000ms.
*/
expect?: number;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function formatObject(value: any, indent = ' ', mode: 'multiline' | 'one
if (Array.isArray(value))
return `[${value.map(o => formatObject(o)).join(', ')}]`;
if (typeof value === 'object') {
const keys = Object.keys(value).filter(key => value[key] !== undefined).sort();
const keys = Object.keys(value).filter(key => key !== 'timeout' && value[key] !== undefined).sort();
if (!keys.length)
return '{}';
const tokens: string[] = [];
Expand Down
Loading