Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:39:20 +08:00
commit b92d349a51
9 changed files with 1101 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
{
"name": "ui-tests",
"description": "Claude Code Skill for web testing and browser automation with Playwright. Auto-detects dev servers, saves test scripts and screenshots to your working directory.",
"version": "1.0.0",
"author": {
"name": "Claude Craftkit"
},
"skills": [
"./skills"
],
"commands": [
"./commands"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# ui-tests
Claude Code Skill for web testing and browser automation with Playwright. Auto-detects dev servers, saves test scripts and screenshots to your working directory.

135
commands/check.md Normal file
View File

@@ -0,0 +1,135 @@
---
description: Check webpage for issues (broken links, errors, etc.)
---
<url>--url</url>
<check-type>--check-type</check-type>
## Check Webpage for Issues
Automatically check webpages for common issues like broken links, console errors, missing images, or accessibility problems.
**Works from any directory** - Saves report to your working directory's `.web-tests/` folder.
## How It Works
When you run `/check`, Claude will:
1. **Auto-detect dev server** (or use provided URL)
2. **Create check script** in `.web-tests/scripts/`
3. **Run automated checks** based on check-type
4. **Generate report** in `.web-tests/reports/`
5. **Show summary** of findings
## Usage
```bash
# Check for broken links
/check --check-type links
# Check for console errors
/check --url http://localhost:3001 --check-type console
# Check for missing images
/check --check-type images
# Check accessibility
/check --check-type a11y
# Check all issues
/check --check-type all
```
## Check Types
- `links` - Find broken links (404s, timeouts)
- `console` - Capture JavaScript errors and warnings
- `images` - Check for missing or broken images
- `a11y` - Basic accessibility checks (ARIA, alt text, etc.)
- `performance` - Basic performance metrics
- `all` - Run all checks
## What Claude Will Do
1. **Auto-detect dev server** (if no URL provided)
2. **Create check script** in `.web-tests/scripts/check-{type}-{timestamp}.js`
3. **Execute checks** with: `CWD=$(pwd) cd <skill-dir> && node run.js .web-tests/scripts/check-*.js`
4. **Generate report** in `.web-tests/reports/`
5. **Display summary** of issues found
## Output Structure
```
your-repo/
└── .web-tests/
├── scripts/
│ └── check-links-2025-10-23.js
└── reports/
└── check-links-2025-10-23.md
```
## Report Contents
The report will include:
- **Summary** - Total issues found
- **Details** - Each issue with location and description
- **Severity** - Critical, Warning, Info
- **Suggestions** - How to fix each issue
## Examples
```bash
# Check for broken links
/check --check-type links
# Check for console errors on specific page
/check --url http://localhost:3001/dashboard --check-type console
# Full health check
/check --check-type all
# Check accessibility
/check --url http://localhost:3001 --check-type a11y
```
## What Gets Checked
**Links:**
- External links (status codes)
- Internal links (navigation)
- Anchor links
- Download links
**Console:**
- JavaScript errors
- Network failures
- Console warnings
**Images:**
- Missing images (404s)
- Broken src attributes
- Missing alt text
**Accessibility:**
- Missing ARIA labels
- Color contrast (basic)
- Semantic HTML
- Keyboard navigation
**Performance:**
- Page load time
- Resource sizes
- Number of requests
## Tips
- **Run before deployment** - Catch issues early
- **Check after changes** - Verify nothing broke
- **Commit reports** - Track issues over time
- **Automated quality checks** - Quick health check
## See Also
- `/test` - Run automated tests
- `/screenshot` - Take screenshots

107
commands/take-screenshot.md Normal file
View File

@@ -0,0 +1,107 @@
---
description: Take screenshots of web pages
---
<url>--url</url>
<viewports>--viewports</viewports>
## Take Screenshots
Capture screenshots of web pages across different viewports. Screenshots are saved to `.web-tests/screenshots/` in your working directory.
**Works from any directory** - Saves screenshots to your working directory's `.web-tests/screenshots/` folder.
## How It Works
When you run `/screenshot`, Claude will:
1. **Auto-detect dev server** (or use provided URL)
2. **Create screenshot script** in `.web-tests/scripts/`
3. **Capture screenshots** across specified viewports
4. **Save to** `.web-tests/screenshots/` with timestamps
5. **Report** file locations
## Usage
```bash
# Screenshot current dev server
/screenshot
# Screenshot specific URL
/screenshot --url http://localhost:3001
# Screenshot across all viewports
/screenshot --viewports all
# Screenshot specific viewports
/screenshot --viewports desktop,mobile
# Screenshot external site
/screenshot --url https://example.com --viewports desktop
```
## Viewport Options
- `all` - Desktop (1920x1080), Tablet (768x1024), Mobile (375x667)
- `desktop` - 1920x1080
- `tablet` - 768x1024
- `mobile` - 375x667
- `custom` - Claude will ask for dimensions
## What Claude Will Do
1. **Auto-detect dev server** (if no URL provided)
2. **Create screenshot script** in `.web-tests/scripts/screenshot-{timestamp}.js`
3. **Execute script** with: `CWD=$(pwd) cd <skill-dir> && node run.js .web-tests/scripts/screenshot-*.js`
4. **Take screenshots** across specified viewports
5. **Report file locations**
## Output Structure
```
your-repo/
└── .web-tests/
├── scripts/
│ └── screenshot-2025-10-23.js
└── screenshots/
├── desktop-2025-10-23T12-30-45.png
├── tablet-2025-10-23T12-30-48.png
└── mobile-2025-10-23T12-30-51.png
```
## Screenshot Options
Claude can configure:
- **Full page** - Captures entire scrollable page (default: `true`)
- **Specific element** - Screenshot just one element
- **Visible viewport** - Only visible portion
- **Wait for load** - Waits for page to fully load
## Examples
```bash
# Quick screenshot of current dev server
/screenshot
# Screenshot homepage across all devices
/screenshot --url http://localhost:3001 --viewports all
# Mobile-only screenshot
/screenshot --viewports mobile
# Screenshot specific page
/screenshot --url http://localhost:3001/dashboard --viewports desktop
```
## Tips
- Screenshots include **full page** by default (scrolls entire page)
- **Timestamps in filenames** prevent overwriting
- **Saved in working directory** - easy to commit or share
- **Browser visible** - see what's being captured
## See Also
- `/test` - Run automated tests
- `/check` - Check for broken links or issues

100
commands/test-feature.md Normal file
View File

@@ -0,0 +1,100 @@
---
description: Test a webapp feature with Playwright
---
<url>--url</url>
<test-type>--test-type</test-type>
## Test Webapp Feature
Run automated browser tests for your webapp using Playwright. Tests are saved to `.web-tests/` in your current working directory.
**Works from any directory** - Saves test scripts and screenshots to your working directory's `.web-tests/` folder.
## How It Works
When you run `/test`, Claude will:
1. **Auto-detect running dev servers** (or use the provided URL)
2. **Create a custom test script** in `.web-tests/scripts/`
3. **Execute the test** with visible browser
4. **Save screenshots** to `.web-tests/screenshots/`
5. **Show results** with console output
## Usage
```bash
# Test specific feature
/test --test-type login
# Test with specific URL
/test --url http://localhost:3001 --test-type form
# Test responsive design
/test --test-type responsive
# Test page load
/test --url https://example.com --test-type page-load
```
## Common Test Types
- `login` - Test login flow and authentication
- `form` - Test form filling and submission
- `responsive` - Test across multiple viewports (desktop, tablet, mobile)
- `page-load` - Test if page loads correctly
- `navigation` - Test navigation and routing
- `custom` - Claude will ask what to test
## What Claude Will Do
1. **Auto-detect dev server** (if testing localhost)
2. **Write test script** to `.web-tests/scripts/test-{type}-{timestamp}.js`
3. **Execute test** using: `CWD=$(pwd) cd <skill-dir> && node run.js .web-tests/scripts/test-*.js`
4. **Report results** with any errors or successes
5. **Save screenshots** automatically to `.web-tests/screenshots/`
## Output Structure
```
your-repo/
└── .web-tests/
├── scripts/
│ └── test-login-2025-10-23.js
└── screenshots/
├── login-page-2025-10-23T12-30-45.png
└── dashboard-2025-10-23T12-30-50.png
```
## Environment Variables
Claude can use these environment variables:
- `CWD` - Your current working directory (auto-set)
- `HEADLESS` - Set to `true` for background execution (default: `false`)
- `SLOW_MO` - Slow down actions for debugging (default: `0`)
## Examples
```bash
# Test login with credentials
/test --test-type login
# Test contact form
/test --test-type form --url http://localhost:3001/contact
# Test homepage across viewports
/test --test-type responsive --url http://localhost:3001
```
## Tips
- Tests run with **visible browser** by default (easier to debug)
- **Screenshots are automatic** - saved to `.web-tests/screenshots/`
- **Test scripts are reusable** - found in `.web-tests/scripts/`
- **Auto-detects servers** - no need to specify URL for localhost
## See Also
- `/screenshot` - Take screenshots of pages
- `/check` - Check for broken links or issues

69
plugin.lock.json Normal file
View File

@@ -0,0 +1,69 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:marcioaltoe/claude-craftkit:plugins/ui-tests",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "1cda1f763c6ffd508a88065babf84b146c2d7e5a",
"treeHash": "4c126a02d9c5801bd76e4f65b15a58ab5fb267ce31a82ca6fbecf8803d717f5c",
"generatedAt": "2025-11-28T10:27:01.884187Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "ui-tests",
"description": "Claude Code Skill for web testing and browser automation with Playwright. Auto-detects dev servers, saves test scripts and screenshots to your working directory.",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "44893006b3ee6c1de73de0383ae9e3c3668c13eb697fb01ed0a89816fddc94fd"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "0b86167354822a59df9159483a756fa163c2cd900a303db46377dba095f4fb45"
},
{
"path": "commands/check.md",
"sha256": "e017b0bf18523f44d26d0183340c6d0b585d05a530622975605a1bda321e1b4c"
},
{
"path": "commands/take-screenshot.md",
"sha256": "ccb94488e8a90dc24dc2a2425582ee92fad55be5525bc846eacdea5380739bf9"
},
{
"path": "commands/test-feature.md",
"sha256": "eb79d2fa987489298414674073a3790d463b0cad0659701fcce34deb85785437"
},
{
"path": "skills/web-tests/run.js",
"sha256": "8c23dbecbc1b2436561119226ec3f2b7359db85a863613f5d0276db7d9cd9450"
},
{
"path": "skills/web-tests/package.json",
"sha256": "5aeec2bb881a2e6d1b8544f938c62f90fde91d4cdb2eb63e91704a42490a6e59"
},
{
"path": "skills/web-tests/SKILL.md",
"sha256": "270061b5c72e60c5a93dd11ca1203a1b6285113c8701fdebd5de08d0a1cf1d45"
},
{
"path": "skills/web-tests/lib/helpers.js",
"sha256": "e514cf7979e89189652cea7ddc17fb8b613f10111bad5de338b5a90b9cad54d3"
}
],
"dirSha256": "4c126a02d9c5801bd76e4f65b15a58ab5fb267ce31a82ca6fbecf8803d717f5c"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

