chore: roll playwright to latest (#269)

This commit is contained in:
Pavel Feldman 2025-04-28 13:44:24 -07:00 committed by GitHub
parent bf7dbabca4
commit b02370df2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 21 deletions

26
package-lock.json generated
View File

@ -11,7 +11,7 @@
"dependencies": {
"@modelcontextprotocol/sdk": "^1.10.1",
"commander": "^13.1.0",
"playwright": "1.53.0-alpha-1745357020000",
"playwright": "1.53.0-alpha-2025-04-25",
"yaml": "^2.7.1",
"zod-to-json-schema": "^3.24.4"
},
@ -21,7 +21,7 @@
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.19.0",
"@playwright/test": "1.53.0-alpha-1745357020000",
"@playwright/test": "1.53.0-alpha-2025-04-25",
"@stylistic/eslint-plugin": "^3.0.1",
"@types/node": "^22.13.10",
"@typescript-eslint/eslint-plugin": "^8.26.1",
@ -287,13 +287,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.53.0-alpha-1745357020000",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.0-alpha-1745357020000.tgz",
"integrity": "sha512-7xQRHhsS//elVJVt2WybJPXAy++WiE8yJzMtVFcnzdQNg9VNSbpqo4b61io5IIG1nEfB22N4BhjQ/8jPrUyu9A==",
"version": "1.53.0-alpha-2025-04-25",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.0-alpha-2025-04-25.tgz",
"integrity": "sha512-3y4C2ZjAc2oUpwavC2yG2JzH53TOKgcMZvWb5GmpxnOa6fhuSVXK0kIsiIaImKmdffIVM1agsqNHp8yldeBTHQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.53.0-alpha-1745357020000"
"playwright": "1.53.0-alpha-2025-04-25"
},
"bin": {
"playwright": "cli.js"
@ -3299,12 +3299,12 @@
}
},
"node_modules/playwright": {
"version": "1.53.0-alpha-1745357020000",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.0-alpha-1745357020000.tgz",
"integrity": "sha512-evnZJIB1CRSA1HfwCkLhyqyGZybSWdNwfyyUWhBoez9ISbYMuYrTtidx75oiGVXtbKr5s8iC0+opuvagu4L1vA==",
"version": "1.53.0-alpha-2025-04-25",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.0-alpha-2025-04-25.tgz",
"integrity": "sha512-b5VT4lWgyhhy99zHeCoUBt/FQckPxeQVA5ksvxBv0HeqcEvzZzhuyqrrcZewJyflE+5U+bmvqI+yoU0ks8mE3Q==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.53.0-alpha-1745357020000"
"playwright-core": "1.53.0-alpha-2025-04-25"
},
"bin": {
"playwright": "cli.js"
@ -3317,9 +3317,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.53.0-alpha-1745357020000",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0-alpha-1745357020000.tgz",
"integrity": "sha512-3oPzOUwJ/yhNWUs3fh5UbmI1Mf18sHUDo3gxzuPwqxN3QCSFKx9Ncg7cSB+FyJCkgz7ZD8fUlzJ75YsDE+PMfA==",
"version": "1.53.0-alpha-2025-04-25",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0-alpha-2025-04-25.tgz",
"integrity": "sha512-gjV01l6A4q/zg+/pwEX50k9lhYWaE9NcDVypSDD331jB3EYrdk0LeDQxqz5XFDOzq/tC/8QTouDs9a/s/p95hA==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"

View File

@ -36,14 +36,14 @@
"dependencies": {
"@modelcontextprotocol/sdk": "^1.10.1",
"commander": "^13.1.0",
"playwright": "1.53.0-alpha-1745357020000",
"playwright": "1.53.0-alpha-2025-04-25",
"yaml": "^2.7.1",
"zod-to-json-schema": "^3.24.4"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.19.0",
"@playwright/test": "1.53.0-alpha-1745357020000",
"@playwright/test": "1.53.0-alpha-2025-04-25",
"@stylistic/eslint-plugin": "^3.0.1",
"@types/node": "^22.13.10",
"@typescript-eslint/eslint-plugin": "^8.26.1",

View File

@ -14,6 +14,8 @@
* limitations under the License.
*/
import net from 'net';
import * as playwright from 'playwright';
import yaml from 'yaml';
@ -26,7 +28,7 @@ import type { ModalState, Tool, ToolActionResult } from './tools/tool';
export type ContextOptions = {
browserName?: 'chromium' | 'firefox' | 'webkit';
userDataDir: string;
launchOptions?: playwright.LaunchOptions;
launchOptions?: playwright.LaunchOptions & playwright.BrowserContextOptions;
cdpEndpoint?: string;
remoteEndpoint?: string;
};
@ -42,6 +44,7 @@ export class Context {
readonly options: ContextOptions;
private _browser: playwright.Browser | undefined;
private _browserContext: playwright.BrowserContext | undefined;
private _createBrowserContextPromise: Promise<{ browser?: playwright.Browser, browserContext: playwright.BrowserContext }> | undefined;
private _tabs: Tab[] = [];
private _currentTab: Tab | undefined;
private _modalStates: (ModalState & { tab: Tab })[] = [];
@ -259,6 +262,7 @@ ${code.join('\n')}
return;
const browserContext = this._browserContext;
const browser = this._browser;
this._createBrowserContextPromise = undefined;
this._browserContext = undefined;
this._browser = undefined;
@ -280,6 +284,15 @@ ${code.join('\n')}
}
private async _createBrowserContext(): Promise<{ browser?: playwright.Browser, browserContext: playwright.BrowserContext }> {
if (!this._createBrowserContextPromise)
this._createBrowserContextPromise = this._innerCreateBrowserContext();
return this._createBrowserContextPromise;
}
private async _innerCreateBrowserContext(): Promise<{ browser?: playwright.Browser, browserContext: playwright.BrowserContext }> {
if (this.options.browserName === 'chromium')
(this.options.launchOptions as any).webSocketPort = await findFreePort();
if (this.options.remoteEndpoint) {
const url = new URL(this.options.remoteEndpoint);
if (this.options.browserName)
@ -468,3 +481,14 @@ class PageSnapshot {
export async function generateLocator(locator: playwright.Locator): Promise<string> {
return (locator as any)._generateLocatorString();
}
async function findFreePort() {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(0, () => {
const { port } = server.address() as net.AddressInfo;
server.close(() => resolve(port));
});
server.on('error', reject);
});
}

View File

@ -34,7 +34,7 @@ import screen from './tools/screen';
import type { Tool, ToolCapability } from './tools/tool';
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
import type { LaunchOptions } from 'playwright';
import type { LaunchOptions, BrowserContextOptions } from 'playwright';
const snapshotTools: Tool<any>[] = [
...common(true),
@ -84,6 +84,7 @@ export async function createServer(options?: Options): Promise<Server> {
case 'chrome-beta':
case 'chrome-canary':
case 'chrome-dev':
case 'chromium':
case 'msedge':
case 'msedge-beta':
case 'msedge-canary':
@ -91,9 +92,6 @@ export async function createServer(options?: Options): Promise<Server> {
browserName = 'chromium';
channel = options.browser;
break;
case 'chromium':
browserName = 'chromium';
break;
case 'firefox':
browserName = 'firefox';
break;
@ -106,10 +104,12 @@ export async function createServer(options?: Options): Promise<Server> {
}
const userDataDir = options?.userDataDir ?? await createUserDataDir(browserName);
const launchOptions: LaunchOptions = {
const launchOptions: LaunchOptions & BrowserContextOptions = {
headless: !!(options?.headless ?? (os.platform() === 'linux' && !process.env.DISPLAY)),
channel,
executablePath: options?.executablePath,
viewport: null,
...{ assistantMode: true },
};
const allTools = options?.vision ? screenshotTools : snapshotTools;

38
tests/webdriver.spec.ts Normal file
View File

@ -0,0 +1,38 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect } from './fixtures';
test('do not falsely advertise user agent as a test driver', async ({ client, server, mcpBrowser }) => {
test.skip(mcpBrowser === 'firefox');
test.skip(mcpBrowser === 'webkit');
server.route('/', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<body></body>
<script>
document.body.textContent = 'webdriver: ' + navigator.webdriver;
</script>
`);
});
expect(await client.callTool({
name: 'browser_navigate',
arguments: {
url: server.PREFIX,
},
})).toContainTextContent('webdriver: false');
});