@cotestdev/mcp_playwright 0.0.14 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/mcp/browser/browserContextFactory.js +49 -13
- package/lib/mcp/browser/browserServerBackend.js +5 -2
- package/lib/mcp/browser/config.js +95 -23
- package/lib/mcp/browser/context.js +28 -3
- package/lib/mcp/browser/response.js +240 -57
- package/lib/mcp/browser/sessionLog.js +1 -1
- package/lib/mcp/browser/tab.js +96 -69
- package/lib/mcp/browser/tools/common.js +8 -8
- package/lib/mcp/browser/tools/console.js +6 -3
- package/lib/mcp/browser/tools/dialogs.js +13 -13
- package/lib/mcp/browser/tools/evaluate.js +9 -20
- package/lib/mcp/browser/tools/files.js +10 -5
- package/lib/mcp/browser/tools/form.js +11 -22
- package/lib/mcp/browser/tools/install.js +3 -3
- package/lib/mcp/browser/tools/keyboard.js +12 -12
- package/lib/mcp/browser/tools/mouse.js +14 -14
- package/lib/mcp/browser/tools/navigate.js +5 -5
- package/lib/mcp/browser/tools/network.js +16 -5
- package/lib/mcp/browser/tools/pdf.js +7 -18
- package/lib/mcp/browser/tools/runCode.js +77 -0
- package/lib/mcp/browser/tools/screenshot.js +44 -33
- package/lib/mcp/browser/tools/snapshot.js +42 -33
- package/lib/mcp/browser/tools/tabs.js +7 -10
- package/lib/mcp/browser/tools/tool.js +8 -7
- package/lib/mcp/browser/tools/tracing.js +4 -4
- package/lib/mcp/browser/tools/utils.js +50 -52
- package/lib/mcp/browser/tools/verify.js +23 -34
- package/lib/mcp/browser/tools/wait.js +6 -6
- package/lib/mcp/browser/tools.js +4 -3
- package/lib/mcp/extension/cdpRelay.js +1 -1
- package/lib/mcp/extension/extensionContextFactory.js +4 -3
- package/lib/mcp/log.js +2 -2
- package/lib/mcp/program.js +21 -29
- package/lib/mcp/sdk/exports.js +1 -5
- package/lib/mcp/sdk/http.js +37 -50
- package/lib/mcp/sdk/server.js +61 -9
- package/lib/mcp/sdk/tool.js +5 -4
- package/lib/mcp/test/browserBackend.js +67 -61
- package/lib/mcp/test/generatorTools.js +122 -0
- package/lib/mcp/test/plannerTools.js +144 -0
- package/lib/mcp/test/seed.js +82 -0
- package/lib/mcp/test/streams.js +10 -7
- package/lib/mcp/test/testBackend.js +44 -24
- package/lib/mcp/test/testContext.js +243 -14
- package/lib/mcp/test/testTools.js +23 -109
- package/lib/util.js +12 -6
- package/package.json +1 -1
|
@@ -30,36 +30,25 @@ var testTools_exports = {};
|
|
|
30
30
|
__export(testTools_exports, {
|
|
31
31
|
debugTest: () => debugTest,
|
|
32
32
|
listTests: () => listTests,
|
|
33
|
-
runTests: () => runTests
|
|
34
|
-
setupPage: () => setupPage
|
|
33
|
+
runTests: () => runTests
|
|
35
34
|
});
|
|
36
35
|
module.exports = __toCommonJS(testTools_exports);
|
|
37
|
-
var
|
|
38
|
-
var import_path = __toESM(require("path"));
|
|
39
|
-
var import_utils = require("playwright-core/lib/utils");
|
|
40
|
-
var import_bundle = require("../sdk/bundle");
|
|
41
|
-
var import_base = require("../../reporters/base");
|
|
42
|
-
var import_list = __toESM(require("../../reporters/list"));
|
|
36
|
+
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
|
43
37
|
var import_listModeReporter = __toESM(require("../../reporters/listModeReporter"));
|
|
44
|
-
var import_projectUtils = require("../../runner/projectUtils");
|
|
45
38
|
var import_testTool = require("./testTool");
|
|
46
|
-
var import_streams = require("./streams");
|
|
47
39
|
const listTests = (0, import_testTool.defineTestTool)({
|
|
48
40
|
schema: {
|
|
49
41
|
name: "test_list",
|
|
50
42
|
title: "List tests",
|
|
51
43
|
description: "List tests",
|
|
52
|
-
inputSchema:
|
|
44
|
+
inputSchema: import_mcpBundle.z.object({}),
|
|
53
45
|
type: "readOnly"
|
|
54
46
|
},
|
|
55
47
|
handle: async (context) => {
|
|
56
|
-
const { screen,
|
|
48
|
+
const { testRunner, screen, output } = await context.createTestRunner();
|
|
57
49
|
const reporter = new import_listModeReporter.default({ screen, includeTestId: true });
|
|
58
|
-
const testRunner = await context.createTestRunner();
|
|
59
50
|
await testRunner.listTests(reporter, {});
|
|
60
|
-
return {
|
|
61
|
-
content: [{ type: "text", text: stream.content() }]
|
|
62
|
-
};
|
|
51
|
+
return { content: output.map((text) => ({ type: "text", text })) };
|
|
63
52
|
}
|
|
64
53
|
});
|
|
65
54
|
const runTests = (0, import_testTool.defineTestTool)({
|
|
@@ -67,28 +56,19 @@ const runTests = (0, import_testTool.defineTestTool)({
|
|
|
67
56
|
name: "test_run",
|
|
68
57
|
title: "Run tests",
|
|
69
58
|
description: "Run tests",
|
|
70
|
-
inputSchema:
|
|
71
|
-
locations:
|
|
72
|
-
projects:
|
|
59
|
+
inputSchema: import_mcpBundle.z.object({
|
|
60
|
+
locations: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe('Folder, file or location to run: "test/e2e" or "test/e2e/file.spec.ts" or "test/e2e/file.spec.ts:20"'),
|
|
61
|
+
projects: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with "chromium" is a good start')
|
|
73
62
|
}),
|
|
74
63
|
type: "readOnly"
|
|
75
64
|
},
|
|
76
65
|
handle: async (context, params) => {
|
|
77
|
-
const {
|
|
78
|
-
|
|
79
|
-
const reporter = new import_list.default({ configDir, screen, includeTestId: true });
|
|
80
|
-
const testRunner = await context.createTestRunner();
|
|
81
|
-
await testRunner.runTests(reporter, {
|
|
82
|
-
locations: params.locations,
|
|
66
|
+
const { output } = await context.runTestsWithGlobalSetupAndPossiblePause({
|
|
67
|
+
locations: params.locations ?? [],
|
|
83
68
|
projects: params.projects,
|
|
84
69
|
disableConfigReporters: true
|
|
85
70
|
});
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
content: [
|
|
89
|
-
{ type: "text", text }
|
|
90
|
-
]
|
|
91
|
-
};
|
|
71
|
+
return { content: [{ type: "text", text: output }] };
|
|
92
72
|
}
|
|
93
73
|
});
|
|
94
74
|
const debugTest = (0, import_testTool.defineTestTool)({
|
|
@@ -96,99 +76,33 @@ const debugTest = (0, import_testTool.defineTestTool)({
|
|
|
96
76
|
name: "test_debug",
|
|
97
77
|
title: "Debug single test",
|
|
98
78
|
description: "Debug single test",
|
|
99
|
-
inputSchema:
|
|
100
|
-
test:
|
|
101
|
-
id:
|
|
102
|
-
title:
|
|
79
|
+
inputSchema: import_mcpBundle.z.object({
|
|
80
|
+
test: import_mcpBundle.z.object({
|
|
81
|
+
id: import_mcpBundle.z.string().describe("Test ID to debug."),
|
|
82
|
+
title: import_mcpBundle.z.string().describe("Human readable test title for granting permission to debug the test.")
|
|
103
83
|
})
|
|
104
84
|
}),
|
|
105
85
|
type: "readOnly"
|
|
106
86
|
},
|
|
107
87
|
handle: async (context, params) => {
|
|
108
|
-
const {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const result = await testRunner.runTests(reporter, {
|
|
113
|
-
headed: !context.options?.headless,
|
|
88
|
+
const { output, status } = await context.runTestsWithGlobalSetupAndPossiblePause({
|
|
89
|
+
headed: context.computedHeaded,
|
|
90
|
+
locations: [],
|
|
91
|
+
// we can make this faster by passing the test's location, so we don't need to scan all tests to find the ID
|
|
114
92
|
testIds: [params.test.id],
|
|
115
93
|
// For automatic recovery
|
|
116
94
|
timeout: 0,
|
|
117
95
|
workers: 1,
|
|
118
96
|
pauseOnError: true,
|
|
119
|
-
disableConfigReporters: true
|
|
120
|
-
|
|
121
|
-
const text = stream.content();
|
|
122
|
-
return {
|
|
123
|
-
content: [
|
|
124
|
-
{ type: "text", text }
|
|
125
|
-
],
|
|
126
|
-
isError: result.status !== "passed"
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
const setupPage = (0, import_testTool.defineTestTool)({
|
|
131
|
-
schema: {
|
|
132
|
-
name: "test_setup_page",
|
|
133
|
-
title: "Setup page",
|
|
134
|
-
description: "Setup the page for test",
|
|
135
|
-
inputSchema: import_bundle.z.object({
|
|
136
|
-
project: import_bundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
|
|
137
|
-
testLocation: import_bundle.z.string().optional().describe('Location of the seed test to use for setup. For example: "test/seed/default.spec.ts:20".')
|
|
138
|
-
}),
|
|
139
|
-
type: "readOnly"
|
|
140
|
-
},
|
|
141
|
-
handle: async (context, params) => {
|
|
142
|
-
const { screen, stream } = createScreen();
|
|
143
|
-
const configDir = context.configLocation.configDir;
|
|
144
|
-
const reporter = new import_list.default({ configDir, screen });
|
|
145
|
-
const testRunner = await context.createTestRunner();
|
|
146
|
-
let testLocation = params.testLocation;
|
|
147
|
-
if (!testLocation) {
|
|
148
|
-
testLocation = "default.seed.spec.ts";
|
|
149
|
-
const config = await testRunner.loadConfig();
|
|
150
|
-
const project = params.project ? config.projects.find((p) => p.project.name === params.project) : (0, import_projectUtils.findTopLevelProjects)(config)[0];
|
|
151
|
-
const testDir = project?.project.testDir || configDir;
|
|
152
|
-
const seedFile = import_path.default.join(testDir, testLocation);
|
|
153
|
-
if (!import_fs.default.existsSync(seedFile)) {
|
|
154
|
-
await import_fs.default.promises.mkdir(import_path.default.dirname(seedFile), { recursive: true });
|
|
155
|
-
await import_fs.default.promises.writeFile(seedFile, `import { test, expect } from '@playwright/test';
|
|
156
|
-
|
|
157
|
-
test('seed', async ({ page }) => {});
|
|
158
|
-
`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const result = await testRunner.runTests(reporter, {
|
|
162
|
-
headed: !context.options?.headless,
|
|
163
|
-
locations: [testLocation],
|
|
164
|
-
projects: params.project ? [params.project] : void 0,
|
|
165
|
-
timeout: 0,
|
|
166
|
-
workers: 1,
|
|
167
|
-
pauseAtEnd: true,
|
|
168
|
-
disableConfigReporters: true
|
|
97
|
+
disableConfigReporters: true,
|
|
98
|
+
actionTimeout: 5e3
|
|
169
99
|
});
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
content: [{ type: "text", text }],
|
|
173
|
-
isError: result.status !== "passed"
|
|
174
|
-
};
|
|
100
|
+
return { content: [{ type: "text", text: output }], isError: status !== "paused" && status !== "passed" };
|
|
175
101
|
}
|
|
176
102
|
});
|
|
177
|
-
function createScreen() {
|
|
178
|
-
const stream = new import_streams.StringWriteStream();
|
|
179
|
-
const screen = {
|
|
180
|
-
...import_base.terminalScreen,
|
|
181
|
-
isTTY: false,
|
|
182
|
-
colors: import_utils.noColors,
|
|
183
|
-
stdout: stream,
|
|
184
|
-
stderr: stream
|
|
185
|
-
};
|
|
186
|
-
return { screen, stream };
|
|
187
|
-
}
|
|
188
103
|
// Annotate the CommonJS export names for ESM import in node:
|
|
189
104
|
0 && (module.exports = {
|
|
190
105
|
debugTest,
|
|
191
106
|
listTests,
|
|
192
|
-
runTests
|
|
193
|
-
setupPage
|
|
107
|
+
runTests
|
|
194
108
|
});
|
package/lib/util.js
CHANGED
|
@@ -48,6 +48,7 @@ __export(util_exports, {
|
|
|
48
48
|
getPackageJsonPath: () => getPackageJsonPath,
|
|
49
49
|
mergeObjects: () => mergeObjects,
|
|
50
50
|
normalizeAndSaveAttachment: () => normalizeAndSaveAttachment,
|
|
51
|
+
parseLocationArg: () => parseLocationArg,
|
|
51
52
|
relativeFilePath: () => relativeFilePath,
|
|
52
53
|
removeDirAndLogToConsole: () => removeDirAndLogToConsole,
|
|
53
54
|
resolveImportSpecifierAfterMapping: () => resolveImportSpecifierAfterMapping,
|
|
@@ -105,14 +106,18 @@ function serializeError(error) {
|
|
|
105
106
|
value: import_util.default.inspect(error)
|
|
106
107
|
};
|
|
107
108
|
}
|
|
109
|
+
function parseLocationArg(arg) {
|
|
110
|
+
const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg);
|
|
111
|
+
return {
|
|
112
|
+
file: match ? match[1] : arg,
|
|
113
|
+
line: match ? parseInt(match[2], 10) : null,
|
|
114
|
+
column: match?.[3] ? parseInt(match[3], 10) : null
|
|
115
|
+
};
|
|
116
|
+
}
|
|
108
117
|
function createFileFiltersFromArguments(args) {
|
|
109
118
|
return args.map((arg) => {
|
|
110
|
-
const
|
|
111
|
-
return {
|
|
112
|
-
re: forceRegExp(match ? match[1] : arg),
|
|
113
|
-
line: match ? parseInt(match[2], 10) : null,
|
|
114
|
-
column: match?.[3] ? parseInt(match[3], 10) : null
|
|
115
|
-
};
|
|
119
|
+
const parsed = parseLocationArg(arg);
|
|
120
|
+
return { re: forceRegExp(parsed.file), line: parsed.line, column: parsed.column };
|
|
116
121
|
});
|
|
117
122
|
}
|
|
118
123
|
function createFileMatcherFromArguments(args) {
|
|
@@ -385,6 +390,7 @@ function stripAnsiEscapes(str) {
|
|
|
385
390
|
getPackageJsonPath,
|
|
386
391
|
mergeObjects,
|
|
387
392
|
normalizeAndSaveAttachment,
|
|
393
|
+
parseLocationArg,
|
|
388
394
|
relativeFilePath,
|
|
389
395
|
removeDirAndLogToConsole,
|
|
390
396
|
resolveImportSpecifierAfterMapping,
|