[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-a01501c7-fc4e-44e1-a903-5b0ce4ccfc8d":3,"$fCd5P_onLFBeuNuspc5zVHv59mgIevjGoM1tIVxMa0E8":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"a01501c7-fc4e-44e1-a903-5b0ce4ccfc8d","playwright-skill","重要 - 路径解析：此技能可以安装在不同位置（插件系统、手动安装、全局或项目特定）。在执行任何命令之前，根据您加载此SKILL.md文件的位置确定技能目录，并在所有以下命令中使用该路径。","cat_coding_review","mod_coding","sickn33,coding","---\nname: playwright-skill\ndescription: \"IMPORTANT - Path Resolution: This skill can be installed in different locations (plugin system, manual installation, global, or project-specific). Before executing any commands, determine the skill directory based on where you loaded this SKILL.md file, and use that path in all commands below.\"\nrisk: unknown\nsource: community\ndate_added: \"2026-02-27\"\nplugin:\n  setup:\n    type: manual\n    summary: \"Run `npm run setup` in the skill directory before first use to install Playwright and Chromium.\"\n    docs: \"SKILL.md\"\n---\n\n**IMPORTANT - Path Resolution:**\nThis skill can be installed in different locations (plugin system, manual installation, global, or project-specific). Before executing any commands, determine the skill directory based on where you loaded this SKILL.md file, and use that path in all commands below. Replace `$SKILL_DIR` with the actual discovered path.\n\nCommon installation paths:\n\n- Plugin system: `\u003Cplugin-root>\u002Fskills\u002Fplaywright-skill`\n- Manual global: `\u003Cagent-home>\u002Fskills\u002Fplaywright-skill`\n- Project-specific: `\u003Cproject>\u002F.agent\u002Fskills\u002Fplaywright-skill`\n\n# Playwright Browser Automation\n\nGeneral-purpose browser automation skill. I'll write custom Playwright code for any automation task you request and execute it via the universal executor.\n\n**CRITICAL WORKFLOW - Follow these steps in order:**\n\n1. **Auto-detect dev servers** - For localhost testing, ALWAYS run server detection FIRST:\n\n   ```bash\n   cd $SKILL_DIR && node -e \"require('.\u002Flib\u002Fhelpers').detectDevServers().then(servers => console.log(JSON.stringify(servers)))\"\n   ```\n\n   - If **1 server found**: Use it automatically, inform user\n   - If **multiple servers found**: Ask user which one to test\n   - If **no servers found**: Ask for URL or offer to help start dev server\n\n2. **Write scripts to \u002Ftmp** - NEVER write test files to skill directory; always use `\u002Ftmp\u002Fplaywright-test-*.js`\n\n3. **Use visible browser by default** - Always use `headless: false` unless user specifically requests headless mode\n\n4. **Parameterize URLs** - Always make URLs configurable via environment variable or constant at top of script\n\n## How It Works\n\n1. You describe what you want to test\u002Fautomate\n2. I auto-detect running dev servers (or ask for URL if testing external site)\n3. I write custom Playwright code in `\u002Ftmp\u002Fplaywright-test-*.js` (won't clutter your project)\n4. I execute it via: `cd $SKILL_DIR && node run.js \u002Ftmp\u002Fplaywright-test-*.js`\n5. Results displayed in real-time, browser window visible for debugging\n6. Test files auto-cleaned from \u002Ftmp by your OS\n\n## Setup (First Time)\n\n```bash\ncd $SKILL_DIR\nnpm run setup\n```\n\nThis installs Playwright and Chromium browser. Only needed once.\n\n## Execution Pattern\n\n**Step 1: Detect dev servers (for localhost testing)**\n\n```bash\ncd $SKILL_DIR && node -e \"require('.\u002Flib\u002Fhelpers').detectDevServers().then(s => console.log(JSON.stringify(s)))\"\n```\n\n**Step 2: Write test script to \u002Ftmp with URL parameter**\n\n```javascript\n\u002F\u002F \u002Ftmp\u002Fplaywright-test-page.js\nconst { chromium } = require('playwright');\n\n\u002F\u002F Parameterized URL (detected or user-provided)\nconst TARGET_URL = 'http:\u002F\u002Flocalhost:3001'; \u002F\u002F \u003C-- Auto-detected or from user\n\n(async () => {\n  const browser = await chromium.launch({ headless: false });\n  const page = await browser.newPage();\n\n  await page.goto(TARGET_URL);\n  console.log('Page loaded:', await page.title());\n\n  await page.screenshot({ path: '\u002Ftmp\u002Fscreenshot.png', fullPage: true });\n  console.log('📸 Screenshot saved to \u002Ftmp\u002Fscreenshot.png');\n\n  await browser.close();\n})();\n```\n\n**Step 3: Execute from skill directory**\n\n```bash\ncd $SKILL_DIR && node run.js \u002Ftmp\u002Fplaywright-test-page.js\n```\n\n## Common Patterns\n\n### Test a Page (Multiple Viewports)\n\n```javascript\n\u002F\u002F \u002Ftmp\u002Fplaywright-test-responsive.js\nconst { chromium } = require('playwright');\n\nconst TARGET_URL = 'http:\u002F\u002Flocalhost:3001'; \u002F\u002F Auto-detected\n\n(async () => {\n  const browser = await chromium.launch({ headless: false, slowMo: 100 });\n  const page = await browser.newPage();\n\n  \u002F\u002F Desktop test\n  await page.setViewportSize({ width: 1920, height: 1080 });\n  await page.goto(TARGET_URL);\n  console.log('Desktop - Title:', await page.title());\n  await page.screenshot({ path: '\u002Ftmp\u002Fdesktop.png', fullPage: true });\n\n  \u002F\u002F Mobile test\n  await page.setViewportSize({ width: 375, height: 667 });\n  await page.screenshot({ path: '\u002Ftmp\u002Fmobile.png', fullPage: true });\n\n  await browser.close();\n})();\n```\n\n### Test Login Flow\n\n```javascript\n\u002F\u002F \u002Ftmp\u002Fplaywright-test-login.js\nconst { chromium } = require('playwright');\n\nconst TARGET_URL = 'http:\u002F\u002Flocalhost:3001'; \u002F\u002F Auto-detected\n\n(async () => {\n  const browser = await chromium.launch({ headless: false });\n  const page = await browser.newPage();\n\n  await page.goto(`${TARGET_URL}\u002Flogin`);\n\n  await page.fill('input[name=\"email\"]', 'test@example.com');\n  await page.fill('input[name=\"password\"]', 'password123');\n  await page.click('button[type=\"submit\"]');\n\n  \u002F\u002F Wait for redirect\n  await page.waitForURL('**\u002Fdashboard');\n  console.log('✅ Login successful, redirected to dashboard');\n\n  await browser.close();\n})();\n```\n\n### Fill and Submit Form\n\n```javascript\n\u002F\u002F \u002Ftmp\u002Fplaywright-test-form.js\nconst { chromium } = require('playwright');\n\nconst TARGET_URL = 'http:\u002F\u002Flocalhost:3001'; \u002F\u002F Auto-detected\n\n(async () => {\n  const browser = await chromium.launch({ headless: false, slowMo: 50 });\n  const page = await browser.newPage();\n\n  await page.goto(`${TARGET_URL}\u002Fcontact`);\n\n  await page.fill('input[name=\"name\"]', 'John Doe');\n  await page.fill('input[name=\"email\"]', 'john@example.com');\n  await page.fill('textarea[name=\"message\"]', 'Test message');\n  await page.click('button[type=\"submit\"]');\n\n  \u002F\u002F Verify submission\n  await page.waitForSelector('.success-message');\n  console.log('✅ Form submitted successfully');\n\n  await browser.close();\n})();\n```\n\n### Check for Broken Links\n\n```javascript\nconst { chromium } = require('playwright');\n\n(async () => {\n  const browser = await chromium.launch({ headless: false });\n  const page = await browser.newPage();\n\n  await page.goto('http:\u002F\u002Flocalhost:3000');\n\n  const links = await page.locator('a[href^=\"http\"]').all();\n  const results = { working: 0, broken: [] };\n\n  for (const link of links) {\n    const href = await link.getAttribute('href');\n    try {\n      const response = await page.request.head(href);\n      if (response.ok()) {\n        results.working++;\n      } else {\n        results.broken.push({ url: href, status: response.status() });\n      }\n    } catch (e) {\n      results.broken.push({ url: href, error: e.message });\n    }\n  }\n\n  console.log(`✅ Working links: ${results.working}`);\n  console.log(`❌ Broken links:`, results.broken);\n\n  await browser.close();\n})();\n```\n\n### Take Screenshot with Error Handling\n\n```javascript\nconst { chromium } = require('playwright');\n\n(async () => {\n  const browser = await chromium.launch({ headless: false });\n  const page = await browser.newPage();\n\n  try {\n    await page.goto('http:\u002F\u002Flocalhost:3000', {\n      waitUntil: 'networkidle',\n      timeout: 10000,\n    });\n\n    await page.screenshot({\n      path: '\u002Ftmp\u002Fscreenshot.png',\n      fullPage: true,\n    });\n\n    console.log('📸 Screenshot saved to \u002Ftmp\u002Fscreenshot.png');\n  } catch (error) {\n    console.error('❌ Error:', error.message);\n  } finally {\n    await browser.close();\n  }\n})();\n```\n\n### Test Responsive Design\n\n```javascript\n\u002F\u002F \u002Ftmp\u002Fplaywright-test-responsive-full.js\nconst { chromium } = require('playwright');\n\nconst TARGET_URL = 'http:\u002F\u002Flocalhost:3001'; \u002F\u002F Auto-detected\n\n(async () => {\n  const browser = await chromium.launch({ headless: false });\n  const page = await browser.newPage();\n\n  const viewports = [\n    { name: 'Desktop', width: 1920, height: 1080 },\n    { name: 'Tablet', width: 768, height: 1024 },\n    { name: 'Mobile', width: 375, height: 667 },\n  ];\n\n  for (const viewport of viewports) {\n    console.log(\n      `Testing ${viewport.name} (${viewport.width}x${viewport.height})`,\n    );\n\n    await page.setViewportSize({\n      width: viewport.width,\n      height: viewport.height,\n    });\n\n    await page.goto(TARGET_URL);\n    await page.waitForTimeout(1000);\n\n    await page.screenshot({\n      path: `\u002Ftmp\u002F${viewport.name.toLowerCase()}.png`,\n      fullPage: true,\n    });\n  }\n\n  console.log('✅ All viewports tested');\n  await browser.close();\n})();\n```\n\n## Inline Execution (Simple Tasks)\n\nFor quick one-off tasks, you can execute code inline without creating files:\n\n```bash\n# Take a quick screenshot\ncd $SKILL_DIR && node run.js \"\nconst browser = await chromium.launch({ headless: false });\nconst page = await browser.newPage();\nawait page.goto('http:\u002F\u002Flocalhost:3001');\nawait page.screenshot({ path: '\u002Ftmp\u002Fquick-screenshot.png', fullPage: true });\nconsole.log('Screenshot saved');\nawait browser.close();\n\"\n```\n\n**When to use inline vs files:**\n\n- **Inline**: Quick one-off tasks (screenshot, check if element exists, get page title)\n- **Files**: Complex tests, responsive design checks, anything user might want to re-run\n\n## Available Helpers\n\nOptional utility functions in `lib\u002Fhelpers.js`:\n\n```javascript\nconst helpers = require('.\u002Flib\u002Fhelpers');\n\n\u002F\u002F Detect running dev servers (CRITICAL - use this first!)\nconst servers = await helpers.detectDevServers();\nconsole.log('Found servers:', servers);\n\n\u002F\u002F Safe click with retry\nawait helpers.safeClick(page, 'button.submit', { retries: 3 });\n\n\u002F\u002F Safe type with clear\nawait helpers.safeType(page, '#username', 'testuser');\n\n\u002F\u002F Take timestamped screenshot\nawait helpers.takeScreenshot(page, 'test-result');\n\n\u002F\u002F Handle cookie banners\nawait helpers.handleCookieBanner(page);\n\n\u002F\u002F Extract table data\nconst data = await helpers.extractTableData(page, 'table.results');\n```\n\nSee `lib\u002Fhelpers.js` for full list.\n\n## Custom HTTP Headers\n\nConfigure custom headers for all HTTP requests via environment variables. Useful for:\n\n- Identifying automated traffic to your backend\n- Getting LLM-optimized responses (e.g., plain text errors instead of styled HTML)\n- Adding authentication tokens globally\n\n### Configuration\n\n**Single header (common case):**\n\n```bash\nPW_HEADER_NAME=X-Automated-By PW_HEADER_VALUE=playwright-skill \\\n  cd $SKILL_DIR && node run.js \u002Ftmp\u002Fmy-script.js\n```\n\n**Multiple headers (JSON format):**\n\n```bash\nPW_EXTRA_HEADERS='{\"X-Automated-By\":\"playwright-skill\",\"X-Debug\":\"true\"}' \\\n  cd $SKILL_DIR && node run.js \u002Ftmp\u002Fmy-script.js\n```\n\n### How It Works\n\nHeaders are automatically applied when using `helpers.createContext()`:\n\n```javascript\nconst context = await helpers.createContext(browser);\nconst page = await context.newPage();\n\u002F\u002F All requests from this page include your custom headers\n```\n\nFor scripts using raw Playwright API, use the injected `getContextOptionsWithHeaders()`:\n\n```javascript\nconst context = await browser.newContext(\n  getContextOptionsWithHeaders({ viewport: { width: 1920, height: 1080 } }),\n);\n```\n\n## Advanced Usage\n\nFor comprehensive Playwright API documentation, see [API_REFERENCE.md](API_REFERENCE.md):\n\n- Selectors & Locators best practices\n- Network interception & API mocking\n- Authentication & session management\n- Visual regression testing\n- Mobile device emulation\n- Performance testing\n- Debugging techniques\n- CI\u002FCD integration\n\n## Tips\n\n- **CRITICAL: Detect servers FIRST** - Always run `detectDevServers()` before writing test code for localhost testing\n- **Custom headers** - Use `PW_HEADER_NAME`\u002F`PW_HEADER_VALUE` env vars to identify automated traffic to your backend\n- **Use \u002Ftmp for test files** - Write to `\u002Ftmp\u002Fplaywright-test-*.js`, never to skill directory or user's project\n- **Parameterize URLs** - Put detected\u002Fprovided URL in a `TARGET_URL` constant at the top of every script\n- **DEFAULT: Visible browser** - Always use `headless: false` unless user explicitly asks for headless mode\n- **Headless mode** - Only use `headless: true` when user specifically requests \"headless\" or \"background\" execution\n- **Slow down:** Use `slowMo: 100` to make actions visible and easier to follow\n- **Wait strategies:** Use `waitForURL`, `waitForSelector`, `waitForLoadState` instead of fixed timeouts\n- **Error handling:** Always use try-catch for robust automation\n- **Console output:** Use `console.log()` to track progress and show what's happening\n\n## Troubleshooting\n\n**Playwright not installed:**\n\n```bash\ncd $SKILL_DIR && npm run setup\n```\n\n**Module not found:**\nEnsure running from skill directory via `run.js` wrapper\n\n**Browser doesn't open:**\nCheck `headless: false` and ensure display available\n\n**Element not found:**\nAdd wait: `await page.waitForSelector('.element', { timeout: 10000 })`\n\n## Example Usage\n\n```\nUser: \"Test if the marketing page looks good\"\n\nClaude: I'll test the marketing page across multiple viewports. Let me first detect running servers...\n[Runs: detectDevServers()]\n[Output: Found server on port 3001]\nI found your dev server running on http:\u002F\u002Flocalhost:3001\n\n[Writes custom automation script to \u002Ftmp\u002Fplaywright-test-marketing.js with URL parameterized]\n[Runs: cd $SKILL_DIR && node run.js \u002Ftmp\u002Fplaywright-test-marketing.js]\n[Shows results with screenshots from \u002Ftmp\u002F]\n```\n\n```\nUser: \"Check if login redirects correctly\"\n\nClaude: I'll test the login flow. First, let me check for running servers...\n[Runs: detectDevServers()]\n[Output: Found servers on ports 3000 and 3001]\nI found 2 dev servers. Which one should I test?\n- http:\u002F\u002Flocalhost:3000\n- http:\u002F\u002Flocalhost:3001\n\nUser: \"Use 3001\"\n\n[Writes login automation to \u002Ftmp\u002Fplaywright-test-login.js]\n[Runs: cd $SKILL_DIR && node run.js \u002Ftmp\u002Fplaywright-test-login.js]\n[Reports: ✅ Login successful, redirected to \u002Fdashboard]\n```\n\n## Notes\n\n- Each automation is custom-written for your specific request\n- Not limited to pre-built scripts - any browser task possible\n- Auto-detects running dev servers to eliminate hardcoded URLs\n- Test scripts written to `\u002Ftmp` for automatic cleanup (no clutter)\n- Code executes reliably with proper module resolution via `run.js`\n- Progressive disclosure - API_REFERENCE.md loaded only when advanced features needed\n\n## When to Use\nThis skill is applicable to execute the workflow or actions described in the overview.\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,74,718,"2026-05-16 13:34:09",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"编程开发","coding","mdi-code-braces","代码生成、调试、审查，提升开发效率",2,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"代码审查","review","mdi-magnify-scan","代码质量分析、安全审查",4,145,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"9a6cf8e9-9c41-4013-8575-1fd7e6f5666f","1.0.0","playwright-skill.zip",17439,"uploads\u002Fskills\u002Fa01501c7-fc4e-44e1-a903-5b0ce4ccfc8d\u002Fplaywright-skill.zip","4fc9a585849d596ba73ec64ef34aef24a2fe94c8e28e8bbd0fa330f1c174609d","[{\"path\":\"API_REFERENCE.md\",\"isDirectory\":false,\"size\":16366},{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":14461},{\"path\":\"lib\u002Fhelpers.js\",\"isDirectory\":false,\"size\":12605},{\"path\":\"package.json\",\"isDirectory\":false,\"size\":652},{\"path\":\"run.js\",\"isDirectory\":false,\"size\":5788}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]