Skip to content

Commit 981bcd5

Browse files
authored
chore: split run tests into run and debug (microsoft#37246)
1 parent 9181309 commit 981bcd5

9 files changed

Lines changed: 168 additions & 174 deletions

File tree

packages/playwright/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
240240
(testInfo as TestInfoImpl)._setDebugMode();
241241

242242
playwright._defaultContextOptions = _combinedContextOptions;
243-
playwright._defaultContextTimeout = process.env.PLAYWRIGHT_DEBUGGER_MCP ? 5000 : actionTimeout || 0;
243+
playwright._defaultContextTimeout = process.env.PLAYWRIGHT_DEBUGGER_ENABLED ? 5000 : actionTimeout || 0;
244244
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
245245
await use();
246246
playwright._defaultContextOptions = undefined;

packages/playwright/src/mcp/browser/backend.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const doneToolSchema = defineToolSchema({
7373
});
7474

7575
export async function runBrowserBackendOnError(page: playwright.Page, message: () => string) {
76-
if (!process.env.PLAYWRIGHT_DEBUGGER_MCP)
76+
if (!process.env.PLAYWRIGHT_DEBUGGER_ENABLED)
7777
return;
7878
const snapshot = await (page as PageEx)._snapshotForAI();
7979
const introMessage = `### Paused on error:
@@ -84,5 +84,5 @@ ${snapshot}
8484
8585
### Task
8686
Try recovering from the error prior to continuing, use following tools to recover: ${tools.map(tool => tool.schema.name).join(', ')}`;
87-
await runOnPauseBackendLoop(process.env.PLAYWRIGHT_DEBUGGER_MCP!, new BrowserBackend(page), introMessage);
87+
await runOnPauseBackendLoop(process.env.PLAYWRIGHT_MDB_URL!, new BrowserBackend(page), introMessage);
8888
}

packages/playwright/src/mcp/sdk/mdb.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export async function runMainBackend(backendFactory: mcpServer.ServerBackendFact
153153
create: () => mdbBackend
154154
};
155155
const url = await startAsHttp(factory, { port: options?.port || 0 });
156-
process.env.PLAYWRIGHT_DEBUGGER_MCP = url;
156+
process.env.PLAYWRIGHT_MDB_URL = url;
157157

158158
if (options?.port !== undefined)
159159
return url;

packages/playwright/src/mcp/test/backend.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616

1717
import * as mcp from '../sdk/exports.js';
1818
import { Context } from './context';
19-
import { listTests } from './listTests';
20-
import { runTests } from './runTests';
19+
import { listTests, runTests, debugTest } from './tools.js';
2120
import { snapshot, pickLocator, evaluate } from '../browser/tools';
2221

2322
import type { ConfigLocation } from '../../common/config';
@@ -27,7 +26,7 @@ import type { Tool } from './tool';
2726
export class TestServerBackend implements mcp.ServerBackend {
2827
readonly name = 'Playwright';
2928
readonly version = '0.0.1';
30-
private _tools: Tool<any>[] = [listTests, runTests];
29+
private _tools: Tool<any>[] = [listTests, runTests, debugTest];
3130
private _context: Context;
3231

3332
constructor(resolvedLocation: ConfigLocation, options?: { muteConsole?: boolean }) {

packages/playwright/src/mcp/test/listTests.ts

Lines changed: 0 additions & 78 deletions
This file was deleted.

packages/playwright/src/mcp/test/runTests.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { noColors } from 'playwright-core/lib/utils';
18+
19+
import { z } from '../sdk/bundle';
20+
import { terminalScreen } from '../../reporters/base';
21+
import ListReporter from '../../reporters/list';
22+
import ListModeReporter from '../../reporters/listModeReporter';
23+
24+
import { defineTool } from './tool';
25+
import { StringWriteStream } from './streams';
26+
27+
export const listTests = defineTool({
28+
schema: {
29+
name: 'playwright_test_list_tests',
30+
title: 'List tests',
31+
description: 'List tests',
32+
inputSchema: z.object({}),
33+
type: 'readOnly',
34+
},
35+
36+
handle: async context => {
37+
const { screen, stream } = createScreen();
38+
const reporter = new ListModeReporter({ screen, includeTestId: true });
39+
const testRunner = await context.createTestRunner();
40+
await testRunner.listTests(reporter, {});
41+
42+
return {
43+
content: [{ type: 'text', text: stream.content() }],
44+
};
45+
},
46+
});
47+
48+
export const runTests = defineTool({
49+
schema: {
50+
name: 'playwright_test_run_tests',
51+
title: 'Run tests',
52+
description: 'Run tests',
53+
inputSchema: z.object({
54+
locations: z.array(z.string()).describe('Folder, file or location to run: "test/e2e" or "test/e2e/file.spec.ts" or "test/e2e/file.spec.ts:20"'),
55+
projects: z.array(z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with "chromium" is a good start'),
56+
}),
57+
type: 'readOnly',
58+
},
59+
60+
handle: async (context, params) => {
61+
const { screen, stream } = createScreen();
62+
const configDir = context.configLocation.configDir;
63+
const reporter = new ListReporter({ configDir, screen, includeTestId: true });
64+
const testRunner = await context.createTestRunner();
65+
const result = await testRunner.runTests(reporter, {
66+
locations: params.locations,
67+
projects: params.projects,
68+
});
69+
70+
const text = stream.content();
71+
return {
72+
content: [
73+
{ type: 'text', text },
74+
],
75+
isError: result.status !== 'passed',
76+
};
77+
},
78+
});
79+
80+
export const debugTest = defineTool({
81+
schema: {
82+
name: 'playwright_test_debug_test',
83+
title: 'Debug single test',
84+
description: 'Debug single test',
85+
inputSchema: z.object({
86+
test: z.object({
87+
id: z.string().describe('Test ID to debug.'),
88+
title: z.string().describe('Human readable test title for granting permission to debug the test.'),
89+
}),
90+
}),
91+
type: 'readOnly',
92+
},
93+
94+
handle: async (context, params) => {
95+
const stream = new StringWriteStream();
96+
const screen = {
97+
...terminalScreen,
98+
isTTY: false,
99+
colors: noColors,
100+
stdout: stream as unknown as NodeJS.WriteStream,
101+
stderr: stream as unknown as NodeJS.WriteStream,
102+
};
103+
const configDir = context.configLocation.configDir;
104+
const reporter = new ListReporter({ configDir, screen });
105+
const testRunner = await context.createTestRunner();
106+
process.env.PLAYWRIGHT_DEBUGGER_ENABLED = '1';
107+
const result = await testRunner.runTests(reporter, {
108+
headed: true,
109+
testIds: [params.test.id],
110+
// For automatic recovery
111+
timeout: 0,
112+
workers: 1,
113+
}).finally(() => {
114+
process.env.PLAYWRIGHT_DEBUGGER_ENABLED = undefined;
115+
});
116+
117+
const text = stream.content();
118+
return {
119+
content: [
120+
{ type: 'text', text },
121+
],
122+
isError: result.status !== 'passed',
123+
};
124+
},
125+
});
126+
127+
function createScreen() {
128+
const stream = new StringWriteStream();
129+
const screen = {
130+
...terminalScreen,
131+
isTTY: false,
132+
colors: noColors,
133+
stdout: stream as unknown as NodeJS.WriteStream,
134+
stderr: stream as unknown as NodeJS.WriteStream,
135+
};
136+
return { screen, stream };
137+
}

0 commit comments

Comments
 (0)