/** * generate-playwright-tests.js * * Generates Playwright + MCP spec files from the editor's test data. */ const fs = require('fs'); const path = require('path'); const { loadActionCatalog, getAction } = require('./action-catalog/index.js'); const DATA_FILE = path.join(__dirname, '..', '..', 'legacy', 'data', 'testcases.json'); // legacy JSON (for export scripts) const OUTPUT_BASE = path.join(__dirname, '..', '..', 'tests', 'generated'); function parseArgs() { const args = process.argv.slice(2); const options = {}; for (let i = 0; i < args.length; i++) { if (args[i] === '--group' && args[i + 1]) options.group = args[++i]; if (args[i] === '--dry-run') options.dryRun = true; } return options; } function loadTests() { return JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8')); } function getTestsToGenerate(tests, options) { let filtered = tests.filter(t => t.fullyAutomated === true || t.status === 'automated'); if (options.group) { filtered = filtered.filter(t => t.group === options.group); } return filtered; } function generateSpecFile(group, tests, catalog) { let code = `import { test, expect } from '@playwright/test';\n\n`; code += `// === AUTOGENERATED BY AUTOTEST EDITOR ===\n`; code += `// Group: ${group}\n`; code += `// Generated: ${new Date().toISOString()}\n\n`; code += `test.describe('${group}', () => {\n`; code += ` test.use({ ignoreHTTPSErrors: true });\n\n`; for (const test of tests) { code += generateSingleTest(test, catalog); } code += `});\n`; return code; } function generateSingleTest(test, catalog) { let body = ''; const steps = test.codeSteps && test.codeSteps.length > 0 ? test.codeSteps : (test.steps || []).map(s => ({ stepName: s, actionKey: 'search_for_document' })); for (const step of steps) { const action = getAction(step.actionKey); if (action) { let rendered = action.template; if (step.params) { Object.keys(step.params).forEach(key => { rendered = rendered.replace(new RegExp(`{{${key}}}`, 'g'), step.params[key]); }); } body += ` await test.step('${step.stepName}', async () => {\n`; body += ` ${rendered}\n`; body += ` });\n\n`; } else { body += ` await test.step('${step.stepName}', async () => {\n`; body += ` // TODO: Implement action "${step.actionKey}"\n`; body += ` });\n\n`; } } let testCode = ` test('${test.id}', async ({ page }, testInfo) => {\n`; testCode += ` testInfo.annotations.push({ type: 'automationStatus', description: '${test.status}' });\n`; testCode += ` testInfo.annotations.push({ type: 'testType', description: '${test.action}' });\n`; testCode += ` testInfo.annotations.push({ type: 'description', description: '${test.description}' });\n\n`; testCode += body; testCode += ` });\n\n`; return testCode; } function ensureDir(dir) { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); } function main() { const options = parseArgs(); const tests = loadTests(); const catalog = loadActionCatalog(); const toGenerate = getTestsToGenerate(tests, options); if (toGenerate.length === 0) { console.log('No automated tests found for the given filters.'); return; } const byGroup = {}; toGenerate.forEach(t => { if (!byGroup[t.group]) byGroup[t.group] = []; byGroup[t.group].push(t); }); for (const group of Object.keys(byGroup)) { const groupTests = byGroup[group]; const moduleFolder = group.startsWith('core-') ? 'core' : 'pbx'; const outputDir = path.join(OUTPUT_BASE, moduleFolder); ensureDir(outputDir); const fileName = group.replace('core-', '').replace('pbx-', '') + '.spec.ts'; const outputPath = path.join(outputDir, fileName); const fileContent = generateSpecFile(group, groupTests, catalog); if (options.dryRun) { console.log(`\n=== DRY RUN: ${outputPath} ===\n`); console.log(fileContent); } else { fs.writeFileSync(outputPath, fileContent, 'utf-8'); console.log(`✅ Generated: ${outputPath} (${groupTests.length} tests)`); } } } main();