diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-19 15:24:51 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-19 15:24:51 -0500 |
| commit | 4ffa7417a359ef4eae09f61d7da4de06539462ca (patch) | |
| tree | b8eeb8aa5ec2344216c0f0cdcdcc82d0df307ce3 /playwright-skill/run.js | |
| parent | 11f5f003eef12bff9633ca8190e3c43c7dab6708 (diff) | |
| download | rulesets-4ffa7417a359ef4eae09f61d7da4de06539462ca.tar.gz rulesets-4ffa7417a359ef4eae09f61d7da4de06539462ca.zip | |
refactor(playwright): split into playwright-js + playwright-py variants
Rename `playwright-skill/` → `playwright-js/` and add `playwright-py/`
as a verbatim fork of Anthropic's official `webapp-testing` skill
(Apache-2.0). Cross-pollinate: each skill gains patterns and helpers
inspired by the other's strengths, with upstream semantics preserved.
## playwright-js (JS/TS stack)
Renamed from playwright-skill; upstream lackeyjb MIT content untouched.
New sections added (clearly marked, preserving upstream semantics):
- Static HTML vs Dynamic Webapp decision tree (core Anthropic methodology)
- Reconnaissance-Then-Action pattern (navigate → networkidle → inspect → act)
- Console Log Capture snippet (page.on console/pageerror/requestfailed)
Description updated to clarify JS/TS stack fit (React/Next/Vue/Svelte/Node)
and reference `/playwright-py` as the Python sibling.
## playwright-py (Python stack)
Verbatim fork of anthropics/skills/skills/webapp-testing; upstream SKILL.md
and bundled `scripts/with_server.py` + examples kept intact. New scripts
and examples added (all lackeyjb-style conveniences in Python):
Scripts:
scripts/detect_dev_servers.py Probe common localhost ports for HTTP
servers; outputs JSON of found services.
scripts/safe_actions.py safe_click, safe_type (retry-wrapped),
handle_cookie_banner (common selectors),
build_context_with_headers (env-var-
driven: PW_HEADER_NAME / PW_HEADER_VALUE /
PW_EXTRA_HEADERS='{…json…}').
Examples:
examples/login_flow.py Login form + wait_for_url.
examples/broken_links.py Scan visible external hrefs via HEAD.
examples/responsive_sweep.py Multi-viewport screenshots to /tmp.
SKILL.md gains 5 "Added:" sections documenting the new scripts, retry
helpers, env-header injection, and /tmp script discipline. Attribution
notes explicitly mark upstream vs local additions.
## Makefile
SKILLS: playwright-skill → playwright-js + playwright-py
deps target: extended Playwright step to install Python package +
Chromium via `python3 -m pip install --user playwright && python3 -m
playwright install chromium` when playwright-py/ is present. Idempotent
(detected via `python3 -c "import playwright"`).
## Usage
Both skills symlinked globally via `make install`. Invoke whichever
matches the project stack — cross-references in descriptions route you
to the right one. Run `make deps` once to install both runtimes.
Diffstat (limited to 'playwright-skill/run.js')
| -rwxr-xr-x | playwright-skill/run.js | 228 |
1 files changed, 0 insertions, 228 deletions
diff --git a/playwright-skill/run.js b/playwright-skill/run.js deleted file mode 100755 index 10f2616..0000000 --- a/playwright-skill/run.js +++ /dev/null @@ -1,228 +0,0 @@ -#!/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'); - 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'); - -// Extra headers from environment variables (if configured) -const __extraHeaders = helpers.getExtraHeadersFromEnv(); - -/** - * Utility to merge environment headers into context options. - * Use when creating contexts with raw Playwright API instead of helpers.createContext(). - * @param {Object} options - Context options - * @returns {Object} Options with extraHTTPHeaders merged in - */ -function getContextOptionsWithHeaders(options = {}) { - if (!__extraHeaders) return options; - return { - ...options, - extraHTTPHeaders: { - ...__extraHeaders, - ...(options.extraHTTPHeaders || {}) - } - }; -} - -(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('🎭 Playwright Skill - Universal Executor\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); -}); |
