diff --git a/config.d.ts b/config.d.ts index d63b06167..29acebfd3 100644 --- a/config.d.ts +++ b/config.d.ts @@ -16,7 +16,7 @@ import type * as playwright from 'playwright'; -export type ToolCapability = 'core' | 'core-tabs' | 'core-install' | 'vision' | 'pdf'; +export type ToolCapability = 'core' | 'core-tabs' | 'core-install' | 'vision' | 'pdf' | 'vscode'; export type Config = { /** diff --git a/src/context.ts b/src/context.ts index 5165c8c59..55b53e2bc 100644 --- a/src/context.ts +++ b/src/context.ts @@ -80,6 +80,27 @@ export class Context { return tab; } + async connectToWindow(connectionString: string) { + await this.closeBrowserContext(); + + this._browserContextFactory = { + async createContext() { + const browser = await playwright.chromium.connect(connectionString); + const params = new URL(connectionString).searchParams; + const contextOptions = JSON.parse(params.get('context-options') ?? '{}'); + const context = browser.contexts()[0] ?? await (browser as any)._newContextForReuse(contextOptions); + return { + browserContext: context, + close: async () => { + await browser.close(); + } + }; + }, + }; + + await this._ensureBrowserContext(); + } + async ensureTab(): Promise { const { browserContext } = await this._ensureBrowserContext(); if (!this._currentTab) diff --git a/src/tools.ts b/src/tools.ts index a1b1531cb..64573d66c 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -29,6 +29,7 @@ import tabs from './tools/tabs.js'; import screenshot from './tools/screenshot.js'; import wait from './tools/wait.js'; import mouse from './tools/mouse.js'; +import vscode from './tools/vscode.js'; import type { Tool } from './tools/tool.js'; import type { FullConfig } from './config.js'; @@ -49,6 +50,7 @@ export const allTools: Tool[] = [ ...snapshot, ...tabs, ...wait, + ...vscode, // TODO: detect "vscode" capability via clientInfo ]; export function filteredTools(config: FullConfig) { diff --git a/src/tools/vscode.ts b/src/tools/vscode.ts new file mode 100644 index 000000000..93d2088dc --- /dev/null +++ b/src/tools/vscode.ts @@ -0,0 +1,45 @@ +/** + * 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 { z } from 'zod'; +import { defineTool } from './tool.js'; + +const connect = defineTool({ + capability: 'vscode', + + schema: { + name: 'browser_connect', + title: 'Connect to a Browser', + description: 'Connect to an open browser window using a connection string.', + inputSchema: z.object({ + connectionString: z.string().describe('Connection string for an existing Playwright browser window, as provided by the playwright_browser_window tool or as included in test reports.'), + }), + type: 'readOnly', + }, + + handle: async (context, params, response) => { + // TODO: this should probably open a new context instead of reconnecting an existing one. + // in the future, connectionString will contain a path to a Playwright installation that we can require, + // to ensure we're using the same version of Playwright. + await context.connectToWindow(params.connectionString); + response.setIncludeSnapshot(); + response.addCode(`// Connect to existing window with connection string`); + }, +}); + +export default [ + connect, +];