430
skills/web-tests/SKILL.md Normal file
View File

@@ -0,0 +1,430 @@
---
name: web-tests
description: Complete browser automation with Playwright. **ALWAYS use when user needs browser testing, E2E testing, screenshots, form testing, or responsive design validation.** Auto-detects dev servers, saves test scripts to working directory. Examples - "test this page", "take screenshots of responsive design", "test login flow", "check for broken links", "validate form submission".
---
**IMPORTANT - Path Resolution:**
This skill is installed globally but saves outputs to the user's working directory. Always pass `CWD` environment variable when executing commands to ensure outputs go to the correct location.
Common 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`
# 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:**
1. **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
2. **Write scripts to user's working directory** - Save to `.web-tests/scripts/test-*.js` in user's repo
3. **Use visible browser by default** - Always use `headless: false` unless user specifically requests headless mode
4. **Parameterize URLs** - Always make URLs configurable via constant at top of script
## How It Works
1. You describe what you want to test/automate
2. I auto-detect running dev servers (or ask for URL if testing external site)
3. I write custom Playwright code in `.web-tests/scripts/test-*.js` (in user's working directory)
4. I execute it via: `CWD=$(pwd) cd $SKILL_DIR && node run.js .web-tests/scripts/test-*.js`
5. Results displayed in real-time, browser window visible for debugging
6. Screenshots automatically saved to `.web-tests/screenshots/`
## Setup (First Time)
```bash
cd $SKILL_DIR
npm run setup
```
This installs Playwright and Chromium browser. Only needed once.
## 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
```
## Common 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();
})();
```
### 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();
})();
```
### 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();
})();
```
### 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();
})();
```
### 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();
}
})();
```
### 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();
})();
```
## Inline Execution (Simple Tasks)
For quick one-off tasks, you can execute code inline without creating files:
```bash
# Take 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-run
## Available Helpers
Optional utility functions in `lib/helpers.js`:
```javascript
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 `lib/helpers.js` for full list.
## Directory 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
```
## Tips
- **CRITICAL: Detect servers FIRST** - Always run `detectDevServers()` before writing test code for localhost testing
- **Save to .web-tests/** - Write scripts to `.web-tests/scripts/`, screenshots auto-save to `.web-tests/screenshots/`
- **Parameterize URLs** - Put detected/provided URL in a `TARGET_URL` constant at the top of every script
- **DEFAULT: Visible browser** - Always use `headless: false` unless user explicitly asks for headless mode
- **Headless mode** - Only use `headless: true` when user specifically requests "headless" or "background" execution
- **Slow down:** Use `slowMo: 100` to make actions visible and easier to follow
- **Wait strategies:** Use `waitForURL`, `waitForSelector`, `waitForLoadState` instead of fixed timeouts
- **Error handling:** Always use try-catch for robust automation
- **Console output:** Use `console.log()` to track progress and show what's happening
- **Use helpers:** The `helpers.takeScreenshot()` automatically saves to `.web-tests/screenshots/`
## Troubleshooting
**Playwright not installed:**
```bash
cd $SKILL_DIR && npm run setup
```
**Module not found:**
Ensure running from skill directory via `run.js` wrapper with CWD set
**Browser doesn't open:**
Check `headless: false` and ensure display available
**Element not found:**
Add wait: `await page.waitForSelector('.element', { timeout: 10000 })`
**Screenshots not in .web-tests/:**
Make sure `CWD` environment variable is set when executing
## Example 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]
```
## 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 `.web-tests/scripts/` for reusability
- Screenshots automatically saved to `.web-tests/screenshots/` with timestamps
- Code executes reliably with proper module resolution via `run.js`
- Works as global tool - no per-repo installation needed

