Update npm packages (73 packages including @jqhtml 2.3.36)
Update npm registry domain from privatenpm.hanson.xyz to npm.internal.hanson.xyz 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
8
node_modules/playwright/README.md
generated
vendored
8
node_modules/playwright/README.md
generated
vendored
@@ -1,16 +1,16 @@
|
||||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
Playwright is a framework for Web Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**.
|
||||
Playwright is a framework for Web Testing and Automation. It allows testing [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable**, and **fast**.
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->143.0.7499.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->145.0.7632.6<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->26.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->144.0.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->146.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||
|
||||
|
||||
2965
node_modules/playwright/ThirdPartyNotices.txt
generated
vendored
2965
node_modules/playwright/ThirdPartyNotices.txt
generated
vendored
File diff suppressed because it is too large
Load Diff
89
node_modules/playwright/lib/agents/agentParser.js
generated
vendored
Normal file
89
node_modules/playwright/lib/agents/agentParser.js
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var agentParser_exports = {};
|
||||
__export(agentParser_exports, {
|
||||
parseAgentSpec: () => parseAgentSpec
|
||||
});
|
||||
module.exports = __toCommonJS(agentParser_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
async function parseAgentSpec(filePath) {
|
||||
const source = await import_fs.default.promises.readFile(filePath, "utf-8");
|
||||
const { header, content } = extractYamlAndContent(source);
|
||||
const { instructions, examples } = extractInstructionsAndExamples(content);
|
||||
return {
|
||||
...header,
|
||||
instructions,
|
||||
examples
|
||||
};
|
||||
}
|
||||
function extractYamlAndContent(markdown) {
|
||||
const lines = markdown.split("\n");
|
||||
if (lines[0] !== "---")
|
||||
throw new Error("Markdown file must start with YAML front matter (---)");
|
||||
let yamlEndIndex = -1;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i] === "---") {
|
||||
yamlEndIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (yamlEndIndex === -1)
|
||||
throw new Error("YAML front matter must be closed with ---");
|
||||
const yamlLines = lines.slice(1, yamlEndIndex);
|
||||
const yamlRaw = yamlLines.join("\n");
|
||||
const contentLines = lines.slice(yamlEndIndex + 1);
|
||||
const content = contentLines.join("\n");
|
||||
let header;
|
||||
try {
|
||||
header = import_utilsBundle.yaml.parse(yamlRaw);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse YAML header: ${error.message}`);
|
||||
}
|
||||
if (!header.name)
|
||||
throw new Error('YAML header must contain a "name" field');
|
||||
if (!header.description)
|
||||
throw new Error('YAML header must contain a "description" field');
|
||||
return { header, content };
|
||||
}
|
||||
function extractInstructionsAndExamples(content) {
|
||||
const examples = [];
|
||||
const instructions = content.split("<example>")[0].trim();
|
||||
const exampleRegex = /<example>([\s\S]*?)<\/example>/g;
|
||||
let match;
|
||||
while ((match = exampleRegex.exec(content)) !== null) {
|
||||
const example = match[1].trim();
|
||||
examples.push(example.replace(/[\n]/g, " ").replace(/ +/g, " "));
|
||||
}
|
||||
return { instructions, examples };
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
parseAgentSpec
|
||||
});
|
||||
101
node_modules/playwright/lib/agents/generateAgents.js
generated
vendored
101
node_modules/playwright/lib/agents/generateAgents.js
generated
vendored
@@ -39,67 +39,20 @@ var import_path = __toESM(require("path"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_seed = require("../mcp/test/seed");
|
||||
class AgentParser {
|
||||
static async loadAgents() {
|
||||
const files = await import_fs.default.promises.readdir(__dirname);
|
||||
return Promise.all(files.filter((file) => file.endsWith(".agent.md")).map((file) => AgentParser.parseFile(import_path.default.join(__dirname, file))));
|
||||
}
|
||||
static async parseFile(filePath) {
|
||||
const source = await import_fs.default.promises.readFile(filePath, "utf-8");
|
||||
const { header, content } = this.extractYamlAndContent(source);
|
||||
const { instructions, examples } = this.extractInstructionsAndExamples(content);
|
||||
return { header, instructions, examples };
|
||||
}
|
||||
static extractYamlAndContent(markdown) {
|
||||
const lines = markdown.split("\n");
|
||||
if (lines[0] !== "---")
|
||||
throw new Error("Markdown file must start with YAML front matter (---)");
|
||||
let yamlEndIndex = -1;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i] === "---") {
|
||||
yamlEndIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (yamlEndIndex === -1)
|
||||
throw new Error("YAML front matter must be closed with ---");
|
||||
const yamlLines = lines.slice(1, yamlEndIndex);
|
||||
const yamlRaw = yamlLines.join("\n");
|
||||
const contentLines = lines.slice(yamlEndIndex + 1);
|
||||
const content = contentLines.join("\n");
|
||||
let header;
|
||||
try {
|
||||
header = import_utilsBundle.yaml.parse(yamlRaw);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse YAML header: ${error.message}`);
|
||||
}
|
||||
if (!header.name)
|
||||
throw new Error('YAML header must contain a "name" field');
|
||||
if (!header.description)
|
||||
throw new Error('YAML header must contain a "description" field');
|
||||
return { header, content };
|
||||
}
|
||||
static extractInstructionsAndExamples(content) {
|
||||
const examples = [];
|
||||
const instructions = content.split("<example>")[0].trim();
|
||||
const exampleRegex = /<example>([\s\S]*?)<\/example>/g;
|
||||
let match;
|
||||
while ((match = exampleRegex.exec(content)) !== null) {
|
||||
const example = match[1].trim();
|
||||
examples.push(example.replace(/[\n]/g, " ").replace(/ +/g, " "));
|
||||
}
|
||||
return { instructions, examples };
|
||||
}
|
||||
var import_agentParser = require("./agentParser");
|
||||
async function loadAgentSpecs() {
|
||||
const files = await import_fs.default.promises.readdir(__dirname);
|
||||
return Promise.all(files.filter((file) => file.endsWith(".agent.md")).map((file) => (0, import_agentParser.parseAgentSpec)(import_path.default.join(__dirname, file))));
|
||||
}
|
||||
class ClaudeGenerator {
|
||||
static async init(config, projectName, prompts) {
|
||||
await initRepo(config, projectName, {
|
||||
promptsFolder: prompts ? ".claude/prompts" : void 0
|
||||
});
|
||||
const agents = await AgentParser.loadAgents();
|
||||
const agents = await loadAgentSpecs();
|
||||
await import_fs.default.promises.mkdir(".claude/agents", { recursive: true });
|
||||
for (const agent of agents)
|
||||
await writeFile(`.claude/agents/${agent.header.name}.md`, ClaudeGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
||||
await writeFile(`.claude/agents/${agent.name}.md`, ClaudeGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
||||
await writeFile(".mcp.json", JSON.stringify({
|
||||
mcpServers: {
|
||||
"playwright-test": {
|
||||
@@ -124,11 +77,11 @@ class ClaudeGenerator {
|
||||
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
|
||||
const lines = [];
|
||||
const header = {
|
||||
name: agent.header.name,
|
||||
description: agent.header.description + examples,
|
||||
tools: agent.header.tools.map((tool) => asClaudeTool(tool)).join(", "),
|
||||
model: agent.header.model,
|
||||
color: agent.header.color
|
||||
name: agent.name,
|
||||
description: agent.description + examples,
|
||||
tools: agent.tools.map((tool) => asClaudeTool(tool)).join(", "),
|
||||
model: agent.model,
|
||||
color: agent.color
|
||||
};
|
||||
lines.push(`---`);
|
||||
lines.push(import_utilsBundle.yaml.stringify(header, { lineWidth: 1e5 }) + `---`);
|
||||
@@ -143,12 +96,12 @@ class OpencodeGenerator {
|
||||
defaultAgentName: "Build",
|
||||
promptsFolder: prompts ? ".opencode/prompts" : void 0
|
||||
});
|
||||
const agents = await AgentParser.loadAgents();
|
||||
const agents = await loadAgentSpecs();
|
||||
for (const agent of agents) {
|
||||
const prompt = [agent.instructions];
|
||||
prompt.push("");
|
||||
prompt.push(...agent.examples.map((example) => `<example>${example}</example>`));
|
||||
await writeFile(`.opencode/prompts/${agent.header.name}.md`, prompt.join("\n"), "\u{1F916}", "agent definition");
|
||||
await writeFile(`.opencode/prompts/${agent.name}.md`, prompt.join("\n"), "\u{1F916}", "agent definition");
|
||||
}
|
||||
await writeFile("opencode.json", OpencodeGenerator.configuration(agents), "\u{1F527}", "opencode configuration");
|
||||
initRepoDone();
|
||||
@@ -176,13 +129,13 @@ class OpencodeGenerator {
|
||||
result["agent"] = {};
|
||||
for (const agent of agents) {
|
||||
const tools = {};
|
||||
result["agent"][agent.header.name] = {
|
||||
description: agent.header.description,
|
||||
result["agent"][agent.name] = {
|
||||
description: agent.description,
|
||||
mode: "subagent",
|
||||
prompt: `{file:.opencode/prompts/${agent.header.name}.md}`,
|
||||
prompt: `{file:.opencode/prompts/${agent.name}.md}`,
|
||||
tools
|
||||
};
|
||||
for (const tool of agent.header.tools)
|
||||
for (const tool of agent.tools)
|
||||
asOpencodeTool(tools, tool);
|
||||
}
|
||||
result["mcp"]["playwright-test"] = {
|
||||
@@ -200,10 +153,10 @@ class CopilotGenerator {
|
||||
promptsFolder: prompts ? ".github/prompts" : void 0,
|
||||
promptSuffix: "prompt"
|
||||
});
|
||||
const agents = await AgentParser.loadAgents();
|
||||
const agents = await loadAgentSpecs();
|
||||
await import_fs.default.promises.mkdir(".github/agents", { recursive: true });
|
||||
for (const agent of agents)
|
||||
await writeFile(`.github/agents/${agent.header.name}.agent.md`, CopilotGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
||||
await writeFile(`.github/agents/${agent.name}.agent.md`, CopilotGenerator.agentSpec(agent), "\u{1F916}", "agent definition");
|
||||
await deleteFile(`.github/chatmodes/ \u{1F3AD} planner.chatmode.md`, "legacy planner chatmode");
|
||||
await deleteFile(`.github/chatmodes/\u{1F3AD} generator.chatmode.md`, "legacy generator chatmode");
|
||||
await deleteFile(`.github/chatmodes/\u{1F3AD} healer.chatmode.md`, "legacy healer chatmode");
|
||||
@@ -228,14 +181,14 @@ class CopilotGenerator {
|
||||
const examples = agent.examples.length ? ` Examples: ${agent.examples.map((example) => `<example>${example}</example>`).join("")}` : "";
|
||||
const lines = [];
|
||||
const header = {
|
||||
"name": agent.header.name,
|
||||
"description": agent.header.description + examples,
|
||||
"tools": agent.header.tools,
|
||||
"name": agent.name,
|
||||
"description": agent.description + examples,
|
||||
"tools": agent.tools,
|
||||
"model": "Claude Sonnet 4",
|
||||
"mcp-servers": CopilotGenerator.mcpServers
|
||||
};
|
||||
lines.push(`---`);
|
||||
lines.push(import_utilsBundle.yaml.stringify(header) + `---`);
|
||||
lines.push(import_utilsBundle.yaml.stringify(header, { lineWidth: 1e5 }) + `---`);
|
||||
lines.push("");
|
||||
lines.push(agent.instructions);
|
||||
lines.push("");
|
||||
@@ -260,7 +213,7 @@ class VSCodeGenerator {
|
||||
await initRepo(config, projectName, {
|
||||
promptsFolder: void 0
|
||||
});
|
||||
const agents = await AgentParser.loadAgents();
|
||||
const agents = await loadAgentSpecs();
|
||||
const nameMap = /* @__PURE__ */ new Map([
|
||||
["playwright-test-planner", " \u{1F3AD} planner"],
|
||||
["playwright-test-generator", "\u{1F3AD} generator"],
|
||||
@@ -268,7 +221,7 @@ class VSCodeGenerator {
|
||||
]);
|
||||
await import_fs.default.promises.mkdir(".github/chatmodes", { recursive: true });
|
||||
for (const agent of agents)
|
||||
await writeFile(`.github/chatmodes/${nameMap.get(agent.header.name)}.chatmode.md`, VSCodeGenerator.agentSpec(agent), "\u{1F916}", "chatmode definition");
|
||||
await writeFile(`.github/chatmodes/${nameMap.get(agent.name)}.chatmode.md`, VSCodeGenerator.agentSpec(agent), "\u{1F916}", "chatmode definition");
|
||||
await VSCodeGenerator.appendToMCPJson();
|
||||
initRepoDone();
|
||||
}
|
||||
@@ -307,7 +260,7 @@ class VSCodeGenerator {
|
||||
return `${vscodeMcpName}/${second}`;
|
||||
return vscodeToolMap.get(first) || first;
|
||||
}
|
||||
const tools = agent.header.tools.map(asVscodeTool).flat().sort((a, b) => {
|
||||
const tools = agent.tools.map(asVscodeTool).flat().sort((a, b) => {
|
||||
const indexA = vscodeToolsOrder.indexOf(a);
|
||||
const indexB = vscodeToolsOrder.indexOf(b);
|
||||
if (indexA === -1 && indexB === -1)
|
||||
@@ -320,7 +273,7 @@ class VSCodeGenerator {
|
||||
}).map((tool) => `'${tool}'`).join(", ");
|
||||
const lines = [];
|
||||
lines.push(`---`);
|
||||
lines.push(`description: ${agent.header.description}.`);
|
||||
lines.push(`description: ${agent.description}.`);
|
||||
lines.push(`tools: [${tools}]`);
|
||||
lines.push(`---`);
|
||||
lines.push("");
|
||||
|
||||
1
node_modules/playwright/lib/agents/playwright-test-planner.agent.md
generated
vendored
1
node_modules/playwright/lib/agents/playwright-test-planner.agent.md
generated
vendored
@@ -17,6 +17,7 @@ tools:
|
||||
- playwright-test/browser_navigate_back
|
||||
- playwright-test/browser_network_requests
|
||||
- playwright-test/browser_press_key
|
||||
- playwright-test/browser_run_code
|
||||
- playwright-test/browser_select_option
|
||||
- playwright-test/browser_snapshot
|
||||
- playwright-test/browser_take_screenshot
|
||||
|
||||
8
node_modules/playwright/lib/common/config.js
generated
vendored
8
node_modules/playwright/lib/common/config.js
generated
vendored
@@ -87,20 +87,22 @@ class FullConfigInternal {
|
||||
maxFailures: takeFirst(configCLIOverrides.debug ? 1 : void 0, configCLIOverrides.maxFailures, userConfig.maxFailures, 0),
|
||||
metadata: metadata ?? userConfig.metadata,
|
||||
preserveOutput: takeFirst(userConfig.preserveOutput, "always"),
|
||||
projects: [],
|
||||
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
|
||||
reporter: takeFirst(configCLIOverrides.reporter, resolveReporters(userConfig.reporter, configDir), [[defaultReporter]]),
|
||||
reportSlowTests: takeFirst(userConfig.reportSlowTests, {
|
||||
max: 5,
|
||||
threshold: 3e5
|
||||
/* 5 minutes */
|
||||
}),
|
||||
quiet: takeFirst(configCLIOverrides.quiet, userConfig.quiet, false),
|
||||
projects: [],
|
||||
// @ts-expect-error runAgents is hidden
|
||||
runAgents: takeFirst(configCLIOverrides.runAgents, "none"),
|
||||
shard: takeFirst(configCLIOverrides.shard, userConfig.shard, null),
|
||||
tags: globalTags,
|
||||
updateSnapshots: takeFirst(configCLIOverrides.updateSnapshots, userConfig.updateSnapshots, "missing"),
|
||||
updateSourceMethod: takeFirst(configCLIOverrides.updateSourceMethod, userConfig.updateSourceMethod, "patch"),
|
||||
version: require("../../package.json").version,
|
||||
workers: resolveWorkers(takeFirst(configCLIOverrides.debug ? 1 : void 0, configCLIOverrides.workers, userConfig.workers, "50%")),
|
||||
workers: resolveWorkers(takeFirst(configCLIOverrides.debug || configCLIOverrides.pause ? 1 : void 0, configCLIOverrides.workers, userConfig.workers, "50%")),
|
||||
webServer: null
|
||||
};
|
||||
for (const key in userConfig) {
|
||||
|
||||
2
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
2
node_modules/playwright/lib/common/esmLoaderHost.js
generated
vendored
@@ -44,6 +44,8 @@ let loaderChannel;
|
||||
function registerESMLoader() {
|
||||
if (process.env.PW_DISABLE_TS_ESM)
|
||||
return true;
|
||||
if ("Bun" in globalThis)
|
||||
return true;
|
||||
if (loaderChannel)
|
||||
return true;
|
||||
const register = require("node:module").register;
|
||||
|
||||
19
node_modules/playwright/lib/common/expectBundle.js
generated
vendored
19
node_modules/playwright/lib/common/expectBundle.js
generated
vendored
@@ -18,26 +18,11 @@ var __copyProps = (to, from, except, desc) => {
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var expectBundle_exports = {};
|
||||
__export(expectBundle_exports, {
|
||||
DIM_COLOR: () => DIM_COLOR,
|
||||
EXPECTED_COLOR: () => EXPECTED_COLOR,
|
||||
INVERTED_COLOR: () => INVERTED_COLOR,
|
||||
RECEIVED_COLOR: () => RECEIVED_COLOR,
|
||||
expect: () => expect,
|
||||
printReceived: () => printReceived
|
||||
expect: () => expect
|
||||
});
|
||||
module.exports = __toCommonJS(expectBundle_exports);
|
||||
const expect = require("./expectBundleImpl").expect;
|
||||
const EXPECTED_COLOR = require("./expectBundleImpl").EXPECTED_COLOR;
|
||||
const INVERTED_COLOR = require("./expectBundleImpl").INVERTED_COLOR;
|
||||
const RECEIVED_COLOR = require("./expectBundleImpl").RECEIVED_COLOR;
|
||||
const DIM_COLOR = require("./expectBundleImpl").DIM_COLOR;
|
||||
const printReceived = require("./expectBundleImpl").printReceived;
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
DIM_COLOR,
|
||||
EXPECTED_COLOR,
|
||||
INVERTED_COLOR,
|
||||
RECEIVED_COLOR,
|
||||
expect,
|
||||
printReceived
|
||||
expect
|
||||
});
|
||||
|
||||
264
node_modules/playwright/lib/common/expectBundleImpl.js
generated
vendored
264
node_modules/playwright/lib/common/expectBundleImpl.js
generated
vendored
File diff suppressed because one or more lines are too long
28
node_modules/playwright/lib/common/process.js
generated
vendored
28
node_modules/playwright/lib/common/process.js
generated
vendored
@@ -30,6 +30,13 @@ class ProcessRunner {
|
||||
const response = { method, params };
|
||||
sendMessageToParent({ method: "__dispatch__", params: response });
|
||||
}
|
||||
async sendRequest(method, params) {
|
||||
return await sendRequestToParent(method, params);
|
||||
}
|
||||
async sendMessageNoReply(method, params) {
|
||||
void sendRequestToParent(method, params).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
let gracefullyCloseCalled = false;
|
||||
let forceExitInitiated = false;
|
||||
@@ -70,6 +77,8 @@ process.on("message", async (message) => {
|
||||
sendMessageToParent({ method: "__dispatch__", params: response });
|
||||
}
|
||||
}
|
||||
if (message.method === "__response__")
|
||||
handleResponseFromParent(message.params);
|
||||
});
|
||||
const kForceExitTimeout = +(process.env.PWTEST_FORCE_EXIT_TIMEOUT || 3e4);
|
||||
async function gracefullyCloseAndExit(forceExit) {
|
||||
@@ -98,6 +107,25 @@ function sendMessageToParent(message) {
|
||||
}
|
||||
}
|
||||
}
|
||||
let lastId = 0;
|
||||
const requestCallbacks = /* @__PURE__ */ new Map();
|
||||
async function sendRequestToParent(method, params) {
|
||||
const id = ++lastId;
|
||||
sendMessageToParent({ method: "__request__", params: { id, method, params } });
|
||||
const promise = new import_utils.ManualPromise();
|
||||
requestCallbacks.set(id, promise);
|
||||
return promise;
|
||||
}
|
||||
function handleResponseFromParent(response) {
|
||||
const promise = requestCallbacks.get(response.id);
|
||||
if (!promise)
|
||||
return;
|
||||
requestCallbacks.delete(response.id);
|
||||
if (response.error)
|
||||
promise.reject(new Error(response.error.message));
|
||||
else
|
||||
promise.resolve(response.result);
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
ProcessRunner
|
||||
|
||||
20
node_modules/playwright/lib/common/validators.js
generated
vendored
20
node_modules/playwright/lib/common/validators.js
generated
vendored
@@ -22,21 +22,21 @@ __export(validators_exports, {
|
||||
validateTestDetails: () => validateTestDetails
|
||||
});
|
||||
module.exports = __toCommonJS(validators_exports);
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
const testAnnotationSchema = import_utilsBundle.zod.object({
|
||||
type: import_utilsBundle.zod.string(),
|
||||
description: import_utilsBundle.zod.string().optional()
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
const testAnnotationSchema = import_mcpBundle.z.object({
|
||||
type: import_mcpBundle.z.string(),
|
||||
description: import_mcpBundle.z.string().optional()
|
||||
});
|
||||
const testDetailsSchema = import_utilsBundle.zod.object({
|
||||
tag: import_utilsBundle.zod.union([
|
||||
import_utilsBundle.zod.string().optional(),
|
||||
import_utilsBundle.zod.array(import_utilsBundle.zod.string())
|
||||
const testDetailsSchema = import_mcpBundle.z.object({
|
||||
tag: import_mcpBundle.z.union([
|
||||
import_mcpBundle.z.string().optional(),
|
||||
import_mcpBundle.z.array(import_mcpBundle.z.string())
|
||||
]).transform((val) => Array.isArray(val) ? val : val !== void 0 ? [val] : []).refine((val) => val.every((v) => v.startsWith("@")), {
|
||||
message: "Tag must start with '@'"
|
||||
}),
|
||||
annotation: import_utilsBundle.zod.union([
|
||||
annotation: import_mcpBundle.z.union([
|
||||
testAnnotationSchema,
|
||||
import_utilsBundle.zod.array(testAnnotationSchema).optional()
|
||||
import_mcpBundle.z.array(testAnnotationSchema).optional()
|
||||
]).transform((val) => Array.isArray(val) ? val : val !== void 0 ? [val] : [])
|
||||
});
|
||||
function validateTestAnnotation(annotation) {
|
||||
|
||||
62
node_modules/playwright/lib/index.js
generated
vendored
62
node_modules/playwright/lib/index.js
generated
vendored
@@ -138,6 +138,7 @@ const playwrightFixtures = {
|
||||
}, { option: true, box: true }],
|
||||
serviceWorkers: [({ contextOptions }, use) => use(contextOptions.serviceWorkers ?? "allow"), { option: true, box: true }],
|
||||
contextOptions: [{}, { option: true, box: true }],
|
||||
agentOptions: [void 0, { option: true, box: true }],
|
||||
_combinedContextOptions: [async ({
|
||||
acceptDownloads,
|
||||
bypassCSP,
|
||||
@@ -162,7 +163,7 @@ const playwrightFixtures = {
|
||||
baseURL,
|
||||
contextOptions,
|
||||
serviceWorkers
|
||||
}, use) => {
|
||||
}, use, testInfo) => {
|
||||
const options = {};
|
||||
if (acceptDownloads !== void 0)
|
||||
options.acceptDownloads = acceptDownloads;
|
||||
@@ -213,21 +214,19 @@ const playwrightFixtures = {
|
||||
...options
|
||||
});
|
||||
}, { box: true }],
|
||||
_setupContextOptions: [async ({ playwright, _combinedContextOptions, actionTimeout, navigationTimeout, testIdAttribute }, use, testInfo) => {
|
||||
_setupContextOptions: [async ({ playwright, actionTimeout, navigationTimeout, testIdAttribute }, use, testInfo) => {
|
||||
if (testIdAttribute)
|
||||
playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
|
||||
testInfo.snapshotSuffix = process.platform;
|
||||
if ((0, import_utils.debugMode)() === "inspector")
|
||||
testInfo._setDebugMode();
|
||||
playwright._defaultContextOptions = _combinedContextOptions;
|
||||
playwright._defaultContextTimeout = actionTimeout || 0;
|
||||
playwright._defaultContextNavigationTimeout = navigationTimeout || 0;
|
||||
await use();
|
||||
playwright._defaultContextOptions = void 0;
|
||||
playwright._defaultContextTimeout = void 0;
|
||||
playwright._defaultContextNavigationTimeout = void 0;
|
||||
}, { auto: "all-hooks-included", title: "context configuration", box: true }],
|
||||
_setupArtifacts: [async ({ playwright, screenshot }, use, testInfo) => {
|
||||
_setupArtifacts: [async ({ playwright, screenshot, _combinedContextOptions }, use, testInfo) => {
|
||||
testInfo.setTimeout(testInfo.project.timeout);
|
||||
const artifactsRecorder = new ArtifactsRecorder(playwright, tracing().artifactsDir(), screenshot);
|
||||
await artifactsRecorder.willStartTest(testInfo);
|
||||
@@ -275,20 +274,32 @@ const playwrightFixtures = {
|
||||
if (!keepTestTimeout)
|
||||
(0, import_globals.currentTestInfo)()?._setDebugMode();
|
||||
},
|
||||
runBeforeCreateBrowserContext: async (options) => {
|
||||
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
||||
if (!(key in options))
|
||||
options[key] = value;
|
||||
}
|
||||
},
|
||||
runBeforeCreateRequestContext: async (options) => {
|
||||
for (const [key, value] of Object.entries(_combinedContextOptions)) {
|
||||
if (!(key in options))
|
||||
options[key] = value;
|
||||
}
|
||||
},
|
||||
runAfterCreateBrowserContext: async (context) => {
|
||||
await artifactsRecorder?.didCreateBrowserContext(context);
|
||||
await artifactsRecorder.didCreateBrowserContext(context);
|
||||
const testInfo2 = (0, import_globals.currentTestInfo)();
|
||||
if (testInfo2)
|
||||
attachConnectedHeaderIfNeeded(testInfo2, context.browser());
|
||||
},
|
||||
runAfterCreateRequestContext: async (context) => {
|
||||
await artifactsRecorder?.didCreateRequestContext(context);
|
||||
await artifactsRecorder.didCreateRequestContext(context);
|
||||
},
|
||||
runBeforeCloseBrowserContext: async (context) => {
|
||||
await artifactsRecorder?.willCloseBrowserContext(context);
|
||||
await artifactsRecorder.willCloseBrowserContext(context);
|
||||
},
|
||||
runBeforeCloseRequestContext: async (context) => {
|
||||
await artifactsRecorder?.willCloseRequestContext(context);
|
||||
await artifactsRecorder.willCloseRequestContext(context);
|
||||
}
|
||||
};
|
||||
const clientInstrumentation = playwright._instrumentation;
|
||||
@@ -401,6 +412,39 @@ const playwrightFixtures = {
|
||||
page = await context.newPage();
|
||||
await use(page);
|
||||
},
|
||||
agent: async ({ page, agentOptions }, use, testInfo) => {
|
||||
const testInfoImpl = testInfo;
|
||||
const cachePathTemplate = agentOptions?.cachePathTemplate ?? "{testDir}/{testFilePath}-cache.json";
|
||||
const resolvedCacheFile = testInfoImpl._applyPathTemplate(cachePathTemplate, "", ".json");
|
||||
const cacheFile = testInfoImpl.config.runAgents === "all" ? void 0 : await testInfoImpl._cloneStorage(resolvedCacheFile);
|
||||
const cacheOutFile = import_path.default.join(testInfoImpl.artifactsDir(), "agent-cache-" + (0, import_utils.createGuid)() + ".json");
|
||||
const provider = agentOptions?.provider && testInfo.config.runAgents !== "none" ? agentOptions.provider : void 0;
|
||||
if (provider)
|
||||
testInfo.setTimeout(0);
|
||||
const cache = {
|
||||
cacheFile,
|
||||
cacheOutFile
|
||||
};
|
||||
const agent = await page.agent({
|
||||
provider,
|
||||
cache,
|
||||
limits: agentOptions?.limits,
|
||||
secrets: agentOptions?.secrets,
|
||||
systemPrompt: agentOptions?.systemPrompt,
|
||||
expect: {
|
||||
timeout: testInfoImpl._projectInternal.expect?.timeout
|
||||
}
|
||||
});
|
||||
await use(agent);
|
||||
const usage = await agent.usage();
|
||||
if (usage.turns > 0)
|
||||
await testInfoImpl.attach("agent-usage", { contentType: "application/json", body: Buffer.from(JSON.stringify(usage, null, 2)) });
|
||||
if (!resolvedCacheFile || !cacheOutFile)
|
||||
return;
|
||||
if (testInfo.status !== "passed")
|
||||
return;
|
||||
await testInfoImpl._upstreamStorage(resolvedCacheFile, cacheOutFile);
|
||||
},
|
||||
request: async ({ playwright }, use) => {
|
||||
const request = await playwright.request.newContext();
|
||||
await use(request);
|
||||
|
||||
17
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
17
node_modules/playwright/lib/isomorphic/teleReceiver.js
generated
vendored
@@ -58,6 +58,10 @@ class TeleReporterReceiver {
|
||||
this._onTestBegin(params.testId, params.result);
|
||||
return;
|
||||
}
|
||||
if (method === "onTestPaused") {
|
||||
this._onTestPaused(params.testId, params.resultId, params.errors);
|
||||
return;
|
||||
}
|
||||
if (method === "onTestEnd") {
|
||||
this._onTestEnd(params.test, params.result);
|
||||
return;
|
||||
@@ -116,6 +120,13 @@ class TeleReporterReceiver {
|
||||
testResult.setStartTimeNumber(payload.startTime);
|
||||
this._reporter.onTestBegin?.(test, testResult);
|
||||
}
|
||||
_onTestPaused(testId, resultId, errors) {
|
||||
const test = this._tests.get(testId);
|
||||
const result = test.results.find((r) => r._id === resultId);
|
||||
result.errors.push(...errors);
|
||||
result.error = result.errors[0];
|
||||
void this._reporter.onTestPaused?.(test, result);
|
||||
}
|
||||
_onTestEnd(testEndPayload, payload) {
|
||||
const test = this._tests.get(testEndPayload.testId);
|
||||
test.timeout = testEndPayload.timeout;
|
||||
@@ -123,8 +134,8 @@ class TeleReporterReceiver {
|
||||
const result = test.results.find((r) => r._id === payload.id);
|
||||
result.duration = payload.duration;
|
||||
result.status = payload.status;
|
||||
result.errors = payload.errors;
|
||||
result.error = result.errors?.[0];
|
||||
result.errors.push(...payload.errors ?? []);
|
||||
result.error = result.errors[0];
|
||||
if (!!payload.attachments)
|
||||
result.attachments = this._parseAttachments(payload.attachments);
|
||||
if (payload.annotations) {
|
||||
@@ -448,6 +459,8 @@ const baseFullConfig = {
|
||||
tags: [],
|
||||
updateSnapshots: "missing",
|
||||
updateSourceMethod: "patch",
|
||||
// @ts-expect-error runAgents is hidden
|
||||
runAgents: "none",
|
||||
version: "",
|
||||
workers: 0,
|
||||
webServer: null
|
||||
|
||||
28
node_modules/playwright/lib/isomorphic/teleSuiteUpdater.js
generated
vendored
28
node_modules/playwright/lib/isomorphic/teleSuiteUpdater.js
generated
vendored
@@ -36,7 +36,7 @@ class TeleSuiteUpdater {
|
||||
this._receiver = new import_teleReceiver.TeleReporterReceiver(this._createReporter(), {
|
||||
mergeProjects: true,
|
||||
mergeTestCases: true,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + options.pathSeparator + relativePath,
|
||||
resolvePath: createPathResolve(options.pathSeparator),
|
||||
clearPreviousResultsWhenTestBegins: true
|
||||
});
|
||||
this._options = options;
|
||||
@@ -44,8 +44,8 @@ class TeleSuiteUpdater {
|
||||
_createReporter() {
|
||||
return {
|
||||
version: () => "v2",
|
||||
onConfigure: (c) => {
|
||||
this.config = c;
|
||||
onConfigure: (config) => {
|
||||
this.config = config;
|
||||
this._lastRunReceiver = new import_teleReceiver.TeleReporterReceiver({
|
||||
version: () => "v2",
|
||||
onBegin: (suite) => {
|
||||
@@ -55,8 +55,9 @@ class TeleSuiteUpdater {
|
||||
}, {
|
||||
mergeProjects: true,
|
||||
mergeTestCases: false,
|
||||
resolvePath: (rootDir, relativePath) => rootDir + this._options.pathSeparator + relativePath
|
||||
resolvePath: createPathResolve(this._options.pathSeparator)
|
||||
});
|
||||
void this._lastRunReceiver.dispatch({ method: "onConfigure", params: { config } });
|
||||
},
|
||||
onBegin: (suite) => {
|
||||
if (!this.rootSuite)
|
||||
@@ -131,6 +132,25 @@ class TeleSuiteUpdater {
|
||||
};
|
||||
}
|
||||
}
|
||||
function createPathResolve(pathSeparator) {
|
||||
return (rootDir, relativePath) => {
|
||||
const segments = [];
|
||||
for (const segment of [...rootDir.split(pathSeparator), ...relativePath.split(pathSeparator)]) {
|
||||
const isAfterDrive = pathSeparator === "\\" && segments.length === 1 && segments[0].endsWith(":");
|
||||
const isFirst = !segments.length;
|
||||
if (!segment && !isFirst && !isAfterDrive)
|
||||
continue;
|
||||
if (segment === ".")
|
||||
continue;
|
||||
if (segment === "..") {
|
||||
segments.pop();
|
||||
continue;
|
||||
}
|
||||
segments.push(segment);
|
||||
}
|
||||
return segments.join(pathSeparator);
|
||||
};
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
TeleSuiteUpdater
|
||||
|
||||
31
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
31
node_modules/playwright/lib/isomorphic/testTree.js
generated
vendored
@@ -19,7 +19,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
||||
var testTree_exports = {};
|
||||
__export(testTree_exports, {
|
||||
TestTree: () => TestTree,
|
||||
collectTestIds: () => collectTestIds,
|
||||
sortAndPropagateStatus: () => sortAndPropagateStatus,
|
||||
statusEx: () => statusEx
|
||||
});
|
||||
@@ -242,16 +241,6 @@ class TestTree {
|
||||
shortRoot.location = this.rootItem.location;
|
||||
this.rootItem = shortRoot;
|
||||
}
|
||||
testIds() {
|
||||
const result = /* @__PURE__ */ new Set();
|
||||
const visit = (treeItem) => {
|
||||
if (treeItem.kind === "case")
|
||||
treeItem.tests.forEach((t) => result.add(t.id));
|
||||
treeItem.children.forEach(visit);
|
||||
};
|
||||
visit(this.rootItem);
|
||||
return result;
|
||||
}
|
||||
fileNames() {
|
||||
const result = /* @__PURE__ */ new Set();
|
||||
const visit = (treeItem) => {
|
||||
@@ -276,7 +265,7 @@ class TestTree {
|
||||
return this._treeItemById.get(id);
|
||||
}
|
||||
collectTestIds(treeItem) {
|
||||
return treeItem ? collectTestIds(treeItem) : /* @__PURE__ */ new Set();
|
||||
return collectTestIds(treeItem);
|
||||
}
|
||||
}
|
||||
function sortAndPropagateStatus(treeItem) {
|
||||
@@ -313,22 +302,28 @@ function sortAndPropagateStatus(treeItem) {
|
||||
}
|
||||
function collectTestIds(treeItem) {
|
||||
const testIds = /* @__PURE__ */ new Set();
|
||||
const locations = /* @__PURE__ */ new Set();
|
||||
const visit = (treeItem2) => {
|
||||
if (treeItem2.kind !== "test" && treeItem2.kind !== "case") {
|
||||
treeItem2.children.forEach(visit);
|
||||
return;
|
||||
}
|
||||
let fileItem = treeItem2;
|
||||
while (fileItem && fileItem.parent && !(fileItem.kind === "group" && fileItem.subKind === "file"))
|
||||
fileItem = fileItem.parent;
|
||||
locations.add(fileItem.location.file);
|
||||
if (treeItem2.kind === "case")
|
||||
treeItem2.tests.map((t) => t.id).forEach((id) => testIds.add(id));
|
||||
else if (treeItem2.kind === "test")
|
||||
testIds.add(treeItem2.id);
|
||||
treeItem2.tests.forEach((test) => testIds.add(test.id));
|
||||
else
|
||||
treeItem2.children?.forEach(visit);
|
||||
testIds.add(treeItem2.id);
|
||||
};
|
||||
visit(treeItem);
|
||||
return testIds;
|
||||
return { testIds, locations };
|
||||
}
|
||||
const statusEx = Symbol("statusEx");
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
TestTree,
|
||||
collectTestIds,
|
||||
sortAndPropagateStatus,
|
||||
statusEx
|
||||
});
|
||||
|
||||
17
node_modules/playwright/lib/matchers/expect.js
generated
vendored
17
node_modules/playwright/lib/matchers/expect.js
generated
vendored
@@ -19,9 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
||||
var expect_exports = {};
|
||||
__export(expect_exports, {
|
||||
expect: () => expect,
|
||||
mergeExpects: () => mergeExpects,
|
||||
printReceivedStringContainExpectedResult: () => printReceivedStringContainExpectedResult,
|
||||
printReceivedStringContainExpectedSubstring: () => printReceivedStringContainExpectedSubstring
|
||||
mergeExpects: () => mergeExpects
|
||||
});
|
||||
module.exports = __toCommonJS(expect_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
@@ -33,15 +31,6 @@ var import_expectBundle = require("../common/expectBundle");
|
||||
var import_globals = require("../common/globals");
|
||||
var import_util = require("../util");
|
||||
var import_testInfo = require("../worker/testInfo");
|
||||
const printSubstring = (val) => val.replace(/"|\\/g, "\\$&");
|
||||
const printReceivedStringContainExpectedSubstring = (received, start, length) => (0, import_expectBundle.RECEIVED_COLOR)(
|
||||
'"' + printSubstring(received.slice(0, start)) + (0, import_expectBundle.INVERTED_COLOR)(printSubstring(received.slice(start, start + length))) + printSubstring(received.slice(start + length)) + '"'
|
||||
);
|
||||
const printReceivedStringContainExpectedResult = (received, result) => result === null ? (0, import_expectBundle.printReceived)(received) : printReceivedStringContainExpectedSubstring(
|
||||
received,
|
||||
result.index,
|
||||
result[0].length
|
||||
);
|
||||
function createMatchers(actual, info, prefix) {
|
||||
return new Proxy((0, import_expectBundle.expect)(actual), new ExpectMetaInfoProxyHandler(actual, info, prefix));
|
||||
}
|
||||
@@ -318,7 +307,5 @@ function mergeExpects(...expects) {
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
expect,
|
||||
mergeExpects,
|
||||
printReceivedStringContainExpectedResult,
|
||||
printReceivedStringContainExpectedSubstring
|
||||
mergeExpects
|
||||
});
|
||||
|
||||
45
node_modules/playwright/lib/matchers/matcherHint.js
generated
vendored
45
node_modules/playwright/lib/matchers/matcherHint.js
generated
vendored
@@ -19,43 +19,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
||||
var matcherHint_exports = {};
|
||||
__export(matcherHint_exports, {
|
||||
ExpectError: () => ExpectError,
|
||||
callLogText: () => callLogText,
|
||||
formatMatcherMessage: () => formatMatcherMessage,
|
||||
isJestError: () => isJestError
|
||||
});
|
||||
module.exports = __toCommonJS(matcherHint_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_expectBundle = require("../common/expectBundle");
|
||||
function formatMatcherMessage(state, details) {
|
||||
const receiver = details.receiver ?? (details.locator ? "locator" : "page");
|
||||
let message = (0, import_expectBundle.DIM_COLOR)("expect(") + (0, import_expectBundle.RECEIVED_COLOR)(receiver) + (0, import_expectBundle.DIM_COLOR)(")" + (state.promise ? "." + state.promise : "") + (state.isNot ? ".not" : "") + ".") + details.matcherName + (0, import_expectBundle.DIM_COLOR)("(") + (0, import_expectBundle.EXPECTED_COLOR)(details.expectation) + (0, import_expectBundle.DIM_COLOR)(")") + " failed\n\n";
|
||||
const diffLines = details.printedDiff?.split("\n");
|
||||
if (diffLines?.length === 2) {
|
||||
details.printedExpected = diffLines[0];
|
||||
details.printedReceived = diffLines[1];
|
||||
details.printedDiff = void 0;
|
||||
}
|
||||
const align = !details.errorMessage && details.printedExpected?.startsWith("Expected:") && (!details.printedReceived || details.printedReceived.startsWith("Received:"));
|
||||
if (details.locator)
|
||||
message += `Locator: ${align ? " " : ""}${String(details.locator)}
|
||||
`;
|
||||
if (details.printedExpected)
|
||||
message += details.printedExpected + "\n";
|
||||
if (details.printedReceived)
|
||||
message += details.printedReceived + "\n";
|
||||
if (details.timedOut && details.timeout)
|
||||
message += `Timeout: ${align ? " " : ""}${details.timeout}ms
|
||||
`;
|
||||
if (details.printedDiff)
|
||||
message += details.printedDiff + "\n";
|
||||
if (details.errorMessage) {
|
||||
message += details.errorMessage;
|
||||
if (!details.errorMessage.endsWith("\n"))
|
||||
message += "\n";
|
||||
}
|
||||
message += callLogText(details.log);
|
||||
return message;
|
||||
}
|
||||
class ExpectError extends Error {
|
||||
constructor(jestError, customMessage, stackFrames) {
|
||||
super("");
|
||||
@@ -68,20 +35,10 @@ class ExpectError extends Error {
|
||||
}
|
||||
}
|
||||
function isJestError(e) {
|
||||
return e instanceof Error && "matcherResult" in e;
|
||||
return e instanceof Error && "matcherResult" in e && !!e.matcherResult;
|
||||
}
|
||||
const callLogText = (log) => {
|
||||
if (!log || !log.some((l) => !!l))
|
||||
return "";
|
||||
return `
|
||||
Call log:
|
||||
${(0, import_expectBundle.DIM_COLOR)(log.join("\n"))}
|
||||
`;
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
ExpectError,
|
||||
callLogText,
|
||||
formatMatcherMessage,
|
||||
isJestError
|
||||
});
|
||||
|
||||
5
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
5
node_modules/playwright/lib/matchers/matchers.js
generated
vendored
@@ -61,7 +61,6 @@ var import_toMatchSnapshot = require("./toMatchSnapshot");
|
||||
var import_config = require("../common/config");
|
||||
var import_globals = require("../common/globals");
|
||||
var import_testInfo = require("../worker/testInfo");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
function toBeAttached(locator, options) {
|
||||
const attached = !options || options.attached === void 0 || options.attached;
|
||||
const expected = attached ? "attached" : "detached";
|
||||
@@ -297,7 +296,9 @@ async function toBeOK(response) {
|
||||
response._fetchLog(),
|
||||
isTextEncoding ? response.text() : null
|
||||
]) : [];
|
||||
const message = () => (0, import_matcherHint.formatMatcherMessage)(this, {
|
||||
const message = () => (0, import_utils.formatMatcherMessage)(this.utils, {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise,
|
||||
matcherName,
|
||||
receiver: "response",
|
||||
expectation: "",
|
||||
|
||||
8
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
8
node_modules/playwright/lib/matchers/toBeTruthy.js
generated
vendored
@@ -21,8 +21,8 @@ __export(toBeTruthy_exports, {
|
||||
toBeTruthy: () => toBeTruthy
|
||||
});
|
||||
module.exports = __toCommonJS(toBeTruthy_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_util = require("../util");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
async function toBeTruthy(matcherName, locator, receiverType, expected, arg, query, options = {}) {
|
||||
(0, import_util.expectTypes)(locator, [receiverType], matcherName);
|
||||
const timeout = options.timeout ?? this.timeout;
|
||||
@@ -45,10 +45,12 @@ async function toBeTruthy(matcherName, locator, receiverType, expected, arg, que
|
||||
printedReceived = errorMessage ? "" : `Received: ${received}`;
|
||||
}
|
||||
const message = () => {
|
||||
return (0, import_matcherHint.formatMatcherMessage)(this, {
|
||||
return (0, import_utils.formatMatcherMessage)(this.utils, {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise,
|
||||
matcherName,
|
||||
expectation: arg,
|
||||
locator,
|
||||
locator: locator.toString(),
|
||||
timeout,
|
||||
timedOut,
|
||||
printedExpected,
|
||||
|
||||
7
node_modules/playwright/lib/matchers/toEqual.js
generated
vendored
7
node_modules/playwright/lib/matchers/toEqual.js
generated
vendored
@@ -23,7 +23,6 @@ __export(toEqual_exports, {
|
||||
module.exports = __toCommonJS(toEqual_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_util = require("../util");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
const EXPECTED_LABEL = "Expected";
|
||||
const RECEIVED_LABEL = "Received";
|
||||
async function toEqual(matcherName, locator, receiverType, query, expected, options = {}) {
|
||||
@@ -70,10 +69,12 @@ async function toEqual(matcherName, locator, receiverType, query, expected, opti
|
||||
);
|
||||
}
|
||||
const message = () => {
|
||||
return (0, import_matcherHint.formatMatcherMessage)(this, {
|
||||
return (0, import_utils.formatMatcherMessage)(this.utils, {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise,
|
||||
matcherName,
|
||||
expectation: "expected",
|
||||
locator,
|
||||
locator: locator.toString(),
|
||||
timeout,
|
||||
timedOut,
|
||||
printedExpected,
|
||||
|
||||
11
node_modules/playwright/lib/matchers/toHaveURL.js
generated
vendored
11
node_modules/playwright/lib/matchers/toHaveURL.js
generated
vendored
@@ -22,9 +22,6 @@ __export(toHaveURL_exports, {
|
||||
});
|
||||
module.exports = __toCommonJS(toHaveURL_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_expect = require("./expect");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
var import_expectBundle = require("../common/expectBundle");
|
||||
async function toHaveURLWithPredicate(page, expected, options) {
|
||||
const matcherName = "toHaveURL";
|
||||
const timeout = options?.timeout ?? this.timeout;
|
||||
@@ -75,18 +72,20 @@ function toHaveURLMessage(state, matcherName, expected, received, pass, timedOut
|
||||
let printedDiff;
|
||||
if (typeof expected === "function") {
|
||||
printedExpected = `Expected: predicate to ${!state.isNot ? "succeed" : "fail"}`;
|
||||
printedReceived = `Received: ${(0, import_expectBundle.printReceived)(receivedString)}`;
|
||||
printedReceived = `Received: ${state.utils.printReceived(receivedString)}`;
|
||||
} else {
|
||||
if (pass) {
|
||||
printedExpected = `Expected pattern: not ${state.utils.printExpected(expected)}`;
|
||||
const formattedReceived = (0, import_expect.printReceivedStringContainExpectedResult)(receivedString, null);
|
||||
const formattedReceived = (0, import_utils.printReceivedStringContainExpectedResult)(state.utils, receivedString, null);
|
||||
printedReceived = `Received string: ${formattedReceived}`;
|
||||
} else {
|
||||
const labelExpected = `Expected ${typeof expected === "string" ? "string" : "pattern"}`;
|
||||
printedDiff = state.utils.printDiffOrStringify(expected, receivedString, labelExpected, "Received string", false);
|
||||
}
|
||||
}
|
||||
return (0, import_matcherHint.formatMatcherMessage)(state, {
|
||||
return (0, import_utils.formatMatcherMessage)(state.utils, {
|
||||
isNot: state.isNot,
|
||||
promise: state.promise,
|
||||
matcherName,
|
||||
expectation: "expected",
|
||||
timeout,
|
||||
|
||||
10
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
10
node_modules/playwright/lib/matchers/toMatchAriaSnapshot.js
generated
vendored
@@ -34,9 +34,7 @@ module.exports = __toCommonJS(toMatchAriaSnapshot_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
var import_util = require("../util");
|
||||
var import_expect = require("./expect");
|
||||
var import_globals = require("../common/globals");
|
||||
async function toMatchAriaSnapshot(locator, expectedParam, options = {}) {
|
||||
const matcherName = "toMatchAriaSnapshot";
|
||||
@@ -79,16 +77,18 @@ async function toMatchAriaSnapshot(locator, expectedParam, options = {}) {
|
||||
if (errorMessage) {
|
||||
printedExpected = `Expected: ${this.isNot ? "not " : ""}${this.utils.printExpected(expected)}`;
|
||||
} else if (pass) {
|
||||
const receivedString = (0, import_expect.printReceivedStringContainExpectedSubstring)(typedReceived.raw, typedReceived.raw.indexOf(expected), expected.length);
|
||||
const receivedString = (0, import_utils.printReceivedStringContainExpectedSubstring)(this.utils, typedReceived.raw, typedReceived.raw.indexOf(expected), expected.length);
|
||||
printedExpected = `Expected: not ${this.utils.printExpected(expected)}`;
|
||||
printedReceived = `Received: ${receivedString}`;
|
||||
} else {
|
||||
printedDiff = this.utils.printDiffOrStringify(expected, typedReceived.raw, "Expected", "Received", false);
|
||||
}
|
||||
return (0, import_matcherHint.formatMatcherMessage)(this, {
|
||||
return (0, import_utils.formatMatcherMessage)(this.utils, {
|
||||
isNot: this.isNot,
|
||||
promise: this.promise,
|
||||
matcherName,
|
||||
expectation: "expected",
|
||||
locator,
|
||||
locator: locator.toString(),
|
||||
timeout,
|
||||
timedOut,
|
||||
printedExpected,
|
||||
|
||||
17
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
17
node_modules/playwright/lib/matchers/toMatchSnapshot.js
generated
vendored
@@ -39,7 +39,6 @@ var import_utils = require("playwright-core/lib/utils");
|
||||
var import_utils2 = require("playwright-core/lib/utils");
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_util = require("../util");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
var import_globals = require("../common/globals");
|
||||
const NonConfigProperties = [
|
||||
"clip",
|
||||
@@ -50,7 +49,7 @@ const NonConfigProperties = [
|
||||
"timeout"
|
||||
];
|
||||
class SnapshotHelper {
|
||||
constructor(testInfo, matcherName, locator, anonymousSnapshotExtension, configOptions, nameOrOptions, optOptions) {
|
||||
constructor(state, testInfo, matcherName, locator, anonymousSnapshotExtension, configOptions, nameOrOptions, optOptions) {
|
||||
let name;
|
||||
if (Array.isArray(nameOrOptions) || typeof nameOrOptions === "string") {
|
||||
name = nameOrOptions;
|
||||
@@ -90,6 +89,7 @@ class SnapshotHelper {
|
||||
this.mimeType = import_utilsBundle.mime.getType(import_path.default.basename(this.expectedPath)) ?? "application/octet-stream";
|
||||
this.comparator = (0, import_utils.getComparator)(this.mimeType);
|
||||
this.testInfo = testInfo;
|
||||
this.state = state;
|
||||
this.kind = this.mimeType.startsWith("image/") ? "Screenshot" : "Snapshot";
|
||||
}
|
||||
createMatcherResult(message, pass, log) {
|
||||
@@ -162,7 +162,7 @@ class SnapshotHelper {
|
||||
step?._attachToStep({ name: (0, import_util.addSuffixToFilePath)(this.attachmentBaseName, "-diff"), contentType: this.mimeType, path: this.diffPath });
|
||||
}
|
||||
if (log?.length)
|
||||
output.push((0, import_matcherHint.callLogText)(log));
|
||||
output.push((0, import_utils.callLogText)(this.state.utils, log));
|
||||
else
|
||||
output.push("");
|
||||
return this.createMatcherResult(output.join("\n"), false, log);
|
||||
@@ -181,6 +181,7 @@ function toMatchSnapshot(received, nameOrOptions = {}, optOptions = {}) {
|
||||
return { pass: !this.isNot, message: () => "", name: "toMatchSnapshot", expected: nameOrOptions };
|
||||
const configOptions = testInfo._projectInternal.expect?.toMatchSnapshot || {};
|
||||
const helper = new SnapshotHelper(
|
||||
this,
|
||||
testInfo,
|
||||
"toMatchSnapshot",
|
||||
void 0,
|
||||
@@ -216,7 +217,7 @@ function toMatchSnapshot(received, nameOrOptions = {}, optOptions = {}) {
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result)
|
||||
return helper.handleMatching();
|
||||
const header = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toMatchSnapshot", receiver: (0, import_utils.isString)(received) ? "string" : "Buffer", expectation: "expected" });
|
||||
const header = (0, import_utils.formatMatcherMessage)(this.utils, { promise: this.promise, isNot: this.isNot, matcherName: "toMatchSnapshot", receiver: (0, import_utils.isString)(received) ? "string" : "Buffer", expectation: "expected" });
|
||||
return helper.handleDifferent(received, expected, void 0, result.diff, header, result.errorMessage, void 0, this._stepInfo);
|
||||
}
|
||||
function toHaveScreenshotStepTitle(nameOrOptions = {}, optOptions = {}) {
|
||||
@@ -236,7 +237,7 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
|
||||
(0, import_util.expectTypes)(pageOrLocator, ["Page", "Locator"], "toHaveScreenshot");
|
||||
const [page, locator] = pageOrLocator.constructor.name === "Page" ? [pageOrLocator, void 0] : [pageOrLocator.page(), pageOrLocator];
|
||||
const configOptions = testInfo._projectInternal.expect?.toHaveScreenshot || {};
|
||||
const helper = new SnapshotHelper(testInfo, "toHaveScreenshot", locator, void 0, configOptions, nameOrOptions, optOptions);
|
||||
const helper = new SnapshotHelper(this, testInfo, "toHaveScreenshot", locator, void 0, configOptions, nameOrOptions, optOptions);
|
||||
if (!helper.expectedPath.toLowerCase().endsWith(".png"))
|
||||
throw new Error(`Screenshot name "${import_path.default.basename(helper.expectedPath)}" must have '.png' extension`);
|
||||
(0, import_util.expectTypes)(pageOrLocator, ["Page", "Locator"], "toHaveScreenshot");
|
||||
@@ -273,7 +274,7 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
|
||||
if (!hasSnapshot) {
|
||||
const { actual: actual2, previous: previous2, diff: diff2, errorMessage: errorMessage2, log: log2, timedOut: timedOut2 } = await page._expectScreenshot(expectScreenshotOptions);
|
||||
if (errorMessage2) {
|
||||
const header2 = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut: timedOut2 });
|
||||
const header2 = (0, import_utils.formatMatcherMessage)(this.utils, { promise: this.promise, isNot: this.isNot, matcherName: "toHaveScreenshot", locator: locator?.toString(), expectation: "expected", timeout, timedOut: timedOut2 });
|
||||
return helper.handleDifferent(actual2, void 0, previous2, diff2, header2, errorMessage2, log2, this._stepInfo);
|
||||
}
|
||||
return helper.handleMissing(actual2, this._stepInfo);
|
||||
@@ -297,11 +298,11 @@ async function toHaveScreenshot(pageOrLocator, nameOrOptions = {}, optOptions =
|
||||
if (helper.updateSnapshots === "changed" || helper.updateSnapshots === "all") {
|
||||
if (actual)
|
||||
return writeFiles(actual);
|
||||
let header2 = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut });
|
||||
let header2 = (0, import_utils.formatMatcherMessage)(this.utils, { promise: this.promise, isNot: this.isNot, matcherName: "toHaveScreenshot", locator: locator?.toString(), expectation: "expected", timeout, timedOut });
|
||||
header2 += " Failed to re-generate expected.\n";
|
||||
return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header2, errorMessage, log, this._stepInfo);
|
||||
}
|
||||
const header = (0, import_matcherHint.formatMatcherMessage)(this, { matcherName: "toHaveScreenshot", locator, expectation: "expected", timeout, timedOut });
|
||||
const header = (0, import_utils.formatMatcherMessage)(this.utils, { promise: this.promise, isNot: this.isNot, matcherName: "toHaveScreenshot", locator: locator?.toString(), expectation: "expected", timeout, timedOut });
|
||||
return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log, this._stepInfo);
|
||||
}
|
||||
function writeFileSync(aPath, content) {
|
||||
|
||||
18
node_modules/playwright/lib/matchers/toMatchText.js
generated
vendored
18
node_modules/playwright/lib/matchers/toMatchText.js
generated
vendored
@@ -21,17 +21,15 @@ __export(toMatchText_exports, {
|
||||
toMatchText: () => toMatchText
|
||||
});
|
||||
module.exports = __toCommonJS(toMatchText_exports);
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_util = require("../util");
|
||||
var import_expect = require("./expect");
|
||||
var import_matcherHint = require("./matcherHint");
|
||||
var import_expectBundle = require("../common/expectBundle");
|
||||
async function toMatchText(matcherName, receiver, receiverType, query, expected, options = {}) {
|
||||
(0, import_util.expectTypes)(receiver, [receiverType], matcherName);
|
||||
const locator = receiverType === "Locator" ? receiver : void 0;
|
||||
if (!(typeof expected === "string") && !(expected && typeof expected.test === "function")) {
|
||||
const errorMessage2 = `Error: ${(0, import_expectBundle.EXPECTED_COLOR)("expected")} value must be a string or regular expression
|
||||
const errorMessage2 = `Error: ${this.utils.EXPECTED_COLOR("expected")} value must be a string or regular expression
|
||||
${this.utils.printWithType("Expected", expected, this.utils.printExpected)}`;
|
||||
throw new Error((0, import_matcherHint.formatMatcherMessage)(this, { locator, matcherName, expectation: "expected", errorMessage: errorMessage2 }));
|
||||
throw new Error((0, import_utils.formatMatcherMessage)(this.utils, { promise: this.promise, isNot: this.isNot, locator: locator?.toString(), matcherName, expectation: "expected", errorMessage: errorMessage2 }));
|
||||
}
|
||||
const timeout = options.timeout ?? this.timeout;
|
||||
const { matches: pass, received, log, timedOut, errorMessage } = await query(!!this.isNot, timeout);
|
||||
@@ -53,13 +51,13 @@ ${this.utils.printWithType("Expected", expected, this.utils.printExpected)}`;
|
||||
if (typeof expected === "string") {
|
||||
printedExpected = `Expected${expectedSuffix}: not ${this.utils.printExpected(expected)}`;
|
||||
if (!errorMessage) {
|
||||
const formattedReceived = (0, import_expect.printReceivedStringContainExpectedSubstring)(receivedString, receivedString.indexOf(expected), expected.length);
|
||||
const formattedReceived = (0, import_utils.printReceivedStringContainExpectedSubstring)(this.utils, receivedString, receivedString.indexOf(expected), expected.length);
|
||||
printedReceived = `Received${receivedSuffix}: ${formattedReceived}`;
|
||||
}
|
||||
} else {
|
||||
printedExpected = `Expected${expectedSuffix}: not ${this.utils.printExpected(expected)}`;
|
||||
if (!errorMessage) {
|
||||
const formattedReceived = (0, import_expect.printReceivedStringContainExpectedResult)(receivedString, typeof expected.exec === "function" ? expected.exec(receivedString) : null);
|
||||
const formattedReceived = (0, import_utils.printReceivedStringContainExpectedResult)(this.utils, receivedString, typeof expected.exec === "function" ? expected.exec(receivedString) : null);
|
||||
printedReceived = `Received${receivedSuffix}: ${formattedReceived}`;
|
||||
}
|
||||
}
|
||||
@@ -70,10 +68,12 @@ ${this.utils.printWithType("Expected", expected, this.utils.printExpected)}`;
|
||||
printedDiff = this.utils.printDiffOrStringify(expected, receivedString, `Expected${expectedSuffix}`, `Received${receivedSuffix}`, false);
|
||||
}
|
||||
const message = () => {
|
||||
return (0, import_matcherHint.formatMatcherMessage)(this, {
|
||||
return (0, import_utils.formatMatcherMessage)(this.utils, {
|
||||
promise: this.promise,
|
||||
isNot: this.isNot,
|
||||
matcherName,
|
||||
expectation: "expected",
|
||||
locator,
|
||||
locator: locator?.toString(),
|
||||
timeout,
|
||||
timedOut,
|
||||
printedExpected,
|
||||
|
||||
16
node_modules/playwright/lib/mcp/browser/actions.d.js
generated
vendored
16
node_modules/playwright/lib/mcp/browser/actions.d.js
generated
vendored
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var actions_d_exports = {};
|
||||
module.exports = __toCommonJS(actions_d_exports);
|
||||
91
node_modules/playwright/lib/mcp/browser/browserContextFactory.js
generated
vendored
91
node_modules/playwright/lib/mcp/browser/browserContextFactory.js
generated
vendored
@@ -29,7 +29,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
||||
var browserContextFactory_exports = {};
|
||||
__export(browserContextFactory_exports, {
|
||||
SharedContextFactory: () => SharedContextFactory,
|
||||
contextFactory: () => contextFactory
|
||||
contextFactory: () => contextFactory,
|
||||
identityBrowserContextFactory: () => identityBrowserContextFactory
|
||||
});
|
||||
module.exports = __toCommonJS(browserContextFactory_exports);
|
||||
var import_crypto = __toESM(require("crypto"));
|
||||
@@ -53,16 +54,27 @@ function contextFactory(config) {
|
||||
return new IsolatedContextFactory(config);
|
||||
return new PersistentContextFactory(config);
|
||||
}
|
||||
function identityBrowserContextFactory(browserContext) {
|
||||
return {
|
||||
createContext: async (clientInfo, abortSignal, options) => {
|
||||
return {
|
||||
browserContext,
|
||||
close: async () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
class BaseContextFactory {
|
||||
constructor(name, config) {
|
||||
this._logName = name;
|
||||
this.config = config;
|
||||
}
|
||||
async _obtainBrowser(clientInfo) {
|
||||
async _obtainBrowser(clientInfo, options) {
|
||||
if (this._browserPromise)
|
||||
return this._browserPromise;
|
||||
(0, import_log.testDebug)(`obtain browser (${this._logName})`);
|
||||
this._browserPromise = this._doObtainBrowser(clientInfo);
|
||||
this._browserPromise = this._doObtainBrowser(clientInfo, options);
|
||||
void this._browserPromise.then((browser) => {
|
||||
browser.on("disconnected", () => {
|
||||
this._browserPromise = void 0;
|
||||
@@ -72,28 +84,27 @@ class BaseContextFactory {
|
||||
});
|
||||
return this._browserPromise;
|
||||
}
|
||||
async _doObtainBrowser(clientInfo) {
|
||||
async _doObtainBrowser(clientInfo, options) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
async createContext(clientInfo) {
|
||||
async createContext(clientInfo, _, options) {
|
||||
(0, import_log.testDebug)(`create browser context (${this._logName})`);
|
||||
const browser = await this._obtainBrowser(clientInfo);
|
||||
const browserContext = await this._doCreateContext(browser);
|
||||
const browser = await this._obtainBrowser(clientInfo, options);
|
||||
const browserContext = await this._doCreateContext(browser, clientInfo);
|
||||
await addInitScript(browserContext, this.config.browser.initScript);
|
||||
return {
|
||||
browserContext,
|
||||
close: (afterClose) => this._closeBrowserContext(browserContext, browser, afterClose)
|
||||
close: () => this._closeBrowserContext(browserContext, browser)
|
||||
};
|
||||
}
|
||||
async _doCreateContext(browser) {
|
||||
async _doCreateContext(browser, clientInfo) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
async _closeBrowserContext(browserContext, browser, afterClose) {
|
||||
async _closeBrowserContext(browserContext, browser) {
|
||||
(0, import_log.testDebug)(`close browser context (${this._logName})`);
|
||||
if (browser.contexts().length === 1)
|
||||
this._browserPromise = void 0;
|
||||
await browserContext.close().catch(import_log.logUnhandledError);
|
||||
await afterClose();
|
||||
if (browser.contexts().length === 0) {
|
||||
(0, import_log.testDebug)(`close browser (${this._logName})`);
|
||||
await browser.close().catch(import_log.logUnhandledError);
|
||||
@@ -104,7 +115,7 @@ class IsolatedContextFactory extends BaseContextFactory {
|
||||
constructor(config) {
|
||||
super("isolated", config);
|
||||
}
|
||||
async _doObtainBrowser(clientInfo) {
|
||||
async _doObtainBrowser(clientInfo, options) {
|
||||
await injectCdpPort(this.config.browser);
|
||||
const browserType = playwright[this.config.browser.browserName];
|
||||
const tracesDir = await computeTracesDir(this.config, clientInfo);
|
||||
@@ -114,15 +125,16 @@ class IsolatedContextFactory extends BaseContextFactory {
|
||||
tracesDir,
|
||||
...this.config.browser.launchOptions,
|
||||
handleSIGINT: false,
|
||||
handleSIGTERM: false
|
||||
handleSIGTERM: false,
|
||||
...options.forceHeadless !== void 0 ? { headless: options.forceHeadless === "headless" } : {}
|
||||
}).catch((error) => {
|
||||
if (error.message.includes("Executable doesn't exist"))
|
||||
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
async _doCreateContext(browser) {
|
||||
return browser.newContext(this.config.browser.contextOptions);
|
||||
async _doCreateContext(browser, clientInfo) {
|
||||
return browser.newContext(await browserContextOptionsFromConfig(this.config, clientInfo));
|
||||
}
|
||||
}
|
||||
class CdpContextFactory extends BaseContextFactory {
|
||||
@@ -130,7 +142,10 @@ class CdpContextFactory extends BaseContextFactory {
|
||||
super("cdp", config);
|
||||
}
|
||||
async _doObtainBrowser() {
|
||||
return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint, { headers: this.config.browser.cdpHeaders });
|
||||
return playwright.chromium.connectOverCDP(this.config.browser.cdpEndpoint, {
|
||||
headers: this.config.browser.cdpHeaders,
|
||||
timeout: this.config.browser.cdpTimeout
|
||||
});
|
||||
}
|
||||
async _doCreateContext(browser) {
|
||||
return this.config.browser.isolated ? await browser.newContext() : browser.contexts()[0];
|
||||
@@ -158,7 +173,7 @@ class PersistentContextFactory {
|
||||
this._userDataDirs = /* @__PURE__ */ new Set();
|
||||
this.config = config;
|
||||
}
|
||||
async createContext(clientInfo) {
|
||||
async createContext(clientInfo, abortSignal, options) {
|
||||
await injectCdpPort(this.config.browser);
|
||||
(0, import_log.testDebug)("create browser context (persistent)");
|
||||
const userDataDir = this.config.browser.userDataDir ?? await this._createUserDataDir(clientInfo);
|
||||
@@ -172,23 +187,29 @@ class PersistentContextFactory {
|
||||
const launchOptions = {
|
||||
tracesDir,
|
||||
...this.config.browser.launchOptions,
|
||||
...this.config.browser.contextOptions,
|
||||
...await browserContextOptionsFromConfig(this.config, clientInfo),
|
||||
handleSIGINT: false,
|
||||
handleSIGTERM: false,
|
||||
ignoreDefaultArgs: [
|
||||
"--disable-extensions"
|
||||
],
|
||||
assistantMode: true
|
||||
assistantMode: true,
|
||||
...options.forceHeadless !== void 0 ? { headless: options.forceHeadless === "headless" } : {}
|
||||
};
|
||||
try {
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, launchOptions);
|
||||
await addInitScript(browserContext, this.config.browser.initScript);
|
||||
const close = (afterClose) => this._closeBrowserContext(browserContext, userDataDir, afterClose);
|
||||
const close = () => this._closeBrowserContext(browserContext, userDataDir);
|
||||
return { browserContext, close };
|
||||
} catch (error) {
|
||||
if (error.message.includes("Executable doesn't exist"))
|
||||
throw new Error(`Browser specified in your config is not installed. Either install it (likely) or change the config.`);
|
||||
if (error.message.includes("ProcessSingleton") || error.message.includes("Invalid URL")) {
|
||||
if (error.message.includes("cannot open shared object file: No such file or directory")) {
|
||||
const browserName = launchOptions.channel ?? this.config.browser.browserName;
|
||||
throw new Error(`Missing system dependencies required to run browser ${browserName}. Install them with: sudo npx playwright install-deps ${browserName}`);
|
||||
}
|
||||
if (error.message.includes("ProcessSingleton") || // On Windows the process exits silently with code 21 when the profile is in use.
|
||||
error.message.includes("exitCode=21")) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
||||
continue;
|
||||
}
|
||||
@@ -197,13 +218,14 @@ class PersistentContextFactory {
|
||||
}
|
||||
throw new Error(`Browser is already in use for ${userDataDir}, use --isolated to run multiple instances of the same browser`);
|
||||
}
|
||||
async _closeBrowserContext(browserContext, userDataDir, afterClose) {
|
||||
async _closeBrowserContext(browserContext, userDataDir) {
|
||||
(0, import_log.testDebug)("close browser context (persistent)");
|
||||
(0, import_log.testDebug)("release user data dir", userDataDir);
|
||||
await browserContext.close().catch(() => {
|
||||
});
|
||||
await afterClose();
|
||||
this._userDataDirs.delete(userDataDir);
|
||||
if (process.env.PWMCP_PROFILES_DIR_FOR_TEST && userDataDir.startsWith(process.env.PWMCP_PROFILES_DIR_FOR_TEST))
|
||||
await import_fs.default.promises.rm(userDataDir, { recursive: true }).catch(import_log.logUnhandledError);
|
||||
(0, import_log.testDebug)("close browser context complete (persistent)");
|
||||
}
|
||||
async _createUserDataDir(clientInfo) {
|
||||
@@ -257,10 +279,10 @@ class SharedContextFactory {
|
||||
constructor(baseFactory) {
|
||||
this._baseFactory = baseFactory;
|
||||
}
|
||||
async createContext(clientInfo, abortSignal, toolName) {
|
||||
async createContext(clientInfo, abortSignal, options) {
|
||||
if (!this._contextPromise) {
|
||||
(0, import_log.testDebug)("create shared browser context");
|
||||
this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, toolName);
|
||||
this._contextPromise = this._baseFactory.createContext(clientInfo, abortSignal, options);
|
||||
}
|
||||
const { browserContext } = await this._contextPromise;
|
||||
(0, import_log.testDebug)(`shared context client connected`);
|
||||
@@ -280,17 +302,28 @@ class SharedContextFactory {
|
||||
if (!contextPromise)
|
||||
return;
|
||||
const { close } = await contextPromise;
|
||||
await close(async () => {
|
||||
});
|
||||
await close();
|
||||
}
|
||||
}
|
||||
async function computeTracesDir(config, clientInfo) {
|
||||
if (!config.saveTrace && !config.capabilities?.includes("tracing"))
|
||||
return;
|
||||
return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", reason: "Collecting trace" });
|
||||
return await (0, import_config.outputFile)(config, clientInfo, `traces`, { origin: "code", title: "Collecting trace" });
|
||||
}
|
||||
async function browserContextOptionsFromConfig(config, clientInfo) {
|
||||
const result = { ...config.browser.contextOptions };
|
||||
if (config.saveVideo) {
|
||||
const dir = await (0, import_config.outputFile)(config, clientInfo, `videos`, { origin: "code", title: "Saving video" });
|
||||
result.recordVideo = {
|
||||
dir,
|
||||
size: config.saveVideo
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
SharedContextFactory,
|
||||
contextFactory
|
||||
contextFactory,
|
||||
identityBrowserContextFactory
|
||||
});
|
||||
|
||||
26
node_modules/playwright/lib/mcp/browser/browserServerBackend.js
generated
vendored
26
node_modules/playwright/lib/mcp/browser/browserServerBackend.js
generated
vendored
@@ -47,24 +47,32 @@ class BrowserServerBackend {
|
||||
}
|
||||
async callTool(name, rawArguments) {
|
||||
const tool = this._tools.find((tool2) => tool2.schema.name === name);
|
||||
if (!tool)
|
||||
throw new Error(`Tool "${name}" not found`);
|
||||
if (!tool) {
|
||||
return {
|
||||
content: [{ type: "text", text: `### Error
|
||||
Tool "${name}" not found` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {});
|
||||
const context = this._context;
|
||||
const response = new import_response.Response(context, name, parsedArguments);
|
||||
response.logBegin();
|
||||
const response = import_response.Response.create(context, name, parsedArguments);
|
||||
context.setRunningTool(name);
|
||||
let responseObject;
|
||||
try {
|
||||
await tool.handle(context, parsedArguments, response);
|
||||
await response.finish();
|
||||
this._sessionLog?.logResponse(response);
|
||||
responseObject = await response.build();
|
||||
this._sessionLog?.logResponse(name, parsedArguments, responseObject);
|
||||
} catch (error) {
|
||||
response.addError(String(error));
|
||||
return {
|
||||
content: [{ type: "text", text: `### Error
|
||||
${String(error)}` }],
|
||||
isError: true
|
||||
};
|
||||
} finally {
|
||||
context.setRunningTool(void 0);
|
||||
}
|
||||
response.logEnd();
|
||||
return response.serialize();
|
||||
return responseObject;
|
||||
}
|
||||
serverClosed() {
|
||||
void this._context?.dispose().catch(import_log.logUnhandledError);
|
||||
|
||||
66
node_modules/playwright/lib/mcp/browser/codegen.js
generated
vendored
66
node_modules/playwright/lib/mcp/browser/codegen.js
generated
vendored
@@ -1,66 +0,0 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var codegen_exports = {};
|
||||
__export(codegen_exports, {
|
||||
escapeWithQuotes: () => escapeWithQuotes,
|
||||
formatObject: () => formatObject,
|
||||
quote: () => quote
|
||||
});
|
||||
module.exports = __toCommonJS(codegen_exports);
|
||||
function escapeWithQuotes(text, char = "'") {
|
||||
const stringified = JSON.stringify(text);
|
||||
const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, '"');
|
||||
if (char === "'")
|
||||
return char + escapedText.replace(/[']/g, "\\'") + char;
|
||||
if (char === '"')
|
||||
return char + escapedText.replace(/["]/g, '\\"') + char;
|
||||
if (char === "`")
|
||||
return char + escapedText.replace(/[`]/g, "\\`") + char;
|
||||
throw new Error("Invalid escape char");
|
||||
}
|
||||
function quote(text) {
|
||||
return escapeWithQuotes(text, "'");
|
||||
}
|
||||
function formatObject(value, indent = " ", mode = "multiline") {
|
||||
if (typeof value === "string")
|
||||
return quote(value);
|
||||
if (Array.isArray(value))
|
||||
return `[${value.map((o) => formatObject(o)).join(", ")}]`;
|
||||
if (typeof value === "object") {
|
||||
const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
|
||||
if (!keys.length)
|
||||
return "{}";
|
||||
const tokens = [];
|
||||
for (const key of keys)
|
||||
tokens.push(`${key}: ${formatObject(value[key])}`);
|
||||
if (mode === "multiline")
|
||||
return `{
|
||||
${tokens.join(`,
|
||||
${indent}`)}
|
||||
}`;
|
||||
return `{ ${tokens.join(", ")} }`;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
escapeWithQuotes,
|
||||
formatObject,
|
||||
quote
|
||||
});
|
||||
77
node_modules/playwright/lib/mcp/browser/config.js
generated
vendored
77
node_modules/playwright/lib/mcp/browser/config.js
generated
vendored
@@ -32,13 +32,15 @@ __export(config_exports, {
|
||||
configFromCLIOptions: () => configFromCLIOptions,
|
||||
defaultConfig: () => defaultConfig,
|
||||
dotenvFileLoader: () => dotenvFileLoader,
|
||||
enumParser: () => enumParser,
|
||||
headerParser: () => headerParser,
|
||||
numberParser: () => numberParser,
|
||||
outputDir: () => outputDir,
|
||||
outputFile: () => outputFile,
|
||||
resolutionParser: () => resolutionParser,
|
||||
resolveCLIConfig: () => resolveCLIConfig,
|
||||
resolveConfig: () => resolveConfig
|
||||
resolveConfig: () => resolveConfig,
|
||||
semicolonSeparatedList: () => semicolonSeparatedList
|
||||
});
|
||||
module.exports = __toCommonJS(config_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
@@ -60,8 +62,19 @@ const defaultConfig = {
|
||||
viewport: null
|
||||
}
|
||||
},
|
||||
console: {
|
||||
level: "info"
|
||||
},
|
||||
network: {
|
||||
allowedOrigins: void 0,
|
||||
blockedOrigins: void 0
|
||||
},
|
||||
server: {},
|
||||
saveTrace: false,
|
||||
snapshot: {
|
||||
mode: "incremental",
|
||||
output: "stdout"
|
||||
},
|
||||
timeouts: {
|
||||
action: 5e3,
|
||||
navigation: 6e4
|
||||
@@ -88,6 +101,12 @@ async function validateConfig(config) {
|
||||
throw new Error(`Init script file does not exist: ${script}`);
|
||||
}
|
||||
}
|
||||
if (config.browser.initPage) {
|
||||
for (const page of config.browser.initPage) {
|
||||
if (!await (0, import_util.fileExistsAsync)(page))
|
||||
throw new Error(`Init page file does not exist: ${page}`);
|
||||
}
|
||||
}
|
||||
if (config.sharedBrowserContext && config.saveVideo)
|
||||
throw new Error("saveVideo is not supported when sharedBrowserContext is true");
|
||||
}
|
||||
@@ -143,13 +162,6 @@ function configFromCLIOptions(cliOptions) {
|
||||
contextOptions.serviceWorkers = "block";
|
||||
if (cliOptions.grantPermissions)
|
||||
contextOptions.permissions = cliOptions.grantPermissions;
|
||||
if (cliOptions.saveVideo) {
|
||||
contextOptions.recordVideo = {
|
||||
// Videos are moved to output directory on saveAs.
|
||||
dir: tmpDir(),
|
||||
size: cliOptions.saveVideo
|
||||
};
|
||||
}
|
||||
const result = {
|
||||
browser: {
|
||||
browserName,
|
||||
@@ -168,11 +180,22 @@ function configFromCLIOptions(cliOptions) {
|
||||
allowedHosts: cliOptions.allowedHosts
|
||||
},
|
||||
capabilities: cliOptions.caps,
|
||||
console: {
|
||||
level: cliOptions.consoleLevel
|
||||
},
|
||||
network: {
|
||||
allowedOrigins: cliOptions.allowedOrigins,
|
||||
blockedOrigins: cliOptions.blockedOrigins
|
||||
},
|
||||
allowUnrestrictedFileAccess: cliOptions.allowUnrestrictedFileAccess,
|
||||
codegen: cliOptions.codegen,
|
||||
saveSession: cliOptions.saveSession,
|
||||
saveTrace: cliOptions.saveTrace,
|
||||
saveVideo: cliOptions.saveVideo,
|
||||
secrets: cliOptions.secrets,
|
||||
sharedBrowserContext: cliOptions.sharedBrowserContext,
|
||||
snapshot: cliOptions.snapshotMode ? { mode: cliOptions.snapshotMode } : void 0,
|
||||
outputMode: cliOptions.outputMode,
|
||||
outputDir: cliOptions.outputDir,
|
||||
imageResponses: cliOptions.imageResponses,
|
||||
testIdAttribute: cliOptions.testIdAttribute,
|
||||
@@ -186,12 +209,17 @@ function configFromCLIOptions(cliOptions) {
|
||||
function configFromEnv() {
|
||||
const options = {};
|
||||
options.allowedHosts = commaSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_HOSTNAMES);
|
||||
options.allowedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_ALLOWED_ORIGINS);
|
||||
options.allowUnrestrictedFileAccess = envToBoolean(process.env.PLAYWRIGHT_MCP_ALLOW_UNRESTRICTED_FILE_ACCESS);
|
||||
options.blockedOrigins = semicolonSeparatedList(process.env.PLAYWRIGHT_MCP_BLOCKED_ORIGINS);
|
||||
options.blockServiceWorkers = envToBoolean(process.env.PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS);
|
||||
options.browser = envToString(process.env.PLAYWRIGHT_MCP_BROWSER);
|
||||
options.caps = commaSeparatedList(process.env.PLAYWRIGHT_MCP_CAPS);
|
||||
options.cdpEndpoint = envToString(process.env.PLAYWRIGHT_MCP_CDP_ENDPOINT);
|
||||
options.cdpHeader = headerParser(process.env.PLAYWRIGHT_MCP_CDP_HEADERS, {});
|
||||
options.config = envToString(process.env.PLAYWRIGHT_MCP_CONFIG);
|
||||
if (process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL)
|
||||
options.consoleLevel = enumParser("--console-level", ["error", "warning", "info", "debug"], process.env.PLAYWRIGHT_MCP_CONSOLE_LEVEL);
|
||||
options.device = envToString(process.env.PLAYWRIGHT_MCP_DEVICE);
|
||||
options.executablePath = envToString(process.env.PLAYWRIGHT_MCP_EXECUTABLE_PATH);
|
||||
options.grantPermissions = commaSeparatedList(process.env.PLAYWRIGHT_MCP_GRANT_PERMISSIONS);
|
||||
@@ -205,8 +233,8 @@ function configFromEnv() {
|
||||
if (initScript)
|
||||
options.initScript = [initScript];
|
||||
options.isolated = envToBoolean(process.env.PLAYWRIGHT_MCP_ISOLATED);
|
||||
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES === "omit")
|
||||
options.imageResponses = "omit";
|
||||
if (process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES)
|
||||
options.imageResponses = enumParser("--image-responses", ["allow", "omit"], process.env.PLAYWRIGHT_MCP_IMAGE_RESPONSES);
|
||||
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
|
||||
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
|
||||
options.port = numberParser(process.env.PLAYWRIGHT_MCP_PORT);
|
||||
@@ -242,7 +270,8 @@ function outputDir(config, clientInfo) {
|
||||
}
|
||||
async function outputFile(config, clientInfo, fileName, options) {
|
||||
const file = await resolveFile(config, clientInfo, fileName, options);
|
||||
(0, import_utilsBundle.debug)("pw:mcp:file")(options.reason, file);
|
||||
await import_fs.default.promises.mkdir(import_path.default.dirname(file), { recursive: true });
|
||||
(0, import_utilsBundle.debug)("pw:mcp:file")(options.title, file);
|
||||
return file;
|
||||
}
|
||||
async function resolveFile(config, clientInfo, fileName, options) {
|
||||
@@ -285,16 +314,33 @@ function mergeConfig(base, overrides) {
|
||||
...pickDefined(base),
|
||||
...pickDefined(overrides),
|
||||
browser,
|
||||
console: {
|
||||
...pickDefined(base.console),
|
||||
...pickDefined(overrides.console)
|
||||
},
|
||||
network: {
|
||||
...pickDefined(base.network),
|
||||
...pickDefined(overrides.network)
|
||||
},
|
||||
server: {
|
||||
...pickDefined(base.server),
|
||||
...pickDefined(overrides.server)
|
||||
},
|
||||
snapshot: {
|
||||
...pickDefined(base.snapshot),
|
||||
...pickDefined(overrides.snapshot)
|
||||
},
|
||||
timeouts: {
|
||||
...pickDefined(base.timeouts),
|
||||
...pickDefined(overrides.timeouts)
|
||||
}
|
||||
};
|
||||
}
|
||||
function semicolonSeparatedList(value) {
|
||||
if (!value)
|
||||
return void 0;
|
||||
return value.split(";").map((v) => v.trim());
|
||||
}
|
||||
function commaSeparatedList(value) {
|
||||
if (!value)
|
||||
return void 0;
|
||||
@@ -335,6 +381,11 @@ function headerParser(arg, previous) {
|
||||
result[name] = value;
|
||||
return result;
|
||||
}
|
||||
function enumParser(name, options, value) {
|
||||
if (!options.includes(value))
|
||||
throw new Error(`Invalid ${name}: ${value}. Valid values are: ${options.join(", ")}`);
|
||||
return value;
|
||||
}
|
||||
function envToBoolean(value) {
|
||||
if (value === "true" || value === "1")
|
||||
return true;
|
||||
@@ -358,11 +409,13 @@ function sanitizeForFilePath(s) {
|
||||
configFromCLIOptions,
|
||||
defaultConfig,
|
||||
dotenvFileLoader,
|
||||
enumParser,
|
||||
headerParser,
|
||||
numberParser,
|
||||
outputDir,
|
||||
outputFile,
|
||||
resolutionParser,
|
||||
resolveCLIConfig,
|
||||
resolveConfig
|
||||
resolveConfig,
|
||||
semicolonSeparatedList
|
||||
});
|
||||
|
||||
163
node_modules/playwright/lib/mcp/browser/context.js
generated
vendored
163
node_modules/playwright/lib/mcp/browser/context.js
generated
vendored
@@ -28,19 +28,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var context_exports = {};
|
||||
__export(context_exports, {
|
||||
Context: () => Context,
|
||||
InputRecorder: () => InputRecorder
|
||||
Context: () => Context
|
||||
});
|
||||
module.exports = __toCommonJS(context_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_playwright_core = require("playwright-core");
|
||||
var import_url = require("url");
|
||||
var import_os = __toESM(require("os"));
|
||||
var import_log = require("../log");
|
||||
var import_tab = require("./tab");
|
||||
var import_config = require("./config");
|
||||
var codegen = __toESM(require("./codegen"));
|
||||
var import_utils = require("./tools/utils");
|
||||
const testDebug = (0, import_utilsBundle.debug)("pw:mcp:test");
|
||||
class Context {
|
||||
constructor(options) {
|
||||
@@ -68,14 +66,13 @@ class Context {
|
||||
}
|
||||
currentTabOrDie() {
|
||||
if (!this._currentTab)
|
||||
throw new Error('No open pages available. Use the "browser_navigate" tool to navigate to a page first.');
|
||||
throw new Error("No open pages available.");
|
||||
return this._currentTab;
|
||||
}
|
||||
async newTab() {
|
||||
const { browserContext } = await this._ensureBrowserContext();
|
||||
const { browserContext } = await this._ensureBrowserContext({});
|
||||
const page = await browserContext.newPage();
|
||||
this._currentTab = this._tabs.find((t) => t.page === page);
|
||||
await this._currentTab.initializedPromise;
|
||||
return this._currentTab;
|
||||
}
|
||||
async selectTab(index) {
|
||||
@@ -86,8 +83,8 @@ class Context {
|
||||
this._currentTab = tab;
|
||||
return tab;
|
||||
}
|
||||
async ensureTab() {
|
||||
const { browserContext } = await this._ensureBrowserContext();
|
||||
async ensureTab(options = {}) {
|
||||
const { browserContext } = await this._ensureBrowserContext(options);
|
||||
if (!this._currentTab)
|
||||
await browserContext.newPage();
|
||||
return this._currentTab;
|
||||
@@ -137,31 +134,11 @@ class Context {
|
||||
testDebug("close context");
|
||||
const promise = this._browserContextPromise;
|
||||
this._browserContextPromise = void 0;
|
||||
this._browserContextOption = void 0;
|
||||
await promise.then(async ({ browserContext, close }) => {
|
||||
if (this.config.saveTrace)
|
||||
await browserContext.tracing.stop();
|
||||
const videos = this.config.saveVideo ? browserContext.pages().map((page) => page.video()).filter((video) => !!video) : [];
|
||||
await close(async () => {
|
||||
for (const video of videos) {
|
||||
const name = await this.outputFile((0, import_utils.dateAsFileName)("webm"), { origin: "code", reason: "Saving video" });
|
||||
await import_fs.default.promises.mkdir(import_path.default.dirname(name), { recursive: true });
|
||||
const p = await video.path();
|
||||
if (import_fs.default.existsSync(p)) {
|
||||
try {
|
||||
await import_fs.default.promises.rename(p, name);
|
||||
} catch (e) {
|
||||
if (e.code !== "EXDEV")
|
||||
(0, import_log.logUnhandledError)(e);
|
||||
try {
|
||||
await import_fs.default.promises.copyFile(p, name);
|
||||
await import_fs.default.promises.unlink(p);
|
||||
} catch (e2) {
|
||||
(0, import_log.logUnhandledError)(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
await close();
|
||||
});
|
||||
}
|
||||
async dispose() {
|
||||
@@ -169,28 +146,45 @@ class Context {
|
||||
await this.closeBrowserContext();
|
||||
Context._allContexts.delete(this);
|
||||
}
|
||||
async ensureBrowserContext() {
|
||||
const { browserContext } = await this._ensureBrowserContext();
|
||||
async _setupRequestInterception(context) {
|
||||
if (this.config.network?.allowedOrigins?.length) {
|
||||
await context.route("**", (route) => route.abort("blockedbyclient"));
|
||||
for (const origin of this.config.network.allowedOrigins)
|
||||
await context.route(originOrHostGlob(origin), (route) => route.continue());
|
||||
}
|
||||
if (this.config.network?.blockedOrigins?.length) {
|
||||
for (const origin of this.config.network.blockedOrigins)
|
||||
await context.route(originOrHostGlob(origin), (route) => route.abort("blockedbyclient"));
|
||||
}
|
||||
}
|
||||
async ensureBrowserContext(options = {}) {
|
||||
const { browserContext } = await this._ensureBrowserContext(options);
|
||||
return browserContext;
|
||||
}
|
||||
_ensureBrowserContext() {
|
||||
if (!this._browserContextPromise) {
|
||||
this._browserContextPromise = this._setupBrowserContext();
|
||||
this._browserContextPromise.catch(() => {
|
||||
this._browserContextPromise = void 0;
|
||||
});
|
||||
}
|
||||
_ensureBrowserContext(options) {
|
||||
if (this._browserContextPromise && (options.forceHeadless === void 0 || this._browserContextOption?.forceHeadless === options.forceHeadless))
|
||||
return this._browserContextPromise;
|
||||
const closePrework = this._browserContextPromise ? this.closeBrowserContext() : Promise.resolve();
|
||||
this._browserContextPromise = closePrework.then(() => this._setupBrowserContext(options));
|
||||
this._browserContextPromise.catch(() => {
|
||||
this._browserContextPromise = void 0;
|
||||
this._browserContextOption = void 0;
|
||||
});
|
||||
this._browserContextOption = options;
|
||||
return this._browserContextPromise;
|
||||
}
|
||||
async _setupBrowserContext() {
|
||||
async _setupBrowserContext(options) {
|
||||
if (this._closeBrowserContextPromise)
|
||||
throw new Error("Another browser context is being closed.");
|
||||
if (this.config.testIdAttribute)
|
||||
import_playwright_core.selectors.setTestIdAttribute(this.config.testIdAttribute);
|
||||
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, this._runningToolName);
|
||||
const result = await this._browserContextFactory.createContext(this._clientInfo, this._abortController.signal, { toolName: this._runningToolName, ...options });
|
||||
const { browserContext } = result;
|
||||
if (this.sessionLog)
|
||||
await InputRecorder.create(this, browserContext);
|
||||
if (!this.config.allowUnrestrictedFileAccess) {
|
||||
browserContext._setAllowedProtocols(["http:", "https:", "about:", "data:"]);
|
||||
browserContext._setAllowedDirectories(allRootPaths(this._clientInfo));
|
||||
}
|
||||
await this._setupRequestInterception(browserContext);
|
||||
for (const page of browserContext.pages())
|
||||
this._onPageCreated(page);
|
||||
browserContext.on("page", (page) => this._onPageCreated(page));
|
||||
@@ -206,62 +200,45 @@ class Context {
|
||||
}
|
||||
lookupSecret(secretName) {
|
||||
if (!this.config.secrets?.[secretName])
|
||||
return { value: secretName, code: codegen.quote(secretName) };
|
||||
return { value: secretName, code: (0, import_utils.escapeWithQuotes)(secretName, "'") };
|
||||
return {
|
||||
value: this.config.secrets[secretName],
|
||||
code: `process.env['${secretName}']`
|
||||
};
|
||||
}
|
||||
firstRootPath() {
|
||||
return allRootPaths(this._clientInfo)[0];
|
||||
}
|
||||
}
|
||||
class InputRecorder {
|
||||
constructor(context, browserContext) {
|
||||
this._context = context;
|
||||
this._browserContext = browserContext;
|
||||
function allRootPaths(clientInfo) {
|
||||
const paths = [];
|
||||
for (const root of clientInfo.roots) {
|
||||
const url = new URL(root.uri);
|
||||
let rootPath;
|
||||
try {
|
||||
rootPath = (0, import_url.fileURLToPath)(url);
|
||||
} catch (e) {
|
||||
if (e.code === "ERR_INVALID_FILE_URL_PATH" && import_os.default.platform() === "win32")
|
||||
rootPath = decodeURIComponent(url.pathname);
|
||||
}
|
||||
if (!rootPath)
|
||||
continue;
|
||||
paths.push(rootPath);
|
||||
}
|
||||
static async create(context, browserContext) {
|
||||
const recorder = new InputRecorder(context, browserContext);
|
||||
await recorder._initialize();
|
||||
return recorder;
|
||||
}
|
||||
async _initialize() {
|
||||
const sessionLog = this._context.sessionLog;
|
||||
await this._browserContext._enableRecorder({
|
||||
mode: "recording",
|
||||
recorderMode: "api"
|
||||
}, {
|
||||
actionAdded: (page, data, code) => {
|
||||
if (this._context.isRunningTool())
|
||||
return;
|
||||
const tab = import_tab.Tab.forPage(page);
|
||||
if (tab)
|
||||
sessionLog.logUserAction(data.action, tab, code, false);
|
||||
},
|
||||
actionUpdated: (page, data, code) => {
|
||||
if (this._context.isRunningTool())
|
||||
return;
|
||||
const tab = import_tab.Tab.forPage(page);
|
||||
if (tab)
|
||||
sessionLog.logUserAction(data.action, tab, code, true);
|
||||
},
|
||||
signalAdded: (page, data) => {
|
||||
if (this._context.isRunningTool())
|
||||
return;
|
||||
if (data.signal.name !== "navigation")
|
||||
return;
|
||||
const tab = import_tab.Tab.forPage(page);
|
||||
const navigateAction = {
|
||||
name: "navigate",
|
||||
url: data.signal.url,
|
||||
signals: []
|
||||
};
|
||||
if (tab)
|
||||
sessionLog.logUserAction(navigateAction, tab, `await page.goto('${data.signal.url}');`, false);
|
||||
}
|
||||
});
|
||||
if (paths.length === 0)
|
||||
paths.push(process.cwd());
|
||||
return paths;
|
||||
}
|
||||
function originOrHostGlob(originOrHost) {
|
||||
try {
|
||||
const url = new URL(originOrHost);
|
||||
if (url.origin !== "null")
|
||||
return `${url.origin}/**`;
|
||||
} catch {
|
||||
}
|
||||
return `*://${originOrHost}/**`;
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Context,
|
||||
InputRecorder
|
||||
Context
|
||||
});
|
||||
|
||||
313
node_modules/playwright/lib/mcp/browser/response.js
generated
vendored
313
node_modules/playwright/lib/mcp/browser/response.js
generated
vendored
@@ -1,7 +1,9 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -15,179 +17,213 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var response_exports = {};
|
||||
__export(response_exports, {
|
||||
Response: () => Response,
|
||||
parseResponse: () => parseResponse,
|
||||
renderTabMarkdown: () => renderTabMarkdown,
|
||||
renderTabsMarkdown: () => renderTabsMarkdown,
|
||||
requestDebug: () => requestDebug
|
||||
});
|
||||
module.exports = __toCommonJS(response_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_tab = require("./tab");
|
||||
var import_utils = require("./tools/utils");
|
||||
const requestDebug = (0, import_utilsBundle.debug)("pw:mcp:request");
|
||||
class Response {
|
||||
constructor(context, toolName, toolArgs) {
|
||||
this._result = [];
|
||||
constructor(ordinal, context, toolName, toolArgs) {
|
||||
this._results = [];
|
||||
this._errors = [];
|
||||
this._code = [];
|
||||
this._images = [];
|
||||
this._includeSnapshot = "none";
|
||||
this._includeTabs = false;
|
||||
this._ordinal = ordinal;
|
||||
this._context = context;
|
||||
this.toolName = toolName;
|
||||
this.toolArgs = toolArgs;
|
||||
}
|
||||
addResult(result) {
|
||||
this._result.push(result);
|
||||
static {
|
||||
this._ordinal = 0;
|
||||
}
|
||||
static create(context, toolName, toolArgs) {
|
||||
return new Response(++Response._ordinal, context, toolName, toolArgs);
|
||||
}
|
||||
addTextResult(result) {
|
||||
this._results.push({ text: result });
|
||||
}
|
||||
async addResult(result) {
|
||||
if (result.data && !result.suggestedFilename)
|
||||
result.suggestedFilename = (0, import_utils.dateAsFileName)(result.ext ?? "bin");
|
||||
if (this._context.config.outputMode === "file") {
|
||||
if (!result.suggestedFilename)
|
||||
result.suggestedFilename = (0, import_utils.dateAsFileName)(result.ext ?? (result.text ? "txt" : "bin"));
|
||||
}
|
||||
const entry = { text: result.text, data: result.data, title: result.title };
|
||||
if (result.suggestedFilename)
|
||||
entry.filename = await this._context.outputFile(result.suggestedFilename, { origin: "llm", title: result.title ?? "Saved result" });
|
||||
this._results.push(entry);
|
||||
return { fileName: entry.filename };
|
||||
}
|
||||
addError(error) {
|
||||
this._result.push(error);
|
||||
this._isError = true;
|
||||
}
|
||||
isError() {
|
||||
return this._isError;
|
||||
}
|
||||
result() {
|
||||
return this._result.join("\n");
|
||||
this._errors.push(error);
|
||||
}
|
||||
addCode(code) {
|
||||
this._code.push(code);
|
||||
}
|
||||
code() {
|
||||
return this._code.join("\n");
|
||||
}
|
||||
addImage(image) {
|
||||
this._images.push(image);
|
||||
}
|
||||
images() {
|
||||
return this._images;
|
||||
setIncludeSnapshot() {
|
||||
this._includeSnapshot = this._context.config.snapshot.mode;
|
||||
}
|
||||
setIncludeSnapshot(full) {
|
||||
this._includeSnapshot = full ?? "incremental";
|
||||
setIncludeFullSnapshot(includeSnapshotFileName) {
|
||||
this._includeSnapshot = "full";
|
||||
this._includeSnapshotFileName = includeSnapshotFileName;
|
||||
}
|
||||
setIncludeTabs() {
|
||||
this._includeTabs = true;
|
||||
}
|
||||
async finish() {
|
||||
if (this._includeSnapshot !== "none" && this._context.currentTab())
|
||||
this._tabSnapshot = await this._context.currentTabOrDie().captureSnapshot();
|
||||
for (const tab of this._context.tabs())
|
||||
await tab.updateTitle();
|
||||
}
|
||||
tabSnapshot() {
|
||||
return this._tabSnapshot;
|
||||
}
|
||||
logBegin() {
|
||||
if (requestDebug.enabled)
|
||||
requestDebug(this.toolName, this.toolArgs);
|
||||
}
|
||||
logEnd() {
|
||||
if (requestDebug.enabled)
|
||||
requestDebug(this.serialize({ omitSnapshot: true, omitBlobs: true }));
|
||||
}
|
||||
serialize(options = {}) {
|
||||
const response = [];
|
||||
if (this._result.length) {
|
||||
response.push("### Result");
|
||||
response.push(this._result.join("\n"));
|
||||
response.push("");
|
||||
async build() {
|
||||
const rootPath = this._context.firstRootPath();
|
||||
const sections = [];
|
||||
const addSection = (title) => {
|
||||
const section = { title, content: [] };
|
||||
sections.push(section);
|
||||
return section.content;
|
||||
};
|
||||
if (this._errors.length) {
|
||||
const text = addSection("Error");
|
||||
text.push("### Error");
|
||||
text.push(this._errors.join("\n"));
|
||||
}
|
||||
if (this._code.length) {
|
||||
response.push(`### Ran Playwright code
|
||||
\`\`\`js
|
||||
${this._code.join("\n")}
|
||||
\`\`\``);
|
||||
response.push("");
|
||||
if (this._results.length) {
|
||||
const text = addSection("Result");
|
||||
for (const result of this._results) {
|
||||
if (result.filename) {
|
||||
text.push(`- [${result.title}](${rootPath ? import_path.default.relative(rootPath, result.filename) : result.filename})`);
|
||||
if (result.data)
|
||||
await import_fs.default.promises.writeFile(result.filename, result.data);
|
||||
else if (result.text)
|
||||
await import_fs.default.promises.writeFile(result.filename, this._redactText(result.text));
|
||||
} else if (result.text) {
|
||||
text.push(result.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this._includeSnapshot !== "none" || this._includeTabs)
|
||||
response.push(...renderTabsMarkdown(this._context.tabs(), this._includeTabs));
|
||||
if (this._tabSnapshot?.modalStates.length) {
|
||||
response.push(...(0, import_tab.renderModalStates)(this._context, this._tabSnapshot.modalStates));
|
||||
response.push("");
|
||||
} else if (this._tabSnapshot) {
|
||||
const includeSnapshot = options.omitSnapshot ? "none" : this._includeSnapshot;
|
||||
response.push(renderTabSnapshot(this._tabSnapshot, includeSnapshot));
|
||||
response.push("");
|
||||
if (this._context.config.codegen !== "none" && this._code.length) {
|
||||
const text = addSection("Ran Playwright code");
|
||||
text.push(...this._code);
|
||||
}
|
||||
const tabSnapshot = this._context.currentTab() ? await this._context.currentTabOrDie().captureSnapshot() : void 0;
|
||||
const tabHeaders = await Promise.all(this._context.tabs().map((tab) => tab.headerSnapshot()));
|
||||
if (tabHeaders.some((header) => header.changed)) {
|
||||
if (tabHeaders.length !== 1) {
|
||||
const text2 = addSection("Open tabs");
|
||||
text2.push(...renderTabsMarkdown(tabHeaders));
|
||||
}
|
||||
const text = addSection("Page");
|
||||
text.push(...renderTabMarkdown(tabHeaders[0]));
|
||||
}
|
||||
if (tabSnapshot?.modalStates.length) {
|
||||
const text = addSection("Modal state");
|
||||
text.push(...(0, import_tab.renderModalStates)(tabSnapshot.modalStates));
|
||||
}
|
||||
if (tabSnapshot && this._includeSnapshot === "full") {
|
||||
let fileName;
|
||||
if (this._includeSnapshotFileName)
|
||||
fileName = await this._context.outputFile(this._includeSnapshotFileName, { origin: "llm", title: "Saved snapshot" });
|
||||
else if (this._context.config.outputMode === "file")
|
||||
fileName = await this._context.outputFile(`snapshot-${this._ordinal}.yml`, { origin: "code", title: "Saved snapshot" });
|
||||
if (fileName) {
|
||||
await import_fs.default.promises.writeFile(fileName, tabSnapshot.ariaSnapshot);
|
||||
const text = addSection("Snapshot");
|
||||
text.push(`- File: ${rootPath ? import_path.default.relative(rootPath, fileName) : fileName}`);
|
||||
} else {
|
||||
const text = addSection("Snapshot");
|
||||
text.push("```yaml");
|
||||
text.push(tabSnapshot.ariaSnapshot);
|
||||
text.push("```");
|
||||
}
|
||||
}
|
||||
if (tabSnapshot && this._includeSnapshot === "incremental") {
|
||||
const text = addSection("Snapshot");
|
||||
text.push("```yaml");
|
||||
if (tabSnapshot.ariaSnapshotDiff !== void 0)
|
||||
text.push(tabSnapshot.ariaSnapshotDiff);
|
||||
else
|
||||
text.push(tabSnapshot.ariaSnapshot);
|
||||
text.push("```");
|
||||
}
|
||||
if (tabSnapshot?.events.filter((event) => event.type !== "request").length) {
|
||||
const text = addSection("Events");
|
||||
for (const event of tabSnapshot.events) {
|
||||
if (event.type === "console") {
|
||||
if ((0, import_tab.shouldIncludeMessage)(this._context.config.console.level, event.message.type))
|
||||
text.push(`- ${trimMiddle(event.message.toString(), 100)}`);
|
||||
} else if (event.type === "download-start") {
|
||||
text.push(`- Downloading file ${event.download.download.suggestedFilename()} ...`);
|
||||
} else if (event.type === "download-finish") {
|
||||
text.push(`- Downloaded file ${event.download.download.suggestedFilename()} to "${rootPath ? import_path.default.relative(rootPath, event.download.outputFile) : event.download.outputFile}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const allText = sections.flatMap((section) => {
|
||||
const content2 = [];
|
||||
content2.push(`### ${section.title}`);
|
||||
content2.push(...section.content);
|
||||
content2.push("");
|
||||
return content2;
|
||||
}).join("\n");
|
||||
const content = [
|
||||
{ type: "text", text: response.join("\n") }
|
||||
{
|
||||
type: "text",
|
||||
text: this._redactText(allText)
|
||||
}
|
||||
];
|
||||
if (this._context.config.imageResponses !== "omit") {
|
||||
for (const image of this._images)
|
||||
content.push({ type: "image", data: options.omitBlobs ? "<blob>" : image.data.toString("base64"), mimeType: image.contentType });
|
||||
content.push({ type: "image", data: image.data.toString("base64"), mimeType: image.contentType });
|
||||
}
|
||||
this._redactSecrets(content);
|
||||
return { content, isError: this._isError };
|
||||
return {
|
||||
content,
|
||||
...this._errors.length > 0 ? { isError: true } : {}
|
||||
};
|
||||
}
|
||||
_redactSecrets(content) {
|
||||
if (!this._context.config.secrets)
|
||||
return;
|
||||
for (const item of content) {
|
||||
if (item.type !== "text")
|
||||
continue;
|
||||
for (const [secretName, secretValue] of Object.entries(this._context.config.secrets))
|
||||
item.text = item.text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
|
||||
}
|
||||
_redactText(text) {
|
||||
for (const [secretName, secretValue] of Object.entries(this._context.config.secrets ?? {}))
|
||||
text = text.replaceAll(secretValue, `<secret>${secretName}</secret>`);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
function renderTabSnapshot(tabSnapshot, includeSnapshot) {
|
||||
const lines = [];
|
||||
if (tabSnapshot.consoleMessages.length) {
|
||||
lines.push(`### New console messages`);
|
||||
for (const message of tabSnapshot.consoleMessages)
|
||||
lines.push(`- ${trim(message.toString(), 100)}`);
|
||||
lines.push("");
|
||||
}
|
||||
if (tabSnapshot.downloads.length) {
|
||||
lines.push(`### Downloads`);
|
||||
for (const entry of tabSnapshot.downloads) {
|
||||
if (entry.finished)
|
||||
lines.push(`- Downloaded file ${entry.download.suggestedFilename()} to ${entry.outputFile}`);
|
||||
else
|
||||
lines.push(`- Downloading file ${entry.download.suggestedFilename()} ...`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff === "") {
|
||||
return lines.join("\n");
|
||||
}
|
||||
lines.push(`### Page state`);
|
||||
lines.push(`- Page URL: ${tabSnapshot.url}`);
|
||||
lines.push(`- Page Title: ${tabSnapshot.title}`);
|
||||
if (includeSnapshot !== "none") {
|
||||
lines.push(`- Page Snapshot:`);
|
||||
lines.push("```yaml");
|
||||
if (includeSnapshot === "incremental" && tabSnapshot.ariaSnapshotDiff !== void 0)
|
||||
lines.push(tabSnapshot.ariaSnapshotDiff);
|
||||
else
|
||||
lines.push(tabSnapshot.ariaSnapshot);
|
||||
lines.push("```");
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
function renderTabsMarkdown(tabs, force = false) {
|
||||
if (tabs.length === 1 && !force)
|
||||
return [];
|
||||
if (!tabs.length) {
|
||||
return [
|
||||
"### Open tabs",
|
||||
'No open tabs. Use the "browser_navigate" tool to navigate to a page first.',
|
||||
""
|
||||
];
|
||||
}
|
||||
const lines = ["### Open tabs"];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
const current = tab.isCurrentTab() ? " (current)" : "";
|
||||
lines.push(`- ${i}:${current} [${tab.lastTitle()}] (${tab.page.url()})`);
|
||||
}
|
||||
lines.push("");
|
||||
function renderTabMarkdown(tab) {
|
||||
const lines = [`- Page URL: ${tab.url}`];
|
||||
if (tab.title)
|
||||
lines.push(`- Page Title: ${tab.title}`);
|
||||
return lines;
|
||||
}
|
||||
function trim(text, maxLength) {
|
||||
function renderTabsMarkdown(tabs) {
|
||||
if (!tabs.length)
|
||||
return ['No open tabs. Use the "browser_navigate" tool to navigate to a page first.'];
|
||||
const lines = [];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
const current = tab.current ? " (current)" : "";
|
||||
lines.push(`- ${i}:${current} [${tab.title}](${tab.url})`);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
function trimMiddle(text, maxLength) {
|
||||
if (text.length <= maxLength)
|
||||
return text;
|
||||
return text.slice(0, maxLength) + "...";
|
||||
return text.slice(0, Math.floor(maxLength / 2)) + "..." + text.slice(-3 - Math.floor(maxLength / 2));
|
||||
}
|
||||
function parseSections(text) {
|
||||
const sections = /* @__PURE__ */ new Map();
|
||||
@@ -207,31 +243,36 @@ function parseResponse(response) {
|
||||
return void 0;
|
||||
const text = response.content[0].text;
|
||||
const sections = parseSections(text);
|
||||
const error = sections.get("Error");
|
||||
const result = sections.get("Result");
|
||||
const code = sections.get("Ran Playwright code");
|
||||
const tabs = sections.get("Open tabs");
|
||||
const pageState = sections.get("Page state");
|
||||
const consoleMessages = sections.get("New console messages");
|
||||
const page = sections.get("Page");
|
||||
const snapshot = sections.get("Snapshot");
|
||||
const events = sections.get("Events");
|
||||
const modalState = sections.get("Modal state");
|
||||
const downloads = sections.get("Downloads");
|
||||
const codeNoFrame = code?.replace(/^```js\n/, "").replace(/\n```$/, "");
|
||||
const isError = response.isError;
|
||||
const attachments = response.content.slice(1);
|
||||
const attachments = response.content.length > 1 ? response.content.slice(1) : void 0;
|
||||
return {
|
||||
result,
|
||||
error,
|
||||
code: codeNoFrame,
|
||||
tabs,
|
||||
pageState,
|
||||
consoleMessages,
|
||||
page,
|
||||
snapshot,
|
||||
events,
|
||||
modalState,
|
||||
downloads,
|
||||
isError,
|
||||
attachments
|
||||
attachments,
|
||||
text
|
||||
};
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Response,
|
||||
parseResponse,
|
||||
renderTabMarkdown,
|
||||
renderTabsMarkdown,
|
||||
requestDebug
|
||||
});
|
||||
|
||||
123
node_modules/playwright/lib/mcp/browser/sessionLog.js
generated
vendored
123
node_modules/playwright/lib/mcp/browser/sessionLog.js
generated
vendored
@@ -33,124 +33,39 @@ __export(sessionLog_exports, {
|
||||
module.exports = __toCommonJS(sessionLog_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_log = require("../log");
|
||||
var import_config = require("./config");
|
||||
var import_response = require("./response");
|
||||
class SessionLog {
|
||||
constructor(sessionFolder) {
|
||||
this._ordinal = 0;
|
||||
this._pendingEntries = [];
|
||||
this._sessionFileQueue = Promise.resolve();
|
||||
this._folder = sessionFolder;
|
||||
this._file = import_path.default.join(this._folder, "session.md");
|
||||
}
|
||||
static async create(config, clientInfo) {
|
||||
const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`, { origin: "code", reason: "Saving session" });
|
||||
const sessionFolder = await (0, import_config.outputFile)(config, clientInfo, `session-${Date.now()}`, { origin: "code", title: "Saving session" });
|
||||
await import_fs.default.promises.mkdir(sessionFolder, { recursive: true });
|
||||
console.error(`Session: ${sessionFolder}`);
|
||||
return new SessionLog(sessionFolder);
|
||||
}
|
||||
logResponse(response) {
|
||||
const entry = {
|
||||
timestamp: performance.now(),
|
||||
toolCall: {
|
||||
toolName: response.toolName,
|
||||
toolArgs: response.toolArgs,
|
||||
result: response.result(),
|
||||
isError: response.isError()
|
||||
},
|
||||
code: response.code(),
|
||||
tabSnapshot: response.tabSnapshot()
|
||||
};
|
||||
this._appendEntry(entry);
|
||||
}
|
||||
logUserAction(action, tab, code, isUpdate) {
|
||||
code = code.trim();
|
||||
if (isUpdate) {
|
||||
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
|
||||
if (lastEntry?.userAction?.name === action.name) {
|
||||
lastEntry.userAction = action;
|
||||
lastEntry.code = code;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (action.name === "navigate") {
|
||||
const lastEntry = this._pendingEntries[this._pendingEntries.length - 1];
|
||||
if (lastEntry?.tabSnapshot?.url === action.url)
|
||||
return;
|
||||
}
|
||||
const entry = {
|
||||
timestamp: performance.now(),
|
||||
userAction: action,
|
||||
code,
|
||||
tabSnapshot: {
|
||||
url: tab.page.url(),
|
||||
title: "",
|
||||
ariaSnapshot: action.ariaSnapshot || "",
|
||||
modalStates: [],
|
||||
consoleMessages: [],
|
||||
downloads: []
|
||||
}
|
||||
};
|
||||
this._appendEntry(entry);
|
||||
}
|
||||
_appendEntry(entry) {
|
||||
this._pendingEntries.push(entry);
|
||||
if (this._flushEntriesTimeout)
|
||||
clearTimeout(this._flushEntriesTimeout);
|
||||
this._flushEntriesTimeout = setTimeout(() => this._flushEntries(), 1e3);
|
||||
}
|
||||
async _flushEntries() {
|
||||
clearTimeout(this._flushEntriesTimeout);
|
||||
const entries = this._pendingEntries;
|
||||
this._pendingEntries = [];
|
||||
logResponse(toolName, toolArgs, responseObject) {
|
||||
const parsed = (0, import_response.parseResponse)(responseObject);
|
||||
if (parsed)
|
||||
delete parsed.text;
|
||||
const lines = [""];
|
||||
for (const entry of entries) {
|
||||
const ordinal = (++this._ordinal).toString().padStart(3, "0");
|
||||
if (entry.toolCall) {
|
||||
lines.push(
|
||||
`### Tool call: ${entry.toolCall.toolName}`,
|
||||
`- Args`,
|
||||
"```json",
|
||||
JSON.stringify(entry.toolCall.toolArgs, null, 2),
|
||||
"```"
|
||||
);
|
||||
if (entry.toolCall.result) {
|
||||
lines.push(
|
||||
entry.toolCall.isError ? `- Error` : `- Result`,
|
||||
"```",
|
||||
entry.toolCall.result,
|
||||
"```"
|
||||
);
|
||||
}
|
||||
}
|
||||
if (entry.userAction) {
|
||||
const actionData = { ...entry.userAction };
|
||||
delete actionData.ariaSnapshot;
|
||||
delete actionData.selector;
|
||||
delete actionData.signals;
|
||||
lines.push(
|
||||
`### User action: ${entry.userAction.name}`,
|
||||
`- Args`,
|
||||
"```json",
|
||||
JSON.stringify(actionData, null, 2),
|
||||
"```"
|
||||
);
|
||||
}
|
||||
if (entry.code) {
|
||||
lines.push(
|
||||
`- Code`,
|
||||
"```js",
|
||||
entry.code,
|
||||
"```"
|
||||
);
|
||||
}
|
||||
if (entry.tabSnapshot) {
|
||||
const fileName = `${ordinal}.snapshot.yml`;
|
||||
import_fs.default.promises.writeFile(import_path.default.join(this._folder, fileName), entry.tabSnapshot.ariaSnapshot).catch(import_log.logUnhandledError);
|
||||
lines.push(`- Snapshot: ${fileName}`);
|
||||
}
|
||||
lines.push("", "");
|
||||
lines.push(
|
||||
`### Tool call: ${toolName}`,
|
||||
`- Args`,
|
||||
"```json",
|
||||
JSON.stringify(toolArgs, null, 2),
|
||||
"```"
|
||||
);
|
||||
if (parsed) {
|
||||
lines.push(`- Result`);
|
||||
lines.push("```json");
|
||||
lines.push(JSON.stringify(parsed, null, 2));
|
||||
lines.push("```");
|
||||
}
|
||||
lines.push("");
|
||||
this._sessionFileQueue = this._sessionFileQueue.then(() => import_fs.default.promises.appendFile(this._file, lines.join("\n")));
|
||||
}
|
||||
}
|
||||
|
||||
133
node_modules/playwright/lib/mcp/browser/tab.js
generated
vendored
133
node_modules/playwright/lib/mcp/browser/tab.js
generated
vendored
@@ -20,7 +20,8 @@ var tab_exports = {};
|
||||
__export(tab_exports, {
|
||||
Tab: () => Tab,
|
||||
TabEvents: () => TabEvents,
|
||||
renderModalStates: () => renderModalStates
|
||||
renderModalStates: () => renderModalStates,
|
||||
shouldIncludeMessage: () => shouldIncludeMessage
|
||||
});
|
||||
module.exports = __toCommonJS(tab_exports);
|
||||
var import_events = require("events");
|
||||
@@ -36,19 +37,20 @@ const TabEvents = {
|
||||
class Tab extends import_events.EventEmitter {
|
||||
constructor(context, page, onPageClose) {
|
||||
super();
|
||||
this._lastTitle = "about:blank";
|
||||
this._lastHeader = { title: "about:blank", url: "about:blank", current: false };
|
||||
this._consoleMessages = [];
|
||||
this._recentConsoleMessages = [];
|
||||
this._downloads = [];
|
||||
this._requests = /* @__PURE__ */ new Set();
|
||||
this._modalStates = [];
|
||||
this._downloads = [];
|
||||
this._needsFullSnapshot = false;
|
||||
this._eventEntries = [];
|
||||
this._recentEventEntries = [];
|
||||
this.context = context;
|
||||
this.page = page;
|
||||
this._onPageClose = onPageClose;
|
||||
page.on("console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event)));
|
||||
page.on("pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
|
||||
page.on("request", (request) => this._requests.add(request));
|
||||
page.on("request", (request) => this._handleRequest(request));
|
||||
page.on("close", () => this._onClose());
|
||||
page.on("filechooser", (chooser) => {
|
||||
this.setModalState({
|
||||
@@ -65,7 +67,7 @@ class Tab extends import_events.EventEmitter {
|
||||
page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
|
||||
page.setDefaultTimeout(this.context.config.timeouts.action);
|
||||
page[tabSymbol] = this;
|
||||
this.initializedPromise = this._initialize();
|
||||
this._initializedPromise = this._initialize();
|
||||
}
|
||||
static forPage(page) {
|
||||
return page[tabSymbol];
|
||||
@@ -105,9 +107,6 @@ class Tab extends import_events.EventEmitter {
|
||||
clearModalState(modalState) {
|
||||
this._modalStates = this._modalStates.filter((state) => state !== modalState);
|
||||
}
|
||||
modalStatesMarkdown() {
|
||||
return renderModalStates(this.context, this.modalStates());
|
||||
}
|
||||
_dialogShown(dialog) {
|
||||
this.setModalState({
|
||||
type: "dialog",
|
||||
@@ -120,53 +119,68 @@ class Tab extends import_events.EventEmitter {
|
||||
const entry = {
|
||||
download,
|
||||
finished: false,
|
||||
outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", reason: "Saving download" })
|
||||
outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", title: "Saving download" })
|
||||
};
|
||||
this._downloads.push(entry);
|
||||
this._addLogEntry({ type: "download-start", wallTime: Date.now(), download: entry });
|
||||
await download.saveAs(entry.outputFile);
|
||||
entry.finished = true;
|
||||
this._addLogEntry({ type: "download-finish", wallTime: Date.now(), download: entry });
|
||||
}
|
||||
_clearCollectedArtifacts() {
|
||||
this._consoleMessages.length = 0;
|
||||
this._recentConsoleMessages.length = 0;
|
||||
this._downloads.length = 0;
|
||||
this._requests.clear();
|
||||
this._eventEntries.length = 0;
|
||||
this._recentEventEntries.length = 0;
|
||||
}
|
||||
_handleRequest(request) {
|
||||
this._requests.add(request);
|
||||
this._addLogEntry({ type: "request", wallTime: Date.now(), request });
|
||||
}
|
||||
_handleConsoleMessage(message) {
|
||||
this._consoleMessages.push(message);
|
||||
this._recentConsoleMessages.push(message);
|
||||
this._addLogEntry({ type: "console", wallTime: Date.now(), message });
|
||||
}
|
||||
_addLogEntry(entry) {
|
||||
this._eventEntries.push(entry);
|
||||
this._recentEventEntries.push(entry);
|
||||
}
|
||||
_onClose() {
|
||||
this._clearCollectedArtifacts();
|
||||
this._onPageClose(this);
|
||||
}
|
||||
async updateTitle() {
|
||||
async headerSnapshot() {
|
||||
let title;
|
||||
await this._raceAgainstModalStates(async () => {
|
||||
this._lastTitle = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
|
||||
title = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
|
||||
});
|
||||
}
|
||||
lastTitle() {
|
||||
return this._lastTitle;
|
||||
if (this._lastHeader.title !== title || this._lastHeader.url !== this.page.url() || this._lastHeader.current !== this.isCurrentTab()) {
|
||||
this._lastHeader = { title: title ?? "", url: this.page.url(), current: this.isCurrentTab() };
|
||||
return { ...this._lastHeader, changed: true };
|
||||
}
|
||||
return { ...this._lastHeader, changed: false };
|
||||
}
|
||||
isCurrentTab() {
|
||||
return this === this.context.currentTab();
|
||||
}
|
||||
async waitForLoadState(state, options) {
|
||||
await this._initializedPromise;
|
||||
await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.waitForLoadState(state, options).catch(import_log.logUnhandledError));
|
||||
}
|
||||
async navigate(url) {
|
||||
await this._initializedPromise;
|
||||
this._clearCollectedArtifacts();
|
||||
const downloadEvent = (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.waitForEvent("download").catch(import_log.logUnhandledError));
|
||||
const { promise: downloadEvent, abort: abortDownloadEvent } = (0, import_utils2.eventWaiter)(this.page, "download", 3e3);
|
||||
try {
|
||||
await this.page.goto(url, { waitUntil: "domcontentloaded" });
|
||||
abortDownloadEvent();
|
||||
} catch (_e) {
|
||||
const e = _e;
|
||||
const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
|
||||
if (!mightBeDownload)
|
||||
throw e;
|
||||
const download = await Promise.race([
|
||||
downloadEvent,
|
||||
new Promise((resolve) => setTimeout(resolve, 3e3))
|
||||
]);
|
||||
const download = await downloadEvent;
|
||||
if (!download)
|
||||
throw e;
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
@@ -174,40 +188,36 @@ class Tab extends import_events.EventEmitter {
|
||||
}
|
||||
await this.waitForLoadState("load", { timeout: 5e3 });
|
||||
}
|
||||
async consoleMessages(type) {
|
||||
await this.initializedPromise;
|
||||
return this._consoleMessages.filter((message) => type ? message.type === type : true);
|
||||
async consoleMessages(level) {
|
||||
await this._initializedPromise;
|
||||
return this._consoleMessages.filter((message) => shouldIncludeMessage(level, message.type));
|
||||
}
|
||||
async requests() {
|
||||
await this.initializedPromise;
|
||||
await this._initializedPromise;
|
||||
return this._requests;
|
||||
}
|
||||
async captureSnapshot() {
|
||||
await this._initializedPromise;
|
||||
let tabSnapshot;
|
||||
const modalStates = await this._raceAgainstModalStates(async () => {
|
||||
const snapshot = await this.page._snapshotForAI({ track: "response" });
|
||||
tabSnapshot = {
|
||||
url: this.page.url(),
|
||||
title: await this.page.title(),
|
||||
ariaSnapshot: snapshot.full,
|
||||
ariaSnapshotDiff: this._needsFullSnapshot ? void 0 : snapshot.incremental,
|
||||
modalStates: [],
|
||||
consoleMessages: [],
|
||||
downloads: this._downloads
|
||||
events: []
|
||||
};
|
||||
});
|
||||
if (tabSnapshot) {
|
||||
tabSnapshot.consoleMessages = this._recentConsoleMessages;
|
||||
this._recentConsoleMessages = [];
|
||||
tabSnapshot.events = this._recentEventEntries;
|
||||
this._recentEventEntries = [];
|
||||
}
|
||||
this._needsFullSnapshot = !tabSnapshot;
|
||||
return tabSnapshot ?? {
|
||||
url: this.page.url(),
|
||||
title: "",
|
||||
ariaSnapshot: "",
|
||||
ariaSnapshotDiff: "",
|
||||
modalStates,
|
||||
consoleMessages: [],
|
||||
downloads: []
|
||||
events: []
|
||||
};
|
||||
}
|
||||
_javaScriptBlocked() {
|
||||
@@ -228,15 +238,20 @@ class Tab extends import_events.EventEmitter {
|
||||
]);
|
||||
}
|
||||
async waitForCompletion(callback) {
|
||||
await this._initializedPromise;
|
||||
await this._raceAgainstModalStates(() => (0, import_utils2.waitForCompletion)(this, callback));
|
||||
}
|
||||
async refLocator(params) {
|
||||
await this._initializedPromise;
|
||||
return (await this.refLocators([params]))[0];
|
||||
}
|
||||
async refLocators(params) {
|
||||
await this._initializedPromise;
|
||||
return Promise.all(params.map(async (param) => {
|
||||
try {
|
||||
const locator = this.page.locator(`aria-ref=${param.ref}`).describe(param.element);
|
||||
let locator = this.page.locator(`aria-ref=${param.ref}`);
|
||||
if (param.element)
|
||||
locator = locator.describe(param.element);
|
||||
const { resolvedSelector } = await locator._resolveSelector();
|
||||
return { locator, resolved: (0, import_utils.asLocator)("javascript", resolvedSelector) };
|
||||
} catch (e) {
|
||||
@@ -250,7 +265,8 @@ class Tab extends import_events.EventEmitter {
|
||||
return;
|
||||
}
|
||||
await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => {
|
||||
return page.evaluate(() => new Promise((f) => setTimeout(f, 1e3)));
|
||||
return page.evaluate(() => new Promise((f) => setTimeout(f, 1e3))).catch(() => {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -275,18 +291,53 @@ function pageErrorToConsoleMessage(errorOrValue) {
|
||||
toString: () => String(errorOrValue)
|
||||
};
|
||||
}
|
||||
function renderModalStates(context, modalStates) {
|
||||
const result = ["### Modal state"];
|
||||
function renderModalStates(modalStates) {
|
||||
const result = [];
|
||||
if (modalStates.length === 0)
|
||||
result.push("- There is no modal state present");
|
||||
for (const state of modalStates)
|
||||
result.push(`- [${state.description}]: can be handled by the "${state.clearedBy}" tool`);
|
||||
return result;
|
||||
}
|
||||
const consoleMessageLevels = ["error", "warning", "info", "debug"];
|
||||
function shouldIncludeMessage(thresholdLevel, type) {
|
||||
const messageLevel = consoleLevelForMessageType(type);
|
||||
return consoleMessageLevels.indexOf(messageLevel) <= consoleMessageLevels.indexOf(thresholdLevel);
|
||||
}
|
||||
function consoleLevelForMessageType(type) {
|
||||
switch (type) {
|
||||
case "assert":
|
||||
case "error":
|
||||
return "error";
|
||||
case "warning":
|
||||
return "warning";
|
||||
case "count":
|
||||
case "dir":
|
||||
case "dirxml":
|
||||
case "info":
|
||||
case "log":
|
||||
case "table":
|
||||
case "time":
|
||||
case "timeEnd":
|
||||
return "info";
|
||||
case "clear":
|
||||
case "debug":
|
||||
case "endGroup":
|
||||
case "profile":
|
||||
case "profileEnd":
|
||||
case "startGroup":
|
||||
case "startGroupCollapsed":
|
||||
case "trace":
|
||||
return "debug";
|
||||
default:
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
const tabSymbol = Symbol("tabSymbol");
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Tab,
|
||||
TabEvents,
|
||||
renderModalStates
|
||||
renderModalStates,
|
||||
shouldIncludeMessage
|
||||
});
|
||||
|
||||
4
node_modules/playwright/lib/mcp/browser/tools.js
generated
vendored
4
node_modules/playwright/lib/mcp/browser/tools.js
generated
vendored
@@ -43,6 +43,7 @@ var import_keyboard = __toESM(require("./tools/keyboard"));
|
||||
var import_mouse = __toESM(require("./tools/mouse"));
|
||||
var import_navigate = __toESM(require("./tools/navigate"));
|
||||
var import_network = __toESM(require("./tools/network"));
|
||||
var import_open = __toESM(require("./tools/open"));
|
||||
var import_pdf = __toESM(require("./tools/pdf"));
|
||||
var import_runCode = __toESM(require("./tools/runCode"));
|
||||
var import_snapshot = __toESM(require("./tools/snapshot"));
|
||||
@@ -60,9 +61,10 @@ const browserTools = [
|
||||
...import_form.default,
|
||||
...import_install.default,
|
||||
...import_keyboard.default,
|
||||
...import_mouse.default,
|
||||
...import_navigate.default,
|
||||
...import_network.default,
|
||||
...import_mouse.default,
|
||||
...import_open.default,
|
||||
...import_pdf.default,
|
||||
...import_runCode.default,
|
||||
...import_screenshot.default,
|
||||
|
||||
14
node_modules/playwright/lib/mcp/browser/tools/common.js
generated
vendored
14
node_modules/playwright/lib/mcp/browser/tools/common.js
generated
vendored
@@ -21,20 +21,22 @@ __export(common_exports, {
|
||||
default: () => common_default
|
||||
});
|
||||
module.exports = __toCommonJS(common_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
var import_response = require("../response");
|
||||
const close = (0, import_tool.defineTool)({
|
||||
capability: "core",
|
||||
schema: {
|
||||
name: "browser_close",
|
||||
title: "Close browser",
|
||||
description: "Close the page",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "action"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
await context.closeBrowserContext();
|
||||
response.setIncludeTabs();
|
||||
const result = (0, import_response.renderTabsMarkdown)([]);
|
||||
response.addTextResult(result.join("\n"));
|
||||
response.addCode(`await page.close()`);
|
||||
}
|
||||
});
|
||||
@@ -44,9 +46,9 @@ const resize = (0, import_tool.defineTabTool)({
|
||||
name: "browser_resize",
|
||||
title: "Resize browser window",
|
||||
description: "Resize the browser window",
|
||||
inputSchema: import_bundle.z.object({
|
||||
width: import_bundle.z.number().describe("Width of the browser window"),
|
||||
height: import_bundle.z.number().describe("Height of the browser window")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
width: import_mcpBundle.z.number().describe("Width of the browser window"),
|
||||
height: import_mcpBundle.z.number().describe("Height of the browser window")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
|
||||
12
node_modules/playwright/lib/mcp/browser/tools/console.js
generated
vendored
12
node_modules/playwright/lib/mcp/browser/tools/console.js
generated
vendored
@@ -21,7 +21,7 @@ __export(console_exports, {
|
||||
default: () => console_default
|
||||
});
|
||||
module.exports = __toCommonJS(console_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const console = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -29,14 +29,16 @@ const console = (0, import_tool.defineTabTool)({
|
||||
name: "browser_console_messages",
|
||||
title: "Get console messages",
|
||||
description: "Returns all console messages",
|
||||
inputSchema: import_bundle.z.object({
|
||||
onlyErrors: import_bundle.z.boolean().optional().describe("Only return error messages")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
level: import_mcpBundle.z.enum(["error", "warning", "info", "debug"]).default("info").describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".'),
|
||||
filename: import_mcpBundle.z.string().optional().describe("Filename to save the console messages to. If not provided, messages are returned as text.")
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
const messages = await tab.consoleMessages(params.onlyErrors ? "error" : void 0);
|
||||
messages.map((message) => response.addResult(message.toString()));
|
||||
const messages = await tab.consoleMessages(params.level);
|
||||
const text = messages.map((message) => message.toString()).join("\n");
|
||||
await response.addResult({ text, suggestedFilename: params.filename });
|
||||
}
|
||||
});
|
||||
var console_default = [
|
||||
|
||||
8
node_modules/playwright/lib/mcp/browser/tools/dialogs.js
generated
vendored
8
node_modules/playwright/lib/mcp/browser/tools/dialogs.js
generated
vendored
@@ -22,7 +22,7 @@ __export(dialogs_exports, {
|
||||
handleDialog: () => handleDialog
|
||||
});
|
||||
module.exports = __toCommonJS(dialogs_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const handleDialog = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -30,9 +30,9 @@ const handleDialog = (0, import_tool.defineTabTool)({
|
||||
name: "browser_handle_dialog",
|
||||
title: "Handle a dialog",
|
||||
description: "Handle a dialog",
|
||||
inputSchema: import_bundle.z.object({
|
||||
accept: import_bundle.z.boolean().describe("Whether to accept the dialog."),
|
||||
promptText: import_bundle.z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
accept: import_mcpBundle.z.boolean().describe("Whether to accept the dialog."),
|
||||
promptText: import_mcpBundle.z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
|
||||
30
node_modules/playwright/lib/mcp/browser/tools/evaluate.js
generated
vendored
30
node_modules/playwright/lib/mcp/browser/tools/evaluate.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,27 +15,20 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var evaluate_exports = {};
|
||||
__export(evaluate_exports, {
|
||||
default: () => evaluate_default
|
||||
});
|
||||
module.exports = __toCommonJS(evaluate_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_tool = require("./tool");
|
||||
var javascript = __toESM(require("../codegen"));
|
||||
const evaluateSchema = import_bundle.z.object({
|
||||
function: import_bundle.z.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
|
||||
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
|
||||
ref: import_bundle.z.string().optional().describe("Exact target element reference from the page snapshot")
|
||||
const evaluateSchema = import_mcpBundle.z.object({
|
||||
function: import_mcpBundle.z.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
|
||||
element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
|
||||
ref: import_mcpBundle.z.string().optional().describe("Exact target element reference from the page snapshot"),
|
||||
filename: import_mcpBundle.z.string().optional().describe("Filename to save the result to. If not provided, result is returned as JSON string.")
|
||||
});
|
||||
const evaluate = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -53,14 +44,15 @@ const evaluate = (0, import_tool.defineTabTool)({
|
||||
let locator;
|
||||
if (params.ref && params.element) {
|
||||
locator = await tab.refLocator({ ref: params.ref, element: params.element });
|
||||
response.addCode(`await page.${locator.resolved}.evaluate(${javascript.quote(params.function)});`);
|
||||
response.addCode(`await page.${locator.resolved}.evaluate(${(0, import_utils.escapeWithQuotes)(params.function)});`);
|
||||
} else {
|
||||
response.addCode(`await page.evaluate(${javascript.quote(params.function)});`);
|
||||
response.addCode(`await page.evaluate(${(0, import_utils.escapeWithQuotes)(params.function)});`);
|
||||
}
|
||||
await tab.waitForCompletion(async () => {
|
||||
const receiver = locator?.locator ?? tab.page;
|
||||
const result = await receiver._evaluateFunction(params.function);
|
||||
response.addResult(JSON.stringify(result, null, 2) || "undefined");
|
||||
const text = JSON.stringify(result, null, 2) || "undefined";
|
||||
await response.addResult({ text, suggestedFilename: params.filename });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
6
node_modules/playwright/lib/mcp/browser/tools/files.js
generated
vendored
6
node_modules/playwright/lib/mcp/browser/tools/files.js
generated
vendored
@@ -22,7 +22,7 @@ __export(files_exports, {
|
||||
uploadFile: () => uploadFile
|
||||
});
|
||||
module.exports = __toCommonJS(files_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const uploadFile = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -30,8 +30,8 @@ const uploadFile = (0, import_tool.defineTabTool)({
|
||||
name: "browser_file_upload",
|
||||
title: "Upload files",
|
||||
description: "Upload one or multiple files",
|
||||
inputSchema: import_bundle.z.object({
|
||||
paths: import_bundle.z.array(import_bundle.z.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
paths: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
|
||||
28
node_modules/playwright/lib/mcp/browser/tools/form.js
generated
vendored
28
node_modules/playwright/lib/mcp/browser/tools/form.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,35 +15,27 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var form_exports = {};
|
||||
__export(form_exports, {
|
||||
default: () => form_default
|
||||
});
|
||||
module.exports = __toCommonJS(form_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_tool = require("./tool");
|
||||
var codegen = __toESM(require("../codegen"));
|
||||
const fillForm = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
schema: {
|
||||
name: "browser_fill_form",
|
||||
title: "Fill form",
|
||||
description: "Fill multiple form fields",
|
||||
inputSchema: import_bundle.z.object({
|
||||
fields: import_bundle.z.array(import_bundle.z.object({
|
||||
name: import_bundle.z.string().describe("Human-readable field name"),
|
||||
type: import_bundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
|
||||
ref: import_bundle.z.string().describe("Exact target field reference from the page snapshot"),
|
||||
value: import_bundle.z.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
fields: import_mcpBundle.z.array(import_mcpBundle.z.object({
|
||||
name: import_mcpBundle.z.string().describe("Human-readable field name"),
|
||||
type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the field"),
|
||||
ref: import_mcpBundle.z.string().describe("Exact target field reference from the page snapshot"),
|
||||
value: import_mcpBundle.z.string().describe("Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.")
|
||||
})).describe("Fields to fill in")
|
||||
}),
|
||||
type: "input"
|
||||
@@ -63,7 +53,7 @@ const fillForm = (0, import_tool.defineTabTool)({
|
||||
response.addCode(`${locatorSource}.setChecked(${field.value});`);
|
||||
} else if (field.type === "combobox") {
|
||||
await locator.selectOption({ label: field.value });
|
||||
response.addCode(`${locatorSource}.selectOption(${codegen.quote(field.value)});`);
|
||||
response.addCode(`${locatorSource}.selectOption(${(0, import_utils.escapeWithQuotes)(field.value)});`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
node_modules/playwright/lib/mcp/browser/tools/install.js
generated
vendored
9
node_modules/playwright/lib/mcp/browser/tools/install.js
generated
vendored
@@ -33,15 +33,16 @@ __export(install_exports, {
|
||||
module.exports = __toCommonJS(install_exports);
|
||||
var import_child_process = require("child_process");
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
var import_response = require("../response");
|
||||
const install = (0, import_tool.defineTool)({
|
||||
capability: "core-install",
|
||||
schema: {
|
||||
name: "browser_install",
|
||||
title: "Install the browser specified in the config",
|
||||
description: "Install the browser specified in the config. Call this if you get an error about the browser not being installed.",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "action"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
@@ -61,7 +62,9 @@ const install = (0, import_tool.defineTool)({
|
||||
reject(new Error(`Failed to install browser: ${output.join("")}`));
|
||||
});
|
||||
});
|
||||
response.setIncludeTabs();
|
||||
const tabHeaders = await Promise.all(context.tabs().map((tab) => tab.headerSnapshot()));
|
||||
const result = (0, import_response.renderTabsMarkdown)(tabHeaders);
|
||||
response.addTextResult(result.join("\n"));
|
||||
}
|
||||
});
|
||||
var install_default = [
|
||||
|
||||
45
node_modules/playwright/lib/mcp/browser/tools/keyboard.js
generated
vendored
45
node_modules/playwright/lib/mcp/browser/tools/keyboard.js
generated
vendored
@@ -21,7 +21,7 @@ __export(keyboard_exports, {
|
||||
default: () => keyboard_default
|
||||
});
|
||||
module.exports = __toCommonJS(keyboard_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
var import_snapshot = require("./snapshot");
|
||||
const pressKey = (0, import_tool.defineTabTool)({
|
||||
@@ -30,24 +30,46 @@ const pressKey = (0, import_tool.defineTabTool)({
|
||||
name: "browser_press_key",
|
||||
title: "Press a key",
|
||||
description: "Press a key on the keyboard",
|
||||
inputSchema: import_bundle.z.object({
|
||||
key: import_bundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`// Press ${params.key}`);
|
||||
response.addCode(`await page.keyboard.press('${params.key}');`);
|
||||
await tab.waitForCompletion(async () => {
|
||||
await tab.page.keyboard.press(params.key);
|
||||
});
|
||||
await tab.page.keyboard.press(params.key);
|
||||
}
|
||||
});
|
||||
const pressSequentially = (0, import_tool.defineTabTool)({
|
||||
capability: "internal",
|
||||
schema: {
|
||||
name: "browser_press_sequentially",
|
||||
title: "Press sequentially",
|
||||
description: "Press text sequentially on the keyboard",
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
text: import_mcpBundle.z.string().describe("Text to press sequentially"),
|
||||
submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
response.addCode(`// Press ${params.text}`);
|
||||
response.addCode(`await page.keyboard.type('${params.text}');`);
|
||||
await tab.page.keyboard.type(params.text);
|
||||
if (params.submit) {
|
||||
response.addCode(`await page.keyboard.press('Enter');`);
|
||||
response.setIncludeSnapshot();
|
||||
await tab.waitForCompletion(async () => {
|
||||
await tab.page.keyboard.press("Enter");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const typeSchema = import_snapshot.elementSchema.extend({
|
||||
text: import_bundle.z.string().describe("Text to type into the element"),
|
||||
submit: import_bundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
|
||||
slowly: import_bundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
|
||||
text: import_mcpBundle.z.string().describe("Text to type into the element"),
|
||||
submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)"),
|
||||
slowly: import_mcpBundle.z.boolean().optional().describe("Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.")
|
||||
});
|
||||
const type = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -80,5 +102,6 @@ const type = (0, import_tool.defineTabTool)({
|
||||
});
|
||||
var keyboard_default = [
|
||||
pressKey,
|
||||
type
|
||||
type,
|
||||
pressSequentially
|
||||
];
|
||||
|
||||
22
node_modules/playwright/lib/mcp/browser/tools/mouse.js
generated
vendored
22
node_modules/playwright/lib/mcp/browser/tools/mouse.js
generated
vendored
@@ -21,10 +21,10 @@ __export(mouse_exports, {
|
||||
default: () => mouse_default
|
||||
});
|
||||
module.exports = __toCommonJS(mouse_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const elementSchema = import_bundle.z.object({
|
||||
element: import_bundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
|
||||
const elementSchema = import_mcpBundle.z.object({
|
||||
element: import_mcpBundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element")
|
||||
});
|
||||
const mouseMove = (0, import_tool.defineTabTool)({
|
||||
capability: "vision",
|
||||
@@ -33,8 +33,8 @@ const mouseMove = (0, import_tool.defineTabTool)({
|
||||
title: "Move mouse",
|
||||
description: "Move mouse to a given position",
|
||||
inputSchema: elementSchema.extend({
|
||||
x: import_bundle.z.number().describe("X coordinate"),
|
||||
y: import_bundle.z.number().describe("Y coordinate")
|
||||
x: import_mcpBundle.z.number().describe("X coordinate"),
|
||||
y: import_mcpBundle.z.number().describe("Y coordinate")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
@@ -53,8 +53,8 @@ const mouseClick = (0, import_tool.defineTabTool)({
|
||||
title: "Click",
|
||||
description: "Click left mouse button at a given position",
|
||||
inputSchema: elementSchema.extend({
|
||||
x: import_bundle.z.number().describe("X coordinate"),
|
||||
y: import_bundle.z.number().describe("Y coordinate")
|
||||
x: import_mcpBundle.z.number().describe("X coordinate"),
|
||||
y: import_mcpBundle.z.number().describe("Y coordinate")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
@@ -78,10 +78,10 @@ const mouseDrag = (0, import_tool.defineTabTool)({
|
||||
title: "Drag mouse",
|
||||
description: "Drag left mouse button to a given position",
|
||||
inputSchema: elementSchema.extend({
|
||||
startX: import_bundle.z.number().describe("Start X coordinate"),
|
||||
startY: import_bundle.z.number().describe("Start Y coordinate"),
|
||||
endX: import_bundle.z.number().describe("End X coordinate"),
|
||||
endY: import_bundle.z.number().describe("End Y coordinate")
|
||||
startX: import_mcpBundle.z.number().describe("Start X coordinate"),
|
||||
startY: import_mcpBundle.z.number().describe("Start Y coordinate"),
|
||||
endX: import_mcpBundle.z.number().describe("End X coordinate"),
|
||||
endY: import_mcpBundle.z.number().describe("End Y coordinate")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
|
||||
19
node_modules/playwright/lib/mcp/browser/tools/navigate.js
generated
vendored
19
node_modules/playwright/lib/mcp/browser/tools/navigate.js
generated
vendored
@@ -21,7 +21,7 @@ __export(navigate_exports, {
|
||||
default: () => navigate_default
|
||||
});
|
||||
module.exports = __toCommonJS(navigate_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const navigate = (0, import_tool.defineTool)({
|
||||
capability: "core",
|
||||
@@ -29,14 +29,23 @@ const navigate = (0, import_tool.defineTool)({
|
||||
name: "browser_navigate",
|
||||
title: "Navigate to a URL",
|
||||
description: "Navigate to a URL",
|
||||
inputSchema: import_bundle.z.object({
|
||||
url: import_bundle.z.string().describe("The URL to navigate to")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
url: import_mcpBundle.z.string().describe("The URL to navigate to")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
const tab = await context.ensureTab();
|
||||
await tab.navigate(params.url);
|
||||
let url = params.url;
|
||||
try {
|
||||
new URL(url);
|
||||
} catch (e) {
|
||||
if (url.startsWith("localhost"))
|
||||
url = "http://" + url;
|
||||
else
|
||||
url = "https://" + url;
|
||||
}
|
||||
await tab.navigate(url);
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`await page.goto('${params.url}');`);
|
||||
}
|
||||
@@ -47,7 +56,7 @@ const goBack = (0, import_tool.defineTabTool)({
|
||||
name: "browser_navigate_back",
|
||||
title: "Go back",
|
||||
description: "Go back to the previous page",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "action"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
|
||||
31
node_modules/playwright/lib/mcp/browser/tools/network.js
generated
vendored
31
node_modules/playwright/lib/mcp/browser/tools/network.js
generated
vendored
@@ -21,7 +21,7 @@ __export(network_exports, {
|
||||
default: () => network_default
|
||||
});
|
||||
module.exports = __toCommonJS(network_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const requests = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -29,24 +29,33 @@ const requests = (0, import_tool.defineTabTool)({
|
||||
name: "browser_network_requests",
|
||||
title: "List network requests",
|
||||
description: "Returns all network requests since loading the page",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
includeStatic: import_mcpBundle.z.boolean().default(false).describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false."),
|
||||
filename: import_mcpBundle.z.string().optional().describe("Filename to save the network requests to. If not provided, requests are returned as text.")
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
const requests2 = await tab.requests();
|
||||
for (const request of requests2)
|
||||
response.addResult(await renderRequest(request));
|
||||
const text = [];
|
||||
for (const request of requests2) {
|
||||
const rendered = await renderRequest(request, params.includeStatic);
|
||||
if (rendered)
|
||||
text.push(rendered);
|
||||
}
|
||||
await response.addResult({ text: text.join("\n"), suggestedFilename: params.filename });
|
||||
}
|
||||
});
|
||||
async function renderRequest(request) {
|
||||
async function renderRequest(request, includeStatic) {
|
||||
const response = request._hasResponse ? await request.response() : void 0;
|
||||
const isStaticRequest = ["document", "stylesheet", "image", "media", "font", "script", "manifest"].includes(request.resourceType());
|
||||
const isSuccessfulRequest = !response || response.status() < 400;
|
||||
if (isStaticRequest && isSuccessfulRequest && !includeStatic)
|
||||
return void 0;
|
||||
const result = [];
|
||||
result.push(`[${request.method().toUpperCase()}] ${request.url()}`);
|
||||
const hasResponse = request._hasResponse;
|
||||
if (hasResponse) {
|
||||
const response = await request.response();
|
||||
if (response)
|
||||
result.push(`=> [${response.status()}] ${response.statusText()}`);
|
||||
}
|
||||
if (response)
|
||||
result.push(`=> [${response.status()}] ${response.statusText()}`);
|
||||
return result.join(" ");
|
||||
}
|
||||
var network_default = [
|
||||
|
||||
57
node_modules/playwright/lib/mcp/browser/tools/open.js
generated
vendored
Normal file
57
node_modules/playwright/lib/mcp/browser/tools/open.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var open_exports = {};
|
||||
__export(open_exports, {
|
||||
default: () => open_default
|
||||
});
|
||||
module.exports = __toCommonJS(open_exports);
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const open = (0, import_tool.defineTool)({
|
||||
capability: "internal",
|
||||
schema: {
|
||||
name: "browser_open",
|
||||
title: "Open URL",
|
||||
description: "Open a URL in the browser",
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
url: import_mcpBundle.z.string().describe("The URL to open"),
|
||||
headed: import_mcpBundle.z.boolean().optional().describe("Run browser in headed mode")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
const forceHeadless = params.headed ? "headed" : "headless";
|
||||
const tab = await context.ensureTab({ forceHeadless });
|
||||
let url = params.url;
|
||||
try {
|
||||
new URL(url);
|
||||
} catch (e) {
|
||||
if (url.startsWith("localhost"))
|
||||
url = "http://" + url;
|
||||
else
|
||||
url = "https://" + url;
|
||||
}
|
||||
await tab.navigate(url);
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(`await page.goto('${params.url}');`);
|
||||
}
|
||||
});
|
||||
var open_default = [
|
||||
open
|
||||
];
|
||||
28
node_modules/playwright/lib/mcp/browser/tools/pdf.js
generated
vendored
28
node_modules/playwright/lib/mcp/browser/tools/pdf.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,26 +15,18 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var pdf_exports = {};
|
||||
__export(pdf_exports, {
|
||||
default: () => pdf_default
|
||||
});
|
||||
module.exports = __toCommonJS(pdf_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_tool = require("./tool");
|
||||
var javascript = __toESM(require("../codegen"));
|
||||
var import_utils = require("./utils");
|
||||
const pdfSchema = import_bundle.z.object({
|
||||
filename: import_bundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
|
||||
var import_utils2 = require("./utils");
|
||||
const pdfSchema = import_mcpBundle.z.object({
|
||||
filename: import_mcpBundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified. Prefer relative file names to stay within the output directory.")
|
||||
});
|
||||
const pdf = (0, import_tool.defineTabTool)({
|
||||
capability: "pdf",
|
||||
@@ -48,10 +38,10 @@ const pdf = (0, import_tool.defineTabTool)({
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
const fileName = await tab.context.outputFile(params.filename ?? (0, import_utils.dateAsFileName)("pdf"), { origin: "llm", reason: "Saving PDF" });
|
||||
response.addCode(`await page.pdf(${javascript.formatObject({ path: fileName })});`);
|
||||
response.addResult(`Saved page as ${fileName}`);
|
||||
await tab.page.pdf({ path: fileName });
|
||||
const data = await tab.page.pdf();
|
||||
const suggestedFilename = params.filename ?? (0, import_utils2.dateAsFileName)("pdf");
|
||||
await response.addResult({ data, title: "Page as pdf", suggestedFilename });
|
||||
response.addCode(`await page.pdf(${(0, import_utils.formatObject)({ path: suggestedFilename })});`);
|
||||
}
|
||||
});
|
||||
var pdf_default = [
|
||||
|
||||
19
node_modules/playwright/lib/mcp/browser/tools/runCode.js
generated
vendored
19
node_modules/playwright/lib/mcp/browser/tools/runCode.js
generated
vendored
@@ -33,10 +33,11 @@ __export(runCode_exports, {
|
||||
module.exports = __toCommonJS(runCode_exports);
|
||||
var import_vm = __toESM(require("vm"));
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const codeSchema = import_bundle.z.object({
|
||||
code: import_bundle.z.string().describe(`Playwright code snippet to run. The snippet should access the \`page\` object to interact with the page. Can make multiple statements. For example: \`await page.getByRole('button', { name: 'Submit' }).click();\``)
|
||||
const codeSchema = import_mcpBundle.z.object({
|
||||
code: import_mcpBundle.z.string().describe(`A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: \`async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }\``),
|
||||
filename: import_mcpBundle.z.string().optional().describe("Filename to save the result to. If not provided, result is returned as JSON string.")
|
||||
});
|
||||
const runCode = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -49,7 +50,7 @@ const runCode = (0, import_tool.defineTabTool)({
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
response.setIncludeSnapshot();
|
||||
response.addCode(params.code);
|
||||
response.addCode(`await (${params.code})(page);`);
|
||||
const __end__ = new import_utils.ManualPromise();
|
||||
const context = {
|
||||
page: tab.page,
|
||||
@@ -59,14 +60,16 @@ const runCode = (0, import_tool.defineTabTool)({
|
||||
await tab.waitForCompletion(async () => {
|
||||
const snippet = `(async () => {
|
||||
try {
|
||||
${params.code};
|
||||
__end__.resolve();
|
||||
const result = await (${params.code})(page);
|
||||
__end__.resolve(JSON.stringify(result));
|
||||
} catch (e) {
|
||||
__end__.reject(e);
|
||||
}
|
||||
})()`;
|
||||
import_vm.default.runInContext(snippet, context);
|
||||
await __end__;
|
||||
await import_vm.default.runInContext(snippet, context);
|
||||
const result = await __end__;
|
||||
if (typeof result === "string")
|
||||
await response.addResult({ text: result, suggestedFilename: params.filename });
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
45
node_modules/playwright/lib/mcp/browser/tools/screenshot.js
generated
vendored
45
node_modules/playwright/lib/mcp/browser/tools/screenshot.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var screenshot_exports = {};
|
||||
__export(screenshot_exports, {
|
||||
@@ -32,19 +22,18 @@ __export(screenshot_exports, {
|
||||
scaleImageToFitMessage: () => scaleImageToFitMessage
|
||||
});
|
||||
module.exports = __toCommonJS(screenshot_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_utils2 = require("playwright-core/lib/utils");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
var javascript = __toESM(require("../codegen"));
|
||||
var import_utils2 = require("./utils");
|
||||
const screenshotSchema = import_bundle.z.object({
|
||||
type: import_bundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
|
||||
filename: import_bundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
|
||||
element: import_bundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
|
||||
ref: import_bundle.z.string().optional().describe("Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too."),
|
||||
fullPage: import_bundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
|
||||
var import_utils3 = require("./utils");
|
||||
const screenshotSchema = import_mcpBundle.z.object({
|
||||
type: import_mcpBundle.z.enum(["png", "jpeg"]).default("png").describe("Image format for the screenshot. Default is png."),
|
||||
filename: import_mcpBundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified. Prefer relative file names to stay within the output directory."),
|
||||
element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to screenshot the element. If not provided, the screenshot will be taken of viewport. If element is provided, ref must be provided too."),
|
||||
ref: import_mcpBundle.z.string().optional().describe("Exact target element reference from the page snapshot. If not provided, the screenshot will be taken of viewport. If ref is provided, element must be provided too."),
|
||||
fullPage: import_mcpBundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.")
|
||||
});
|
||||
const screenshot = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -61,7 +50,6 @@ const screenshot = (0, import_tool.defineTabTool)({
|
||||
if (params.fullPage && params.ref)
|
||||
throw new Error("fullPage cannot be used with element screenshots.");
|
||||
const fileType = params.type || "png";
|
||||
const fileName = await tab.context.outputFile(params.filename || (0, import_utils2.dateAsFileName)(fileType), { origin: "llm", reason: "Saving screenshot" });
|
||||
const options = {
|
||||
type: fileType,
|
||||
quality: fileType === "png" ? void 0 : 90,
|
||||
@@ -70,19 +58,18 @@ const screenshot = (0, import_tool.defineTabTool)({
|
||||
};
|
||||
const isElementScreenshot = params.element && params.ref;
|
||||
const screenshotTarget = isElementScreenshot ? params.element : params.fullPage ? "full page" : "viewport";
|
||||
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
|
||||
const ref = params.ref ? await tab.refLocator({ element: params.element || "", ref: params.ref }) : null;
|
||||
const data = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
|
||||
const fileName = params.filename || (0, import_utils3.dateAsFileName)(fileType);
|
||||
response.addCode(`// Screenshot ${screenshotTarget} and save it as ${fileName}`);
|
||||
if (ref)
|
||||
response.addCode(`await page.${ref.resolved}.screenshot(${javascript.formatObject(options)});`);
|
||||
response.addCode(`await page.${ref.resolved}.screenshot(${(0, import_utils2.formatObject)({ ...options, path: fileName })});`);
|
||||
else
|
||||
response.addCode(`await page.screenshot(${javascript.formatObject(options)});`);
|
||||
const buffer = ref ? await ref.locator.screenshot(options) : await tab.page.screenshot(options);
|
||||
await (0, import_utils.mkdirIfNeeded)(fileName);
|
||||
await import_fs.default.promises.writeFile(fileName, buffer);
|
||||
response.addResult(`Took the ${screenshotTarget} screenshot and saved it as ${fileName}`);
|
||||
response.addCode(`await page.screenshot(${(0, import_utils2.formatObject)({ ...options, path: fileName })});`);
|
||||
await response.addResult({ data, title: `Screenshot of ${screenshotTarget}`, suggestedFilename: fileName });
|
||||
response.addImage({
|
||||
contentType: fileType === "png" ? "image/png" : "image/jpeg",
|
||||
data: scaleImageToFitMessage(buffer, fileType)
|
||||
data: scaleImageToFitMessage(data, fileType)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
50
node_modules/playwright/lib/mcp/browser/tools/snapshot.js
generated
vendored
50
node_modules/playwright/lib/mcp/browser/tools/snapshot.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var snapshot_exports = {};
|
||||
__export(snapshot_exports, {
|
||||
@@ -32,31 +22,33 @@ __export(snapshot_exports, {
|
||||
elementSchema: () => elementSchema
|
||||
});
|
||||
module.exports = __toCommonJS(snapshot_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_tool = require("./tool");
|
||||
var javascript = __toESM(require("../codegen"));
|
||||
const snapshot = (0, import_tool.defineTool)({
|
||||
capability: "core",
|
||||
schema: {
|
||||
name: "browser_snapshot",
|
||||
title: "Page snapshot",
|
||||
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
filename: import_mcpBundle.z.string().optional().describe("Save snapshot to markdown file instead of returning it in the response.")
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
await context.ensureTab();
|
||||
response.setIncludeSnapshot("full");
|
||||
response.setIncludeFullSnapshot(params.filename);
|
||||
}
|
||||
});
|
||||
const elementSchema = import_bundle.z.object({
|
||||
element: import_bundle.z.string().describe("Human-readable element description used to obtain permission to interact with the element"),
|
||||
ref: import_bundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
const elementSchema = import_mcpBundle.z.object({
|
||||
element: import_mcpBundle.z.string().optional().describe("Human-readable element description used to obtain permission to interact with the element"),
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
});
|
||||
const clickSchema = elementSchema.extend({
|
||||
doubleClick: import_bundle.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),
|
||||
button: import_bundle.z.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
|
||||
modifiers: import_bundle.z.array(import_bundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
|
||||
doubleClick: import_mcpBundle.z.boolean().optional().describe("Whether to perform a double click instead of a single click"),
|
||||
button: import_mcpBundle.z.enum(["left", "right", "middle"]).optional().describe("Button to click, defaults to left"),
|
||||
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.enum(["Alt", "Control", "ControlOrMeta", "Meta", "Shift"])).optional().describe("Modifier keys to press")
|
||||
});
|
||||
const click = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -74,7 +66,7 @@ const click = (0, import_tool.defineTabTool)({
|
||||
button: params.button,
|
||||
modifiers: params.modifiers
|
||||
};
|
||||
const formatted = javascript.formatObject(options, " ", "oneline");
|
||||
const formatted = (0, import_utils.formatObject)(options, " ", "oneline");
|
||||
const optionsAttr = formatted !== "{}" ? formatted : "";
|
||||
if (params.doubleClick)
|
||||
response.addCode(`await page.${resolved}.dblclick(${optionsAttr});`);
|
||||
@@ -94,11 +86,11 @@ const drag = (0, import_tool.defineTabTool)({
|
||||
name: "browser_drag",
|
||||
title: "Drag mouse",
|
||||
description: "Perform drag and drop between two elements",
|
||||
inputSchema: import_bundle.z.object({
|
||||
startElement: import_bundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
||||
startRef: import_bundle.z.string().describe("Exact source element reference from the page snapshot"),
|
||||
endElement: import_bundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
||||
endRef: import_bundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
startElement: import_mcpBundle.z.string().describe("Human-readable source element description used to obtain the permission to interact with the element"),
|
||||
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
||||
endElement: import_mcpBundle.z.string().describe("Human-readable target element description used to obtain the permission to interact with the element"),
|
||||
endRef: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
type: "input"
|
||||
},
|
||||
@@ -133,7 +125,7 @@ const hover = (0, import_tool.defineTabTool)({
|
||||
}
|
||||
});
|
||||
const selectOptionSchema = elementSchema.extend({
|
||||
values: import_bundle.z.array(import_bundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
|
||||
values: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
|
||||
});
|
||||
const selectOption = (0, import_tool.defineTabTool)({
|
||||
capability: "core",
|
||||
@@ -147,7 +139,7 @@ const selectOption = (0, import_tool.defineTabTool)({
|
||||
handle: async (tab, params, response) => {
|
||||
response.setIncludeSnapshot();
|
||||
const { locator, resolved } = await tab.refLocator(params);
|
||||
response.addCode(`await page.${resolved}.selectOption(${javascript.formatObject(params.values)});`);
|
||||
response.addCode(`await page.${resolved}.selectOption(${(0, import_utils.formatObject)(params.values)});`);
|
||||
await tab.waitForCompletion(async () => {
|
||||
await locator.selectOption(params.values);
|
||||
});
|
||||
@@ -164,7 +156,7 @@ const pickLocator = (0, import_tool.defineTabTool)({
|
||||
},
|
||||
handle: async (tab, params, response) => {
|
||||
const { resolved } = await tab.refLocator(params);
|
||||
response.addResult(resolved);
|
||||
response.addTextResult(resolved);
|
||||
}
|
||||
});
|
||||
var snapshot_default = [
|
||||
|
||||
24
node_modules/playwright/lib/mcp/browser/tools/tabs.js
generated
vendored
24
node_modules/playwright/lib/mcp/browser/tools/tabs.js
generated
vendored
@@ -21,17 +21,18 @@ __export(tabs_exports, {
|
||||
default: () => tabs_default
|
||||
});
|
||||
module.exports = __toCommonJS(tabs_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
var import_response = require("../response");
|
||||
const browserTabs = (0, import_tool.defineTool)({
|
||||
capability: "core-tabs",
|
||||
schema: {
|
||||
name: "browser_tabs",
|
||||
title: "Manage tabs",
|
||||
description: "List, create, close, or select a browser tab.",
|
||||
inputSchema: import_bundle.z.object({
|
||||
action: import_bundle.z.enum(["list", "new", "close", "select"]).describe("Operation to perform"),
|
||||
index: import_bundle.z.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed.")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
action: import_mcpBundle.z.enum(["list", "new", "close", "select"]).describe("Operation to perform"),
|
||||
index: import_mcpBundle.z.number().optional().describe("Tab index, used for close/select. If omitted for close, current tab is closed.")
|
||||
}),
|
||||
type: "action"
|
||||
},
|
||||
@@ -39,27 +40,26 @@ const browserTabs = (0, import_tool.defineTool)({
|
||||
switch (params.action) {
|
||||
case "list": {
|
||||
await context.ensureTab();
|
||||
response.setIncludeTabs();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case "new": {
|
||||
await context.newTab();
|
||||
response.setIncludeTabs();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case "close": {
|
||||
await context.closeTab(params.index);
|
||||
response.setIncludeSnapshot("full");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case "select": {
|
||||
if (params.index === void 0)
|
||||
throw new Error("Tab index is required");
|
||||
await context.selectTab(params.index);
|
||||
response.setIncludeSnapshot("full");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const tabHeaders = await Promise.all(context.tabs().map((tab) => tab.headerSnapshot()));
|
||||
const result = (0, import_response.renderTabsMarkdown)(tabHeaders);
|
||||
response.addTextResult(result.join("\n"));
|
||||
}
|
||||
});
|
||||
var tabs_default = [
|
||||
|
||||
6
node_modules/playwright/lib/mcp/browser/tools/tool.js
generated
vendored
6
node_modules/playwright/lib/mcp/browser/tools/tool.js
generated
vendored
@@ -32,11 +32,9 @@ function defineTabTool(tool) {
|
||||
const tab = await context.ensureTab();
|
||||
const modalStates = tab.modalStates().map((state) => state.type);
|
||||
if (tool.clearsModalState && !modalStates.includes(tool.clearsModalState))
|
||||
response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.
|
||||
` + tab.modalStatesMarkdown().join("\n"));
|
||||
response.addError(`Error: The tool "${tool.schema.name}" can only be used when there is related modal state present.`);
|
||||
else if (!tool.clearsModalState && modalStates.length)
|
||||
response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.
|
||||
` + tab.modalStatesMarkdown().join("\n"));
|
||||
response.addError(`Error: Tool "${tool.schema.name}" does not handle the modal state.`);
|
||||
else
|
||||
return tool.handle(tab, params, response);
|
||||
}
|
||||
|
||||
12
node_modules/playwright/lib/mcp/browser/tools/tracing.js
generated
vendored
12
node_modules/playwright/lib/mcp/browser/tools/tracing.js
generated
vendored
@@ -21,7 +21,7 @@ __export(tracing_exports, {
|
||||
default: () => tracing_default
|
||||
});
|
||||
module.exports = __toCommonJS(tracing_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const tracingStart = (0, import_tool.defineTool)({
|
||||
capability: "tracing",
|
||||
@@ -29,12 +29,12 @@ const tracingStart = (0, import_tool.defineTool)({
|
||||
name: "browser_start_tracing",
|
||||
title: "Start tracing",
|
||||
description: "Start trace recording",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
const browserContext = await context.ensureBrowserContext();
|
||||
const tracesDir = await context.outputFile(`traces`, { origin: "code", reason: "Collecting trace" });
|
||||
const tracesDir = await context.outputFile(`traces`, { origin: "code", title: "Collecting trace" });
|
||||
const name = "trace-" + Date.now();
|
||||
await browserContext.tracing.start({
|
||||
name,
|
||||
@@ -45,7 +45,7 @@ const tracingStart = (0, import_tool.defineTool)({
|
||||
const traceLegend = `- Action log: ${tracesDir}/${name}.trace
|
||||
- Network log: ${tracesDir}/${name}.network
|
||||
- Resources with content by sha1: ${tracesDir}/resources`;
|
||||
response.addResult(`Tracing started, saving to ${tracesDir}.
|
||||
response.addTextResult(`Tracing started, saving to ${tracesDir}.
|
||||
${traceLegend}`);
|
||||
browserContext.tracing[traceLegendSymbol] = traceLegend;
|
||||
}
|
||||
@@ -56,14 +56,14 @@ const tracingStop = (0, import_tool.defineTool)({
|
||||
name: "browser_stop_tracing",
|
||||
title: "Stop tracing",
|
||||
description: "Stop trace recording",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context, params, response) => {
|
||||
const browserContext = await context.ensureBrowserContext();
|
||||
await browserContext.tracing.stop();
|
||||
const traceLegend = browserContext.tracing[traceLegendSymbol];
|
||||
response.addResult(`Tracing stopped.
|
||||
response.addTextResult(`Tracing stopped.
|
||||
${traceLegend}`);
|
||||
}
|
||||
});
|
||||
|
||||
93
node_modules/playwright/lib/mcp/browser/tools/utils.js
generated
vendored
93
node_modules/playwright/lib/mcp/browser/tools/utils.js
generated
vendored
@@ -20,59 +20,44 @@ var utils_exports = {};
|
||||
__export(utils_exports, {
|
||||
callOnPageNoTrace: () => callOnPageNoTrace,
|
||||
dateAsFileName: () => dateAsFileName,
|
||||
eventWaiter: () => eventWaiter,
|
||||
waitForCompletion: () => waitForCompletion
|
||||
});
|
||||
module.exports = __toCommonJS(utils_exports);
|
||||
async function waitForCompletion(tab, callback) {
|
||||
const requests = /* @__PURE__ */ new Set();
|
||||
let frameNavigated = false;
|
||||
let waitCallback = () => {
|
||||
};
|
||||
const waitBarrier = new Promise((f) => {
|
||||
waitCallback = f;
|
||||
});
|
||||
const responseListener = (request) => {
|
||||
requests.delete(request);
|
||||
if (!requests.size)
|
||||
waitCallback();
|
||||
};
|
||||
const requestListener = (request) => {
|
||||
requests.add(request);
|
||||
void request.response().then(() => responseListener(request)).catch(() => {
|
||||
});
|
||||
};
|
||||
const frameNavigateListener = (frame) => {
|
||||
if (frame.parentFrame())
|
||||
return;
|
||||
frameNavigated = true;
|
||||
dispose();
|
||||
clearTimeout(timeout);
|
||||
void tab.waitForLoadState("load").then(waitCallback);
|
||||
};
|
||||
const onTimeout = () => {
|
||||
dispose();
|
||||
waitCallback();
|
||||
const requests = [];
|
||||
const requestListener = (request) => requests.push(request);
|
||||
const disposeListeners = () => {
|
||||
tab.page.off("request", requestListener);
|
||||
};
|
||||
tab.page.on("request", requestListener);
|
||||
tab.page.on("requestfailed", responseListener);
|
||||
tab.page.on("framenavigated", frameNavigateListener);
|
||||
const timeout = setTimeout(onTimeout, 1e4);
|
||||
const dispose = () => {
|
||||
tab.page.off("request", requestListener);
|
||||
tab.page.off("requestfailed", responseListener);
|
||||
tab.page.off("framenavigated", frameNavigateListener);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
let result;
|
||||
try {
|
||||
const result = await callback();
|
||||
if (!requests.size && !frameNavigated)
|
||||
waitCallback();
|
||||
await waitBarrier;
|
||||
await tab.waitForTimeout(1e3);
|
||||
return result;
|
||||
result = await callback();
|
||||
await tab.waitForTimeout(500);
|
||||
} finally {
|
||||
dispose();
|
||||
disposeListeners();
|
||||
}
|
||||
const requestedNavigation = requests.some((request) => request.isNavigationRequest());
|
||||
if (requestedNavigation) {
|
||||
await tab.page.mainFrame().waitForLoadState("load", { timeout: 1e4 }).catch(() => {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
const promises = [];
|
||||
for (const request of requests) {
|
||||
if (["document", "stylesheet", "script", "xhr", "fetch"].includes(request.resourceType()))
|
||||
promises.push(request.response().then((r) => r?.finished()).catch(() => {
|
||||
}));
|
||||
else
|
||||
promises.push(request.response().catch(() => {
|
||||
}));
|
||||
}
|
||||
const timeout = new Promise((resolve) => setTimeout(resolve, 5e3));
|
||||
await Promise.race([Promise.all(promises), timeout]);
|
||||
if (requests.length)
|
||||
await tab.waitForTimeout(500);
|
||||
return result;
|
||||
}
|
||||
async function callOnPageNoTrace(page, callback) {
|
||||
return await page._wrapApiCall(() => callback(page), { internal: true });
|
||||
@@ -81,9 +66,29 @@ function dateAsFileName(extension) {
|
||||
const date = /* @__PURE__ */ new Date();
|
||||
return `page-${date.toISOString().replace(/[:.]/g, "-")}.${extension}`;
|
||||
}
|
||||
function eventWaiter(page, event, timeout) {
|
||||
const disposables = [];
|
||||
const eventPromise = new Promise((resolve, reject) => {
|
||||
page.on(event, resolve);
|
||||
disposables.push(() => page.off(event, resolve));
|
||||
});
|
||||
let abort;
|
||||
const abortPromise = new Promise((resolve, reject) => {
|
||||
abort = () => resolve(void 0);
|
||||
});
|
||||
const timeoutPromise = new Promise((f) => {
|
||||
const timeoutId = setTimeout(() => f(void 0), timeout);
|
||||
disposables.push(() => clearTimeout(timeoutId));
|
||||
});
|
||||
return {
|
||||
promise: Promise.race([eventPromise, abortPromise, timeoutPromise]).finally(() => disposables.forEach((dispose) => dispose())),
|
||||
abort
|
||||
};
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
callOnPageNoTrace,
|
||||
dateAsFileName,
|
||||
eventWaiter,
|
||||
waitForCompletion
|
||||
});
|
||||
|
||||
58
node_modules/playwright/lib/mcp/browser/tools/verify.js
generated
vendored
58
node_modules/playwright/lib/mcp/browser/tools/verify.js
generated
vendored
@@ -1,9 +1,7 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -17,32 +15,24 @@ var __copyProps = (to, from, except, desc) => {
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var verify_exports = {};
|
||||
__export(verify_exports, {
|
||||
default: () => verify_default
|
||||
});
|
||||
module.exports = __toCommonJS(verify_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_tool = require("./tool");
|
||||
var javascript = __toESM(require("../codegen"));
|
||||
const verifyElement = (0, import_tool.defineTabTool)({
|
||||
capability: "testing",
|
||||
schema: {
|
||||
name: "browser_verify_element_visible",
|
||||
title: "Verify element visible",
|
||||
description: "Verify element is visible on the page",
|
||||
inputSchema: import_bundle.z.object({
|
||||
role: import_bundle.z.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
|
||||
accessibleName: import_bundle.z.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
role: import_mcpBundle.z.string().describe('ROLE of the element. Can be found in the snapshot like this: `- {ROLE} "Accessible Name":`'),
|
||||
accessibleName: import_mcpBundle.z.string().describe('ACCESSIBLE_NAME of the element. Can be found in the snapshot like this: `- role "{ACCESSIBLE_NAME}"`')
|
||||
}),
|
||||
type: "assertion"
|
||||
},
|
||||
@@ -52,8 +42,8 @@ const verifyElement = (0, import_tool.defineTabTool)({
|
||||
response.addError(`Element with role "${params.role}" and accessible name "${params.accessibleName}" not found`);
|
||||
return;
|
||||
}
|
||||
response.addCode(`await expect(page.getByRole(${javascript.escapeWithQuotes(params.role)}, { name: ${javascript.escapeWithQuotes(params.accessibleName)} })).toBeVisible();`);
|
||||
response.addResult("Done");
|
||||
response.addCode(`await expect(page.getByRole(${(0, import_utils.escapeWithQuotes)(params.role)}, { name: ${(0, import_utils.escapeWithQuotes)(params.accessibleName)} })).toBeVisible();`);
|
||||
response.addTextResult("Done");
|
||||
}
|
||||
});
|
||||
const verifyText = (0, import_tool.defineTabTool)({
|
||||
@@ -62,8 +52,8 @@ const verifyText = (0, import_tool.defineTabTool)({
|
||||
name: "browser_verify_text_visible",
|
||||
title: "Verify text visible",
|
||||
description: `Verify text is visible on the page. Prefer ${verifyElement.schema.name} if possible.`,
|
||||
inputSchema: import_bundle.z.object({
|
||||
text: import_bundle.z.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
text: import_mcpBundle.z.string().describe('TEXT to verify. Can be found in the snapshot like this: `- role "Accessible Name": {TEXT}` or like this: `- text: {TEXT}`')
|
||||
}),
|
||||
type: "assertion"
|
||||
},
|
||||
@@ -73,8 +63,8 @@ const verifyText = (0, import_tool.defineTabTool)({
|
||||
response.addError("Text not found");
|
||||
return;
|
||||
}
|
||||
response.addCode(`await expect(page.getByText(${javascript.escapeWithQuotes(params.text)})).toBeVisible();`);
|
||||
response.addResult("Done");
|
||||
response.addCode(`await expect(page.getByText(${(0, import_utils.escapeWithQuotes)(params.text)})).toBeVisible();`);
|
||||
response.addTextResult("Done");
|
||||
}
|
||||
});
|
||||
const verifyList = (0, import_tool.defineTabTool)({
|
||||
@@ -83,10 +73,10 @@ const verifyList = (0, import_tool.defineTabTool)({
|
||||
name: "browser_verify_list_visible",
|
||||
title: "Verify list visible",
|
||||
description: "Verify list is visible on the page",
|
||||
inputSchema: import_bundle.z.object({
|
||||
element: import_bundle.z.string().describe("Human-readable list description"),
|
||||
ref: import_bundle.z.string().describe("Exact target element reference that points to the list"),
|
||||
items: import_bundle.z.array(import_bundle.z.string()).describe("Items to verify")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
element: import_mcpBundle.z.string().describe("Human-readable list description"),
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference that points to the list"),
|
||||
items: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Items to verify")
|
||||
}),
|
||||
type: "assertion"
|
||||
},
|
||||
@@ -103,10 +93,10 @@ const verifyList = (0, import_tool.defineTabTool)({
|
||||
}
|
||||
const ariaSnapshot = `\`
|
||||
- list:
|
||||
${itemTexts.map((t) => ` - listitem: ${javascript.escapeWithQuotes(t, '"')}`).join("\n")}
|
||||
${itemTexts.map((t) => ` - listitem: ${(0, import_utils.escapeWithQuotes)(t, '"')}`).join("\n")}
|
||||
\``;
|
||||
response.addCode(`await expect(page.locator('body')).toMatchAriaSnapshot(${ariaSnapshot});`);
|
||||
response.addResult("Done");
|
||||
response.addTextResult("Done");
|
||||
}
|
||||
});
|
||||
const verifyValue = (0, import_tool.defineTabTool)({
|
||||
@@ -115,11 +105,11 @@ const verifyValue = (0, import_tool.defineTabTool)({
|
||||
name: "browser_verify_value",
|
||||
title: "Verify value",
|
||||
description: "Verify element value",
|
||||
inputSchema: import_bundle.z.object({
|
||||
type: import_bundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
|
||||
element: import_bundle.z.string().describe("Human-readable element description"),
|
||||
ref: import_bundle.z.string().describe("Exact target element reference that points to the element"),
|
||||
value: import_bundle.z.string().describe('Value to verify. For checkbox, use "true" or "false".')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
type: import_mcpBundle.z.enum(["textbox", "checkbox", "radio", "combobox", "slider"]).describe("Type of the element"),
|
||||
element: import_mcpBundle.z.string().describe("Human-readable element description"),
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference that points to the element"),
|
||||
value: import_mcpBundle.z.string().describe('Value to verify. For checkbox, use "true" or "false".')
|
||||
}),
|
||||
type: "assertion"
|
||||
},
|
||||
@@ -132,7 +122,7 @@ const verifyValue = (0, import_tool.defineTabTool)({
|
||||
response.addError(`Expected value "${params.value}", but got "${value}"`);
|
||||
return;
|
||||
}
|
||||
response.addCode(`await expect(${locatorSource}).toHaveValue(${javascript.quote(params.value)});`);
|
||||
response.addCode(`await expect(${locatorSource}).toHaveValue(${(0, import_utils.escapeWithQuotes)(params.value)});`);
|
||||
} else if (params.type === "checkbox" || params.type === "radio") {
|
||||
const value = await locator.isChecked();
|
||||
if (value !== (params.value === "true")) {
|
||||
@@ -142,7 +132,7 @@ const verifyValue = (0, import_tool.defineTabTool)({
|
||||
const matcher = value ? "toBeChecked" : "not.toBeChecked";
|
||||
response.addCode(`await expect(${locatorSource}).${matcher}();`);
|
||||
}
|
||||
response.addResult("Done");
|
||||
response.addTextResult("Done");
|
||||
}
|
||||
});
|
||||
var verify_default = [
|
||||
|
||||
12
node_modules/playwright/lib/mcp/browser/tools/wait.js
generated
vendored
12
node_modules/playwright/lib/mcp/browser/tools/wait.js
generated
vendored
@@ -21,7 +21,7 @@ __export(wait_exports, {
|
||||
default: () => wait_default
|
||||
});
|
||||
module.exports = __toCommonJS(wait_exports);
|
||||
var import_bundle = require("../../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_tool = require("./tool");
|
||||
const wait = (0, import_tool.defineTool)({
|
||||
capability: "core",
|
||||
@@ -29,10 +29,10 @@ const wait = (0, import_tool.defineTool)({
|
||||
name: "browser_wait_for",
|
||||
title: "Wait for",
|
||||
description: "Wait for text to appear or disappear or a specified time to pass",
|
||||
inputSchema: import_bundle.z.object({
|
||||
time: import_bundle.z.number().optional().describe("The time to wait in seconds"),
|
||||
text: import_bundle.z.string().optional().describe("The text to wait for"),
|
||||
textGone: import_bundle.z.string().optional().describe("The text to wait for to disappear")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
time: import_mcpBundle.z.number().optional().describe("The time to wait in seconds"),
|
||||
text: import_mcpBundle.z.string().optional().describe("The text to wait for"),
|
||||
textGone: import_mcpBundle.z.string().optional().describe("The text to wait for to disappear")
|
||||
}),
|
||||
type: "assertion"
|
||||
},
|
||||
@@ -54,7 +54,7 @@ const wait = (0, import_tool.defineTool)({
|
||||
response.addCode(`await page.getByText(${JSON.stringify(params.text)}).first().waitFor({ state: 'visible' });`);
|
||||
await locator.waitFor({ state: "visible" });
|
||||
}
|
||||
response.addResult(`Waited for ${params.text || params.textGone || params.time}`);
|
||||
response.addTextResult(`Waited for ${params.text || params.textGone || params.time}`);
|
||||
response.setIncludeSnapshot();
|
||||
}
|
||||
});
|
||||
|
||||
2
node_modules/playwright/lib/mcp/extension/cdpRelay.js
generated
vendored
2
node_modules/playwright/lib/mcp/extension/cdpRelay.js
generated
vendored
@@ -44,7 +44,7 @@ class CDPRelayServer {
|
||||
this._playwrightConnection = null;
|
||||
this._extensionConnection = null;
|
||||
this._nextSessionId = 1;
|
||||
this._wsHost = (0, import_http2.httpAddressToString)(server.address()).replace(/^http/, "ws");
|
||||
this._wsHost = (0, import_http2.addressToString)(server.address(), { protocol: "ws" });
|
||||
this._browserChannel = browserChannel;
|
||||
this._userDataDir = userDataDir;
|
||||
this._executablePath = executablePath;
|
||||
|
||||
11
node_modules/playwright/lib/mcp/extension/extensionContextFactory.js
generated
vendored
11
node_modules/playwright/lib/mcp/extension/extensionContextFactory.js
generated
vendored
@@ -33,7 +33,7 @@ __export(extensionContextFactory_exports, {
|
||||
module.exports = __toCommonJS(extensionContextFactory_exports);
|
||||
var playwright = __toESM(require("playwright-core"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_http = require("../sdk/http");
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var import_cdpRelay = require("./cdpRelay");
|
||||
const debugLogger = (0, import_utilsBundle.debug)("pw:mcp:relay");
|
||||
class ExtensionContextFactory {
|
||||
@@ -42,8 +42,8 @@ class ExtensionContextFactory {
|
||||
this._userDataDir = userDataDir;
|
||||
this._executablePath = executablePath;
|
||||
}
|
||||
async createContext(clientInfo, abortSignal, toolName) {
|
||||
const browser = await this._obtainBrowser(clientInfo, abortSignal, toolName);
|
||||
async createContext(clientInfo, abortSignal, options) {
|
||||
const browser = await this._obtainBrowser(clientInfo, abortSignal, options?.toolName);
|
||||
return {
|
||||
browserContext: browser.contexts()[0],
|
||||
close: async () => {
|
||||
@@ -55,10 +55,11 @@ class ExtensionContextFactory {
|
||||
async _obtainBrowser(clientInfo, abortSignal, toolName) {
|
||||
const relay = await this._startRelay(abortSignal);
|
||||
await relay.ensureExtensionConnectionForMCPContext(clientInfo, abortSignal, toolName);
|
||||
return await playwright.chromium.connectOverCDP(relay.cdpEndpoint());
|
||||
return await playwright.chromium.connectOverCDP(relay.cdpEndpoint(), { isLocal: true });
|
||||
}
|
||||
async _startRelay(abortSignal) {
|
||||
const httpServer = await (0, import_http.startHttpServer)({});
|
||||
const httpServer = (0, import_utils.createHttpServer)();
|
||||
await (0, import_utils.startHttpServer)(httpServer, {});
|
||||
if (abortSignal.aborted) {
|
||||
httpServer.close();
|
||||
throw new Error(abortSignal.reason);
|
||||
|
||||
35
node_modules/playwright/lib/mcp/program.js
generated
vendored
35
node_modules/playwright/lib/mcp/program.js
generated
vendored
File diff suppressed because one or more lines are too long
81
node_modules/playwright/lib/mcp/sdk/bundle.js
generated
vendored
81
node_modules/playwright/lib/mcp/sdk/bundle.js
generated
vendored
@@ -1,81 +0,0 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var bundle_exports = {};
|
||||
__export(bundle_exports, {
|
||||
CallToolRequestSchema: () => CallToolRequestSchema,
|
||||
Client: () => Client,
|
||||
ListRootsRequestSchema: () => ListRootsRequestSchema,
|
||||
ListToolsRequestSchema: () => ListToolsRequestSchema,
|
||||
PingRequestSchema: () => PingRequestSchema,
|
||||
ProgressNotificationSchema: () => ProgressNotificationSchema,
|
||||
SSEClientTransport: () => SSEClientTransport,
|
||||
SSEServerTransport: () => SSEServerTransport,
|
||||
Server: () => Server,
|
||||
StdioClientTransport: () => StdioClientTransport,
|
||||
StdioServerTransport: () => StdioServerTransport,
|
||||
StreamableHTTPClientTransport: () => StreamableHTTPClientTransport,
|
||||
StreamableHTTPServerTransport: () => StreamableHTTPServerTransport,
|
||||
z: () => z,
|
||||
zodToJsonSchema: () => zodToJsonSchema
|
||||
});
|
||||
module.exports = __toCommonJS(bundle_exports);
|
||||
var bundle = __toESM(require("../../mcpBundleImpl"));
|
||||
const zodToJsonSchema = bundle.zodToJsonSchema;
|
||||
const Client = bundle.Client;
|
||||
const Server = bundle.Server;
|
||||
const SSEClientTransport = bundle.SSEClientTransport;
|
||||
const SSEServerTransport = bundle.SSEServerTransport;
|
||||
const StdioClientTransport = bundle.StdioClientTransport;
|
||||
const StdioServerTransport = bundle.StdioServerTransport;
|
||||
const StreamableHTTPServerTransport = bundle.StreamableHTTPServerTransport;
|
||||
const StreamableHTTPClientTransport = bundle.StreamableHTTPClientTransport;
|
||||
const CallToolRequestSchema = bundle.CallToolRequestSchema;
|
||||
const ListRootsRequestSchema = bundle.ListRootsRequestSchema;
|
||||
const ProgressNotificationSchema = bundle.ProgressNotificationSchema;
|
||||
const ListToolsRequestSchema = bundle.ListToolsRequestSchema;
|
||||
const PingRequestSchema = bundle.PingRequestSchema;
|
||||
const z = bundle.z;
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
CallToolRequestSchema,
|
||||
Client,
|
||||
ListRootsRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
PingRequestSchema,
|
||||
ProgressNotificationSchema,
|
||||
SSEClientTransport,
|
||||
SSEServerTransport,
|
||||
Server,
|
||||
StdioClientTransport,
|
||||
StdioServerTransport,
|
||||
StreamableHTTPClientTransport,
|
||||
StreamableHTTPServerTransport,
|
||||
z,
|
||||
zodToJsonSchema
|
||||
});
|
||||
2
node_modules/playwright/lib/mcp/sdk/exports.js
generated
vendored
2
node_modules/playwright/lib/mcp/sdk/exports.js
generated
vendored
@@ -16,14 +16,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
||||
var exports_exports = {};
|
||||
module.exports = __toCommonJS(exports_exports);
|
||||
__reExport(exports_exports, require("./inProcessTransport"), module.exports);
|
||||
__reExport(exports_exports, require("./proxyBackend"), module.exports);
|
||||
__reExport(exports_exports, require("./server"), module.exports);
|
||||
__reExport(exports_exports, require("./tool"), module.exports);
|
||||
__reExport(exports_exports, require("./http"), module.exports);
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
...require("./inProcessTransport"),
|
||||
...require("./proxyBackend"),
|
||||
...require("./server"),
|
||||
...require("./tool"),
|
||||
...require("./http")
|
||||
|
||||
75
node_modules/playwright/lib/mcp/sdk/http.js
generated
vendored
75
node_modules/playwright/lib/mcp/sdk/http.js
generated
vendored
@@ -28,51 +28,36 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var http_exports = {};
|
||||
__export(http_exports, {
|
||||
httpAddressToString: () => httpAddressToString,
|
||||
installHttpTransport: () => installHttpTransport,
|
||||
startHttpServer: () => startHttpServer
|
||||
addressToString: () => addressToString,
|
||||
startMcpHttpServer: () => startMcpHttpServer
|
||||
});
|
||||
module.exports = __toCommonJS(http_exports);
|
||||
var import_assert = __toESM(require("assert"));
|
||||
var import_http = __toESM(require("http"));
|
||||
var import_crypto = __toESM(require("crypto"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var mcpBundle = __toESM(require("./bundle"));
|
||||
var mcpBundle = __toESM(require("playwright-core/lib/mcpBundle"));
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
var mcpServer = __toESM(require("./server"));
|
||||
const testDebug = (0, import_utilsBundle.debug)("pw:mcp:test");
|
||||
async function startHttpServer(config, abortSignal) {
|
||||
const { host, port } = config;
|
||||
const httpServer = import_http.default.createServer();
|
||||
decorateServer(httpServer);
|
||||
await new Promise((resolve, reject) => {
|
||||
httpServer.on("error", reject);
|
||||
abortSignal?.addEventListener("abort", () => {
|
||||
httpServer.close();
|
||||
reject(new Error("Aborted"));
|
||||
});
|
||||
httpServer.listen(port, host, () => {
|
||||
resolve();
|
||||
httpServer.removeListener("error", reject);
|
||||
});
|
||||
});
|
||||
return httpServer;
|
||||
async function startMcpHttpServer(config, serverBackendFactory, allowedHosts) {
|
||||
const httpServer = (0, import_utils.createHttpServer)();
|
||||
await (0, import_utils.startHttpServer)(httpServer, config);
|
||||
return await installHttpTransport(httpServer, serverBackendFactory, allowedHosts);
|
||||
}
|
||||
function httpAddressToString(address) {
|
||||
function addressToString(address, options) {
|
||||
(0, import_assert.default)(address, "Could not bind server socket");
|
||||
if (typeof address === "string")
|
||||
return address;
|
||||
const resolvedPort = address.port;
|
||||
let resolvedHost = address.family === "IPv4" ? address.address : `[${address.address}]`;
|
||||
if (resolvedHost === "0.0.0.0" || resolvedHost === "[::]")
|
||||
resolvedHost = "localhost";
|
||||
return `http://${resolvedHost}:${resolvedPort}`;
|
||||
throw new Error("Unexpected address type: " + address);
|
||||
let host = address.family === "IPv4" ? address.address : `[${address.address}]`;
|
||||
if (options.normalizeLoopback && (host === "0.0.0.0" || host === "[::]" || host === "[::1]" || host === "127.0.0.1"))
|
||||
host = "localhost";
|
||||
return `${options.protocol}://${host}:${address.port}`;
|
||||
}
|
||||
async function installHttpTransport(httpServer, serverBackendFactory, unguessableUrl, allowedHosts) {
|
||||
const url = httpAddressToString(httpServer.address());
|
||||
async function installHttpTransport(httpServer, serverBackendFactory, allowedHosts) {
|
||||
const url = addressToString(httpServer.address(), { protocol: "http", normalizeLoopback: true });
|
||||
const host = new URL(url).host;
|
||||
allowedHosts = (allowedHosts || [host]).map((h) => h.toLowerCase());
|
||||
const allowAnyHost = allowedHosts.includes("*");
|
||||
const pathPrefix = unguessableUrl ? `/${import_crypto.default.randomUUID()}` : "";
|
||||
const sseSessions = /* @__PURE__ */ new Map();
|
||||
const streamableSessions = /* @__PURE__ */ new Map();
|
||||
httpServer.on("request", async (req, res) => {
|
||||
@@ -87,12 +72,7 @@ async function installHttpTransport(httpServer, serverBackendFactory, unguessabl
|
||||
return res.end("Access is only allowed at " + allowedHosts.join(", "));
|
||||
}
|
||||
}
|
||||
if (!req.url?.startsWith(pathPrefix)) {
|
||||
res.statusCode = 404;
|
||||
return res.end("Not found");
|
||||
}
|
||||
const path = req.url?.slice(pathPrefix.length);
|
||||
const url2 = new URL(`http://localhost${path}`);
|
||||
const url2 = new URL(`http://localhost${req.url}`);
|
||||
if (url2.pathname === "/killkillkill" && req.method === "GET") {
|
||||
res.statusCode = 200;
|
||||
res.end("Killing process");
|
||||
@@ -104,7 +84,7 @@ async function installHttpTransport(httpServer, serverBackendFactory, unguessabl
|
||||
else
|
||||
await handleStreamable(serverBackendFactory, req, res, streamableSessions);
|
||||
});
|
||||
return `${url}${pathPrefix}`;
|
||||
return url;
|
||||
}
|
||||
async function handleSSE(serverBackendFactory, req, res, url, sessions) {
|
||||
if (req.method === "POST") {
|
||||
@@ -165,23 +145,8 @@ async function handleStreamable(serverBackendFactory, req, res, sessions) {
|
||||
res.statusCode = 400;
|
||||
res.end("Invalid request");
|
||||
}
|
||||
function decorateServer(server) {
|
||||
const sockets = /* @__PURE__ */ new Set();
|
||||
server.on("connection", (socket) => {
|
||||
sockets.add(socket);
|
||||
socket.once("close", () => sockets.delete(socket));
|
||||
});
|
||||
const close = server.close;
|
||||
server.close = (callback) => {
|
||||
for (const socket of sockets)
|
||||
socket.destroy();
|
||||
sockets.clear();
|
||||
return close.call(server, callback);
|
||||
};
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
httpAddressToString,
|
||||
installHttpTransport,
|
||||
startHttpServer
|
||||
addressToString,
|
||||
startMcpHttpServer
|
||||
});
|
||||
|
||||
128
node_modules/playwright/lib/mcp/sdk/proxyBackend.js
generated
vendored
128
node_modules/playwright/lib/mcp/sdk/proxyBackend.js
generated
vendored
@@ -1,128 +0,0 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var proxyBackend_exports = {};
|
||||
__export(proxyBackend_exports, {
|
||||
ProxyBackend: () => ProxyBackend
|
||||
});
|
||||
module.exports = __toCommonJS(proxyBackend_exports);
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var mcpBundle = __toESM(require("./bundle"));
|
||||
const errorsDebug = (0, import_utilsBundle.debug)("pw:mcp:errors");
|
||||
const { z, zodToJsonSchema } = mcpBundle;
|
||||
class ProxyBackend {
|
||||
constructor(mcpProviders) {
|
||||
this._mcpProviders = mcpProviders;
|
||||
this._contextSwitchTool = this._defineContextSwitchTool();
|
||||
}
|
||||
async initialize(clientInfo) {
|
||||
this._clientInfo = clientInfo;
|
||||
}
|
||||
async listTools() {
|
||||
const currentClient = await this._ensureCurrentClient();
|
||||
const response = await currentClient.listTools();
|
||||
if (this._mcpProviders.length === 1)
|
||||
return response.tools;
|
||||
return [
|
||||
...response.tools,
|
||||
this._contextSwitchTool
|
||||
];
|
||||
}
|
||||
async callTool(name, args) {
|
||||
if (name === this._contextSwitchTool.name)
|
||||
return this._callContextSwitchTool(args);
|
||||
const currentClient = await this._ensureCurrentClient();
|
||||
return await currentClient.callTool({
|
||||
name,
|
||||
arguments: args
|
||||
});
|
||||
}
|
||||
serverClosed() {
|
||||
void this._currentClient?.close().catch(errorsDebug);
|
||||
}
|
||||
async _callContextSwitchTool(params) {
|
||||
try {
|
||||
const factory = this._mcpProviders.find((factory2) => factory2.name === params.name);
|
||||
if (!factory)
|
||||
throw new Error("Unknown connection method: " + params.name);
|
||||
await this._setCurrentClient(factory);
|
||||
return {
|
||||
content: [{ type: "text", text: "### Result\nSuccessfully changed connection method.\n" }]
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [{ type: "text", text: `### Result
|
||||
Error: ${error}
|
||||
` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
_defineContextSwitchTool() {
|
||||
return {
|
||||
name: "browser_connect",
|
||||
description: [
|
||||
"Connect to a browser using one of the available methods:",
|
||||
...this._mcpProviders.map((factory) => `- "${factory.name}": ${factory.description}`)
|
||||
].join("\n"),
|
||||
inputSchema: zodToJsonSchema(z.object({
|
||||
name: z.enum(this._mcpProviders.map((factory) => factory.name)).default(this._mcpProviders[0].name).describe("The method to use to connect to the browser")
|
||||
}), { strictUnions: true }),
|
||||
annotations: {
|
||||
title: "Connect to a browser context",
|
||||
readOnlyHint: true,
|
||||
openWorldHint: false
|
||||
}
|
||||
};
|
||||
}
|
||||
async _ensureCurrentClient() {
|
||||
if (this._currentClient)
|
||||
return this._currentClient;
|
||||
return await this._setCurrentClient(this._mcpProviders[0]);
|
||||
}
|
||||
async _setCurrentClient(factory) {
|
||||
await this._currentClient?.close();
|
||||
this._currentClient = void 0;
|
||||
const client = new mcpBundle.Client({ name: "Playwright MCP Proxy", version: "0.0.0" });
|
||||
client.registerCapabilities({
|
||||
roots: {
|
||||
listRoots: true
|
||||
}
|
||||
});
|
||||
client.setRequestHandler(mcpBundle.ListRootsRequestSchema, () => ({ roots: this._clientInfo?.roots || [] }));
|
||||
client.setRequestHandler(mcpBundle.PingRequestSchema, () => ({}));
|
||||
const transport = await factory.connect();
|
||||
await client.connect(transport);
|
||||
this._currentClient = client;
|
||||
return client;
|
||||
}
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
ProxyBackend
|
||||
});
|
||||
33
node_modules/playwright/lib/mcp/sdk/server.js
generated
vendored
33
node_modules/playwright/lib/mcp/sdk/server.js
generated
vendored
@@ -28,16 +28,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var server_exports = {};
|
||||
__export(server_exports, {
|
||||
allRootPaths: () => allRootPaths,
|
||||
connect: () => connect,
|
||||
createServer: () => createServer,
|
||||
firstRootPath: () => firstRootPath,
|
||||
start: () => start,
|
||||
wrapInClient: () => wrapInClient,
|
||||
wrapInProcess: () => wrapInProcess
|
||||
});
|
||||
module.exports = __toCommonJS(server_exports);
|
||||
var import_url = require("url");
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var mcpBundle = __toESM(require("./bundle"));
|
||||
var mcpBundle = __toESM(require("playwright-core/lib/mcpBundle"));
|
||||
var import_http = require("./http");
|
||||
var import_inProcessTransport = require("./inProcessTransport");
|
||||
const serverDebug = (0, import_utilsBundle.debug)("pw:mcp:server");
|
||||
@@ -46,10 +48,18 @@ async function connect(factory, transport, runHeartbeat) {
|
||||
const server = createServer(factory.name, factory.version, factory.create(), runHeartbeat);
|
||||
await server.connect(transport);
|
||||
}
|
||||
async function wrapInProcess(backend) {
|
||||
function wrapInProcess(backend) {
|
||||
const server = createServer("Internal", "0.0.0", backend, false);
|
||||
return new import_inProcessTransport.InProcessTransport(server);
|
||||
}
|
||||
async function wrapInClient(backend, options) {
|
||||
const server = createServer("Internal", "0.0.0", backend, false);
|
||||
const transport = new import_inProcessTransport.InProcessTransport(server);
|
||||
const client = new mcpBundle.Client({ name: options.name, version: options.version });
|
||||
await client.connect(transport);
|
||||
await client.ping();
|
||||
return client;
|
||||
}
|
||||
function createServer(name, version, backend, runHeartbeat) {
|
||||
const server = new mcpBundle.Server({ name, version }, {
|
||||
capabilities: {
|
||||
@@ -141,8 +151,7 @@ async function start(serverBackendFactory, options) {
|
||||
await connect(serverBackendFactory, new mcpBundle.StdioServerTransport(), false);
|
||||
return;
|
||||
}
|
||||
const httpServer = await (0, import_http.startHttpServer)(options);
|
||||
const url = await (0, import_http.installHttpTransport)(httpServer, serverBackendFactory, false, options.allowedHosts);
|
||||
const url = await (0, import_http.startMcpHttpServer)(options, serverBackendFactory, options.allowedHosts);
|
||||
const mcpConfig = { mcpServers: {} };
|
||||
mcpConfig.mcpServers[serverBackendFactory.nameInConfig] = {
|
||||
url: `${url}/mcp`
|
||||
@@ -167,6 +176,20 @@ function firstRootPath(clientInfo) {
|
||||
return void 0;
|
||||
}
|
||||
}
|
||||
function allRootPaths(clientInfo) {
|
||||
const paths = [];
|
||||
for (const root of clientInfo.roots) {
|
||||
try {
|
||||
const url = new URL(root.uri);
|
||||
const path = (0, import_url.fileURLToPath)(url);
|
||||
if (path)
|
||||
paths.push(path);
|
||||
} catch (error) {
|
||||
serverDebug(error);
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
function mergeTextParts(result) {
|
||||
const content = [];
|
||||
const testParts = [];
|
||||
@@ -190,9 +213,11 @@ function mergeTextParts(result) {
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
allRootPaths,
|
||||
connect,
|
||||
createServer,
|
||||
firstRootPath,
|
||||
start,
|
||||
wrapInClient,
|
||||
wrapInProcess
|
||||
});
|
||||
|
||||
4
node_modules/playwright/lib/mcp/sdk/tool.js
generated
vendored
4
node_modules/playwright/lib/mcp/sdk/tool.js
generated
vendored
@@ -22,13 +22,13 @@ __export(tool_exports, {
|
||||
toMcpTool: () => toMcpTool
|
||||
});
|
||||
module.exports = __toCommonJS(tool_exports);
|
||||
var import_bundle = require("../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
function toMcpTool(tool) {
|
||||
const readOnly = tool.type === "readOnly" || tool.type === "assertion";
|
||||
return {
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
inputSchema: (0, import_bundle.zodToJsonSchema)(tool.inputSchema, { strictUnions: true }),
|
||||
inputSchema: import_mcpBundle.z.toJSONSchema(tool.inputSchema),
|
||||
annotations: {
|
||||
title: tool.title,
|
||||
readOnlyHint: readOnly,
|
||||
|
||||
296
node_modules/playwright/lib/mcp/terminal/cli.js
generated
vendored
Normal file
296
node_modules/playwright/lib/mcp/terminal/cli.js
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var import_child_process = require("child_process");
|
||||
var import_crypto = __toESM(require("crypto"));
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_net = __toESM(require("net"));
|
||||
var import_os = __toESM(require("os"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_socketConnection = require("./socketConnection");
|
||||
const debugCli = (0, import_utilsBundle.debug)("pw:cli");
|
||||
const packageJSON = require("../../../package.json");
|
||||
async function runCliCommand(sessionName, args) {
|
||||
const session = await connectToDaemon(sessionName);
|
||||
const result = await session.runCliCommand(args);
|
||||
console.log(result);
|
||||
session.dispose();
|
||||
}
|
||||
async function socketExists(socketPath) {
|
||||
try {
|
||||
const stat = await import_fs.default.promises.stat(socketPath);
|
||||
if (stat?.isSocket())
|
||||
return true;
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
class SocketSession {
|
||||
constructor(connection) {
|
||||
this._nextMessageId = 1;
|
||||
this._callbacks = /* @__PURE__ */ new Map();
|
||||
this._connection = connection;
|
||||
this._connection.onmessage = (message) => this._onMessage(message);
|
||||
this._connection.onclose = () => this.dispose();
|
||||
}
|
||||
async callTool(name, args) {
|
||||
return this._send(name, args);
|
||||
}
|
||||
async runCliCommand(args) {
|
||||
return await this._send("runCliCommand", { args });
|
||||
}
|
||||
async _send(method, params = {}) {
|
||||
const messageId = this._nextMessageId++;
|
||||
const message = {
|
||||
id: messageId,
|
||||
method,
|
||||
params
|
||||
};
|
||||
await this._connection.send(message);
|
||||
return new Promise((resolve, reject) => {
|
||||
this._callbacks.set(messageId, { resolve, reject });
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
for (const callback of this._callbacks.values())
|
||||
callback.reject(new Error("Disposed"));
|
||||
this._callbacks.clear();
|
||||
this._connection.close();
|
||||
}
|
||||
_onMessage(object) {
|
||||
if (object.id && this._callbacks.has(object.id)) {
|
||||
const callback = this._callbacks.get(object.id);
|
||||
this._callbacks.delete(object.id);
|
||||
if (object.error)
|
||||
callback.reject(new Error(object.error));
|
||||
else
|
||||
callback.resolve(object.result);
|
||||
} else if (object.id) {
|
||||
throw new Error(`Unexpected message id: ${object.id}`);
|
||||
} else {
|
||||
throw new Error(`Unexpected message without id: ${JSON.stringify(object)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
function localCacheDir() {
|
||||
if (process.platform === "linux")
|
||||
return process.env.XDG_CACHE_HOME || import_path.default.join(import_os.default.homedir(), ".cache");
|
||||
if (process.platform === "darwin")
|
||||
return import_path.default.join(import_os.default.homedir(), "Library", "Caches");
|
||||
if (process.platform === "win32")
|
||||
return process.env.LOCALAPPDATA || import_path.default.join(import_os.default.homedir(), "AppData", "Local");
|
||||
throw new Error("Unsupported platform: " + process.platform);
|
||||
}
|
||||
function playwrightCacheDir() {
|
||||
return import_path.default.join(localCacheDir(), "ms-playwright");
|
||||
}
|
||||
function calculateSha1(buffer) {
|
||||
const hash = import_crypto.default.createHash("sha1");
|
||||
hash.update(buffer);
|
||||
return hash.digest("hex");
|
||||
}
|
||||
function socketDirHash() {
|
||||
return calculateSha1(__dirname);
|
||||
}
|
||||
function daemonSocketDir() {
|
||||
return import_path.default.resolve(playwrightCacheDir(), "daemon", socketDirHash());
|
||||
}
|
||||
function daemonSocketPath(sessionName) {
|
||||
const socketName = `${sessionName}.sock`;
|
||||
if (import_os.default.platform() === "win32")
|
||||
return `\\\\.\\pipe\\${socketDirHash()}-${socketName}`;
|
||||
return import_path.default.resolve(daemonSocketDir(), socketName);
|
||||
}
|
||||
async function connectToDaemon(sessionName) {
|
||||
const socketPath = daemonSocketPath(sessionName);
|
||||
debugCli(`Connecting to daemon at ${socketPath}`);
|
||||
if (await socketExists(socketPath)) {
|
||||
debugCli(`Socket file exists, attempting to connect...`);
|
||||
try {
|
||||
return await connectToSocket(socketPath);
|
||||
} catch (e) {
|
||||
if (import_os.default.platform() !== "win32")
|
||||
await import_fs.default.promises.unlink(socketPath).catch(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
const cliPath = import_path.default.join(__dirname, "../../../cli.js");
|
||||
debugCli(`Will launch daemon process: ${cliPath}`);
|
||||
const userDataDir = import_path.default.resolve(daemonSocketDir(), `${sessionName}-user-data`);
|
||||
const child = (0, import_child_process.spawn)(process.execPath, [cliPath, "run-mcp-server", `--daemon=${socketPath}`, `--user-data-dir=${userDataDir}`], {
|
||||
detached: true,
|
||||
stdio: "ignore",
|
||||
cwd: process.cwd()
|
||||
// Will be used as root.
|
||||
});
|
||||
child.unref();
|
||||
const maxRetries = 50;
|
||||
const retryDelay = 100;
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
try {
|
||||
return await connectToSocket(socketPath);
|
||||
} catch (e) {
|
||||
if (e.code !== "ENOENT")
|
||||
throw e;
|
||||
debugCli(`Retrying to connect to daemon at ${socketPath} (${i + 1}/${maxRetries})`);
|
||||
}
|
||||
}
|
||||
throw new Error(`Failed to connect to daemon at ${socketPath} after ${maxRetries * retryDelay}ms`);
|
||||
}
|
||||
async function connectToSocket(socketPath) {
|
||||
const socket = await new Promise((resolve, reject) => {
|
||||
const socket2 = import_net.default.createConnection(socketPath, () => {
|
||||
debugCli(`Connected to daemon at ${socketPath}`);
|
||||
resolve(socket2);
|
||||
});
|
||||
socket2.on("error", reject);
|
||||
});
|
||||
return new SocketSession(new import_socketConnection.SocketConnection(socket));
|
||||
}
|
||||
function currentSessionPath() {
|
||||
return import_path.default.resolve(daemonSocketDir(), "current-session");
|
||||
}
|
||||
async function getCurrentSession() {
|
||||
try {
|
||||
const session = await import_fs.default.promises.readFile(currentSessionPath(), "utf-8");
|
||||
return session.trim() || "default";
|
||||
} catch {
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
async function setCurrentSession(sessionName) {
|
||||
await import_fs.default.promises.mkdir(daemonSocketDir(), { recursive: true });
|
||||
await import_fs.default.promises.writeFile(currentSessionPath(), sessionName);
|
||||
}
|
||||
async function canConnectToSocket(socketPath) {
|
||||
return new Promise((resolve) => {
|
||||
const socket = import_net.default.createConnection(socketPath, () => {
|
||||
socket.destroy();
|
||||
resolve(true);
|
||||
});
|
||||
socket.on("error", () => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
async function listSessions() {
|
||||
const dir = daemonSocketDir();
|
||||
try {
|
||||
const files = await import_fs.default.promises.readdir(dir);
|
||||
const sessions = [];
|
||||
for (const file of files) {
|
||||
if (file.endsWith("-user-data")) {
|
||||
const sessionName = file.slice(0, -"-user-data".length);
|
||||
const socketPath = daemonSocketPath(sessionName);
|
||||
const live = await canConnectToSocket(socketPath);
|
||||
sessions.push({ name: sessionName, live });
|
||||
}
|
||||
}
|
||||
return sessions;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
function resolveSessionName(args) {
|
||||
if (args.session)
|
||||
return args.session;
|
||||
if (process.env.PLAYWRIGHT_CLI_SESSION)
|
||||
return process.env.PLAYWRIGHT_CLI_SESSION;
|
||||
return "default";
|
||||
}
|
||||
async function handleSessionCommand(args) {
|
||||
const subcommand = args._[1];
|
||||
if (!subcommand) {
|
||||
const current = await getCurrentSession();
|
||||
console.log(current);
|
||||
return;
|
||||
}
|
||||
if (subcommand === "list") {
|
||||
const sessions = await listSessions();
|
||||
const current = await getCurrentSession();
|
||||
console.log("Sessions:");
|
||||
for (const session of sessions) {
|
||||
const marker = session.name === current ? "->" : " ";
|
||||
const liveMarker = session.live ? " (live)" : "";
|
||||
console.log(`${marker} ${session.name}${liveMarker}`);
|
||||
}
|
||||
if (sessions.length === 0)
|
||||
console.log(" (no sessions)");
|
||||
return;
|
||||
}
|
||||
if (subcommand === "set") {
|
||||
const sessionName = args._[2];
|
||||
if (!sessionName) {
|
||||
console.error("Usage: playwright-cli session set <session-name>");
|
||||
process.exit(1);
|
||||
}
|
||||
await setCurrentSession(sessionName);
|
||||
console.log(`Current session set to: ${sessionName}`);
|
||||
return;
|
||||
}
|
||||
console.error(`Unknown session subcommand: ${subcommand}`);
|
||||
process.exit(1);
|
||||
}
|
||||
async function main() {
|
||||
const argv = process.argv.slice(2);
|
||||
const args = require("minimist")(argv);
|
||||
const help = require("./help.json");
|
||||
const commandName = args._[0];
|
||||
if (args.version || args.v) {
|
||||
console.log(packageJSON.version);
|
||||
process.exit(0);
|
||||
}
|
||||
if (commandName === "session") {
|
||||
await handleSessionCommand(args);
|
||||
return;
|
||||
}
|
||||
const command = help.commands[commandName];
|
||||
if (args.help || args.h) {
|
||||
if (command) {
|
||||
console.log(command);
|
||||
} else {
|
||||
console.log("playwright-cli - run playwright mcp commands from terminal\n");
|
||||
console.log(help.global);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
if (!command) {
|
||||
console.error(`Unknown command: ${commandName}
|
||||
`);
|
||||
console.log(help.global);
|
||||
process.exit(1);
|
||||
}
|
||||
let sessionName = resolveSessionName(args);
|
||||
if (sessionName === "default" && !args.session && !process.env.PLAYWRIGHT_CLI_SESSION)
|
||||
sessionName = await getCurrentSession();
|
||||
runCliCommand(sessionName, args).catch((e) => {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
main().catch((e) => {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
56
node_modules/playwright/lib/mcp/terminal/command.js
generated
vendored
Normal file
56
node_modules/playwright/lib/mcp/terminal/command.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var command_exports = {};
|
||||
__export(command_exports, {
|
||||
declareCommand: () => declareCommand,
|
||||
parseCommand: () => parseCommand
|
||||
});
|
||||
module.exports = __toCommonJS(command_exports);
|
||||
function declareCommand(command) {
|
||||
return command;
|
||||
}
|
||||
function parseCommand(command, args) {
|
||||
const shape = command.args ? command.args.shape : {};
|
||||
const argv = args["_"];
|
||||
const options = command.options?.parse({ ...args, _: void 0 }) ?? {};
|
||||
const argsObject = {};
|
||||
let i = 0;
|
||||
for (const name of Object.keys(shape))
|
||||
argsObject[name] = argv[++i];
|
||||
let parsedArgsObject = {};
|
||||
try {
|
||||
parsedArgsObject = command.args?.parse(argsObject) ?? {};
|
||||
} catch (e) {
|
||||
throw new Error(formatZodError(e));
|
||||
}
|
||||
const toolName = typeof command.toolName === "function" ? command.toolName(parsedArgsObject, options) : command.toolName;
|
||||
const toolParams = command.toolParams(parsedArgsObject, options);
|
||||
return { toolName, toolParams };
|
||||
}
|
||||
function formatZodError(error) {
|
||||
const issue = error.issues[0];
|
||||
if (issue.code === "invalid_type")
|
||||
return `${issue.message} in <${issue.path.join(".")}>`;
|
||||
return error.issues.map((i) => i.message).join("\n");
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
declareCommand,
|
||||
parseCommand
|
||||
});
|
||||
333
node_modules/playwright/lib/mcp/terminal/commands.js
generated
vendored
Normal file
333
node_modules/playwright/lib/mcp/terminal/commands.js
generated
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var commands_exports = {};
|
||||
__export(commands_exports, {
|
||||
commands: () => commands
|
||||
});
|
||||
module.exports = __toCommonJS(commands_exports);
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_command = require("./command");
|
||||
const click = (0, import_command.declareCommand)({
|
||||
name: "click",
|
||||
description: "Perform click on a web page",
|
||||
args: import_mcpBundle.z.object({
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
button: import_mcpBundle.z.string().optional().describe("Button to click, defaults to left"),
|
||||
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe("Modifier keys to press")
|
||||
}),
|
||||
toolName: "browser_click",
|
||||
toolParams: ({ ref }, { button, modifiers }) => ({ ref, button, modifiers })
|
||||
});
|
||||
const doubleClick = (0, import_command.declareCommand)({
|
||||
name: "dblclick",
|
||||
description: "Perform double click on a web page",
|
||||
args: import_mcpBundle.z.object({
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
button: import_mcpBundle.z.string().optional().describe("Button to click, defaults to left"),
|
||||
modifiers: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe("Modifier keys to press")
|
||||
}),
|
||||
toolName: "browser_click",
|
||||
toolParams: ({ ref }, { button, modifiers }) => ({ ref, button, modifiers, doubleClick: true })
|
||||
});
|
||||
const close = (0, import_command.declareCommand)({
|
||||
name: "close",
|
||||
description: "Close the page",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
toolName: "browser_close",
|
||||
toolParams: () => ({})
|
||||
});
|
||||
const consoleMessages = (0, import_command.declareCommand)({
|
||||
name: "console",
|
||||
description: "Returns all console messages",
|
||||
args: import_mcpBundle.z.object({
|
||||
level: import_mcpBundle.z.string().optional().describe('Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to "info".')
|
||||
}),
|
||||
toolName: "browser_console_messages",
|
||||
toolParams: ({ level }) => ({ level })
|
||||
});
|
||||
const drag = (0, import_command.declareCommand)({
|
||||
name: "drag",
|
||||
description: "Perform drag and drop between two elements",
|
||||
args: import_mcpBundle.z.object({
|
||||
startRef: import_mcpBundle.z.string().describe("Exact source element reference from the page snapshot"),
|
||||
endRef: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
headed: import_mcpBundle.z.boolean().default(false).describe("Run browser in headed mode")
|
||||
}),
|
||||
toolName: "browser_drag",
|
||||
toolParams: ({ startRef, endRef }) => ({ startRef, endRef })
|
||||
});
|
||||
const evaluate = (0, import_command.declareCommand)({
|
||||
name: "evaluate",
|
||||
description: "Evaluate JavaScript expression on page or element",
|
||||
args: import_mcpBundle.z.object({
|
||||
function: import_mcpBundle.z.string().describe("() => { /* code */ } or (element) => { /* code */ } when element is provided"),
|
||||
ref: import_mcpBundle.z.string().optional().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
toolName: "browser_evaluate",
|
||||
toolParams: ({ function: fn, ref }) => ({ function: fn, ref })
|
||||
});
|
||||
const fileUpload = (0, import_command.declareCommand)({
|
||||
name: "upload-file",
|
||||
description: "Upload one or multiple files",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
options: import_mcpBundle.z.object({
|
||||
paths: import_mcpBundle.z.array(import_mcpBundle.z.string()).optional().describe("The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.")
|
||||
}),
|
||||
toolName: "browser_file_upload",
|
||||
toolParams: (_, { paths }) => ({ paths })
|
||||
});
|
||||
const handleDialog = (0, import_command.declareCommand)({
|
||||
name: "handle-dialog",
|
||||
description: "Handle a dialog",
|
||||
args: import_mcpBundle.z.object({
|
||||
accept: import_mcpBundle.z.boolean().describe("Whether to accept the dialog."),
|
||||
promptText: import_mcpBundle.z.string().optional().describe("The text of the prompt in case of a prompt dialog.")
|
||||
}),
|
||||
toolName: "browser_handle_dialog",
|
||||
toolParams: ({ accept, promptText }) => ({ accept, promptText })
|
||||
});
|
||||
const hover = (0, import_command.declareCommand)({
|
||||
name: "hover",
|
||||
description: "Hover over element on page",
|
||||
args: import_mcpBundle.z.object({
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot")
|
||||
}),
|
||||
toolName: "browser_hover",
|
||||
toolParams: ({ ref }) => ({ ref })
|
||||
});
|
||||
const open = (0, import_command.declareCommand)({
|
||||
name: "open",
|
||||
description: "Open URL",
|
||||
args: import_mcpBundle.z.object({
|
||||
url: import_mcpBundle.z.string().describe("The URL to navigate to")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
headed: import_mcpBundle.z.boolean().default(false).describe("Run browser in headed mode")
|
||||
}),
|
||||
toolName: "browser_open",
|
||||
toolParams: ({ url }, { headed }) => ({ url, headed })
|
||||
});
|
||||
const navigateBack = (0, import_command.declareCommand)({
|
||||
name: "go-back",
|
||||
description: "Go back to the previous page",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
toolName: "browser_navigate_back",
|
||||
toolParams: () => ({})
|
||||
});
|
||||
const networkRequests = (0, import_command.declareCommand)({
|
||||
name: "network-requests",
|
||||
description: "Returns all network requests since loading the page",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
options: import_mcpBundle.z.object({
|
||||
includeStatic: import_mcpBundle.z.boolean().optional().describe("Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.")
|
||||
}),
|
||||
toolName: "browser_network_requests",
|
||||
toolParams: (_, { includeStatic }) => ({ includeStatic })
|
||||
});
|
||||
const pressKey = (0, import_command.declareCommand)({
|
||||
name: "press",
|
||||
description: "Press a key on the keyboard",
|
||||
args: import_mcpBundle.z.object({
|
||||
key: import_mcpBundle.z.string().describe("Name of the key to press or a character to generate, such as `ArrowLeft` or `a`")
|
||||
}),
|
||||
toolName: "browser_press_key",
|
||||
toolParams: ({ key }) => ({ key })
|
||||
});
|
||||
const resize = (0, import_command.declareCommand)({
|
||||
name: "resize",
|
||||
description: "Resize the browser window",
|
||||
args: import_mcpBundle.z.object({
|
||||
width: import_mcpBundle.z.number().describe("Width of the browser window"),
|
||||
height: import_mcpBundle.z.number().describe("Height of the browser window")
|
||||
}),
|
||||
toolName: "browser_resize",
|
||||
toolParams: ({ width, height }) => ({ width, height })
|
||||
});
|
||||
const runCode = (0, import_command.declareCommand)({
|
||||
name: "run-code",
|
||||
description: "Run Playwright code snippet",
|
||||
args: import_mcpBundle.z.object({
|
||||
code: import_mcpBundle.z.string().describe("A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction.")
|
||||
}),
|
||||
toolName: "browser_run_code",
|
||||
toolParams: ({ code }) => ({ code })
|
||||
});
|
||||
const selectOption = (0, import_command.declareCommand)({
|
||||
name: "select-option",
|
||||
description: "Select an option in a dropdown",
|
||||
args: import_mcpBundle.z.object({
|
||||
ref: import_mcpBundle.z.string().describe("Exact target element reference from the page snapshot"),
|
||||
values: import_mcpBundle.z.array(import_mcpBundle.z.string()).describe("Array of values to select in the dropdown. This can be a single value or multiple values.")
|
||||
}),
|
||||
toolName: "browser_select_option",
|
||||
toolParams: ({ ref, values }) => ({ ref, values })
|
||||
});
|
||||
const snapshot = (0, import_command.declareCommand)({
|
||||
name: "snapshot",
|
||||
description: "Capture accessibility snapshot of the current page, this is better than screenshot",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
options: import_mcpBundle.z.object({
|
||||
filename: import_mcpBundle.z.string().optional().describe("Save snapshot to markdown file instead of returning it in the response.")
|
||||
}),
|
||||
toolName: "browser_snapshot",
|
||||
toolParams: (_, { filename }) => ({ filename })
|
||||
});
|
||||
const screenshot = (0, import_command.declareCommand)({
|
||||
name: "screenshot",
|
||||
description: "Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.",
|
||||
args: import_mcpBundle.z.object({
|
||||
ref: import_mcpBundle.z.string().optional().describe("Exact target element reference from the page snapshot.")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
filename: import_mcpBundle.z.string().optional().describe("File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg}` if not specified."),
|
||||
fullPage: import_mcpBundle.z.boolean().optional().describe("When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport.")
|
||||
}),
|
||||
toolName: "browser_take_screenshot",
|
||||
toolParams: ({ ref }, { filename, fullPage }) => ({ filename, ref, fullPage })
|
||||
});
|
||||
const type = (0, import_command.declareCommand)({
|
||||
name: "type",
|
||||
description: "Type text into editable element",
|
||||
args: import_mcpBundle.z.object({
|
||||
text: import_mcpBundle.z.string().describe("Text to type into the element")
|
||||
}),
|
||||
options: import_mcpBundle.z.object({
|
||||
submit: import_mcpBundle.z.boolean().optional().describe("Whether to submit entered text (press Enter after)")
|
||||
}),
|
||||
toolName: "browser_press_sequentially",
|
||||
toolParams: ({ text }, { submit }) => ({ text, submit })
|
||||
});
|
||||
const waitFor = (0, import_command.declareCommand)({
|
||||
name: "wait-for",
|
||||
description: "Wait for text to appear or disappear or a specified time to pass",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
options: import_mcpBundle.z.object({
|
||||
time: import_mcpBundle.z.number().optional().describe("The time to wait in seconds"),
|
||||
text: import_mcpBundle.z.string().optional().describe("The text to wait for"),
|
||||
textGone: import_mcpBundle.z.string().optional().describe("The text to wait for to disappear")
|
||||
}),
|
||||
toolName: "browser_wait_for",
|
||||
toolParams: (_, { time, text, textGone }) => ({ time, text, textGone })
|
||||
});
|
||||
const tab = (0, import_command.declareCommand)({
|
||||
name: "tab",
|
||||
description: "Close a browser tab",
|
||||
args: import_mcpBundle.z.object({
|
||||
action: import_mcpBundle.z.string().describe(`Action to perform on tabs, 'list' | 'new' | 'close' | 'select'`),
|
||||
index: import_mcpBundle.z.number().optional().describe("Tab index. If omitted, current tab is closed.")
|
||||
}),
|
||||
toolName: "browser_tabs",
|
||||
toolParams: ({ action, index }) => ({ action, index })
|
||||
});
|
||||
const mouseClickXy = (0, import_command.declareCommand)({
|
||||
name: "mouse-click-xy",
|
||||
description: "Click left mouse button at a given position",
|
||||
args: import_mcpBundle.z.object({
|
||||
x: import_mcpBundle.z.number().describe("X coordinate"),
|
||||
y: import_mcpBundle.z.number().describe("Y coordinate")
|
||||
}),
|
||||
toolName: "browser_mouse_click_xy",
|
||||
toolParams: ({ x, y }) => ({ x, y })
|
||||
});
|
||||
const mouseDragXy = (0, import_command.declareCommand)({
|
||||
name: "mouse-drag-xy",
|
||||
description: "Drag left mouse button to a given position",
|
||||
args: import_mcpBundle.z.object({
|
||||
startX: import_mcpBundle.z.number().describe("Start X coordinate"),
|
||||
startY: import_mcpBundle.z.number().describe("Start Y coordinate"),
|
||||
endX: import_mcpBundle.z.number().describe("End X coordinate"),
|
||||
endY: import_mcpBundle.z.number().describe("End Y coordinate")
|
||||
}),
|
||||
toolName: "browser_mouse_drag_xy",
|
||||
toolParams: ({ startX, startY, endX, endY }) => ({ startX, startY, endX, endY })
|
||||
});
|
||||
const mouseMoveXy = (0, import_command.declareCommand)({
|
||||
name: "mouse-move-xy",
|
||||
description: "Move mouse to a given position",
|
||||
args: import_mcpBundle.z.object({
|
||||
x: import_mcpBundle.z.number().describe("X coordinate"),
|
||||
y: import_mcpBundle.z.number().describe("Y coordinate")
|
||||
}),
|
||||
toolName: "browser_mouse_move_xy",
|
||||
toolParams: ({ x, y }) => ({ x, y })
|
||||
});
|
||||
const pdfSave = (0, import_command.declareCommand)({
|
||||
name: "pdf-save",
|
||||
description: "Save page as PDF",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
options: import_mcpBundle.z.object({
|
||||
filename: import_mcpBundle.z.string().optional().describe("File name to save the pdf to. Defaults to `page-{timestamp}.pdf` if not specified.")
|
||||
}),
|
||||
toolName: "browser_pdf_save",
|
||||
toolParams: (_, { filename }) => ({ filename })
|
||||
});
|
||||
const startTracing = (0, import_command.declareCommand)({
|
||||
name: "start-tracing",
|
||||
description: "Start trace recording",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
toolName: "browser_start_tracing",
|
||||
toolParams: () => ({})
|
||||
});
|
||||
const stopTracing = (0, import_command.declareCommand)({
|
||||
name: "stop-tracing",
|
||||
description: "Stop trace recording",
|
||||
args: import_mcpBundle.z.object({}),
|
||||
toolName: "browser_stop_tracing",
|
||||
toolParams: () => ({})
|
||||
});
|
||||
const commandsArray = [
|
||||
click,
|
||||
close,
|
||||
doubleClick,
|
||||
consoleMessages,
|
||||
drag,
|
||||
evaluate,
|
||||
fileUpload,
|
||||
handleDialog,
|
||||
hover,
|
||||
open,
|
||||
navigateBack,
|
||||
networkRequests,
|
||||
pressKey,
|
||||
resize,
|
||||
runCode,
|
||||
selectOption,
|
||||
snapshot,
|
||||
screenshot,
|
||||
type,
|
||||
waitFor,
|
||||
tab,
|
||||
mouseClickXy,
|
||||
mouseDragXy,
|
||||
mouseMoveXy,
|
||||
pdfSave,
|
||||
startTracing,
|
||||
stopTracing
|
||||
];
|
||||
const commands = Object.fromEntries(commandsArray.map((cmd) => [cmd.name, cmd]));
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
commands
|
||||
});
|
||||
129
node_modules/playwright/lib/mcp/terminal/daemon.js
generated
vendored
Normal file
129
node_modules/playwright/lib/mcp/terminal/daemon.js
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var daemon_exports = {};
|
||||
__export(daemon_exports, {
|
||||
startMcpDaemonServer: () => startMcpDaemonServer
|
||||
});
|
||||
module.exports = __toCommonJS(daemon_exports);
|
||||
var import_promises = __toESM(require("fs/promises"));
|
||||
var import_net = __toESM(require("net"));
|
||||
var import_os = __toESM(require("os"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_url = __toESM(require("url"));
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
var import_socketConnection = require("./socketConnection");
|
||||
var import_commands = require("./commands");
|
||||
var import_command = require("./command");
|
||||
const daemonDebug = (0, import_utilsBundle.debug)("pw:daemon");
|
||||
async function socketExists(socketPath) {
|
||||
try {
|
||||
const stat = await import_promises.default.stat(socketPath);
|
||||
if (stat?.isSocket())
|
||||
return true;
|
||||
} catch (e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async function startMcpDaemonServer(socketPath, serverBackendFactory) {
|
||||
if (import_os.default.platform() !== "win32" && await socketExists(socketPath)) {
|
||||
daemonDebug(`Socket already exists, removing: ${socketPath}`);
|
||||
try {
|
||||
await import_promises.default.unlink(socketPath);
|
||||
} catch (error) {
|
||||
daemonDebug(`Failed to remove existing socket: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const backend = serverBackendFactory.create();
|
||||
const cwd = import_url.default.pathToFileURL(process.cwd()).href;
|
||||
await backend.initialize?.({
|
||||
name: "playwright-cli",
|
||||
version: "1.0.0",
|
||||
roots: [{
|
||||
uri: cwd,
|
||||
name: "cwd"
|
||||
}],
|
||||
timestamp: Date.now()
|
||||
});
|
||||
await import_promises.default.mkdir(import_path.default.dirname(socketPath), { recursive: true });
|
||||
const server = import_net.default.createServer((socket) => {
|
||||
daemonDebug("new client connection");
|
||||
const connection = new import_socketConnection.SocketConnection(socket);
|
||||
connection.onclose = () => {
|
||||
daemonDebug("client disconnected");
|
||||
};
|
||||
connection.onmessage = async (message) => {
|
||||
const { id, method, params } = message;
|
||||
try {
|
||||
daemonDebug("received command", method);
|
||||
if (method === "runCliCommand") {
|
||||
const { toolName, toolParams } = parseCliCommand(params.args);
|
||||
const response = await backend.callTool(toolName, toolParams, () => {
|
||||
});
|
||||
await connection.send({ id, result: formatResult(response) });
|
||||
} else {
|
||||
throw new Error(`Unknown method: ${method}`);
|
||||
}
|
||||
} catch (e) {
|
||||
daemonDebug("command failed", e);
|
||||
await connection.send({ id, error: e.message });
|
||||
}
|
||||
};
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
server.on("error", (error) => {
|
||||
daemonDebug(`server error: ${error.message}`);
|
||||
reject(error);
|
||||
});
|
||||
server.listen(socketPath, () => {
|
||||
daemonDebug(`daemon server listening on ${socketPath}`);
|
||||
resolve(socketPath);
|
||||
});
|
||||
});
|
||||
}
|
||||
function formatResult(result) {
|
||||
const lines = [];
|
||||
for (const content of result.content) {
|
||||
if (content.type === "text")
|
||||
lines.push(content.text);
|
||||
else
|
||||
lines.push(`<${content.type} content>`);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
function parseCliCommand(args) {
|
||||
const command = import_commands.commands[args._[0]];
|
||||
if (!command)
|
||||
throw new Error("Command is required");
|
||||
return (0, import_command.parseCommand)(command, args);
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
startMcpDaemonServer
|
||||
});
|
||||
32
node_modules/playwright/lib/mcp/terminal/help.json
generated
vendored
Normal file
32
node_modules/playwright/lib/mcp/terminal/help.json
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"global": "Usage: playwright-cli <command> [options]\nCommands:\n click <ref> perform click on a web page\n close close the page\n dblclick <ref> perform double click on a web page\n console <level> returns all console messages\n drag <startRef> <endRef> perform drag and drop between two elements\n evaluate <function> <ref> evaluate javascript expression on page or element\n upload-file upload one or multiple files\n handle-dialog <accept> <promptText> handle a dialog\n hover <ref> hover over element on page\n open <url> open url\n go-back go back to the previous page\n network-requests returns all network requests since loading the page\n press <key> press a key on the keyboard\n resize <width> <height> resize the browser window\n run-code <code> run playwright code snippet\n select-option <ref> <values> select an option in a dropdown\n snapshot capture accessibility snapshot of the current page, this is better than screenshot\n screenshot <ref> take a screenshot of the current page. you can't perform actions based on the screenshot, use browser_snapshot for actions.\n type <text> type text into editable element\n wait-for wait for text to appear or disappear or a specified time to pass\n tab <action> <index> close a browser tab\n mouse-click-xy <x> <y> click left mouse button at a given position\n mouse-drag-xy <startX> <startY> <endX> <endY> drag left mouse button to a given position\n mouse-move-xy <x> <y> move mouse to a given position\n pdf-save save page as pdf\n start-tracing start trace recording\n stop-tracing stop trace recording",
|
||||
"commands": {
|
||||
"click": "playwright-cli click <ref>\n\nPerform click on a web page\n\nArguments:\n <ref>\tExact target element reference from the page snapshot\nOptions:\n --button\tbutton to click, defaults to left\n --modifiers\tmodifier keys to press",
|
||||
"close": "playwright-cli close \n\nClose the page\n",
|
||||
"dblclick": "playwright-cli dblclick <ref>\n\nPerform double click on a web page\n\nArguments:\n <ref>\tExact target element reference from the page snapshot\nOptions:\n --button\tbutton to click, defaults to left\n --modifiers\tmodifier keys to press",
|
||||
"console": "playwright-cli console <level>\n\nReturns all console messages\n\nArguments:\n <level>\tLevel of the console messages to return. Each level includes the messages of more severe levels. Defaults to \"info\".",
|
||||
"drag": "playwright-cli drag <startRef> <endRef>\n\nPerform drag and drop between two elements\n\nArguments:\n <startRef>\tExact source element reference from the page snapshot\n <endRef>\tExact target element reference from the page snapshot\nOptions:\n --headed\trun browser in headed mode",
|
||||
"evaluate": "playwright-cli evaluate <function> <ref>\n\nEvaluate JavaScript expression on page or element\n\nArguments:\n <function>\t() => { /* code */ } or (element) => { /* code */ } when element is provided\n <ref>\tExact target element reference from the page snapshot",
|
||||
"upload-file": "playwright-cli upload-file \n\nUpload one or multiple files\n\nOptions:\n --paths\tthe absolute paths to the files to upload. can be single file or multiple files. if omitted, file chooser is cancelled.",
|
||||
"handle-dialog": "playwright-cli handle-dialog <accept> <promptText>\n\nHandle a dialog\n\nArguments:\n <accept>\tWhether to accept the dialog.\n <promptText>\tThe text of the prompt in case of a prompt dialog.",
|
||||
"hover": "playwright-cli hover <ref>\n\nHover over element on page\n\nArguments:\n <ref>\tExact target element reference from the page snapshot",
|
||||
"open": "playwright-cli open <url>\n\nOpen URL\n\nArguments:\n <url>\tThe URL to navigate to\nOptions:\n --headed\trun browser in headed mode",
|
||||
"go-back": "playwright-cli go-back \n\nGo back to the previous page\n",
|
||||
"network-requests": "playwright-cli network-requests \n\nReturns all network requests since loading the page\n\nOptions:\n --includeStatic\twhether to include successful static resources like images, fonts, scripts, etc. defaults to false.",
|
||||
"press": "playwright-cli press <key>\n\nPress a key on the keyboard\n\nArguments:\n <key>\tName of the key to press or a character to generate, such as `ArrowLeft` or `a`",
|
||||
"resize": "playwright-cli resize <width> <height>\n\nResize the browser window\n\nArguments:\n <width>\tWidth of the browser window\n <height>\tHeight of the browser window",
|
||||
"run-code": "playwright-cli run-code <code>\n\nRun Playwright code snippet\n\nArguments:\n <code>\tA JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction.",
|
||||
"select-option": "playwright-cli select-option <ref> <values>\n\nSelect an option in a dropdown\n\nArguments:\n <ref>\tExact target element reference from the page snapshot\n <values>\tArray of values to select in the dropdown. This can be a single value or multiple values.",
|
||||
"snapshot": "playwright-cli snapshot \n\nCapture accessibility snapshot of the current page, this is better than screenshot\n\nOptions:\n --filename\tsave snapshot to markdown file instead of returning it in the response.",
|
||||
"screenshot": "playwright-cli screenshot <ref>\n\nTake a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.\n\nArguments:\n <ref>\tExact target element reference from the page snapshot.\nOptions:\n --filename\tfile name to save the screenshot to. defaults to `page-{timestamp}.{png|jpeg}` if not specified.\n --fullPage\twhen true, takes a screenshot of the full scrollable page, instead of the currently visible viewport.",
|
||||
"type": "playwright-cli type <text>\n\nType text into editable element\n\nArguments:\n <text>\tText to type into the element\nOptions:\n --submit\twhether to submit entered text (press enter after)",
|
||||
"wait-for": "playwright-cli wait-for \n\nWait for text to appear or disappear or a specified time to pass\n\nOptions:\n --time\tthe time to wait in seconds\n --text\tthe text to wait for\n --textGone\tthe text to wait for to disappear",
|
||||
"tab": "playwright-cli tab <action> <index>\n\nClose a browser tab\n\nArguments:\n <action>\tAction to perform on tabs, 'list' | 'new' | 'close' | 'select'\n <index>\tTab index. If omitted, current tab is closed.",
|
||||
"mouse-click-xy": "playwright-cli mouse-click-xy <x> <y>\n\nClick left mouse button at a given position\n\nArguments:\n <x>\tX coordinate\n <y>\tY coordinate",
|
||||
"mouse-drag-xy": "playwright-cli mouse-drag-xy <startX> <startY> <endX> <endY>\n\nDrag left mouse button to a given position\n\nArguments:\n <startX>\tStart X coordinate\n <startY>\tStart Y coordinate\n <endX>\tEnd X coordinate\n <endY>\tEnd Y coordinate",
|
||||
"mouse-move-xy": "playwright-cli mouse-move-xy <x> <y>\n\nMove mouse to a given position\n\nArguments:\n <x>\tX coordinate\n <y>\tY coordinate",
|
||||
"pdf-save": "playwright-cli pdf-save \n\nSave page as PDF\n\nOptions:\n --filename\tfile name to save the pdf to. defaults to `page-{timestamp}.pdf` if not specified.",
|
||||
"start-tracing": "playwright-cli start-tracing \n\nStart trace recording\n",
|
||||
"stop-tracing": "playwright-cli stop-tracing \n\nStop trace recording\n"
|
||||
}
|
||||
}
|
||||
88
node_modules/playwright/lib/mcp/terminal/helpGenerator.js
generated
vendored
Normal file
88
node_modules/playwright/lib/mcp/terminal/helpGenerator.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_commands = require("./commands");
|
||||
function generateCommandHelp(command) {
|
||||
const args = [];
|
||||
const shape = command.args ? command.args.shape : {};
|
||||
for (const [name, schema] of Object.entries(shape)) {
|
||||
const zodSchema = schema;
|
||||
const description = zodSchema.description ?? "";
|
||||
args.push({ name, description });
|
||||
}
|
||||
const lines = [
|
||||
`playwright-cli ${command.name} ${Object.keys(shape).map((k) => `<${k}>`).join(" ")}`,
|
||||
"",
|
||||
command.description,
|
||||
""
|
||||
];
|
||||
if (args.length) {
|
||||
lines.push("Arguments:");
|
||||
lines.push(...args.map(({ name, description }) => ` <${name}> ${description}`));
|
||||
}
|
||||
if (command.options) {
|
||||
lines.push("Options:");
|
||||
const optionsShape = command.options.shape;
|
||||
for (const [name, schema] of Object.entries(optionsShape)) {
|
||||
const zodSchema = schema;
|
||||
const description = (zodSchema.description ?? "").toLowerCase();
|
||||
lines.push(` --${name} ${description}`);
|
||||
}
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
function generateHelp() {
|
||||
const lines = [];
|
||||
lines.push("Usage: playwright-cli <command> [options]");
|
||||
lines.push("Commands:");
|
||||
for (const command of Object.values(import_commands.commands))
|
||||
lines.push(" " + generateHelpEntry(command));
|
||||
return lines.join("\n");
|
||||
}
|
||||
function generateHelpEntry(command) {
|
||||
const args = [];
|
||||
const shape = command.args.shape;
|
||||
for (const [name, schema] of Object.entries(shape)) {
|
||||
const zodSchema = schema;
|
||||
const description = zodSchema.description ?? "";
|
||||
args.push({ name, description });
|
||||
}
|
||||
const prefix = `${command.name} ${Object.keys(shape).map((k) => `<${k}>`).join(" ")}`;
|
||||
const suffix = command.description.toLowerCase();
|
||||
const padding = " ".repeat(Math.max(1, 40 - prefix.length));
|
||||
return prefix + padding + suffix;
|
||||
}
|
||||
async function main() {
|
||||
const help = {
|
||||
global: generateHelp(),
|
||||
commands: Object.fromEntries(
|
||||
Object.entries(import_commands.commands).map(([name, command]) => [name, generateCommandHelp(command)])
|
||||
)
|
||||
};
|
||||
const fileName = import_path.default.resolve(__dirname, "help.json").replace("lib", "src");
|
||||
console.log("Writing ", import_path.default.relative(process.cwd(), fileName));
|
||||
await import_fs.default.promises.writeFile(fileName, JSON.stringify(help, null, 2));
|
||||
}
|
||||
void main();
|
||||
80
node_modules/playwright/lib/mcp/terminal/socketConnection.js
generated
vendored
Normal file
80
node_modules/playwright/lib/mcp/terminal/socketConnection.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var socketConnection_exports = {};
|
||||
__export(socketConnection_exports, {
|
||||
SocketConnection: () => SocketConnection
|
||||
});
|
||||
module.exports = __toCommonJS(socketConnection_exports);
|
||||
var import_utilsBundle = require("playwright-core/lib/utilsBundle");
|
||||
const daemonDebug = (0, import_utilsBundle.debug)("pw:daemon");
|
||||
class SocketConnection {
|
||||
constructor(socket) {
|
||||
this._pendingBuffers = [];
|
||||
this._socket = socket;
|
||||
socket.on("data", (buffer) => this._onData(buffer));
|
||||
socket.on("close", () => {
|
||||
this.onclose?.();
|
||||
});
|
||||
socket.on("error", (e) => daemonDebug(`error: ${e.message}`));
|
||||
}
|
||||
async send(message) {
|
||||
await new Promise((resolve, reject) => {
|
||||
this._socket.write(`${JSON.stringify(message)}
|
||||
`, (error) => {
|
||||
if (error)
|
||||
reject(error);
|
||||
else
|
||||
resolve(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
close() {
|
||||
this._socket.destroy();
|
||||
}
|
||||
_onData(buffer) {
|
||||
let end = buffer.indexOf("\n");
|
||||
if (end === -1) {
|
||||
this._pendingBuffers.push(buffer);
|
||||
return;
|
||||
}
|
||||
this._pendingBuffers.push(buffer.slice(0, end));
|
||||
const message = Buffer.concat(this._pendingBuffers).toString();
|
||||
this._dispatchMessage(message);
|
||||
let start = end + 1;
|
||||
end = buffer.indexOf("\n", start);
|
||||
while (end !== -1) {
|
||||
const message2 = buffer.toString(void 0, start, end);
|
||||
this._dispatchMessage(message2);
|
||||
start = end + 1;
|
||||
end = buffer.indexOf("\n", start);
|
||||
}
|
||||
this._pendingBuffers = [buffer.slice(start)];
|
||||
}
|
||||
_dispatchMessage(message) {
|
||||
try {
|
||||
this.onmessage?.(JSON.parse(message));
|
||||
} catch (e) {
|
||||
daemonDebug("failed to dispatch message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
SocketConnection
|
||||
});
|
||||
16
node_modules/playwright/lib/mcp/test/browserBackend.js
generated
vendored
16
node_modules/playwright/lib/mcp/test/browserBackend.js
generated
vendored
@@ -25,13 +25,14 @@ var import_config = require("../browser/config");
|
||||
var import_browserServerBackend = require("../browser/browserServerBackend");
|
||||
var import_tab = require("../browser/tab");
|
||||
var import_util = require("../../util");
|
||||
var import_browserContextFactory = require("../browser/browserContextFactory");
|
||||
function createCustomMessageHandler(testInfo, context) {
|
||||
let backend;
|
||||
return async (data) => {
|
||||
if (data.initialize) {
|
||||
if (backend)
|
||||
throw new Error("MCP backend is already initialized");
|
||||
backend = new import_browserServerBackend.BrowserServerBackend({ ...import_config.defaultConfig, capabilities: ["testing"] }, identityFactory(context));
|
||||
backend = new import_browserServerBackend.BrowserServerBackend({ ...import_config.defaultConfig, capabilities: ["testing"] }, (0, import_browserContextFactory.identityBrowserContextFactory)(context));
|
||||
await backend.initialize(data.initialize.clientInfo);
|
||||
const pausedMessage = await generatePausedMessage(testInfo, context);
|
||||
return { initialize: { pausedMessage } };
|
||||
@@ -73,7 +74,7 @@ async function generatePausedMessage(testInfo, context) {
|
||||
`- Page Title: ${await page.title()}`.trim()
|
||||
);
|
||||
let console = testInfo.errors.length ? await import_tab.Tab.collectConsoleMessages(page) : [];
|
||||
console = console.filter((msg) => !msg.type || msg.type === "error");
|
||||
console = console.filter((msg) => msg.type === "error");
|
||||
if (console.length) {
|
||||
lines.push("- Console Messages:");
|
||||
for (const message of console)
|
||||
@@ -91,17 +92,6 @@ async function generatePausedMessage(testInfo, context) {
|
||||
lines.push(`### Task`, `Try recovering from the error prior to continuing`);
|
||||
return lines.join("\n");
|
||||
}
|
||||
function identityFactory(browserContext) {
|
||||
return {
|
||||
createContext: async (clientInfo, abortSignal, toolName) => {
|
||||
return {
|
||||
browserContext,
|
||||
close: async () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
createCustomMessageHandler
|
||||
|
||||
18
node_modules/playwright/lib/mcp/test/generatorTools.js
generated
vendored
18
node_modules/playwright/lib/mcp/test/generatorTools.js
generated
vendored
@@ -35,7 +35,7 @@ __export(generatorTools_exports, {
|
||||
module.exports = __toCommonJS(generatorTools_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_bundle = require("../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_testTool = require("./testTool");
|
||||
var import_testContext = require("./testContext");
|
||||
const setupPage = (0, import_testTool.defineTestTool)({
|
||||
@@ -43,10 +43,10 @@ const setupPage = (0, import_testTool.defineTestTool)({
|
||||
name: "generator_setup_page",
|
||||
title: "Setup generator page",
|
||||
description: "Setup the page for test.",
|
||||
inputSchema: import_bundle.z.object({
|
||||
plan: import_bundle.z.string().describe("The plan for the test. This should be the actual test plan with all the steps."),
|
||||
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.'),
|
||||
seedFile: import_bundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
plan: import_mcpBundle.z.string().describe("The plan for the test. This should be the actual test plan with all the steps."),
|
||||
project: import_mcpBundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
|
||||
seedFile: import_mcpBundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
@@ -62,7 +62,7 @@ const generatorReadLog = (0, import_testTool.defineTestTool)({
|
||||
name: "generator_read_log",
|
||||
title: "Retrieve test log",
|
||||
description: "Retrieve the performed test log",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context) => {
|
||||
@@ -80,9 +80,9 @@ const generatorWriteTest = (0, import_testTool.defineTestTool)({
|
||||
name: "generator_write_test",
|
||||
title: "Write test",
|
||||
description: "Write the generated test to the test file",
|
||||
inputSchema: import_bundle.z.object({
|
||||
fileName: import_bundle.z.string().describe("The file to write the test to"),
|
||||
code: import_bundle.z.string().describe("The generated test code")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
fileName: import_mcpBundle.z.string().describe("The file to write the test to"),
|
||||
code: import_mcpBundle.z.string().describe("The generated test code")
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
|
||||
45
node_modules/playwright/lib/mcp/test/plannerTools.js
generated
vendored
45
node_modules/playwright/lib/mcp/test/plannerTools.js
generated
vendored
@@ -35,16 +35,16 @@ __export(plannerTools_exports, {
|
||||
module.exports = __toCommonJS(plannerTools_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_bundle = require("../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_testTool = require("./testTool");
|
||||
const setupPage = (0, import_testTool.defineTestTool)({
|
||||
schema: {
|
||||
name: "planner_setup_page",
|
||||
title: "Setup planner page",
|
||||
description: "Setup the page for test planning",
|
||||
inputSchema: import_bundle.z.object({
|
||||
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.'),
|
||||
seedFile: import_bundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
project: import_mcpBundle.z.string().optional().describe('Project to use for setup. For example: "chromium", if no project is provided uses the first project in the config.'),
|
||||
seedFile: import_mcpBundle.z.string().optional().describe('A seed file contains a single test that is used to setup the page for testing, for example: "tests/seed.spec.ts". If no seed file is provided, a default seed file is created.')
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
@@ -54,16 +54,18 @@ const setupPage = (0, import_testTool.defineTestTool)({
|
||||
return { content: [{ type: "text", text: output }], isError: status !== "paused" };
|
||||
}
|
||||
});
|
||||
const planSchema = import_bundle.z.object({
|
||||
overview: import_bundle.z.string().describe("A brief overview of the application to be tested"),
|
||||
suites: import_bundle.z.array(import_bundle.z.object({
|
||||
name: import_bundle.z.string().describe("The name of the suite"),
|
||||
seedFile: import_bundle.z.string().describe("A seed file that was used to setup the page for testing."),
|
||||
tests: import_bundle.z.array(import_bundle.z.object({
|
||||
name: import_bundle.z.string().describe("The name of the test"),
|
||||
file: import_bundle.z.string().describe('The file the test should be saved to, for example: "tests/<suite-name>/<test-name>.spec.ts".'),
|
||||
steps: import_bundle.z.array(import_bundle.z.string().describe(`The steps to be executed to perform the test. For example: 'Click on the "Submit" button'`)),
|
||||
expectedResults: import_bundle.z.array(import_bundle.z.string().describe("The expected results of the steps for test to verify."))
|
||||
const planSchema = import_mcpBundle.z.object({
|
||||
overview: import_mcpBundle.z.string().describe("A brief overview of the application to be tested"),
|
||||
suites: import_mcpBundle.z.array(import_mcpBundle.z.object({
|
||||
name: import_mcpBundle.z.string().describe("The name of the suite"),
|
||||
seedFile: import_mcpBundle.z.string().describe("A seed file that was used to setup the page for testing."),
|
||||
tests: import_mcpBundle.z.array(import_mcpBundle.z.object({
|
||||
name: import_mcpBundle.z.string().describe("The name of the test"),
|
||||
file: import_mcpBundle.z.string().describe('The file the test should be saved to, for example: "tests/<suite-name>/<test-name>.spec.ts".'),
|
||||
steps: import_mcpBundle.z.array(import_mcpBundle.z.object({
|
||||
perform: import_mcpBundle.z.string().optional().describe(`Action to perform. For example: 'Click on the "Submit" button'.`),
|
||||
expect: import_mcpBundle.z.string().array().describe(`Expected result of the action where appropriate. For example: 'The page should show the "Thank you for your submission" message'`)
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
});
|
||||
@@ -90,8 +92,8 @@ const saveTestPlan = (0, import_testTool.defineTestTool)({
|
||||
title: "Save test plan as markdown file",
|
||||
description: "Save the test plan as a markdown file",
|
||||
inputSchema: planSchema.extend({
|
||||
name: import_bundle.z.string().describe('The name of the test plan, for example: "Test Plan".'),
|
||||
fileName: import_bundle.z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.')
|
||||
name: import_mcpBundle.z.string().describe('The name of the test plan, for example: "Test Plan".'),
|
||||
fileName: import_mcpBundle.z.string().describe('The file to save the test plan to, for example: "spec/test.plan.md". Relative to the workspace root.')
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
@@ -118,12 +120,11 @@ const saveTestPlan = (0, import_testTool.defineTestTool)({
|
||||
lines.push(`**File:** \`${test.file}\``);
|
||||
lines.push(``);
|
||||
lines.push(`**Steps:**`);
|
||||
for (let k = 0; k < test.steps.length; k++)
|
||||
lines.push(` ${k + 1}. ${test.steps[k]}`);
|
||||
lines.push(``);
|
||||
lines.push(`**Expected Results:**`);
|
||||
for (const result of test.expectedResults)
|
||||
lines.push(` - ${result}`);
|
||||
for (let k = 0; k < test.steps.length; k++) {
|
||||
lines.push(` ${k + 1}. ${test.steps[k].perform ?? "-"}`);
|
||||
for (const expect of test.steps[k].expect)
|
||||
lines.push(` - expect: ${expect}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.push(``);
|
||||
|
||||
12
node_modules/playwright/lib/mcp/test/testBackend.js
generated
vendored
12
node_modules/playwright/lib/mcp/test/testBackend.js
generated
vendored
@@ -31,15 +31,15 @@ __export(testBackend_exports, {
|
||||
TestServerBackend: () => TestServerBackend
|
||||
});
|
||||
module.exports = __toCommonJS(testBackend_exports);
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var mcp = __toESM(require("../sdk/exports"));
|
||||
var import_testContext = require("./testContext");
|
||||
var testTools = __toESM(require("./testTools.js"));
|
||||
var generatorTools = __toESM(require("./generatorTools.js"));
|
||||
var plannerTools = __toESM(require("./plannerTools.js"));
|
||||
var import_tools = require("../browser/tools");
|
||||
var import_bundle = require("../sdk/bundle");
|
||||
class TestServerBackend {
|
||||
constructor(configOption, options) {
|
||||
constructor(configPath, options) {
|
||||
this.name = "Playwright";
|
||||
this.version = "0.0.1";
|
||||
this._tools = [
|
||||
@@ -55,10 +55,10 @@ class TestServerBackend {
|
||||
...import_tools.browserTools.map((tool) => wrapBrowserTool(tool))
|
||||
];
|
||||
this._options = options || {};
|
||||
this._configOption = configOption;
|
||||
this._configPath = configPath;
|
||||
}
|
||||
async initialize(clientInfo) {
|
||||
this._context = new import_testContext.TestContext(clientInfo, this._configOption, this._options);
|
||||
this._context = new import_testContext.TestContext(clientInfo, this._configPath, this._options);
|
||||
}
|
||||
async listTools() {
|
||||
return this._tools.map((tool) => mcp.toMcpTool(tool.schema));
|
||||
@@ -74,13 +74,13 @@ class TestServerBackend {
|
||||
}
|
||||
}
|
||||
serverClosed() {
|
||||
void this._context.close();
|
||||
void this._context?.close();
|
||||
}
|
||||
}
|
||||
const typesWithIntent = ["action", "assertion", "input"];
|
||||
function wrapBrowserTool(tool) {
|
||||
const inputSchema = typesWithIntent.includes(tool.schema.type) ? tool.schema.inputSchema.extend({
|
||||
intent: import_bundle.z.string().describe("The intent of the call, for example the test step description plan idea")
|
||||
intent: import_mcpBundle.z.string().describe("The intent of the call, for example the test step description plan idea")
|
||||
}) : tool.schema.inputSchema;
|
||||
return {
|
||||
schema: {
|
||||
|
||||
12
node_modules/playwright/lib/mcp/test/testContext.js
generated
vendored
12
node_modules/playwright/lib/mcp/test/testContext.js
generated
vendored
@@ -62,7 +62,7 @@ class GeneratorJournal {
|
||||
const result = [];
|
||||
result.push(`# Plan`);
|
||||
result.push(this._plan);
|
||||
result.push(`# Seed file: ${import_path.default.relative(this._rootPath, this._seed.file)}`);
|
||||
result.push(`# Seed file: ${(0, import_utils.toPosixPath)(import_path.default.relative(this._rootPath, this._seed.file))}`);
|
||||
result.push("```ts");
|
||||
result.push(this._seed.content);
|
||||
result.push("```");
|
||||
@@ -172,7 +172,7 @@ class TestContext {
|
||||
const { testRunner, screen, claimStdio, releaseStdio } = testRunnerAndScreen;
|
||||
claimStdio();
|
||||
try {
|
||||
const setupReporter = new import_list.default({ configDir, screen, includeTestId: true });
|
||||
const setupReporter = new MCPListReporter({ configDir, screen, includeTestId: true });
|
||||
const { status: status2 } = await testRunner.runGlobalSetup([setupReporter]);
|
||||
if (status2 !== "passed")
|
||||
return { output: testRunnerAndScreen.output.join("\n"), status: status2 };
|
||||
@@ -191,7 +191,7 @@ class TestContext {
|
||||
}
|
||||
};
|
||||
try {
|
||||
const reporter = new import_list.default({ configDir, screen, includeTestId: true });
|
||||
const reporter = new MCPListReporter({ configDir, screen, includeTestId: true });
|
||||
status = await Promise.race([
|
||||
testRunner.runTests(reporter, params).then((result) => result.status),
|
||||
testRunnerAndScreen.waitForTestPaused().then(() => "paused")
|
||||
@@ -271,6 +271,12 @@ const bestPracticesMarkdown = `
|
||||
- NEVER! use page.waitForTimeout()
|
||||
- NEVER! use page.evaluate()
|
||||
`;
|
||||
class MCPListReporter extends import_list.default {
|
||||
async onTestPaused() {
|
||||
await new Promise(() => {
|
||||
});
|
||||
}
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
GeneratorJournal,
|
||||
|
||||
22
node_modules/playwright/lib/mcp/test/testTools.js
generated
vendored
22
node_modules/playwright/lib/mcp/test/testTools.js
generated
vendored
@@ -33,7 +33,7 @@ __export(testTools_exports, {
|
||||
runTests: () => runTests
|
||||
});
|
||||
module.exports = __toCommonJS(testTools_exports);
|
||||
var import_bundle = require("../sdk/bundle");
|
||||
var import_mcpBundle = require("playwright-core/lib/mcpBundle");
|
||||
var import_listModeReporter = __toESM(require("../../reporters/listModeReporter"));
|
||||
var import_testTool = require("./testTool");
|
||||
const listTests = (0, import_testTool.defineTestTool)({
|
||||
@@ -41,7 +41,7 @@ const listTests = (0, import_testTool.defineTestTool)({
|
||||
name: "test_list",
|
||||
title: "List tests",
|
||||
description: "List tests",
|
||||
inputSchema: import_bundle.z.object({}),
|
||||
inputSchema: import_mcpBundle.z.object({}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context) => {
|
||||
@@ -56,15 +56,15 @@ const runTests = (0, import_testTool.defineTestTool)({
|
||||
name: "test_run",
|
||||
title: "Run tests",
|
||||
description: "Run tests",
|
||||
inputSchema: import_bundle.z.object({
|
||||
locations: import_bundle.z.array(import_bundle.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"'),
|
||||
projects: import_bundle.z.array(import_bundle.z.string()).optional().describe('Projects to run, projects from playwright.config.ts, by default runs all projects. Running with "chromium" is a good start')
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
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"'),
|
||||
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')
|
||||
}),
|
||||
type: "readOnly"
|
||||
},
|
||||
handle: async (context, params) => {
|
||||
const { output } = await context.runTestsWithGlobalSetupAndPossiblePause({
|
||||
locations: params.locations,
|
||||
locations: params.locations ?? [],
|
||||
projects: params.projects,
|
||||
disableConfigReporters: true
|
||||
});
|
||||
@@ -76,10 +76,10 @@ const debugTest = (0, import_testTool.defineTestTool)({
|
||||
name: "test_debug",
|
||||
title: "Debug single test",
|
||||
description: "Debug single test",
|
||||
inputSchema: import_bundle.z.object({
|
||||
test: import_bundle.z.object({
|
||||
id: import_bundle.z.string().describe("Test ID to debug."),
|
||||
title: import_bundle.z.string().describe("Human readable test title for granting permission to debug the test.")
|
||||
inputSchema: import_mcpBundle.z.object({
|
||||
test: import_mcpBundle.z.object({
|
||||
id: import_mcpBundle.z.string().describe("Test ID to debug."),
|
||||
title: import_mcpBundle.z.string().describe("Human readable test title for granting permission to debug the test.")
|
||||
})
|
||||
}),
|
||||
type: "readOnly"
|
||||
@@ -87,6 +87,8 @@ const debugTest = (0, import_testTool.defineTestTool)({
|
||||
handle: async (context, params) => {
|
||||
const { output, status } = await context.runTestsWithGlobalSetupAndPossiblePause({
|
||||
headed: context.computedHeaded,
|
||||
locations: [],
|
||||
// we can make this faster by passing the test's location, so we don't need to scan all tests to find the ID
|
||||
testIds: [params.test.id],
|
||||
// For automatic recovery
|
||||
timeout: 0,
|
||||
|
||||
41
node_modules/playwright/lib/mcpBundleImpl.js
generated
vendored
41
node_modules/playwright/lib/mcpBundleImpl.js
generated
vendored
File diff suppressed because one or more lines are too long
22
node_modules/playwright/lib/program.js
generated
vendored
22
node_modules/playwright/lib/program.js
generated
vendored
@@ -245,8 +245,8 @@ async function runTests(args, opts) {
|
||||
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
}
|
||||
async function runTestServer(opts) {
|
||||
const host = opts.host || "localhost";
|
||||
const port = opts.port ? +opts.port : 0;
|
||||
const host = opts.host;
|
||||
const port = opts.port ? +opts.port : void 0;
|
||||
const status = await testServer.runTestServer(opts.config, {}, { host, port });
|
||||
const exitCode = status === "interrupted" ? 130 : status === "passed" ? 0 : 1;
|
||||
(0, import_utils.gracefullyProcessExitDoNotHang)(exitCode);
|
||||
@@ -282,12 +282,15 @@ function overridesFromOptions(options) {
|
||||
retries: options.retries ? parseInt(options.retries, 10) : void 0,
|
||||
reporter: resolveReporterOption(options.reporter),
|
||||
shard: resolveShardOption(options.shard),
|
||||
shardWeights: resolveShardWeightsOption(),
|
||||
timeout: options.timeout ? parseInt(options.timeout, 10) : void 0,
|
||||
tsconfig: options.tsconfig ? import_path.default.resolve(process.cwd(), options.tsconfig) : void 0,
|
||||
ignoreSnapshots: options.ignoreSnapshots ? !!options.ignoreSnapshots : void 0,
|
||||
updateSnapshots: options.updateSnapshots,
|
||||
updateSourceMethod: options.updateSourceMethod,
|
||||
workers: options.workers
|
||||
runAgents: options.runAgents,
|
||||
workers: options.workers,
|
||||
pause: process.env.PWPAUSE ? true : void 0
|
||||
};
|
||||
if (options.browser) {
|
||||
const browserOpt = options.browser.toLowerCase();
|
||||
@@ -301,7 +304,7 @@ function overridesFromOptions(options) {
|
||||
};
|
||||
});
|
||||
}
|
||||
if (options.headed || options.debug)
|
||||
if (options.headed || options.debug || overrides.pause)
|
||||
overrides.use = { headless: false };
|
||||
if (!options.ui && options.debug) {
|
||||
overrides.debug = true;
|
||||
@@ -340,6 +343,17 @@ function resolveShardOption(shard) {
|
||||
}
|
||||
return { current, total };
|
||||
}
|
||||
function resolveShardWeightsOption() {
|
||||
const shardWeights = process.env.PWTEST_SHARD_WEIGHTS;
|
||||
if (!shardWeights)
|
||||
return void 0;
|
||||
return shardWeights.split(":").map((w) => {
|
||||
const weight = parseInt(w, 10);
|
||||
if (isNaN(weight) || weight < 0)
|
||||
throw new Error(`PWTEST_SHARD_WEIGHTS="${shardWeights}" weights must be non-negative numbers`);
|
||||
return weight;
|
||||
});
|
||||
}
|
||||
function resolveReporter(id) {
|
||||
if (import_config.builtInReporters.includes(id))
|
||||
return id;
|
||||
|
||||
33
node_modules/playwright/lib/reporters/base.js
generated
vendored
33
node_modules/playwright/lib/reporters/base.js
generated
vendored
@@ -36,6 +36,7 @@ __export(base_exports, {
|
||||
formatRetry: () => formatRetry,
|
||||
internalScreen: () => internalScreen,
|
||||
kOutputSymbol: () => kOutputSymbol,
|
||||
markErrorsAsReported: () => markErrorsAsReported,
|
||||
nonTerminalScreen: () => nonTerminalScreen,
|
||||
prepareErrorStack: () => prepareErrorStack,
|
||||
relativeFilePath: () => relativeFilePath,
|
||||
@@ -296,19 +297,37 @@ class TerminalReporter {
|
||||
formatError(error) {
|
||||
return formatError(this.screen, error);
|
||||
}
|
||||
formatResultErrors(test, result) {
|
||||
return formatResultErrors(this.screen, test, result);
|
||||
}
|
||||
writeLine(line) {
|
||||
this.screen.stdout?.write(line ? line + "\n" : "\n");
|
||||
}
|
||||
}
|
||||
function formatResultErrors(screen, test, result) {
|
||||
const lines = [];
|
||||
if (test.outcome() === "unexpected") {
|
||||
const errorDetails = formatResultFailure(screen, test, result, " ");
|
||||
if (errorDetails.length > 0)
|
||||
lines.push("");
|
||||
for (const error of errorDetails)
|
||||
lines.push(error.message, "");
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
function formatFailure(screen, config, test, index, options) {
|
||||
const lines = [];
|
||||
const header = formatTestHeader(screen, config, test, { indent: " ", index, mode: "error", includeTestId: options?.includeTestId });
|
||||
lines.push(screen.colors.red(header));
|
||||
let printedHeader = false;
|
||||
for (const result of test.results) {
|
||||
const resultLines = [];
|
||||
const errors = formatResultFailure(screen, test, result, " ");
|
||||
if (!errors.length)
|
||||
continue;
|
||||
if (!printedHeader) {
|
||||
const header = formatTestHeader(screen, config, test, { indent: " ", index, mode: "error", includeTestId: options?.includeTestId });
|
||||
lines.push(screen.colors.red(header));
|
||||
printedHeader = true;
|
||||
}
|
||||
if (result.retry) {
|
||||
resultLines.push("");
|
||||
resultLines.push(screen.colors.gray(separator(screen, ` Retry #${result.retry}`)));
|
||||
@@ -383,6 +402,10 @@ function quotePathIfNeeded(path2) {
|
||||
return `"${path2}"`;
|
||||
return path2;
|
||||
}
|
||||
const kReportedSymbol = Symbol("reported");
|
||||
function markErrorsAsReported(result) {
|
||||
result[kReportedSymbol] = result.errors.length;
|
||||
}
|
||||
function formatResultFailure(screen, test, result, initialIndent) {
|
||||
const errorDetails = [];
|
||||
if (result.status === "passed" && test.expectedStatus === "failed") {
|
||||
@@ -395,7 +418,8 @@ function formatResultFailure(screen, test, result, initialIndent) {
|
||||
message: indent(screen.colors.red(`Test was interrupted.`), initialIndent)
|
||||
});
|
||||
}
|
||||
for (const error of result.errors) {
|
||||
const reportedIndex = result[kReportedSymbol] || 0;
|
||||
for (const error of result.errors.slice(reportedIndex)) {
|
||||
const formattedError = formatError(screen, error);
|
||||
errorDetails.push({
|
||||
message: indent(formattedError.message, initialIndent),
|
||||
@@ -423,7 +447,7 @@ function formatTestTitle(screen, config, test, step, options = {}) {
|
||||
const projectLabel = options.includeTestId ? `project=` : "";
|
||||
const projectTitle = projectName ? `[${projectLabel}${projectName}] \u203A ` : "";
|
||||
const testTitle = `${testId}${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`;
|
||||
const extraTags = test.tags.filter((t) => !testTitle.includes(t));
|
||||
const extraTags = test.tags.filter((t) => !testTitle.includes(t) && !config.tags.includes(t));
|
||||
return `${testTitle}${stepSuffix(step)}${extraTags.length ? " " + extraTags.join(" ") : ""}`;
|
||||
}
|
||||
function formatTestHeader(screen, config, test, options = {}) {
|
||||
@@ -599,6 +623,7 @@ function groupAttachments(attachments) {
|
||||
formatRetry,
|
||||
internalScreen,
|
||||
kOutputSymbol,
|
||||
markErrorsAsReported,
|
||||
nonTerminalScreen,
|
||||
prepareErrorStack,
|
||||
relativeFilePath,
|
||||
|
||||
3
node_modules/playwright/lib/reporters/blob.js
generated
vendored
3
node_modules/playwright/lib/reporters/blob.js
generated
vendored
@@ -56,6 +56,7 @@ class BlobReporter extends import_teleEmitter.TeleReporterEmitter {
|
||||
const metadata = {
|
||||
version: currentBlobReportVersion,
|
||||
userAgent: (0, import_utils2.getUserAgent)(),
|
||||
// TODO: remove after some time, recommend config.tag instead.
|
||||
name: process.env.PWTEST_BOT_NAME,
|
||||
shard: config.shard ?? void 0,
|
||||
pathSeparator: import_path.default.sep
|
||||
@@ -67,6 +68,8 @@ class BlobReporter extends import_teleEmitter.TeleReporterEmitter {
|
||||
this._config = config;
|
||||
super.onConfigure(config);
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
const zipFileName = await this._prepareOutputFile();
|
||||
|
||||
17
node_modules/playwright/lib/reporters/dot.js
generated
vendored
17
node_modules/playwright/lib/reporters/dot.js
generated
vendored
@@ -73,6 +73,23 @@ class DotReporter extends import_base.TerminalReporter {
|
||||
this.writeLine("\n" + this.formatError(error).message);
|
||||
this._counter = 0;
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
if (!process.stdin.isTTY && !process.env.PW_TEST_DEBUG_REPORTERS)
|
||||
return;
|
||||
this.screen.stdout.write("\n");
|
||||
if (test.outcome() === "unexpected") {
|
||||
this.writeLine(this.screen.colors.red(this.formatTestHeader(test, { indent: " " })));
|
||||
this.writeLine(this.formatResultErrors(test, result));
|
||||
(0, import_base.markErrorsAsReported)(result);
|
||||
this.writeLine(this.screen.colors.yellow(" Paused on error. Press Ctrl+C to end.") + "\n");
|
||||
} else {
|
||||
this.writeLine(this.screen.colors.yellow(this.formatTestHeader(test, { indent: " " })));
|
||||
this.writeLine(this.screen.colors.yellow(" Paused at test end. Press Ctrl+C to end.") + "\n");
|
||||
}
|
||||
this._counter = 0;
|
||||
await new Promise(() => {
|
||||
});
|
||||
}
|
||||
async onEnd(result) {
|
||||
await super.onEnd(result);
|
||||
this.screen.stdout.write("\n");
|
||||
|
||||
16
node_modules/playwright/lib/reporters/html.js
generated
vendored
16
node_modules/playwright/lib/reporters/html.js
generated
vendored
@@ -51,6 +51,7 @@ const isHtmlReportOption = (type) => {
|
||||
class HtmlReporter {
|
||||
constructor(options) {
|
||||
this._topLevelErrors = [];
|
||||
this._machines = [];
|
||||
this._options = options;
|
||||
}
|
||||
version() {
|
||||
@@ -104,6 +105,9 @@ class HtmlReporter {
|
||||
onError(error) {
|
||||
this._topLevelErrors.push(error);
|
||||
}
|
||||
onMachineEnd(result) {
|
||||
this._machines.push(result);
|
||||
}
|
||||
async onEnd(result) {
|
||||
const projectSuites = this.suite.suites;
|
||||
await (0, import_utils.removeFolders)([this._outputFolder]);
|
||||
@@ -124,7 +128,7 @@ class HtmlReporter {
|
||||
noSnippets,
|
||||
noCopyPrompt
|
||||
});
|
||||
this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors);
|
||||
this._buildResult = await builder.build(this.config.metadata, projectSuites, result, this._topLevelErrors, this._machines);
|
||||
}
|
||||
async onExit() {
|
||||
if (process.env.CI || !this._buildResult)
|
||||
@@ -215,7 +219,7 @@ class HtmlBuilder {
|
||||
this._dataZipFile = new import_zipBundle.yazl.ZipFile();
|
||||
this._attachmentsBaseURL = attachmentsBaseURL;
|
||||
}
|
||||
async build(metadata, projectSuites, result, topLevelErrors) {
|
||||
async build(metadata, projectSuites, result, topLevelErrors, machines) {
|
||||
const data = /* @__PURE__ */ new Map();
|
||||
for (const projectSuite of projectSuites) {
|
||||
const projectName = projectSuite.project().name;
|
||||
@@ -259,7 +263,13 @@ class HtmlBuilder {
|
||||
projectNames: projectSuites.map((r) => r.project().name),
|
||||
stats: { ...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats()) },
|
||||
errors: topLevelErrors.map((error) => (0, import_base.formatError)(import_base.internalScreen, error).message),
|
||||
options: this._options
|
||||
options: this._options,
|
||||
machines: machines.map((s) => ({
|
||||
duration: s.duration,
|
||||
startTime: s.startTime.getTime(),
|
||||
tag: s.tag,
|
||||
shardIndex: s.shardIndex
|
||||
}))
|
||||
};
|
||||
htmlReport.files.sort((f1, f2) => {
|
||||
const w1 = f1.stats.unexpected * 1e3 + f1.stats.flaky;
|
||||
|
||||
6
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
6
node_modules/playwright/lib/reporters/internalReporter.js
generated
vendored
@@ -66,6 +66,10 @@ class InternalReporter {
|
||||
onStdErr(chunk, test, result) {
|
||||
this._reporter.onStdErr?.(chunk, test, result);
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
this._addSnippetToTestErrors(test, result);
|
||||
return await this._reporter.onTestPaused?.(test, result);
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
this._addSnippetToTestErrors(test, result);
|
||||
this._reporter.onTestEnd?.(test, result);
|
||||
@@ -112,6 +116,8 @@ function addLocationAndSnippetToError(config, error, file) {
|
||||
const location = error.location;
|
||||
if (!location)
|
||||
return;
|
||||
if (!!error.snippet)
|
||||
return;
|
||||
try {
|
||||
const tokens = [];
|
||||
const source = import_fs.default.readFileSync(location.file, "utf8");
|
||||
|
||||
18
node_modules/playwright/lib/reporters/line.js
generated
vendored
18
node_modules/playwright/lib/reporters/line.js
generated
vendored
@@ -74,6 +74,24 @@ class LineReporter extends import_base.TerminalReporter {
|
||||
if (this.screen.isTTY && step.category === "test.step")
|
||||
this._updateLine(test, result, step.parent);
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
if (!process.stdin.isTTY && !process.env.PW_TEST_DEBUG_REPORTERS)
|
||||
return;
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS)
|
||||
this.screen.stdout.write(`\x1B[1A\x1B[2K`);
|
||||
if (test.outcome() === "unexpected") {
|
||||
this.writeLine(this.screen.colors.red(this.formatTestHeader(test, { indent: " ", index: ++this._failures })));
|
||||
this.writeLine(this.formatResultErrors(test, result));
|
||||
(0, import_base.markErrorsAsReported)(result);
|
||||
this.writeLine(this.screen.colors.yellow(` Paused on error. Press Ctrl+C to end.`) + "\n\n");
|
||||
} else {
|
||||
this.writeLine(this.screen.colors.yellow(this.formatTestHeader(test, { indent: " " })));
|
||||
this.writeLine(this.screen.colors.yellow(` Paused at test end. Press Ctrl+C to end.`) + "\n\n");
|
||||
}
|
||||
this._updateLine(test, result, void 0);
|
||||
await new Promise(() => {
|
||||
});
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
if (!this.willRetry(test) && (test.outcome() === "flaky" || test.outcome() === "unexpected" || result.status === "interrupted")) {
|
||||
|
||||
22
node_modules/playwright/lib/reporters/list.js
generated
vendored
22
node_modules/playwright/lib/reporters/list.js
generated
vendored
@@ -38,6 +38,7 @@ class ListReporter extends import_base.TerminalReporter {
|
||||
this._resultIndex = /* @__PURE__ */ new Map();
|
||||
this._stepIndex = /* @__PURE__ */ new Map();
|
||||
this._needNewLine = false;
|
||||
this._paused = /* @__PURE__ */ new Set();
|
||||
this._printSteps = (0, import_utils.getAsBooleanFromENV)("PLAYWRIGHT_LIST_PRINT_STEPS", options?.printSteps);
|
||||
}
|
||||
onBegin(suite) {
|
||||
@@ -144,8 +145,29 @@ class ListReporter extends import_base.TerminalReporter {
|
||||
this._updateLineCountAndNewLineFlagForOutput(text);
|
||||
stream.write(chunk);
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
if (!process.stdin.isTTY && !process.env.PW_TEST_DEBUG_REPORTERS)
|
||||
return;
|
||||
this._paused.add(result);
|
||||
this._updateTestLine(test, result);
|
||||
this._maybeWriteNewLine();
|
||||
if (test.outcome() === "unexpected") {
|
||||
const errors = this.formatResultErrors(test, result);
|
||||
this.writeLine(errors);
|
||||
this._updateLineCountAndNewLineFlagForOutput(errors);
|
||||
(0, import_base.markErrorsAsReported)(result);
|
||||
}
|
||||
this._appendLine(this.screen.colors.yellow(`Paused ${test.outcome() === "unexpected" ? "on error" : "at test end"}. Press Ctrl+C to end.`), this._testPrefix("", ""));
|
||||
await new Promise(() => {
|
||||
});
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
super.onTestEnd(test, result);
|
||||
const wasPaused = this._paused.delete(result);
|
||||
if (!wasPaused)
|
||||
this._updateTestLine(test, result);
|
||||
}
|
||||
_updateTestLine(test, result) {
|
||||
const title = this.formatTestTitle(test);
|
||||
let prefix = "";
|
||||
let text = "";
|
||||
|
||||
33
node_modules/playwright/lib/reporters/merge.js
generated
vendored
33
node_modules/playwright/lib/reporters/merge.js
generated
vendored
@@ -55,10 +55,15 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
|
||||
throw new Error(`No report files found in ${dir}`);
|
||||
const eventData = await mergeEvents(dir, shardFiles, stringPool, printStatus, rootDirOverride);
|
||||
const pathSeparator = rootDirOverride ? import_path.default.sep : eventData.pathSeparatorFromMetadata ?? import_path.default.sep;
|
||||
const pathPackage = pathSeparator === "/" ? import_path.default.posix : import_path.default.win32;
|
||||
const receiver = new import_teleReceiver.TeleReporterReceiver(multiplexer, {
|
||||
mergeProjects: false,
|
||||
mergeTestCases: false,
|
||||
resolvePath: (rootDir, relativePath) => stringPool.internString(rootDir + pathSeparator + relativePath),
|
||||
// When merging on a different OS, an absolute path like `C:\foo\bar` from win may look like
|
||||
// a relative path on posix, and vice versa.
|
||||
// Therefore, we cannot use `path.resolve()` here - it will resolve relative-looking paths
|
||||
// against `process.cwd()`, while we just want to normalize ".." and "." segments.
|
||||
resolvePath: (rootDir, relativePath) => stringPool.internString(pathPackage.normalize(pathPackage.join(rootDir, relativePath))),
|
||||
configOverrides: config.config
|
||||
});
|
||||
printStatus(`processing test events`);
|
||||
@@ -72,7 +77,7 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
|
||||
}
|
||||
};
|
||||
await dispatchEvents(eventData.prologue);
|
||||
for (const { reportFile, eventPatchers, metadata, tags } of eventData.reports) {
|
||||
for (const { reportFile, eventPatchers, metadata, tags, startTime, duration } of eventData.reports) {
|
||||
const reportJsonl = await import_fs.default.promises.readFile(reportFile);
|
||||
const events = parseTestEvents(reportJsonl);
|
||||
new import_stringInternPool.JsonStringInternalizer(stringPool).traverse(events);
|
||||
@@ -83,6 +88,12 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
|
||||
eventPatchers.patchers.push(new GlobalErrorPatcher(tags.join(" ")));
|
||||
eventPatchers.patchEvents(events);
|
||||
await dispatchEvents(events);
|
||||
multiplexer.onMachineEnd({
|
||||
startTime: new Date(startTime),
|
||||
duration,
|
||||
tag: tags,
|
||||
shardIndex: metadata.shard?.current
|
||||
});
|
||||
}
|
||||
await dispatchEvents(eventData.epilogue);
|
||||
}
|
||||
@@ -181,6 +192,8 @@ async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootD
|
||||
eventPatchers.patchers.push(new PathSeparatorPatcher(metadata.pathSeparator));
|
||||
eventPatchers.patchEvents(parsedEvents);
|
||||
let tags = [];
|
||||
let startTime = 0;
|
||||
let duration = 0;
|
||||
for (const event of parsedEvents) {
|
||||
if (event.method === "onConfigure") {
|
||||
configureEvents.push(event);
|
||||
@@ -188,14 +201,18 @@ async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootD
|
||||
} else if (event.method === "onProject") {
|
||||
projectEvents.push(event);
|
||||
} else if (event.method === "onEnd") {
|
||||
endEvents.push(event);
|
||||
endEvents.push({ event, metadata, tags });
|
||||
startTime = event.params.result.startTime;
|
||||
duration = event.params.result.duration;
|
||||
}
|
||||
}
|
||||
reports.push({
|
||||
eventPatchers,
|
||||
reportFile: localPath,
|
||||
metadata,
|
||||
tags
|
||||
tags,
|
||||
startTime,
|
||||
duration
|
||||
});
|
||||
}
|
||||
return {
|
||||
@@ -270,8 +287,8 @@ function mergeConfigs(to, from) {
|
||||
function mergeEndEvents(endEvents) {
|
||||
let startTime = endEvents.length ? 1e13 : Date.now();
|
||||
let status = "passed";
|
||||
let duration = 0;
|
||||
for (const event of endEvents) {
|
||||
let endTime = 0;
|
||||
for (const { event } of endEvents) {
|
||||
const shardResult = event.params.result;
|
||||
if (shardResult.status === "failed")
|
||||
status = "failed";
|
||||
@@ -280,12 +297,12 @@ function mergeEndEvents(endEvents) {
|
||||
else if (shardResult.status === "interrupted" && status !== "failed" && status !== "timedout")
|
||||
status = "interrupted";
|
||||
startTime = Math.min(startTime, shardResult.startTime);
|
||||
duration = Math.max(duration, shardResult.duration);
|
||||
endTime = Math.max(endTime, shardResult.startTime + shardResult.duration);
|
||||
}
|
||||
const result = {
|
||||
status,
|
||||
startTime,
|
||||
duration
|
||||
duration: endTime - startTime
|
||||
};
|
||||
return {
|
||||
method: "onEnd",
|
||||
|
||||
8
node_modules/playwright/lib/reporters/multiplexer.js
generated
vendored
8
node_modules/playwright/lib/reporters/multiplexer.js
generated
vendored
@@ -48,10 +48,18 @@ class Multiplexer {
|
||||
for (const reporter of this._reporters)
|
||||
wrap(() => reporter.onStdErr?.(chunk, test, result));
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
for (const reporter of this._reporters)
|
||||
await wrapAsync(() => reporter.onTestPaused?.(test, result));
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
for (const reporter of this._reporters)
|
||||
wrap(() => reporter.onTestEnd?.(test, result));
|
||||
}
|
||||
onMachineEnd(result) {
|
||||
for (const reporter of this._reporters)
|
||||
wrap(() => reporter.onMachineEnd?.(result));
|
||||
}
|
||||
async onEnd(result) {
|
||||
for (const reporter of this._reporters) {
|
||||
const outResult = await wrapAsync(() => reporter.onEnd?.(result));
|
||||
|
||||
26
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
26
node_modules/playwright/lib/reporters/teleEmitter.js
generated
vendored
@@ -37,7 +37,8 @@ var import_teleReceiver = require("../isomorphic/teleReceiver");
|
||||
class TeleReporterEmitter {
|
||||
constructor(messageSink, options = {}) {
|
||||
this._resultKnownAttachmentCounts = /* @__PURE__ */ new Map();
|
||||
// In case there is blob reporter and UI mode, make sure one does override
|
||||
this._resultKnownErrorCounts = /* @__PURE__ */ new Map();
|
||||
// In case there is blob reporter and UI mode, make sure one doesn't override
|
||||
// the id assigned by the other.
|
||||
this._idSymbol = Symbol("id");
|
||||
this._messageSink = messageSink;
|
||||
@@ -66,6 +67,20 @@ class TeleReporterEmitter {
|
||||
}
|
||||
});
|
||||
}
|
||||
async onTestPaused(test, result) {
|
||||
const resultId = result[this._idSymbol];
|
||||
this._resultKnownErrorCounts.set(resultId, result.errors.length);
|
||||
this._messageSink({
|
||||
method: "onTestPaused",
|
||||
params: {
|
||||
testId: test.id,
|
||||
resultId,
|
||||
errors: result.errors
|
||||
}
|
||||
});
|
||||
await new Promise(() => {
|
||||
});
|
||||
}
|
||||
onTestEnd(test, result) {
|
||||
const testEnd = {
|
||||
testId: test.id,
|
||||
@@ -81,7 +96,9 @@ class TeleReporterEmitter {
|
||||
result: this._serializeResultEnd(result)
|
||||
}
|
||||
});
|
||||
this._resultKnownAttachmentCounts.delete(result[this._idSymbol]);
|
||||
const resultId = result[this._idSymbol];
|
||||
this._resultKnownAttachmentCounts.delete(resultId);
|
||||
this._resultKnownErrorCounts.delete(resultId);
|
||||
}
|
||||
onStepBegin(test, result, step) {
|
||||
step[this._idSymbol] = (0, import_utils.createGuid)();
|
||||
@@ -221,11 +238,12 @@ class TeleReporterEmitter {
|
||||
};
|
||||
}
|
||||
_serializeResultEnd(result) {
|
||||
const id = result[this._idSymbol];
|
||||
return {
|
||||
id: result[this._idSymbol],
|
||||
id,
|
||||
duration: result.duration,
|
||||
status: result.status,
|
||||
errors: result.errors,
|
||||
errors: this._resultKnownErrorCounts.has(id) ? result.errors.slice(this._resultKnownAttachmentCounts.get(id)) : result.errors,
|
||||
annotations: result.annotations?.length ? this._relativeAnnotationLocations(result.annotations) : void 0
|
||||
};
|
||||
}
|
||||
|
||||
21
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
21
node_modules/playwright/lib/runner/dispatcher.js
generated
vendored
@@ -28,6 +28,7 @@ var import_workerHost = require("./workerHost");
|
||||
var import_ipc = require("../common/ipc");
|
||||
var import_internalReporter = require("../reporters/internalReporter");
|
||||
var import_util = require("../util");
|
||||
var import_storage = require("./storage");
|
||||
class Dispatcher {
|
||||
constructor(config, reporter, failureTracker) {
|
||||
this._workerSlots = [];
|
||||
@@ -197,6 +198,12 @@ class Dispatcher {
|
||||
const producedEnv = this._producedEnvByProjectId.get(testGroup.projectId) || {};
|
||||
this._producedEnvByProjectId.set(testGroup.projectId, { ...producedEnv, ...worker.producedEnv() });
|
||||
});
|
||||
worker.onRequest("cloneStorage", async (params) => {
|
||||
return await import_storage.Storage.clone(params.storageFile, outputDir);
|
||||
});
|
||||
worker.onRequest("upstreamStorage", async (params) => {
|
||||
await import_storage.Storage.upstream(params.storageFile, params.storageOutFile);
|
||||
});
|
||||
return worker;
|
||||
}
|
||||
producedEnvByProjectId() {
|
||||
@@ -458,11 +465,15 @@ class JobDispatcher {
|
||||
];
|
||||
}
|
||||
_onTestPaused(worker, params) {
|
||||
const data = this._dataByTestId.get(params.testId);
|
||||
if (!data)
|
||||
return;
|
||||
const { result, test } = data;
|
||||
const sendMessage = async (message) => {
|
||||
try {
|
||||
if (this.jobResult.isDone())
|
||||
throw new Error("Test has already stopped");
|
||||
const response = await worker.sendCustomMessage({ testId: params.testId, request: message.request });
|
||||
const response = await worker.sendCustomMessage({ testId: test.id, request: message.request });
|
||||
if (response.error)
|
||||
(0, import_internalReporter.addLocationAndSnippetToError)(this._config.config, response.error);
|
||||
return response;
|
||||
@@ -472,8 +483,12 @@ class JobDispatcher {
|
||||
return { response: void 0, error };
|
||||
}
|
||||
};
|
||||
for (const error of params.errors)
|
||||
(0, import_internalReporter.addLocationAndSnippetToError)(this._config.config, error);
|
||||
result.status = params.status;
|
||||
result.errors = params.errors;
|
||||
result.error = result.errors[0];
|
||||
void this._reporter.onTestPaused?.(test, result).then(() => {
|
||||
worker.sendResume({});
|
||||
});
|
||||
this._failureTracker.onTestPaused?.({ ...params, sendMessage });
|
||||
}
|
||||
skipWholeJob() {
|
||||
|
||||
4
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
4
node_modules/playwright/lib/runner/loadUtils.js
generated
vendored
@@ -162,7 +162,7 @@ async function createRootSuite(testRun, errors, shouldFilterOnly) {
|
||||
for (const group of (0, import_testGroups.createTestGroups)(projectSuite, config.config.shard.total))
|
||||
testGroups.push(group);
|
||||
}
|
||||
const testGroupsInThisShard = (0, import_testGroups.filterForShard)(config.config.shard, testGroups);
|
||||
const testGroupsInThisShard = (0, import_testGroups.filterForShard)(config.config.shard, config.configCLIOverrides.shardWeights, testGroups);
|
||||
const testsInThisShard = /* @__PURE__ */ new Set();
|
||||
for (const group of testGroupsInThisShard) {
|
||||
for (const test of group.tests)
|
||||
@@ -317,7 +317,7 @@ async function loadTestList(config, filePath) {
|
||||
const relativeFile = (0, import_utils.toPosixPath)(import_path.default.relative(config.config.rootDir, test.location.file));
|
||||
if (relativeFile !== d.file)
|
||||
return false;
|
||||
return d.titlePath.length === titles.length && d.titlePath.every((_, index) => titles[index] === d.titlePath[index]);
|
||||
return d.titlePath.length <= titles.length && d.titlePath.every((_, index) => titles[index] === d.titlePath[index]);
|
||||
});
|
||||
} catch (e) {
|
||||
throw (0, import_util.errorWithFile)(filePath, "Cannot read test list file: " + e.message);
|
||||
|
||||
19
node_modules/playwright/lib/runner/processHost.js
generated
vendored
19
node_modules/playwright/lib/runner/processHost.js
generated
vendored
@@ -44,6 +44,7 @@ class ProcessHost extends import_events.EventEmitter {
|
||||
this._lastMessageId = 0;
|
||||
this._callbacks = /* @__PURE__ */ new Map();
|
||||
this._producedEnv = {};
|
||||
this._requestHandlers = /* @__PURE__ */ new Map();
|
||||
this._runnerScript = runnerScript;
|
||||
this._processName = processName;
|
||||
this._extraEnv = env;
|
||||
@@ -51,6 +52,9 @@ class ProcessHost extends import_events.EventEmitter {
|
||||
async startRunner(runnerParams, options = {}) {
|
||||
(0, import_utils.assert)(!this.process, "Internal error: starting the same process twice");
|
||||
this.process = import_child_process.default.fork(require.resolve("../common/process"), {
|
||||
// Note: we pass detached:false, so that workers are in the same process group.
|
||||
// This way Ctrl+C or a kill command can shutdown all workers in case they misbehave.
|
||||
// Otherwise user can end up with a bunch of workers stuck in a busy loop without self-destructing.
|
||||
detached: false,
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -92,6 +96,18 @@ class ProcessHost extends import_events.EventEmitter {
|
||||
} else {
|
||||
this.emit(method, params);
|
||||
}
|
||||
} else if (message.method === "__request__") {
|
||||
const { id, method, params } = message.params;
|
||||
const handler = this._requestHandlers.get(method);
|
||||
if (!handler) {
|
||||
this.send({ method: "__response__", params: { id, error: { message: "Unknown method" } } });
|
||||
} else {
|
||||
handler(params).then((result) => {
|
||||
this.send({ method: "__response__", params: { id, result } });
|
||||
}).catch((error2) => {
|
||||
this.send({ method: "__response__", params: { id, error: { message: error2.message } } });
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.emit(message.method, message.params);
|
||||
}
|
||||
@@ -135,6 +151,9 @@ class ProcessHost extends import_events.EventEmitter {
|
||||
}
|
||||
async onExit() {
|
||||
}
|
||||
onRequest(method, handler) {
|
||||
this._requestHandlers.set(method, handler);
|
||||
}
|
||||
async stop() {
|
||||
if (!this._processDidExit && !this._didSendStop) {
|
||||
this.send({ method: "__stop__" });
|
||||
|
||||
2
node_modules/playwright/lib/runner/projectUtils.js
generated
vendored
2
node_modules/playwright/lib/runner/projectUtils.js
generated
vendored
@@ -145,7 +145,7 @@ function buildDependentProjects(forProjects, projects) {
|
||||
return result;
|
||||
}
|
||||
async function collectFilesForProject(project, fsCache = /* @__PURE__ */ new Map()) {
|
||||
const extensions = /* @__PURE__ */ new Set([".js", ".ts", ".mjs", ".mts", ".cjs", ".cts", ".jsx", ".tsx", ".mjsx", ".mtsx", ".cjsx", ".ctsx"]);
|
||||
const extensions = /* @__PURE__ */ new Set([".js", ".ts", ".mjs", ".mts", ".cjs", ".cts", ".jsx", ".tsx", ".mjsx", ".mtsx", ".cjsx", ".ctsx", ".md"]);
|
||||
const testFileExtension = (file) => extensions.has(import_path.default.extname(file));
|
||||
const allFiles = await cachedCollectFiles(project.project.testDir, project.respectGitIgnore, fsCache);
|
||||
const testMatch = (0, import_util2.createFileMatcher)(project.project.testMatch);
|
||||
|
||||
91
node_modules/playwright/lib/runner/storage.js
generated
vendored
Normal file
91
node_modules/playwright/lib/runner/storage.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var storage_exports = {};
|
||||
__export(storage_exports, {
|
||||
Storage: () => Storage
|
||||
});
|
||||
module.exports = __toCommonJS(storage_exports);
|
||||
var import_fs = __toESM(require("fs"));
|
||||
var import_path = __toESM(require("path"));
|
||||
var import_utils = require("playwright-core/lib/utils");
|
||||
class Storage {
|
||||
static {
|
||||
this._storages = /* @__PURE__ */ new Map();
|
||||
}
|
||||
static {
|
||||
this._serializeQueue = Promise.resolve();
|
||||
}
|
||||
static clone(storageFile, outputDir) {
|
||||
return Storage._withStorage(storageFile, (storage) => storage._clone(outputDir));
|
||||
}
|
||||
static upstream(storageFile, storageOutFile) {
|
||||
return Storage._withStorage(storageFile, (storage) => storage._upstream(storageOutFile));
|
||||
}
|
||||
static _withStorage(fileName, runnable) {
|
||||
this._serializeQueue = this._serializeQueue.then(() => {
|
||||
let storage = Storage._storages.get(fileName);
|
||||
if (!storage) {
|
||||
storage = new Storage(fileName);
|
||||
Storage._storages.set(fileName, storage);
|
||||
}
|
||||
return runnable(storage);
|
||||
});
|
||||
return this._serializeQueue;
|
||||
}
|
||||
constructor(fileName) {
|
||||
this._fileName = fileName;
|
||||
}
|
||||
async _clone(outputDir) {
|
||||
const entries = await this._load();
|
||||
if (this._lastSnapshotFileName)
|
||||
return this._lastSnapshotFileName;
|
||||
const snapshotFile = import_path.default.join(outputDir, `pw-storage-${(0, import_utils.createGuid)()}.json`);
|
||||
await import_fs.default.promises.writeFile(snapshotFile, JSON.stringify(entries, null, 2)).catch(() => {
|
||||
});
|
||||
this._lastSnapshotFileName = snapshotFile;
|
||||
return snapshotFile;
|
||||
}
|
||||
async _upstream(storageOutFile) {
|
||||
const entries = await this._load();
|
||||
const newEntries = await import_fs.default.promises.readFile(storageOutFile, "utf8").then(JSON.parse).catch(() => ({}));
|
||||
for (const [key, newValue] of Object.entries(newEntries))
|
||||
entries[key] = newValue;
|
||||
this._lastSnapshotFileName = void 0;
|
||||
await import_fs.default.promises.writeFile(this._fileName, JSON.stringify(entries, null, 2));
|
||||
}
|
||||
async _load() {
|
||||
if (!this._entriesPromise)
|
||||
this._entriesPromise = import_fs.default.promises.readFile(this._fileName, "utf8").then(JSON.parse).catch(() => ({}));
|
||||
return this._entriesPromise;
|
||||
}
|
||||
}
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Storage
|
||||
});
|
||||
20
node_modules/playwright/lib/runner/testGroups.js
generated
vendored
20
node_modules/playwright/lib/runner/testGroups.js
generated
vendored
@@ -92,15 +92,23 @@ function createTestGroups(projectSuite, expectedParallelism) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function filterForShard(shard, testGroups) {
|
||||
function filterForShard(shard, weights, testGroups) {
|
||||
weights ??= Array.from({ length: shard.total }, () => 1);
|
||||
if (weights.length !== shard.total)
|
||||
throw new Error(`PWTEST_SHARD_WEIGHTS number of weights must match the shard total of ${shard.total}`);
|
||||
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
||||
let shardableTotal = 0;
|
||||
for (const group of testGroups)
|
||||
shardableTotal += group.tests.length;
|
||||
const shardSize = Math.floor(shardableTotal / shard.total);
|
||||
const extraOne = shardableTotal - shardSize * shard.total;
|
||||
const currentShard = shard.current - 1;
|
||||
const from = shardSize * currentShard + Math.min(extraOne, currentShard);
|
||||
const to = from + shardSize + (currentShard < extraOne ? 1 : 0);
|
||||
const shardSizes = weights.map((w) => Math.floor(w * shardableTotal / totalWeight));
|
||||
const remainder = shardableTotal - shardSizes.reduce((a, b) => a + b, 0);
|
||||
for (let i = 0; i < remainder; i++) {
|
||||
shardSizes[i % shardSizes.length]++;
|
||||
}
|
||||
let from = 0;
|
||||
for (let i = 0; i < shard.current - 1; i++)
|
||||
from += shardSizes[i];
|
||||
const to = from + shardSizes[shard.current - 1];
|
||||
let current = 0;
|
||||
const result = /* @__PURE__ */ new Set();
|
||||
for (const group of testGroups) {
|
||||
|
||||
17
node_modules/playwright/lib/runner/testRunner.js
generated
vendored
17
node_modules/playwright/lib/runner/testRunner.js
generated
vendored
@@ -63,6 +63,7 @@ class TestRunner extends import_events.default {
|
||||
this._queue = Promise.resolve();
|
||||
this._watchTestDirs = false;
|
||||
this._populateDependenciesOnList = false;
|
||||
this._startingEnv = {};
|
||||
this.configLocation = configLocation;
|
||||
this._configCLIOverrides = configCLIOverrides;
|
||||
this._watcher = new import_fsWatcher.Watcher((events) => {
|
||||
@@ -75,6 +76,7 @@ class TestRunner extends import_events.default {
|
||||
(0, import_utils.setPlaywrightTestProcessEnv)();
|
||||
this._watchTestDirs = !!params.watchTestDirs;
|
||||
this._populateDependenciesOnList = !!params.populateDependenciesOnList;
|
||||
this._startingEnv = { ...process.env };
|
||||
}
|
||||
resizeTerminal(params) {
|
||||
process.stdout.columns = params.cols;
|
||||
@@ -107,15 +109,20 @@ class TestRunner extends import_events.default {
|
||||
const reporter = new import_internalReporter.InternalReporter(userReporters);
|
||||
const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides);
|
||||
if (!config)
|
||||
return { status: "failed" };
|
||||
return { status: "failed", env: [] };
|
||||
const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [
|
||||
...(0, import_tasks.createGlobalSetupTasks)(config)
|
||||
]);
|
||||
const env = [];
|
||||
for (const key of /* @__PURE__ */ new Set([...Object.keys(process.env), ...Object.keys(this._startingEnv)])) {
|
||||
if (this._startingEnv[key] !== process.env[key])
|
||||
env.push([key, process.env[key] ?? null]);
|
||||
}
|
||||
if (status !== "passed")
|
||||
await cleanup();
|
||||
else
|
||||
this._globalSetup = { cleanup };
|
||||
return { status };
|
||||
return { status, env };
|
||||
}
|
||||
async runGlobalTeardown() {
|
||||
const globalSetup = this._globalSetup;
|
||||
@@ -251,6 +258,7 @@ class TestRunner extends import_events.default {
|
||||
},
|
||||
...params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {},
|
||||
...params.updateSourceMethod ? { updateSourceMethod: params.updateSourceMethod } : {},
|
||||
...params.runAgents ? { runAgents: params.runAgents } : {},
|
||||
...params.workers ? { workers: params.workers } : {}
|
||||
};
|
||||
const config = await this._loadConfigOrReportError(new import_internalReporter.InternalReporter([userReporter]), overrides);
|
||||
@@ -258,7 +266,7 @@ class TestRunner extends import_events.default {
|
||||
return { status: "failed" };
|
||||
config.cliListOnly = false;
|
||||
config.cliPassWithNoTests = true;
|
||||
config.cliArgs = params.locations || [];
|
||||
config.cliArgs = params.locations;
|
||||
config.cliGrep = params.grep;
|
||||
config.cliGrepInvert = params.grepInvert;
|
||||
config.cliProjectFilter = params.projects?.length ? params.projects : void 0;
|
||||
@@ -376,7 +384,8 @@ async function runAllTestsWithConfig(config) {
|
||||
(0, import_tasks.createLoadTask)("in-process", { filterOnly: true, failOnLoadErrors: true }),
|
||||
...(0, import_tasks.createRunTestsTasks)(config)
|
||||
];
|
||||
const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), tasks, config.config.globalTimeout);
|
||||
const testRun = new import_tasks.TestRun(config, reporter, { pauseAtEnd: config.configCLIOverrides.pause, pauseOnError: config.configCLIOverrides.pause });
|
||||
const status = await (0, import_tasks.runTasks)(testRun, tasks, config.config.globalTimeout);
|
||||
await new Promise((resolve) => process.stdout.write("", () => resolve()));
|
||||
await new Promise((resolve) => process.stderr.write("", () => resolve()));
|
||||
return status;
|
||||
|
||||
4
node_modules/playwright/lib/runner/testServer.js
generated
vendored
4
node_modules/playwright/lib/runner/testServer.js
generated
vendored
@@ -114,8 +114,8 @@ class TestServerDispatcher {
|
||||
async runGlobalSetup(params) {
|
||||
const { reporter, report } = await this._collectingReporter();
|
||||
this._globalSetupReport = report;
|
||||
const { status } = await this._testRunner.runGlobalSetup([reporter, new import_list.default()]);
|
||||
return { report, status };
|
||||
const { status, env } = await this._testRunner.runGlobalSetup([reporter, new import_list.default()]);
|
||||
return { report, status, env };
|
||||
}
|
||||
async runGlobalTeardown() {
|
||||
const { status } = await this._testRunner.runGlobalTeardown();
|
||||
|
||||
3
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
3
node_modules/playwright/lib/runner/watchMode.js
generated
vendored
@@ -267,7 +267,8 @@ async function runTests(watchOptions, testServerConnection, options) {
|
||||
await testServerConnection.runTests({
|
||||
grep: watchOptions.grep,
|
||||
testIds: options?.testIds,
|
||||
locations: watchOptions?.files,
|
||||
locations: watchOptions?.files ?? [],
|
||||
// TODO: always collect locations based on knowledge about tree, so that we don't have to load all tests
|
||||
projects: watchOptions.projects,
|
||||
connectWsEndpoint,
|
||||
reuseContext: connectWsEndpoint ? true : void 0,
|
||||
|
||||
6
node_modules/playwright/lib/runner/workerHost.js
generated
vendored
6
node_modules/playwright/lib/runner/workerHost.js
generated
vendored
@@ -61,6 +61,9 @@ class WorkerHost extends import_processHost.ProcessHost {
|
||||
pauseAtEnd: options.pauseAtEnd
|
||||
};
|
||||
}
|
||||
artifactsDir() {
|
||||
return this._params.artifactsDir;
|
||||
}
|
||||
async start() {
|
||||
await import_fs.default.promises.mkdir(this._params.artifactsDir, { recursive: true });
|
||||
return await this.startRunner(this._params, {
|
||||
@@ -82,6 +85,9 @@ class WorkerHost extends import_processHost.ProcessHost {
|
||||
async sendCustomMessage(payload) {
|
||||
return await this.sendMessage({ method: "customMessage", params: payload });
|
||||
}
|
||||
sendResume(payload) {
|
||||
this.sendMessageNoReply({ method: "resume", params: payload });
|
||||
}
|
||||
hash() {
|
||||
return this._hash;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user