web-tests
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseIMPORTANT - Path Resolution:
This skill is installed globally but saves outputs to the user's working directory. Always pass environment variable when executing commands to ensure outputs go to the correct location.
CWDCommon installation paths:
- Plugin system:
~/.claude/plugins/marketplaces/claude-craftkit/plugins/ui-tests/skills/web-tests - Manual global:
~/.claude/skills/web-tests - Project-specific:
<project>/.claude/skills/web-tests
重要提示 - 路径解析:
此Skill为全局安装,但会将输出内容保存到用户的工作目录。执行命令时务必传入环境变量,以确保输出保存到正确位置。
CWD常见安装路径:
- 插件系统:
~/.claude/plugins/marketplaces/claude-craftkit/plugins/ui-tests/skills/web-tests - 手动全局安装:
~/.claude/skills/web-tests - 项目专属:
<project>/.claude/skills/web-tests
Web Testing & Browser Automation
网页测试与浏览器自动化
General-purpose browser automation skill. I'll write custom Playwright code for any automation task you request and execute it via the universal executor.
CRITICAL WORKFLOW - Follow these steps in order:
-
Auto-detect dev servers - For localhost testing, ALWAYS run server detection FIRST:bash
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"- If 1 server found: Use it automatically, inform user
- If multiple servers found: Ask user which one to test
- If no servers found: Ask for URL or offer to help start dev server
-
Write scripts to user's working directory - Save toin user's repo
.web-tests/scripts/test-*.js -
Use visible browser by default - Always useunless user specifically requests headless mode
headless: false -
Parameterize URLs - Always make URLs configurable via constant at top of script
通用型浏览器自动化Skill。我会根据你的需求编写自定义Playwright代码,并通过通用执行器运行。
关键工作流程 - 请按以下步骤执行:
-
自动检测开发服务器 - 针对本地主机测试,务必先运行服务器检测:bash
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))"- 若找到1个服务器:自动使用该服务器,并告知用户
- 若找到多个服务器:询问用户选择测试哪一个
- 若未找到服务器:询问目标URL,或提供启动开发服务器的帮助
-
将脚本写入用户工作目录 - 保存到用户仓库的路径下
.web-tests/scripts/test-*.js -
默认使用可视化浏览器 - 除非用户明确要求无头模式,否则始终使用
headless: false -
参数化URL - 始终在脚本顶部通过常量配置URL
How It Works
工作原理
- You describe what you want to test/automate
- I auto-detect running dev servers (or ask for URL if testing external site)
- I write custom Playwright code in (in user's working directory)
.web-tests/scripts/test-*.js - I execute it via:
CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-*.js - Results displayed in real-time, browser window visible for debugging
- Screenshots automatically saved to
.web-tests/screenshots/
- 你描述需要测试/自动化的任务
- 我自动检测运行中的开发服务器(若测试外部网站则询问URL)
- 我在用户工作目录下的中编写自定义Playwright代码
.web-tests/scripts/test-*.js - 我通过以下命令执行:
CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-*.js - 实时显示结果,浏览器窗口保持可见以便调试
- 截图自动保存到目录
.web-tests/screenshots/
Setup (First Time)
首次设置
bash
cd $SKILL_DIR
npm run setupThis installs Playwright and Chromium browser. Only needed once.
bash
cd $SKILL_DIR
npm run setup此命令将安装Playwright和Chromium浏览器,仅需执行一次。
Execution Pattern
执行流程
Step 1: Detect dev servers (for localhost testing)
bash
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"Step 2: Write test script to user's .web-tests/scripts/ with URL parameter
javascript
// .web-tests/scripts/test-page.js
const { chromium } = require("playwright");
// Parameterized URL (detected or user-provided)
const TARGET_URL = "http://localhost:3001"; // <-- Auto-detected or from user
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
console.log("Page loaded:", await page.title());
await page.screenshot({
path: ".web-tests/screenshots/page.png",
fullPage: true,
});
console.log("📸 Screenshot saved to .web-tests/screenshots/page.png");
await browser.close();
})();Step 3: Execute from skill directory with CWD
bash
CWD=$(pwd) cd $SKILL_DIR && node run.js $(pwd)/.web-tests/scripts/test-page.js步骤1:检测开发服务器(针对本地主机测试)
bash
cd $SKILL_DIR && node -e "require('./lib/helpers').detectDevServers().then(s => console.log(JSON.stringify(s)))"步骤2:将带URL参数的测试脚本写入用户的.web-tests/scripts/目录
javascript
// .web-tests/scripts/test-page.js
const { chromium } = require("playwright");
// 参数化URL(自动检测或用户提供)
const TARGET_URL = "http://localhost:3001"; // <-- 自动检测或用户提供
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
console.log("页面已加载:", await page.title());
await page.screenshot({
path: ".web-tests/screenshots/page.png",
fullPage: true,
});
console.log("📸 截图已保存到 .web-tests/screenshots/page.png");
await browser.close();
})();步骤3:从Skill目录传入CWD执行
bash
CWD=$(pwd) cd $SKILL_DIR && node run.js $(pwd)/.web-tests/scripts/test-page.jsCommon Patterns
常见场景示例
Test a Page (Multiple Viewports)
多视口页面测试
javascript
// .web-tests/scripts/test-responsive.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers"); // Path will be resolved
const TARGET_URL = "http://localhost:3001"; // Auto-detected
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 100 });
const page = await browser.newPage();
// Desktop test
await page.setViewportSize({ width: 1920, height: 1080 });
await page.goto(TARGET_URL);
console.log("Desktop - Title:", await page.title());
await helpers.takeScreenshot(page, "desktop"); // Saves to .web-tests/screenshots/
// Mobile test
await page.setViewportSize({ width: 375, height: 667 });
await helpers.takeScreenshot(page, "mobile");
await browser.close();
})();javascript
// .web-tests/scripts/test-responsive.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers"); // 路径会自动解析
const TARGET_URL = "http://localhost:3001"; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 100 });
const page = await browser.newPage();
// 桌面端测试
await page.setViewportSize({ width: 1920, height: 1080 });
await page.goto(TARGET_URL);
console.log("桌面端 - 页面标题:", await page.title());
await helpers.takeScreenshot(page, "desktop"); // 保存到 .web-tests/screenshots/
// 移动端测试
await page.setViewportSize({ width: 375, height: 667 });
await helpers.takeScreenshot(page, "mobile");
await browser.close();
})();Test Login Flow
登录流程测试
javascript
// .web-tests/scripts/test-login.js
const { chromium } = require("playwright");
const TARGET_URL = "http://localhost:3001"; // Auto-detected
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/login`);
await page.fill('input[name="email"]', "test@example.com");
await page.fill('input[name="password"]', "password123");
await page.click('button[type="submit"]');
// Wait for redirect
await page.waitForURL("**/dashboard");
console.log("✅ Login successful, redirected to dashboard");
await browser.close();
})();javascript
// .web-tests/scripts/test-login.js
const { chromium } = require("playwright");
const TARGET_URL = "http://localhost:3001"; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/login`);
await page.fill('input[name="email"]', "test@example.com");
await page.fill('input[name="password"]', "password123");
await page.click('button[type="submit"]');
// 等待重定向
await page.waitForURL("**/dashboard");
console.log("✅ 登录成功,已重定向到控制台");
await browser.close();
})();Fill and Submit Form
表单填写与提交测试
javascript
// .web-tests/scripts/test-form.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001"; // Auto-detected
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 50 });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/contact`);
await page.fill('input[name="name"]', "John Doe");
await page.fill('input[name="email"]', "john@example.com");
await page.fill('textarea[name="message"]', "Test message");
await helpers.takeScreenshot(page, "form-filled");
await page.click('button[type="submit"]');
// Verify submission
await page.waitForSelector(".success-message");
console.log("✅ Form submitted successfully");
await helpers.takeScreenshot(page, "form-success");
await browser.close();
})();javascript
// .web-tests/scripts/test-form.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001"; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false, slowMo: 50 });
const page = await browser.newPage();
await page.goto(`${TARGET_URL}/contact`);
await page.fill('input[name="name"]', "John Doe");
await page.fill('input[name="email"]', "john@example.com");
await page.fill('textarea[name="message"]', "测试消息");
await helpers.takeScreenshot(page, "form-filled");
await page.click('button[type="submit"]');
// 验证提交结果
await page.waitForSelector(".success-message");
console.log("✅ 表单提交成功");
await helpers.takeScreenshot(page, "form-success");
await browser.close();
})();Check for Broken Links
失效链接检查
javascript
// .web-tests/scripts/test-broken-links.js
const { chromium } = require("playwright");
const TARGET_URL = "http://localhost:3001";
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
const links = await page.locator('a[href^="http"]').all();
const results = { working: 0, broken: [] };
for (const link of links) {
const href = await link.getAttribute("href");
try {
const response = await page.request.head(href);
if (response.ok()) {
results.working++;
} else {
results.broken.push({ url: href, status: response.status() });
}
} catch (e) {
results.broken.push({ url: href, error: e.message });
}
}
console.log(`✅ Working links: ${results.working}`);
console.log(`❌ Broken links:`, results.broken);
await browser.close();
})();javascript
// .web-tests/scripts/test-broken-links.js
const { chromium } = require("playwright");
const TARGET_URL = "http://localhost:3001";
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto(TARGET_URL);
const links = await page.locator('a[href^="http"]').all();
const results = { working: 0, broken: [] };
for (const link of links) {
const href = await link.getAttribute("href");
try {
const response = await page.request.head(href);
if (response.ok()) {
results.working++;
} else {
results.broken.push({ url: href, status: response.status() });
}
} catch (e) {
results.broken.push({ url: href, error: e.message });
}
}
console.log(`✅ 可用链接数: ${results.working}`);
console.log(`❌ 失效链接:`, results.broken);
await browser.close();
})();Take Screenshot with Error Handling
带错误处理的截图
javascript
// .web-tests/scripts/screenshot-with-error-handling.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001";
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
try {
await page.goto(TARGET_URL, {
waitUntil: "networkidle",
timeout: 10000,
});
await helpers.takeScreenshot(page, "page-success");
console.log("📸 Screenshot saved successfully");
} catch (error) {
console.error("❌ Error:", error.message);
await helpers.takeScreenshot(page, "page-error");
} finally {
await browser.close();
}
})();javascript
// .web-tests/scripts/screenshot-with-error-handling.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001";
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
try {
await page.goto(TARGET_URL, {
waitUntil: "networkidle",
timeout: 10000,
});
await helpers.takeScreenshot(page, "page-success");
console.log("📸 截图保存成功");
} catch (error) {
console.error("❌ 错误:", error.message);
await helpers.takeScreenshot(page, "page-error");
} finally {
await browser.close();
}
})();Test Responsive Design (Full)
完整响应式设计测试
javascript
// .web-tests/scripts/test-responsive-full.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001"; // Auto-detected
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const viewports = [
{ name: "Desktop", width: 1920, height: 1080 },
{ name: "Tablet", width: 768, height: 1024 },
{ name: "Mobile", width: 375, height: 667 },
];
for (const viewport of viewports) {
console.log(
`Testing ${viewport.name} (${viewport.width}x${viewport.height})`
);
await page.setViewportSize({
width: viewport.width,
height: viewport.height,
});
await page.goto(TARGET_URL);
await page.waitForTimeout(1000);
await helpers.takeScreenshot(page, viewport.name.toLowerCase());
}
console.log("✅ All viewports tested");
await browser.close();
})();javascript
// .web-tests/scripts/test-responsive-full.js
const { chromium } = require("playwright");
const helpers = require("$SKILL_DIR/lib/helpers");
const TARGET_URL = "http://localhost:3001"; // 自动检测
(async () => {
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
const viewports = [
{ name: "Desktop", width: 1920, height: 1080 },
{ name: "Tablet", width: 768, height: 1024 },
{ name: "Mobile", width: 375, height: 667 },
];
for (const viewport of viewports) {
console.log(
`正在测试 ${viewport.name} (${viewport.width}x${viewport.height})`
);
await page.setViewportSize({
width: viewport.width,
height: viewport.height,
});
await page.goto(TARGET_URL);
await page.waitForTimeout(1000);
await helpers.takeScreenshot(page, viewport.name.toLowerCase());
}
console.log("✅ 所有视口测试完成");
await browser.close();
})();Inline Execution (Simple Tasks)
内联执行(简单任务)
For quick one-off tasks, you can execute code inline without creating files:
bash
undefined对于快速一次性任务,你可以直接内联执行代码而无需创建文件:
bash
undefinedTake a quick screenshot
快速截图
cd $SKILL_DIR && CWD=$(pwd) node run.js "
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3001');
await helpers.takeScreenshot(page, 'quick-test');
console.log('Screenshot saved');
await browser.close();
"
**When to use inline vs files:**
- **Inline**: Quick one-off tasks (screenshot, check if element exists, get page title)
- **Files**: Complex tests, responsive design checks, anything user might want to re-runcd $SKILL_DIR && CWD=$(pwd) node run.js "
const browser = await chromium.launch({ headless: false });
const page = await browser.newPage();
await page.goto('http://localhost:3001');
await helpers.takeScreenshot(page, 'quick-test');
console.log('截图已保存');
await browser.close();
"
**内联执行与文件执行的适用场景:**
- **内联执行**:快速一次性任务(截图、检查元素是否存在、获取页面标题)
- **文件执行**:复杂测试、响应式设计检查、用户可能需要重复运行的任务Available Helpers
可用辅助函数
Optional utility functions in :
lib/helpers.jsjavascript
const helpers = require("./lib/helpers");
// Detect running dev servers (CRITICAL - use this first!)
const servers = await helpers.detectDevServers();
console.log("Found servers:", servers);
// Safe click with retry
await helpers.safeClick(page, "button.submit", { retries: 3 });
// Safe type with clear
await helpers.safeType(page, "#username", "testuser");
// Take timestamped screenshot (auto-saves to .web-tests/screenshots/)
await helpers.takeScreenshot(page, "test-result");
// Handle cookie banners
await helpers.handleCookieBanner(page);
// Extract table data
const data = await helpers.extractTableData(page, "table.results");See for full list.
lib/helpers.jslib/helpers.jsjavascript
const helpers = require("./lib/helpers");
// 检测运行中的开发服务器(关键 - 务必优先使用!)
const servers = await helpers.detectDevServers();
console.log("找到的服务器:", servers);
// 带重试的安全点击
await helpers.safeClick(page, "button.submit", { retries: 3 });
// 带清空的安全输入
await helpers.safeType(page, "#username", "testuser");
// 带时间戳的截图(自动保存到 .web-tests/screenshots/)
await helpers.takeScreenshot(page, "test-result");
// 处理Cookie弹窗
await helpers.handleCookieBanner(page);
// 提取表格数据
const data = await helpers.extractTableData(page, "table.results");完整列表请查看。
lib/helpers.jsDirectory Structure
目录结构
When testing, the skill creates this structure in the user's working directory:
user-repo/
└── .web-tests/
├── scripts/ # Test scripts (reusable)
│ ├── test-login.js
│ ├── test-form.js
│ ├── test-responsive.js
│ └── test-broken-links.js
└── screenshots/ # Screenshots with timestamps
├── desktop-2025-10-23T12-30-45.png
├── mobile-2025-10-23T12-30-51.png
└── form-success-2025-10-23T12-31-05.png测试时,此Skill会在用户工作目录中创建以下结构:
user-repo/
└── .web-tests/
├── scripts/ # 可复用的测试脚本
│ ├── test-login.js
│ ├── test-form.js
│ ├── test-responsive.js
│ └── test-broken-links.js
└── screenshots/ # 带时间戳的截图
├── desktop-2025-10-23T12-30-45.png
├── mobile-2025-10-23T12-30-51.png
└── form-success-2025-10-23T12-31-05.pngTips
技巧提示
- CRITICAL: Detect servers FIRST - Always run before writing test code for localhost testing
detectDevServers() - Save to .web-tests/ - Write scripts to , screenshots auto-save to
.web-tests/scripts/.web-tests/screenshots/ - Parameterize URLs - Put detected/provided URL in a constant at the top of every script
TARGET_URL - DEFAULT: Visible browser - Always use unless user explicitly asks for headless mode
headless: false - Headless mode - Only use when user specifically requests "headless" or "background" execution
headless: true - Slow down: Use to make actions visible and easier to follow
slowMo: 100 - Wait strategies: Use ,
waitForURL,waitForSelectorinstead of fixed timeoutswaitForLoadState - Error handling: Always use try-catch for robust automation
- Console output: Use to track progress and show what's happening
console.log() - Use helpers: The automatically saves to
helpers.takeScreenshot().web-tests/screenshots/
- 关键:优先检测服务器 - 针对本地主机测试,编写测试代码前务必先运行
detectDevServers() - 保存到.web-tests/目录 - 将脚本写入,截图自动保存到
.web-tests/scripts/.web-tests/screenshots/ - 参数化URL - 在每个脚本顶部将检测到的/用户提供的URL放入常量中
TARGET_URL - 默认使用可视化浏览器 - 除非用户明确要求,否则始终使用
headless: false - 无头模式 - 仅当用户明确要求"无头"或"后台"执行时才使用
headless: true - 放慢速度: 使用让操作可见,便于跟踪
slowMo: 100 - 等待策略: 使用、
waitForURL、waitForSelector替代固定超时waitForLoadState - 错误处理: 始终使用try-catch实现健壮的自动化
- 控制台输出: 使用跟踪进度,展示执行过程
console.log() - 使用辅助函数: 会自动将截图保存到
helpers.takeScreenshot().web-tests/screenshots/
Troubleshooting
故障排除
Playwright not installed:
bash
cd $SKILL_DIR && npm run setupModule not found:
Ensure running from skill directory via wrapper with CWD set
run.jsBrowser doesn't open:
Check and ensure display available
headless: falseElement not found:
Add wait:
await page.waitForSelector('.element', { timeout: 10000 })Screenshots not in .web-tests/:
Make sure environment variable is set when executing
CWDPlaywright未安装:
bash
cd $SKILL_DIR && npm run setup模块未找到:
确保通过包装器从Skill目录执行,并已设置CWD
run.js浏览器未打开:
检查,并确保显示设备可用
headless: false元素未找到:
添加等待:
await page.waitForSelector('.element', { timeout: 10000 })截图未保存到.web-tests/:
确保执行时已设置环境变量
CWDExample Usage
使用示例
User: "Test if the marketing page looks good"
Claude: I'll test the marketing page across multiple viewports. Let me first detect running servers...
[Runs: detectDevServers()]
[Output: Found server on port 3001]
I found your dev server running on http://localhost:3001
[Writes custom automation script to .web-tests/scripts/test-marketing.js with URL parameterized]
[Runs: CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-marketing.js]
[Shows results with screenshots from .web-tests/screenshots/]User: "Check if login redirects correctly"
Claude: I'll test the login flow. First, let me check for running servers...
[Runs: detectDevServers()]
[Output: Found servers on ports 3000 and 3001]
I found 2 dev servers. Which one should I test?
- http://localhost:3000
- http://localhost:3001
User: "Use 3001"
[Writes login automation to .web-tests/scripts/test-login.js]
[Runs: CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-login.js]
[Reports: ✅ Login successful, redirected to /dashboard]用户: "测试营销页面的显示效果"
Claude: 我将在多个视口中测试营销页面。先让我检测运行中的服务器...
[执行: detectDevServers()]
[输出: 找到端口3001上的服务器]
我找到你的开发服务器运行在 http://localhost:3001
[将自定义自动化脚本写入.web-tests/scripts/test-marketing.js,并参数化URL]
[执行: CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-marketing.js]
[展示.web-tests/screenshots/中的截图结果]用户: "检查登录是否正确重定向"
Claude: 我将测试登录流程。首先,让我检查运行中的服务器...
[执行: detectDevServers()]
[输出: 找到端口3000和3001上的服务器]
我找到2个开发服务器。你想测试哪一个?
- http://localhost:3000
- http://localhost:3001
用户: "使用3001"
[将登录自动化脚本写入.web-tests/scripts/test-login.js]
[执行: CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-login.js]
[报告: ✅ 登录成功,已重定向到/dashboard]Notes
注意事项
- Each automation is custom-written for your specific request
- Not limited to pre-built scripts - any browser task possible
- Auto-detects running dev servers to eliminate hardcoded URLs
- Test scripts saved to for reusability
.web-tests/scripts/ - Screenshots automatically saved to with timestamps
.web-tests/screenshots/ - Code executes reliably with proper module resolution via
run.js - Works as global tool - no per-repo installation needed
- 每个自动化任务均为你的特定需求定制
- 不限于预构建脚本 - 可实现任何浏览器自动化任务
- 自动检测运行中的开发服务器,无需硬编码URL
- 测试脚本保存到以便复用
.web-tests/scripts/ - 截图自动保存到并带有时间戳
.web-tests/screenshots/ - 通过确保代码可靠执行,解决模块解析问题
run.js - 作为全局工具使用 - 无需每个仓库单独安装",