View File

@@ -0,0 +1,26 @@
{
"name": "ui-tests",
"version": "1.0.0",
"description": "Web testing and browser automation with Playwright for Claude Code",
"author": "Claude Craftkit",
"main": "run.js",
"scripts": {
"setup": "npm install && npx playwright install chromium",
"install-all-browsers": "npx playwright install chromium firefox webkit"
},
"keywords": [
"ui-tests",
"playwright",
"automation",
"browser-testing",
"web-automation",
"claude-skill"
],
"dependencies": {
"playwright": "^1.56.1"
},
"engines": {
"node": ">=14.0.0"
},
"license": "MIT"
}

217
skills/web-tests/run.js Executable file
View File

@@ -0,0 +1,217 @@
#!/usr/bin/env node
/**
* Universal Playwright Executor for Claude Code
*
* Executes Playwright automation code from:
* - File path: node run.js script.js
* - Inline code: node run.js 'await page.goto("...")'
* - Stdin: cat script.js | node run.js
*
* Ensures proper module resolution by running from skill directory.
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// Change to skill directory for proper module resolution
process.chdir(__dirname);
/**
* Check if Playwright is installed
*/
function checkPlaywrightInstalled() {
try {
require.resolve('playwright');
return true;
} catch (e) {
return false;
}
}
/**
* Install Playwright if missing
*/
function installPlaywright() {
console.log('📦 Playwright not found. Installing...');
try {
execSync('npm install', { stdio: 'inherit', cwd: __dirname });
execSync('npx playwright install chromium', { stdio: 'inherit', cwd: __dirname });
console.log('✅ Playwright installed successfully');
return true;
} catch (e) {
console.error('❌ Failed to install Playwright:', e.message);
console.error('Please run manually: cd', __dirname, '&& npm run setup');
return false;
}
}
/**
* Get code to execute from various sources
*/
function getCodeToExecute() {
const args = process.argv.slice(2);
// Case 1: File path provided
if (args.length > 0 && fs.existsSync(args[0])) {
const filePath = path.resolve(args[0]);
console.log(`📄 Executing file: ${filePath}`);
return fs.readFileSync(filePath, 'utf8');
}
// Case 2: Inline code provided as argument
if (args.length > 0) {
console.log('⚡ Executing inline code');
return args.join(' ');
}
// Case 3: Code from stdin
if (!process.stdin.isTTY) {
console.log('📥 Reading from stdin');
return fs.readFileSync(0, 'utf8');
}
// No input
console.error('❌ No code to execute');
console.error('Usage:');
console.error(' node run.js script.js # Execute file');
console.error(' node run.js "code here" # Execute inline');
console.error(' cat script.js | node run.js # Execute from stdin');
console.error('');
console.error('Environment Variables:');
console.error(' CWD=/path/to/repo node run.js script.js # Set working directory for outputs');
process.exit(1);
}
/**
* Clean up old temporary execution files from previous runs
*/
function cleanupOldTempFiles() {
try {
const files = fs.readdirSync(__dirname);
const tempFiles = files.filter(f => f.startsWith('.temp-execution-') && f.endsWith('.js'));
if (tempFiles.length > 0) {
tempFiles.forEach(file => {
const filePath = path.join(__dirname, file);
try {
fs.unlinkSync(filePath);
} catch (e) {
// Ignore errors - file might be in use or already deleted
}
});
}
} catch (e) {
// Ignore directory read errors
}
}
/**
* Wrap code in async IIFE if not already wrapped
*/
function wrapCodeIfNeeded(code) {
// Check if code already has require() and async structure
const hasRequire = code.includes('require(');
const hasAsyncIIFE = code.includes('(async () => {') || code.includes('(async()=>{');
// If it's already a complete script, return as-is
if (hasRequire && hasAsyncIIFE) {
return code;
}
// If it's just Playwright commands, wrap in full template
if (!hasRequire) {
return `
const { chromium, firefox, webkit, devices } = require('playwright');
const helpers = require('./lib/helpers');
(async () => {
try {
${code}
} catch (error) {
console.error('❌ Automation error:', error.message);
if (error.stack) {
console.error(error.stack);
}
process.exit(1);
}
})();
`;
}
// If has require but no async wrapper
if (!hasAsyncIIFE) {
return `
(async () => {
try {
${code}
} catch (error) {
console.error('❌ Automation error:', error.message);
if (error.stack) {
console.error(error.stack);
}
process.exit(1);
}
})();
`;
}
return code;
}
/**
* Main execution
*/
async function main() {
console.log('🎭 Web Tests - Universal Executor\n');
// Show working directory info
if (process.env.CWD) {
console.log(`📁 Working directory: ${process.env.CWD}`);
console.log(`📸 Screenshots will be saved to: ${process.env.CWD}/.web-tests/screenshots/\n`);
}
// Clean up old temp files from previous runs
cleanupOldTempFiles();
// Check Playwright installation
if (!checkPlaywrightInstalled()) {
const installed = installPlaywright();
if (!installed) {
process.exit(1);
}
}
// Get code to execute
const rawCode = getCodeToExecute();
const code = wrapCodeIfNeeded(rawCode);
// Create temporary file for execution
const tempFile = path.join(__dirname, `.temp-execution-${Date.now()}.js`);
try {
// Write code to temp file
fs.writeFileSync(tempFile, code, 'utf8');
// Execute the code
console.log('🚀 Starting automation...\n');
require(tempFile);
// Note: Temp file will be cleaned up on next run
// This allows long-running async operations to complete safely
} catch (error) {
console.error('❌ Execution failed:', error.message);
if (error.stack) {
console.error('\n📋 Stack trace:');
console.error(error.stack);
}
process.exit(1);
}
}
// Run main function
main().catch(error => {
console.error('❌ Fatal error:', error.message);
process.exit(1);
});