aboutsummaryrefslogtreecommitdiff
path: root/playwright-skill/run.js
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-19 15:24:51 -0500
committerCraig Jennings <c@cjennings.net>2026-04-19 15:24:51 -0500
commit4ffa7417a359ef4eae09f61d7da4de06539462ca (patch)
treeb8eeb8aa5ec2344216c0f0cdcdcc82d0df307ce3 /playwright-skill/run.js
parent11f5f003eef12bff9633ca8190e3c43c7dab6708 (diff)
downloadrulesets-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-xplaywright-skill/run.js228
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);
-});