aboutsummaryrefslogtreecommitdiff
path: root/playwright-js/run.js
diff options
context:
space:
mode:
Diffstat (limited to 'playwright-js/run.js')
-rwxr-xr-xplaywright-js/run.js228
1 files changed, 228 insertions, 0 deletions
diff --git a/playwright-js/run.js b/playwright-js/run.js
new file mode 100755
index 0000000..10f2616
--- /dev/null
+++ b/playwright-js/run.js
@@ -0,0 +1,228 @@
+#!/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);
+});