@cementic/cementic-test 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +300 -457
- package/dist/chunk-5QRDTCSM.js +368 -0
- package/dist/chunk-5QRDTCSM.js.map +1 -0
- package/dist/cli.js +1242 -210
- package/dist/cli.js.map +1 -1
- package/dist/{gen-54KYT3RO.js → gen-RMRQOAD3.js} +2 -2
- package/dist/templates/student-framework/README.md +35 -5
- package/dist/templates/student-framework/package.json +3 -2
- package/dist/templates/student-framework/pages/BasePage.js +19 -0
- package/dist/templates/student-framework/pages/DashboardPage.js +16 -0
- package/dist/templates/student-framework/pages/FormPage.js +24 -0
- package/dist/templates/student-framework/pages/LoginPage.js +24 -0
- package/dist/templates/student-framework/tests/dashboard.spec.js +14 -0
- package/dist/templates/student-framework/tests/login.spec.js +14 -0
- package/dist/templates/student-framework-ts/README.md +20 -0
- package/dist/templates/student-framework-ts/package.json +24 -0
- package/dist/templates/student-framework-ts/pages/LandingPage.ts +15 -0
- package/dist/templates/student-framework-ts/playwright.config.ts +29 -0
- package/dist/templates/{student-framework/tests/landing.spec.js → student-framework-ts/tests/landing.spec.ts} +1 -1
- package/dist/templates/student-framework-ts/tsconfig.json +19 -0
- package/dist/templates/student-framework-ts/workflows/playwright.yml +20 -0
- package/package.json +3 -2
- package/dist/chunk-J63TUHIV.js +0 -80
- package/dist/chunk-J63TUHIV.js.map +0 -1
- package/dist/templates/student-framework/pages/LandingPage.js +0 -18
- /package/dist/{gen-54KYT3RO.js.map → gen-RMRQOAD3.js.map} +0 -0
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/new.ts","../src/commands/normalize.ts","../src/commands/test.ts","../src/commands/tc.ts","../src/core/prefix.ts","../src/core/llm.ts","../src/core/scrape.ts","../src/commands/report.ts","../src/commands/serve.ts","../src/commands/flow.ts","../src/commands/ci.ts"],"sourcesContent":["// src/cli.ts\nimport { Command } from 'commander';\nimport { newCmd } from './commands/new.js';\nimport { normalizeCmd } from './commands/normalize.js';\nimport { genCmd } from './commands/gen.js';\nimport { testCmd } from './commands/test.js';\nimport { tcCmd } from './commands/tc.js';\nimport { reportCmd } from './commands/report.js';\nimport { serveCmd } from './commands/serve.js';\nimport { flowCmd } from './commands/flow.js';\nimport { ciCmd } from './commands/ci.js';\n\nconst program = new Command();\nprogram\n .name('cementic-test')\n .description('CementicTest CLI: cases → normalized → POM tests → Playwright')\n .version('0.2.0');\n\nprogram.addCommand(newCmd());\nprogram.addCommand(normalizeCmd());\nprogram.addCommand(genCmd());\nprogram.addCommand(testCmd());\nprogram.addCommand(tcCmd());\nprogram.addCommand(reportCmd());\nprogram.addCommand(serveCmd());\nprogram.addCommand(flowCmd());\nprogram.addCommand(ciCmd());\n\nprogram.parseAsync(process.argv);","import { Command } from 'commander';\nimport { mkdirSync, writeFileSync, existsSync, readFileSync, cpSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { dirname } from 'node:path';\nimport { platform, release } from 'node:os';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nexport function newCmd() {\n const cmd = new Command('new')\n .arguments('<projectName>')\n .description('Scaffold a new CementicTest + Playwright project from scratch')\n .addHelpText('after', `\nExamples:\n $ ct new my-awesome-test-suite\n $ ct new e2e-tests --no-browsers\n`)\n .option('--mode <mode>', 'greenfield|enhance', 'greenfield')\n .option('--no-browsers', 'do not run \"npx playwright install\" during setup')\n .action((projectName: string, opts) => {\n const root = process.cwd();\n const projectPath = join(root, projectName);\n\n console.log(`🚀 Initializing new CementicTest project in ${projectName}...`);\n\n // 1. Create project directory\n if (existsSync(projectPath)) {\n console.error(`❌ Directory ${projectName} already exists.`);\n process.exit(1);\n }\n mkdirSync(projectPath, { recursive: true });\n\n // 2. Copy template files\n // We need to locate the template directory relative to the built CLI file\n // In development (ts-node), it might be different than in production (dist)\n // We'll assume the templates are copied to 'dist/templates' or similar during build\n // For now, let's try to find it relative to __dirname\n \n // In the source structure: src/commands/new.ts -> templates/student-framework is ../../templates/student-framework\n // In the dist structure: dist/cli.js -> templates/student-framework is ./templates/student-framework\n \n // We will rely on the build process to place templates correctly.\n \n // 1. Try relative to __dirname (dist/templates) - most likely for production\n let templatePath = resolve(__dirname, 'templates/student-framework');\n \n // 2. Try sibling of dist (e.g. if running from dist/ but templates are in root/templates)\n if (!existsSync(templatePath)) {\n templatePath = resolve(__dirname, '../templates/student-framework');\n }\n\n // 3. Try source structure (src/commands/new.ts -> ../../templates)\n if (!existsSync(templatePath)) {\n templatePath = resolve(__dirname, '../../templates/student-framework');\n }\n\n // 4. Fallback to CWD (development)\n if (!existsSync(templatePath)) {\n templatePath = resolve(process.cwd(), 'templates/student-framework');\n }\n\n if (!existsSync(templatePath)) {\n console.error(`❌ Could not locate template at ${templatePath}`);\n console.error('Please ensure the package is built correctly with templates included.');\n process.exit(1);\n }\n\n console.log(`📦 Copying template from ${templatePath}...`);\n \n // Recursive copy function\n function copyRecursive(src: string, dest: string) {\n if (statSync(src).isDirectory()) {\n mkdirSync(dest, { recursive: true });\n readdirSync(src).forEach(child => {\n copyRecursive(join(src, child), join(dest, child));\n });\n } else {\n cpSync(src, dest);\n }\n }\n\n copyRecursive(templatePath, projectPath);\n\n // 2.5 Adjust for OS compatibility (macOS 13/Ventura or older)\n if (platform() === 'darwin') {\n const osRelease = release();\n const majorVersion = parseInt(osRelease.split('.')[0], 10);\n \n // macOS 13 is Darwin 22. macOS 14 is Darwin 23.\n if (majorVersion < 23) {\n console.log('🍎 Detected macOS 13 or older. Adjusting versions for compatibility...');\n const pkgJsonPath = join(projectPath, 'package.json');\n if (existsSync(pkgJsonPath)) {\n try {\n const pkgContent = readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(pkgContent);\n if (pkg.devDependencies) {\n pkg.devDependencies['@playwright/test'] = '^1.48.2';\n pkg.devDependencies['allure-playwright'] = '^2.15.1';\n writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2));\n console.log('✅ Downgraded @playwright/test and allure-playwright for legacy macOS support.');\n }\n } catch (err) {\n console.warn('⚠️ Failed to adjust package.json for OS compatibility:', err);\n }\n }\n }\n }\n\n // 3. Initialize git\n try {\n execSync('git init', { cwd: projectPath, stdio: 'ignore' });\n // Create .gitignore if not exists (template should have it, but just in case)\n const gitignorePath = join(projectPath, '.gitignore');\n if (!existsSync(gitignorePath)) {\n writeFileSync(gitignorePath, 'node_modules\\n.env\\ntest-results\\nplaywright-report\\n.cementic\\n');\n }\n } catch (e) {\n console.warn('⚠️ Failed to initialize git repository.');\n }\n\n // 4. Install dependencies\n console.log('📦 Installing dependencies...');\n try {\n execSync('npm install', { cwd: projectPath, stdio: 'inherit' });\n } catch (e) {\n console.error('❌ Failed to install dependencies. Please run \"npm install\" manually.');\n }\n\n // 5. Install browsers (optional)\n // Commander maps --no-browsers to opts.browsers = false\n if (opts.browsers !== false) {\n console.log('🌐 Installing Playwright browsers...');\n try {\n execSync('npx playwright install', { cwd: projectPath, stdio: 'inherit' });\n } catch (e) {\n console.warn('⚠️ Failed to install browsers. Run \"npx playwright install\" manually.');\n }\n }\n\n console.log(`\\n✅ Project ${projectName} created successfully!`);\n console.log(`\\nTo get started:\\n`);\n console.log(` cd ${projectName}`);\n console.log(` npx playwright test`);\n console.log(`\\nHappy testing! 🧪`);\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport fg from 'fast-glob';\nimport { readFileSync, mkdirSync, writeFileSync, statSync } from 'node:fs';\nimport { join, basename, resolve } from 'node:path';\n\ntype NormalizedCase = {\n id?: string;\n title: string;\n tags?: string[];\n steps: string[];\n expected: string[];\n needs_review: boolean;\n source: string;\n};\n\n/** Grab @tags from the title line */\nfunction parseTags(title: string): { clean: string; tags: string[] } {\n const tags = Array.from(title.matchAll(/@([\\w-]+)/g)).map((m) => m[1]);\n const clean = title.replace(/@[\\w-]+/g, '').trim();\n return { clean, tags };\n}\n\n/** Try to extract an ID prefix like AUTH-001 from the title */\nfunction parseId(title: string): string | undefined {\n const m = title.match(/\\b([A-Z]+-\\d+)\\b/);\n return m?.[1];\n}\n\n/** Split a markdown file into case blocks by top-level \"# \" headings */\nfunction splitCasesByHeading(fileText: string): Array<{ titleLine: string; body: string }> {\n const lines = fileText.split(/\\r?\\n/);\n const blocks: Array<{ titleLine: string; body: string }> = [];\n let currentTitle: string | null = null;\n let buf: string[] = [];\n\n const flush = () => {\n if (currentTitle !== null) {\n blocks.push({ titleLine: currentTitle, body: buf.join('\\n') });\n }\n buf = [];\n };\n\n for (const line of lines) {\n const h1 = line.match(/^\\s*#\\s+(.+)$/); // \"# Title\"\n if (h1) {\n if (currentTitle !== null) flush();\n currentTitle = h1[1].trim();\n } else {\n buf.push(line);\n }\n }\n if (currentTitle !== null) flush();\n\n // If no H1 at all, treat whole file as one case (first non-empty line is title).\n if (blocks.length === 0) {\n const first = lines.find((l) => l.trim());\n const title = first?.replace(/^#\\s*/, '').trim() || 'Untitled';\n return [{ titleLine: title, body: lines.join('\\n') }];\n }\n return blocks;\n}\n\n/** From a case body, extract Steps and Expected sections */\nfunction extractSections(body: string): { steps: string[]; expected: string[] } {\n // Find sections by H2 headings\n // Supports: \"## Steps\", \"## Expected\", \"## Expected Results\"\n const sectionRegex = /^\\s*##\\s*(.+?)\\s*$/gim;\n const sections: Record<string, string> = {};\n let match: RegExpExecArray | null;\n const indices: Array<{ name: string; index: number }> = [];\n\n while ((match = sectionRegex.exec(body))) {\n indices.push({ name: match[1].toLowerCase(), index: match.index });\n }\n\n // Add end sentinel\n indices.push({ name: '__END__', index: body.length });\n\n for (let i = 0; i < indices.length - 1; i++) {\n const name = indices[i].name;\n const slice = body.slice(indices[i].index, indices[i + 1].index);\n sections[name] = slice;\n }\n\n const stepsBlock =\n sections['steps'] ??\n ''; // if absent, we’ll infer from bullets later\n\n const expectedBlock =\n sections['expected'] ??\n sections['expected results'] ??\n sections['then'] ??\n '';\n\n const bullet = /^\\s*(?:\\d+\\.|[-*])\\s+(.+)$/gm;\n\n const steps =\n Array.from(stepsBlock.matchAll(bullet)).map((m) => m[1].trim()) ||\n [];\n\n // Fallback: if no dedicated steps section, collect bullets until a new H2\n if (steps.length === 0) {\n const alt = Array.from(body.matchAll(bullet)).map((m) => m[1].trim());\n // Heuristic: take the first bullet run in the body\n steps.push(...alt);\n }\n\n const expectedLines: string[] = Array.from(expectedBlock.matchAll(bullet)).map((m) =>\n m[1].trim()\n );\n\n // Fallback: lines that start with Expected/Then/Verify/Assert in the whole body\n if (expectedLines.length === 0) {\n const exp = Array.from(\n body.matchAll(/^\\s*(?:Expected|Then|Verify|Assert)[^\\n]*:?[\\s-]*(.+)$/gim)\n ).map((m) => m[1].trim());\n expectedLines.push(...exp);\n }\n\n return { steps, expected: expectedLines };\n}\n\n/** Build one normalized case from a title line + body */\nfunction normalizeOne(titleLine: string, body: string, source: string): NormalizedCase {\n const { clean, tags } = parseTags(titleLine);\n const id = parseId(clean);\n const { steps, expected } = extractSections(body);\n\n return {\n id,\n title: clean,\n tags: tags.length ? tags : undefined,\n steps,\n expected,\n needs_review: steps.length === 0 || expected.length === 0,\n source\n };\n}\n\n/** ===== Commander command ===== */\nexport function normalizeCmd() {\n const cmd = new Command('normalize')\n .argument('<path>', 'Input directory or file pattern containing test cases (Markdown, Text, CSV)')\n .description('Convert human-readable test cases into machine-readable JSON format')\n .addHelpText('after', `\nExamples:\n $ ct normalize ./cases\n $ ct normalize \"cases/**/*.md\"\n $ ct normalize ./cases --and-gen --lang ts (Normalize and generate tests in one go)\n`)\n .option('--report', 'Generate a summary report of the normalization process', true)\n .option('--and-gen', 'Automatically run test generation after normalization', false)\n .option('--lang <lang>', 'Target language for generation (ts|js) when using --and-gen', 'ts')\n .action(async (inputPath: string, opts: { report?: boolean; andGen?: boolean; lang?: string }) => {\n // Accept directory OR glob\n let patterns: string[] = [];\n try {\n const abs = resolve(inputPath);\n if (statSync(abs).isDirectory()) {\n const base = inputPath.replace(/\\/$/, '');\n patterns = [`${base}/**/*.{md,markdown,txt,feature,csv,json}`];\n } else {\n patterns = [inputPath];\n }\n } catch {\n patterns = [inputPath];\n }\n\n const files = await fg(patterns, { dot: false, onlyFiles: true });\n if (files.length === 0) {\n console.error(`No files found for: ${inputPath}`);\n process.exit(2);\n }\n\n const outDir = '.cementic/normalized';\n mkdirSync(outDir, { recursive: true });\n\n const index = {\n summary: { total: 0, parsed: 0, withWarnings: 0 },\n cases: [] as Array<{ file: string; normalized: string; status: string }>\n };\n\n for (const f of files) {\n const content = readFileSync(f, 'utf8');\n\n // Split into multiple cases by \"# \"\n const blocks = splitCasesByHeading(content);\n for (const block of blocks) {\n const norm = normalizeOne(block.titleLine, block.body, f);\n\n // filename: <stem>.<ID or sanitized-title>.json\n const stem = basename(f).replace(/\\.[^/.]+$/, '');\n const suffix = (norm.id || norm.title).replace(/[^\\w-]+/g, '-');\n const outFile = join(outDir, `${stem}.${suffix}.json`);\n\n writeFileSync(outFile, JSON.stringify(norm, null, 2));\n\n index.summary.total++;\n index.summary.parsed++;\n if (norm.needs_review) index.summary.withWarnings++;\n index.cases.push({ file: f, normalized: outFile, status: norm.needs_review ? 'warning' : 'ok' });\n }\n }\n\n writeFileSync(join(outDir, '_index.json'), JSON.stringify(index, null, 2));\n\n if (opts.report !== false) {\n const lines = [\n '# Normalize Report',\n '',\n `Total cases: ${index.summary.total} | Parsed: ${index.summary.parsed} | With warnings: ${index.summary.withWarnings}`,\n '',\n '| Source File | Normalized JSON | Status |',\n '|-------------|-----------------|--------|',\n ...index.cases.map(c => `| ${c.file} | ${c.normalized} | ${c.status} |`)\n ];\n mkdirSync('.cementic/reports', { recursive: true });\n writeFileSync('.cementic/reports/normalize-report.md', lines.join('\\n'));\n }\n\n console.log(`✅ Normalized ${index.summary.parsed} case(s). Output → .cementic/normalized/`);\n\n if (opts.andGen) {\n const { gen } = await import('./gen.js');\n await gen({ lang: opts.lang || 'ts', out: 'tests/generated' });\n }\n });\n\n return cmd;\n}","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\n\nexport function testCmd() {\n const cmd = new Command('test')\n .description('Run Playwright tests via \"npx playwright test\"')\n .allowUnknownOption(true)\n .allowExcessArguments(true)\n .argument('[...pwArgs]', 'Arguments to pass through to Playwright')\n .action((pwArgs: string[] = []) => {\n const child = spawn(\n 'npx',\n ['playwright', 'test', ...pwArgs],\n {\n stdio: 'inherit',\n shell: process.platform === 'win32', // needed for Windows\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}","// src/commands/tc.ts\nimport { Command } from 'commander';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { inferPrefix } from '../core/prefix.js';\nimport { generateTcMarkdownWithAi } from '../core/llm.js';\nimport { scrapePageSummary } from '../core/scrape.js';\n\nfunction slugify(text: string): string {\n return (\n text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'tc'\n );\n}\n\nfunction buildManualCasesMarkdown(opts: {\n prefix: string;\n feature: string;\n url?: string;\n numCases: number;\n startIndex?: number;\n}): string {\n const { prefix, feature, url, numCases } = opts;\n const startIndex = opts.startIndex ?? 1;\n\n const lines: string[] = [];\n\n for (let i = 0; i < numCases; i++) {\n const idx = startIndex + i;\n const id = `${prefix}-${String(idx).padStart(3, '0')}`;\n\n const title = `${feature} - scenario ${idx}`;\n const tags = '@regression @ui';\n\n lines.push(`# ${id} — ${title} ${tags}`);\n lines.push(`## Steps`);\n lines.push(`1. Navigate to ${url ?? '<PAGE_URL>'}`);\n lines.push(`2. Perform the main user action for this scenario`);\n lines.push(`3. Observe the result`);\n lines.push('');\n lines.push(`## Expected Results`);\n lines.push(`- The page responds correctly for this scenario`);\n lines.push(`- UI reflects the expected change`);\n lines.push('');\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nasync function promptBasicQuestions(opts: {\n url?: string;\n feature?: string;\n appDescription?: string;\n numCases?: number;\n}) {\n // If we have the bare minimum args for non-interactive mode, use them.\n if (opts.feature) {\n let n = opts.numCases ?? 3;\n if (n < 1) n = 3;\n if (n > 10) n = 10;\n return {\n feature: opts.feature,\n appDescription: opts.appDescription || '',\n numCases: n,\n url: opts.url,\n };\n }\n\n const rl = createInterface({ input, output });\n\n const feature = (await rl.question('🧩 Feature or page to test: ')).trim();\n const appDescription = (await rl.question('📝 Short app description (optional): ')).trim();\n const numCasesRaw = (await rl.question('🔢 How many test cases? (1-10) [3]: ')).trim();\n\n rl.close();\n\n let numCases = parseInt(numCasesRaw, 10);\n if (isNaN(numCases) || numCases < 1) numCases = 3;\n if (numCases > 10) numCases = 10;\n\n return { feature, appDescription, numCases, url: opts.url };\n}\n\n// 🔑 Single source of truth: did the user pass --ai anywhere?\nfunction hasAiFlagInArgv(): boolean {\n return process.argv.includes('--ai');\n}\n\nasync function runTcInteractive(params: {\n url?: string;\n explicitPrefix?: string;\n feature?: string;\n appDescription?: string;\n numCases?: number;\n}) {\n const { feature, appDescription, numCases, url } = await promptBasicQuestions({\n url: params.url,\n feature: params.feature,\n appDescription: params.appDescription,\n numCases: params.numCases,\n });\n\n const prefix = inferPrefix({\n featureText: feature,\n url,\n explicitPrefix: params.explicitPrefix,\n });\n\n mkdirSync('cases', { recursive: true });\n const fileName = `${prefix.toLowerCase()}-${slugify(feature)}.md`;\n const fullPath = join('cases', fileName);\n\n const useAi = hasAiFlagInArgv();\n console.log(`⚙️ Debug: useAi=${useAi}, argv=${JSON.stringify(process.argv)}`);\n\n let markdown: string;\n\n if (useAi) {\n let pageSummaryJson: any = undefined;\n\n if (url) {\n try {\n console.log(`🔍 Scraping page for AI context: ${url}`);\n pageSummaryJson = await scrapePageSummary(url);\n console.log(\n `🔎 Scrape summary: title=\"${pageSummaryJson.title || ''}\", ` +\n `headings=${pageSummaryJson.headings.length}, ` +\n `buttons=${pageSummaryJson.buttons.length}, ` +\n `inputs=${pageSummaryJson.inputs.length}`\n );\n } catch (e: any) {\n console.warn(\n `⚠️ Failed to scrape ${url} (${e?.message || e}). Continuing without page summary.`\n );\n }\n }\n\n try {\n console.log('🤖 AI: generating test cases...');\n markdown = await generateTcMarkdownWithAi({\n appDescription: appDescription || undefined,\n feature,\n url,\n pageSummaryJson,\n prefix,\n startIndex: 1,\n numCases,\n });\n console.log('✅ AI: generated test case markdown.');\n } catch (err: any) {\n console.warn(\n `⚠️ AI generation failed (${err?.message || err}). Falling back to manual templates.`\n );\n markdown = buildManualCasesMarkdown({\n prefix,\n feature,\n url,\n numCases,\n startIndex: 1,\n });\n console.log('📝 Manual: generated test case templates instead.');\n }\n } else {\n markdown = buildManualCasesMarkdown({\n prefix,\n feature,\n url,\n numCases,\n startIndex: 1,\n });\n console.log('📝 Manual: generated test case templates (no --ai).');\n }\n\n writeFileSync(fullPath, markdown);\n\n console.log(`✍️ Wrote ${numCases} test case(s) → ${fullPath}`);\n console.log('Next steps:');\n console.log(' ct normalize ./cases --and-gen --lang ts');\n console.log(' ct test');\n}\n\nexport function tcCmd() {\n const root = new Command('tc')\n .description('Create CT-style test cases (Markdown) under ./cases')\n // we still declare the option so Commander doesn't choke on --ai,\n // but we IGNORE the parsed value and instead use process.argv.\n .option('--ai', 'Use AI if configured (BYO LLM API key).', false)\n .option('--prefix <prefix>', 'Explicit ID prefix, e.g. AUTH, DASH, CART')\n .option('--feature <name>', 'Feature name (non-interactive)')\n .option('--desc <text>', 'App description (non-interactive)')\n .option('--count <n>', 'Number of cases (non-interactive)', parseInt)\n .action(async (opts: { prefix?: string; feature?: string; desc?: string; count?: number }) => {\n await runTcInteractive({\n url: undefined,\n explicitPrefix: opts.prefix,\n feature: opts.feature,\n appDescription: opts.desc,\n numCases: opts.count,\n });\n });\n\n root\n .command('url')\n .argument('<url>', 'Page URL to use as context')\n .option('--ai', 'Use AI if configured (BYO LLM API key).', false)\n .option('--prefix <prefix>', 'Explicit ID prefix, e.g. AUTH, DASH, CART')\n .option('--feature <name>', 'Feature name (non-interactive)')\n .option('--desc <text>', 'App description (non-interactive)')\n .option('--count <n>', 'Number of cases (non-interactive)', parseInt)\n .description('Create test cases with awareness of a specific page URL')\n .action(async (url: string, opts: { prefix?: string; feature?: string; desc?: string; count?: number }) => {\n await runTcInteractive({\n url,\n explicitPrefix: opts.prefix,\n feature: opts.feature,\n appDescription: opts.desc,\n numCases: opts.count,\n });\n });\n\n return root;\n}\n","// src/core/prefix.ts\n\nconst PREFIX_MAP: Array<{ keywords: string[]; prefix: string }> = [\n { keywords: ['login', 'sign in', 'signin', 'auth', 'authentication'], prefix: 'AUTH' },\n { keywords: ['dashboard', 'home'], prefix: 'DASH' },\n { keywords: ['profile', 'account'], prefix: 'PROF' },\n { keywords: ['cart', 'basket'], prefix: 'CART' },\n { keywords: ['checkout', 'payment', 'pay'], prefix: 'CHK' },\n { keywords: ['order', 'orders'], prefix: 'ORD' },\n { keywords: ['settings', 'preferences', 'config'], prefix: 'SET' },\n];\n\nfunction normalizeText(text: string): string {\n return text.toLowerCase().trim();\n}\n\nfunction deriveFromFreeText(text: string): string | undefined {\n const norm = normalizeText(text);\n for (const entry of PREFIX_MAP) {\n if (entry.keywords.some(k => norm.includes(k))) {\n return entry.prefix;\n }\n }\n // fallback: first word, first 4 alphanumerics\n const firstWord = norm.split(/\\s+/).find(w => /[a-z0-9]/.test(w));\n if (!firstWord) return undefined;\n return firstWord.replace(/[^a-z0-9]/gi, '').slice(0, 4).toUpperCase() || undefined;\n}\n\nfunction deriveFromUrl(url: string): string | undefined {\n try {\n const u = new URL(url);\n const segments = u.pathname.split('/').filter(Boolean);\n const last = segments[segments.length - 1] || '';\n if (!last) return undefined;\n return deriveFromFreeText(last);\n } catch {\n return undefined;\n }\n}\n\nexport function inferPrefix(params: {\n featureText?: string;\n url?: string;\n explicitPrefix?: string;\n}): string {\n // 1) explicit flag wins\n if (params.explicitPrefix) {\n return params.explicitPrefix.trim().toUpperCase();\n }\n\n // 2) feature text\n if (params.featureText) {\n const fromFeature = deriveFromFreeText(params.featureText);\n if (fromFeature) return fromFeature;\n }\n\n // 3) url\n if (params.url) {\n const fromUrl = deriveFromUrl(params.url);\n if (fromUrl) return fromUrl;\n }\n\n // 4) last-resort default\n return 'TC';\n}","// src/core/llm.ts\n\nexport type TcAiContext = {\n appDescription?: string;\n feature: string;\n url?: string;\n pageSummaryJson?: any; // optional, from scrape\n prefix: string;\n startIndex: number;\n numCases: number;\n};\n\nfunction buildSystemMessage(): string {\n return `\nYou are a senior QA engineer and test case designer.\n\nYour job:\n- Take the context of a web feature or page,\n- And generate high-quality UI test cases\n- In a very strict Markdown format that another tool will parse.\n\nYou MUST follow this format exactly for each test case:\n\n# <ID> — <Short title> @<tag1> @<tag2> ...\n## Steps\n1. <step>\n2. <step>\n\n## Expected Results\n- <assertion>\n- <assertion>\n\nRules:\n- <ID> must be PREFIX-XXX where PREFIX is provided to you (e.g., AUTH, DASH, CART).\n- XXX must be a 3-digit number starting from the startIndex provided in the context.\n For example: DASH-005, DASH-006, DASH-007 if startIndex is 5.\n- Use 1–3 tags per test (e.g., @smoke, @regression, @auth, @ui, @critical).\n- \"Steps\" should describe user actions in sequence.\n- \"Expected Results\" should describe verifiable outcomes (URL change, element visible, message shown, etc.).\n- Do NOT add any explanation before or after the test cases.\n- Output ONLY the test cases, back-to-back, in Markdown.\n- No code blocks, no extra headings outside the pattern described.\n`.trim();\n}\n\nfunction buildUserMessage(ctx: TcAiContext): string {\n const lines: string[] = [];\n\n lines.push(`App / Product description (optional):`);\n lines.push(ctx.appDescription || 'N/A');\n lines.push('');\n\n lines.push(`Feature or page to test:`);\n lines.push(ctx.feature);\n lines.push('');\n\n if (ctx.url) {\n lines.push(`Page URL:`);\n lines.push(ctx.url);\n lines.push('');\n }\n\n if (ctx.pageSummaryJson) {\n lines.push(`Page structure summary (JSON):`);\n lines.push('```json');\n lines.push(JSON.stringify(ctx.pageSummaryJson, null, 2));\n lines.push('```');\n lines.push('');\n }\n\n lines.push(`Test ID prefix to use: ${ctx.prefix}`);\n lines.push(\n `Start numbering from: ${String(ctx.startIndex).padStart(3, '0')}`\n );\n lines.push(`Number of test cases to generate: ${ctx.numCases}`);\n lines.push('');\n lines.push(`Important formatting rules:`);\n lines.push(`- Use IDs like ${ctx.prefix}-NNN where NNN is 3-digit, sequential from the start index.`);\n lines.push(`- Each test case must follow this pattern exactly:`);\n lines.push(`# ${ctx.prefix}-NNN — <short title> @tag1 @tag2`);\n lines.push(`## Steps`);\n lines.push(`1. ...`);\n lines.push(`2. ...`);\n lines.push(``);\n lines.push(`## Expected Results`);\n lines.push(`- ...`);\n lines.push(`- ...`);\n lines.push('');\n lines.push(`Do NOT add any explanation before or after the test cases. Only output the test cases.`);\n\n return lines.join('\\n');\n}\n\n/**\n * Simple OpenAI-compatible chat call.\n * BYO key via:\n * - CT_LLM_API_KEY or OPENAI_API_KEY\n * BYO model via:\n * - CT_LLM_MODEL (default: gpt-4.1-mini)\n * BYO endpoint via:\n * - CT_LLM_BASE_URL (default: https://api.openai.com/v1)\n */\nexport async function generateTcMarkdownWithAi(ctx: TcAiContext): Promise<string> {\n const apiKey =\n process.env.CT_LLM_API_KEY ||\n process.env.OPENAI_API_KEY ||\n '';\n\n if (!apiKey) {\n throw new Error(\n 'No LLM API key found. Set CT_LLM_API_KEY or OPENAI_API_KEY.'\n );\n }\n\n const baseUrl =\n process.env.CT_LLM_BASE_URL || 'https://api.openai.com/v1';\n const model =\n process.env.CT_LLM_MODEL || 'gpt-4.1-mini';\n\n const system = buildSystemMessage();\n const user = buildUserMessage(ctx);\n\n const response = await fetch(`${baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: user },\n ],\n temperature: 0.2,\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `LLM request failed: ${response.status} ${response.statusText} — ${text}`\n );\n }\n\n const json: any = await response.json();\n const content =\n json.choices?.[0]?.message?.content?.trim() || '';\n\n if (!content) {\n throw new Error('LLM response had no content');\n }\n\n return content;\n}","// src/core/scrape.ts\n\nexport type PageSummary = {\n url: string;\n title?: string;\n headings: string[];\n buttons: string[];\n links: string[];\n inputs: string[];\n rawLength: number;\n};\n\n/**\n * Very lightweight HTML scraper using fetch + regex.\n * v0: good enough to give the LLM some real context (titles, headings, buttons, inputs).\n * Not a full browser; doesn’t execute JS.\n */\nexport async function scrapePageSummary(url: string): Promise<PageSummary> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);\n }\n\n const html = await res.text();\n const rawLength = html.length;\n\n const titleMatch = html.match(/<title[^>]*>([^<]*)<\\/title>/i);\n const title = titleMatch?.[1]?.trim() || undefined;\n\n const headings = Array.from(html.matchAll(/<(h1|h2)[^>]*>([^<]*)<\\/\\1>/gi))\n .map(m => m[2].replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n\n const buttons = Array.from(html.matchAll(/<button[^>]*>([^<]*)<\\/button>/gi))\n .map(m => m[1].replace(/\\s+/g, ' ').trim())\n .filter(Boolean);\n\n const links = Array.from(html.matchAll(/<a[^>]*>([^<]*)<\\/a>/gi))\n .map(m => m[1].replace(/\\s+/g, ' ').trim())\n .filter(Boolean)\n .slice(0, 50);\n\n const inputs: string[] = [];\n\n // labels like <label for=\"email\">Email</label>\n const labelMap = new Map<string, string>();\n for (const m of html.matchAll(/<label[^>]*for=[\"']?([^\"'>\\s]+)[\"']?[^>]*>([^<]*)<\\/label>/gi)) {\n const id = m[1];\n const text = m[2].replace(/\\s+/g, ' ').trim();\n if (id && text) labelMap.set(id, text);\n }\n\n for (const m of html.matchAll(/<input([^>]*)>/gi)) {\n const attrs = m[1];\n const nameMatch = attrs.match(/\\bname=[\"']?([^\"'>\\s]+)[\"']?/i);\n const idMatch = attrs.match(/\\bid=[\"']?([^\"'>\\s]+)[\"']?/i);\n const phMatch = attrs.match(/\\bplaceholder=[\"']([^\"']*)[\"']/i);\n\n const id = idMatch?.[1];\n const label = id ? labelMap.get(id) : undefined;\n const ph = phMatch?.[1]?.trim();\n const name = nameMatch?.[1];\n\n const descriptor = label || ph || name;\n if (descriptor) {\n inputs.push(descriptor);\n }\n }\n\n return {\n url,\n title,\n headings: headings.slice(0, 20),\n buttons: buttons.slice(0, 30),\n links,\n inputs: inputs.slice(0, 30),\n rawLength,\n };\n}","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\n\nexport function reportCmd() {\n const cmd = new Command('report')\n .description('Open the Playwright HTML report')\n .action(() => {\n console.log('📊 Opening Playwright HTML report...');\n \n const child = spawn(\n 'npx',\n ['playwright', 'show-report'],\n {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport function serveCmd() {\n const cmd = new Command('serve')\n .description('Serve the Allure report')\n .action(() => {\n console.log('📊 Serving Allure report...');\n\n // Try to find the local allure binary first (more reliable than global/npx sometimes)\n const localAllureBin = join(process.cwd(), 'node_modules', 'allure-commandline', 'bin', 'allure');\n \n let executable = 'npx';\n let args = ['allure', 'serve', './allure-results'];\n\n // If we can find the direct binary, use it (node node_modules/.../allure)\n // This bypasses the \"require('../')\" issue in the .bin wrapper\n if (existsSync(localAllureBin)) {\n executable = 'node';\n args = [localAllureBin, 'serve', './allure-results'];\n }\n\n console.log(`> ${executable} ${args.join(' ')}`);\n\n const child = spawn(\n executable,\n args,\n {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nfunction runStep(cmd: string, args: string[], stepName: string): Promise<void> {\n return new Promise((resolve, reject) => {\n console.log(`\\n🌊 Flow Step: ${stepName}`);\n console.log(`> ${cmd} ${args.join(' ')}`);\n\n const child = spawn(cmd, args, {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n\n child.on('exit', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`${stepName} failed with exit code ${code}`));\n }\n });\n });\n}\n\nexport function flowCmd() {\n const cmd = new Command('flow')\n .description('End-to-end flow: Normalize -> Generate -> Run Tests')\n .argument('[casesDir]', 'Directory containing test cases', './cases')\n .option('--lang <lang>', 'Target language (ts|js)', 'ts')\n .option('--no-run', 'Skip running tests')\n .action(async (casesDir, opts) => {\n const cliBin = resolve(process.argv[1]); // The current CLI executable\n\n try {\n // 1. Normalize\n await runStep(process.execPath, [cliBin, 'normalize', casesDir], 'Normalize Cases');\n\n // 2. Generate\n await runStep(process.execPath, [cliBin, 'gen', '--lang', opts.lang], 'Generate Tests');\n\n // 3. Test (unless skipped)\n if (opts.run) {\n await runStep(process.execPath, [cliBin, 'test'], 'Run Playwright Tests');\n } else {\n console.log('\\n⏭️ Skipping test execution (--no-run)');\n }\n\n console.log('\\n✅ Flow completed successfully!');\n } catch (err: any) {\n console.error(`\\n❌ Flow failed: ${err.message}`);\n process.exit(1);\n }\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst WORKFLOW_CONTENT = `name: Playwright Tests\non:\n push:\n branches: [ main, master ]\n pull_request:\n branches: [ main, master ]\njobs:\n test:\n timeout-minutes: 60\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: lts/*\n - name: Install dependencies\n run: npm ci\n - name: Install Playwright Browsers\n run: npx playwright install --with-deps\n - name: Run Playwright tests\n run: npx playwright test\n - uses: actions/upload-artifact@v4\n if: always()\n with:\n name: playwright-report\n path: playwright-report/\n retention-days: 30\n`;\n\nexport function ciCmd() {\n const cmd = new Command('ci')\n .description('Generate GitHub Actions workflow for CI')\n .action(() => {\n const githubDir = join(process.cwd(), '.github');\n const workflowsDir = join(githubDir, 'workflows');\n const workflowFile = join(workflowsDir, 'cementic.yml');\n\n console.log('🤖 Setting up CI/CD workflow...');\n\n if (!existsSync(workflowsDir)) {\n mkdirSync(workflowsDir, { recursive: true });\n console.log(`Created directory: ${workflowsDir}`);\n }\n\n if (existsSync(workflowFile)) {\n console.warn(`⚠️ Workflow file already exists at ${workflowFile}. Skipping.`);\n return;\n }\n\n writeFileSync(workflowFile, WORKFLOW_CONTENT.trim() + '\\n');\n console.log(`✅ CI workflow generated at: ${workflowFile}`);\n console.log('Next steps:');\n console.log('1. Commit and push the new file');\n console.log('2. Check the \"Actions\" tab in your GitHub repository');\n });\n\n return cmd;\n}\n"],"mappings":"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","names":["Command","Command","readFileSync","mkdirSync","writeFileSync","statSync","join","resolve","Command","Command","mkdirSync","writeFileSync","join","mkdirSync","join","writeFileSync","Command","Command","spawn","Command","spawn","existsSync","join","Command","spawn","resolve","Command","mkdirSync","writeFileSync","existsSync","join","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/new.ts","../src/commands/normalize.ts","../src/commands/test.ts","../src/commands/tc.ts","../src/core/prefix.ts","../src/core/llm.ts","../src/core/analyse.ts","../src/core/capture.ts","../src/core/report.ts","../src/commands/report.ts","../src/commands/serve.ts","../src/commands/flow.ts","../src/commands/ci.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { createRequire } from 'node:module';\nimport { newCmd } from './commands/new.js';\nimport { normalizeCmd } from './commands/normalize.js';\nimport { genCmd } from './commands/gen.js';\nimport { testCmd } from './commands/test.js';\nimport { tcCmd } from './commands/tc.js';\nimport { reportCmd } from './commands/report.js';\nimport { serveCmd } from './commands/serve.js';\nimport { flowCmd } from './commands/flow.js';\nimport { ciCmd } from './commands/ci.js';\n\nconst require = createRequire(import.meta.url);\nconst { version } = require('../package.json');\n\nconst program = new Command();\nprogram\n .name('ct')\n .description('CementicTest CLI: cases → normalized → POM tests → Playwright')\n .version(version);\n\nprogram.addCommand(newCmd());\nprogram.addCommand(normalizeCmd());\nprogram.addCommand(genCmd());\nprogram.addCommand(testCmd());\nprogram.addCommand(tcCmd());\nprogram.addCommand(reportCmd());\nprogram.addCommand(serveCmd());\nprogram.addCommand(flowCmd());\nprogram.addCommand(ciCmd());\n\nprogram.parseAsync(process.argv);","import { Command } from 'commander';\nimport { mkdirSync, writeFileSync, existsSync, readFileSync, cpSync, readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { dirname } from 'node:path';\nimport { platform, release } from 'node:os';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nfunction resolveTemplatePath(templateDir: string): string | undefined {\n const candidates = [\n resolve(__dirname, `templates/${templateDir}`),\n resolve(__dirname, `../templates/${templateDir}`),\n resolve(__dirname, `../../templates/${templateDir}`),\n resolve(process.cwd(), `templates/${templateDir}`),\n ];\n\n return candidates.find(candidate => existsSync(candidate));\n}\n\nexport function newCmd() {\n const cmd = new Command('new')\n .arguments('<projectName>')\n .description('Scaffold a new CementicTest + Playwright project from scratch')\n .addHelpText('after', `\nExamples:\n $ ct new my-awesome-test-suite\n $ ct new e2e-ts --lang ts\n $ ct new e2e-tests --no-browsers\n`)\n .option('--mode <mode>', 'greenfield|enhance', 'greenfield')\n .option('--lang <lang>', 'Scaffold language (js|ts)', 'js')\n .option('--no-browsers', 'do not run \"npx playwright install\" during setup')\n .action((projectName: string, opts) => {\n const root = process.cwd();\n const projectPath = join(root, projectName);\n\n console.log(`🚀 Initializing new CementicTest project in ${projectName}...`);\n\n // 1. Create project directory\n if (existsSync(projectPath)) {\n console.error(`❌ Directory ${projectName} already exists.`);\n process.exit(1);\n }\n mkdirSync(projectPath, { recursive: true });\n\n const lang = opts.lang === 'ts' ? 'ts' : 'js';\n const templateDir = lang === 'ts' ? 'student-framework-ts' : 'student-framework';\n const templatePath = resolveTemplatePath(templateDir);\n\n if (!templatePath) {\n console.error(`❌ Could not locate template \"${templateDir}\"`);\n console.error('Please ensure the package is built correctly with templates included.');\n process.exit(1);\n }\n\n console.log(`📦 Copying template from ${templatePath}...`);\n \n // Recursive copy function\n function copyRecursive(src: string, dest: string) {\n if (statSync(src).isDirectory()) {\n mkdirSync(dest, { recursive: true });\n readdirSync(src).forEach(child => {\n copyRecursive(join(src, child), join(dest, child));\n });\n } else {\n cpSync(src, dest);\n }\n }\n\n copyRecursive(templatePath, projectPath);\n\n // 2.5 Adjust for OS compatibility (macOS 13/Ventura or older)\n if (platform() === 'darwin') {\n const osRelease = release();\n const majorVersion = parseInt(osRelease.split('.')[0], 10);\n \n // macOS 13 is Darwin 22. macOS 14 is Darwin 23.\n if (majorVersion < 23) {\n console.log('🍎 Detected macOS 13 or older. Adjusting versions for compatibility...');\n const pkgJsonPath = join(projectPath, 'package.json');\n if (existsSync(pkgJsonPath)) {\n try {\n const pkgContent = readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(pkgContent);\n if (pkg.devDependencies) {\n pkg.devDependencies['@playwright/test'] = '^1.48.2';\n pkg.devDependencies['allure-playwright'] = '^2.15.1';\n writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2));\n console.log('✅ Downgraded @playwright/test and allure-playwright for legacy macOS support.');\n }\n } catch (err) {\n console.warn('⚠️ Failed to adjust package.json for OS compatibility:', err);\n }\n }\n }\n }\n\n // 3. Initialize git\n try {\n execSync('git init', { cwd: projectPath, stdio: 'ignore' });\n // Create .gitignore if not exists (template should have it, but just in case)\n const gitignorePath = join(projectPath, '.gitignore');\n if (!existsSync(gitignorePath)) {\n writeFileSync(gitignorePath, 'node_modules\\n.env\\ntest-results\\nplaywright-report\\n.cementic\\n');\n }\n } catch (e) {\n console.warn('⚠️ Failed to initialize git repository.');\n }\n\n // 4. Install dependencies\n console.log('📦 Installing dependencies...');\n try {\n execSync('npm install', { cwd: projectPath, stdio: 'inherit' });\n } catch (e) {\n console.error('❌ Failed to install dependencies. Please run \"npm install\" manually.');\n }\n\n // 5. Install browsers (optional)\n // Commander maps --no-browsers to opts.browsers = false\n if (opts.browsers !== false) {\n console.log('🌐 Installing Playwright browsers...');\n try {\n execSync('npx playwright install', { cwd: projectPath, stdio: 'inherit' });\n } catch (e) {\n console.warn('⚠️ Failed to install browsers. Run \"npx playwright install\" manually.');\n }\n }\n\n console.log(`\\n✅ Project ${projectName} created successfully!`);\n console.log(`\\nTo get started:\\n`);\n console.log(` cd ${projectName}`);\n console.log(` npx playwright test`);\n console.log(`\\nHappy testing! 🧪`);\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport fg from 'fast-glob';\nimport { readFileSync, mkdirSync, writeFileSync, statSync } from 'node:fs';\nimport { join, basename, resolve } from 'node:path';\n\ntype NormalizedCase = {\n id?: string;\n title: string;\n tags?: string[];\n steps: string[];\n step_hints?: Array<{ selector?: string }>;\n expected: string[];\n assertion_hints?: Array<{ playwright?: string }>;\n needs_review: boolean;\n review_reasons: string[];\n source: string;\n url?: string;\n};\n\n/** Grab @tags from the title line */\nfunction parseTags(title: string): { clean: string; tags: string[] } {\n const tags = Array.from(title.matchAll(/@([\\w-]+)/g)).map((m) => m[1]);\n const clean = title.replace(/@[\\w-]+/g, '').trim();\n return { clean, tags };\n}\n\n/** Try to extract an ID prefix like AUTH-001 from the title */\nfunction parseId(title: string): string | undefined {\n const m = title.match(/\\b([A-Z]+-\\d+)\\b/);\n return m?.[1];\n}\n\nfunction stripIdPrefixFromTitle(title: string, id?: string): string {\n if (!id) return title.trim();\n return title.replace(new RegExp(`^${id}\\\\s*[—\\\\-–:]\\\\s*`), '').trim();\n}\n\n/** Split a markdown file into case blocks by top-level \"# \" headings */\nfunction splitCasesByHeading(fileText: string): Array<{ titleLine: string; body: string }> {\n const lines = fileText.split(/\\r?\\n/);\n const blocks: Array<{ titleLine: string; body: string }> = [];\n let currentTitle: string | null = null;\n let buf: string[] = [];\n\n const flush = () => {\n if (currentTitle !== null) {\n blocks.push({ titleLine: currentTitle, body: buf.join('\\n') });\n }\n buf = [];\n };\n\n for (const line of lines) {\n const h1 = line.match(/^\\s*#\\s+(.+)$/); // \"# Title\"\n if (h1) {\n if (currentTitle !== null) flush();\n currentTitle = h1[1].trim();\n } else {\n buf.push(line);\n }\n }\n if (currentTitle !== null) flush();\n\n // If no H1 at all, treat whole file as one case (first non-empty line is title).\n if (blocks.length === 0) {\n const first = lines.find((l) => l.trim());\n const title = first?.replace(/^#\\s*/, '').trim() || 'Untitled';\n return [{ titleLine: title, body: lines.join('\\n') }];\n }\n return blocks;\n}\n\n/** From a case body, extract Steps and Expected sections */\nfunction extractSections(body: string): {\n steps: string[];\n stepHints: Array<{ selector?: string }>;\n expected: string[];\n assertionHints: Array<{ playwright?: string }>;\n} {\n // Find sections by H2 headings\n // Supports: \"## Steps\", \"## Expected\", \"## Expected Results\"\n const sectionRegex = /^\\s*##\\s*(.+?)\\s*$/gim;\n const sections: Record<string, string> = {};\n let match: RegExpExecArray | null;\n const indices: Array<{ name: string; index: number }> = [];\n\n while ((match = sectionRegex.exec(body))) {\n indices.push({ name: match[1].toLowerCase(), index: match.index });\n }\n\n // Add end sentinel\n indices.push({ name: '__END__', index: body.length });\n\n for (let i = 0; i < indices.length - 1; i++) {\n const name = indices[i].name;\n const slice = body.slice(indices[i].index, indices[i + 1].index);\n sections[name] = slice;\n }\n\n const stepsBlock =\n sections['steps'] ??\n ''; // if absent, we’ll infer from bullets later\n\n const expectedBlock =\n sections['expected'] ??\n sections['expected results'] ??\n sections['then'] ??\n '';\n\n const bullet = /^\\s*(?:\\d+\\.|[-*])\\s+(.+)$/gm;\n\n const parsedSteps = Array.from(stepsBlock.matchAll(bullet)).map((m) => parseHintedLine(m[1].trim(), 'selector'));\n const steps = parsedSteps.map((entry) => entry.text);\n const stepHints = parsedSteps.map((entry) => ({ selector: entry.hint }));\n\n // Fallback: if no dedicated steps section, collect bullets until a new H2\n if (steps.length === 0) {\n const alt = Array.from(body.matchAll(bullet)).map((m) => parseHintedLine(m[1].trim(), 'selector'));\n // Heuristic: take the first bullet run in the body\n steps.push(...alt.map((entry) => entry.text));\n stepHints.push(...alt.map((entry) => ({ selector: entry.hint })));\n }\n\n const parsedExpected = Array.from(expectedBlock.matchAll(bullet)).map((m) => parseHintedLine(m[1].trim(), 'playwright'));\n const expectedLines = parsedExpected.map((entry) => entry.text);\n const assertionHints = parsedExpected.map((entry) => ({ playwright: entry.hint }));\n\n // Fallback: lines that start with Expected/Then/Verify/Assert in the whole body\n if (expectedLines.length === 0) {\n const exp = Array.from(\n body.matchAll(/^\\s*(?:Expected|Then|Verify|Assert)[^\\n]*:?[\\s-]*(.+)$/gim)\n ).map((m) => parseHintedLine(m[1].trim(), 'playwright'));\n expectedLines.push(...exp.map((entry) => entry.text));\n assertionHints.push(...exp.map((entry) => ({ playwright: entry.hint })));\n }\n\n return { steps, stepHints, expected: expectedLines, assertionHints };\n}\n\nfunction extractUrlMetadata(body: string): string | undefined {\n const match = body.match(/<!--\\s*ct:url\\s+(https?:\\/\\/[^\\s>]+)\\s*-->/i);\n return match?.[1];\n}\n\nfunction stripCtMetadata(body: string): string {\n return body.replace(/^\\s*<!--\\s*ct:url\\s+https?:\\/\\/[^\\s>]+\\s*-->\\s*\\n?/im, '');\n}\n\nfunction parseHintedLine(\n value: string,\n hintType: 'selector' | 'playwright',\n): { text: string; hint?: string } {\n const match = value.match(new RegExp(`^(.*?)\\\\s*<!--\\\\s*${hintType}:\\\\s*([\\\\s\\\\S]*?)\\\\s*-->\\\\s*$`, 'i'));\n if (!match) return { text: value.trim() };\n return {\n text: match[1].trim(),\n hint: match[2].trim() || undefined,\n };\n}\n\nfunction extractUrlFromSteps(steps: string[]): string | undefined {\n for (const step of steps) {\n const match = step.match(/https?:\\/\\/[^\\s'\"]+/i);\n if (match) return match[0];\n }\n return undefined;\n}\n\n/** Build one normalized case from a title line + body */\nfunction normalizeOne(titleLine: string, body: string, source: string): NormalizedCase {\n const { clean, tags } = parseTags(titleLine);\n const id = parseId(clean);\n const metadataUrl = extractUrlMetadata(body);\n const cleanBody = stripCtMetadata(body);\n const { steps, stepHints, expected, assertionHints } = extractSections(cleanBody);\n const reviewReasons: string[] = [];\n\n if (steps.length === 0) reviewReasons.push('No steps section or bullets were parsed');\n if (expected.length === 0) reviewReasons.push('No expected results section or assertions were parsed');\n\n return {\n id,\n title: stripIdPrefixFromTitle(clean, id),\n tags: tags.length ? tags : undefined,\n steps,\n step_hints: stepHints.some(hint => hint.selector) ? stepHints : undefined,\n expected,\n assertion_hints: assertionHints.some(hint => hint.playwright) ? assertionHints : undefined,\n needs_review: reviewReasons.length > 0,\n review_reasons: reviewReasons,\n source,\n url: metadataUrl ?? extractUrlFromSteps(steps),\n };\n}\n\n/** ===== Commander command ===== */\nexport function normalizeCmd() {\n const cmd = new Command('normalize')\n .argument('<path>', 'Input directory or file pattern containing test cases (Markdown, Text, CSV)')\n .description('Convert human-readable test cases into machine-readable JSON format')\n .addHelpText('after', `\nExamples:\n $ ct normalize ./cases\n $ ct normalize \"cases/**/*.md\"\n $ ct normalize ./cases --and-gen --lang ts (Normalize and generate tests in one go)\n`)\n .option('--report', 'Generate a summary report of the normalization process', true)\n .option('--and-gen', 'Automatically run test generation after normalization', false)\n .option('--lang <lang>', 'Target language for generation (ts|js) when using --and-gen', 'ts')\n .action(async (inputPath: string, opts: { report?: boolean; andGen?: boolean; lang?: string }) => {\n // Accept directory OR glob\n let patterns: string[] = [];\n try {\n const abs = resolve(inputPath);\n if (statSync(abs).isDirectory()) {\n const base = inputPath.replace(/\\/$/, '');\n patterns = [`${base}/**/*.{md,markdown,txt,feature,csv,json}`];\n } else {\n patterns = [inputPath];\n }\n } catch {\n patterns = [inputPath];\n }\n\n const files = await fg(patterns, { dot: false, onlyFiles: true });\n if (files.length === 0) {\n console.error(`No files found for: ${inputPath}`);\n process.exit(2);\n }\n\n const outDir = '.cementic/normalized';\n mkdirSync(outDir, { recursive: true });\n\n const index = {\n summary: { total: 0, parsed: 0, withWarnings: 0 },\n cases: [] as Array<{ file: string; normalized: string; status: string }>\n };\n\n for (const f of files) {\n const content = readFileSync(f, 'utf8');\n\n // Split into multiple cases by \"# \"\n const blocks = splitCasesByHeading(content);\n for (const block of blocks) {\n const norm = normalizeOne(block.titleLine, block.body, f);\n\n // filename: <stem>.<ID or sanitized-title>.json\n const stem = basename(f).replace(/\\.[^/.]+$/, '');\n const suffix = (norm.id || norm.title).replace(/[^\\w-]+/g, '-');\n const outFile = join(outDir, `${stem}.${suffix}.json`);\n\n writeFileSync(outFile, JSON.stringify(norm, null, 2));\n\n index.summary.total++;\n index.summary.parsed++;\n if (norm.needs_review) index.summary.withWarnings++;\n index.cases.push({ file: f, normalized: outFile, status: norm.needs_review ? 'warning' : 'ok' });\n }\n }\n\n writeFileSync(join(outDir, '_index.json'), JSON.stringify(index, null, 2));\n\n if (opts.report !== false) {\n const lines = [\n '# Normalize Report',\n '',\n `Total cases: ${index.summary.total} | Parsed: ${index.summary.parsed} | With warnings: ${index.summary.withWarnings}`,\n '',\n '| Source File | Normalized JSON | Status |',\n '|-------------|-----------------|--------|',\n ...index.cases.map(c => `| ${c.file} | ${c.normalized} | ${c.status} |`)\n ];\n mkdirSync('.cementic/reports', { recursive: true });\n writeFileSync('.cementic/reports/normalize-report.md', lines.join('\\n'));\n }\n\n console.log(`✅ Normalized ${index.summary.parsed} case(s). Output → .cementic/normalized/`);\n\n if (opts.andGen) {\n const { gen } = await import('./gen.js');\n await gen({ lang: opts.lang || 'ts', out: 'tests/generated' });\n }\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\n\nexport function testCmd() {\n const cmd = new Command('test')\n .description('Run Playwright tests via \"npx playwright test\"')\n .allowUnknownOption(true)\n .allowExcessArguments(true)\n .argument('[...pwArgs]', 'Arguments to pass through to Playwright')\n .action((pwArgs: string[] = []) => {\n const child = spawn(\n 'npx',\n ['playwright', 'test', ...pwArgs],\n {\n stdio: 'inherit',\n shell: process.platform === 'win32', // needed for Windows\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}","import { Command } from 'commander';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createInterface } from 'node:readline/promises';\nimport { stdin as input, stdout as output } from 'node:process';\nimport { inferPrefix } from '../core/prefix.js';\nimport { generateTcMarkdownWithAi } from '../core/llm.js';\nimport { analyseElements } from '../core/analyse.js';\nimport { captureElements, toPageSummary } from '../core/capture.js';\nimport {\n printCaptureReport,\n saveCaptureJson,\n saveCasesMarkdown,\n saveSpecPreview,\n} from '../core/report.js';\n\nfunction slugify(text: string): string {\n return (\n text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '') || 'tc'\n );\n}\n\nfunction injectUrlMetadata(markdown: string, url?: string): string {\n if (!url || /<!--\\s*ct:url\\s+/i.test(markdown)) return markdown;\n\n const lines = markdown.split('\\n');\n const output: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n output.push(lines[i]);\n if (/^\\s*#\\s+/.test(lines[i])) {\n output.push(`<!-- ct:url ${url} -->`);\n }\n }\n\n return output.join('\\n');\n}\n\nfunction buildManualCasesMarkdown(opts: {\n prefix: string;\n feature: string;\n url?: string;\n numCases: number;\n startIndex?: number;\n}): string {\n const { prefix, feature, url, numCases } = opts;\n const startIndex = opts.startIndex ?? 1;\n const lines: string[] = [];\n\n for (let i = 0; i < numCases; i++) {\n const idx = startIndex + i;\n const id = `${prefix}-${String(idx).padStart(3, '0')}`;\n\n lines.push(`# ${id} — ${feature} - scenario ${idx} @regression @ui`);\n if (url) lines.push(`<!-- ct:url ${url} -->`);\n lines.push(`## Steps`);\n lines.push(`1. Navigate to ${url ?? '<PAGE_URL>'}`);\n lines.push(`2. Perform the main user action for this scenario`);\n lines.push(`3. Observe the result`);\n lines.push('');\n lines.push(`## Expected Results`);\n lines.push(`- The page responds correctly for this scenario`);\n lines.push(`- UI reflects the expected change`);\n lines.push('');\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nasync function promptBasicQuestions(opts: {\n url?: string;\n feature?: string;\n appDescription?: string;\n numCases?: number;\n}) {\n // Non-interactive mode: all required options were passed as flags\n if (opts.feature) {\n const n = Math.min(Math.max(opts.numCases ?? 3, 1), 10);\n return {\n feature: opts.feature,\n appDescription: opts.appDescription ?? '',\n numCases: n,\n url: opts.url,\n };\n }\n\n // Interactive mode\n const rl = createInterface({ input, output });\n\n const feature = (await rl.question('🧩 Feature or page to test: ')).trim();\n const appDescription = (await rl.question('📝 Short app description (optional): ')).trim();\n const numCasesRaw = (await rl.question('🔢 How many test cases? (1–10) [3]: ')).trim();\n\n rl.close();\n\n let numCases = parseInt(numCasesRaw, 10);\n if (isNaN(numCases) || numCases < 1) numCases = 3;\n if (numCases > 10) numCases = 10;\n\n return { feature, appDescription, numCases, url: opts.url };\n}\n\nasync function runTcInteractive(params: {\n url?: string;\n explicitPrefix?: string;\n feature?: string;\n appDescription?: string;\n numCases?: number;\n useAi: boolean;\n headed?: boolean;\n captureOnly?: boolean;\n}) {\n const { feature, appDescription, numCases, url } = await promptBasicQuestions({\n url: params.url,\n feature: params.feature,\n appDescription: params.appDescription,\n numCases: params.numCases,\n });\n\n const prefix = inferPrefix({\n featureText: feature,\n url,\n explicitPrefix: params.explicitPrefix,\n });\n\n mkdirSync('cases', { recursive: true });\n const fileName = `${prefix.toLowerCase()}-${slugify(feature)}.md`;\n const fullPath = join('cases', fileName);\n\n let markdown: string;\n\n if (params.useAi) {\n let pageSummary = undefined;\n\n if (url) {\n try {\n console.log(`🔍 Capturing page: ${url}`);\n const elementMap = await captureElements(url, {\n headless: !(params.headed ?? false),\n verbose: true,\n });\n printCaptureReport(elementMap);\n\n const jsonPath = saveCaptureJson(elementMap);\n console.log(`📄 Saved capture JSON → ${jsonPath}`);\n\n if (params.captureOnly) {\n console.log('Capture-only mode requested. No test cases were generated.');\n return;\n }\n\n const analysis = await analyseElements(elementMap, { verbose: true });\n printCaptureReport(elementMap, analysis);\n\n const previewPath = saveSpecPreview(analysis);\n if (previewPath) {\n console.log(`🧪 Saved preview spec → ${previewPath}`);\n }\n\n const captureFileName = `${prefix.toLowerCase()}-${slugify(feature)}.md`;\n const generatedPath = saveCasesMarkdown(analysis, 'cases', captureFileName);\n console.log(`✅ Capture-based AI generated test cases → ${generatedPath}`);\n console.log('\\nNext steps:');\n console.log(` ct normalize ${generatedPath} --and-gen --lang ts`);\n console.log(' ct test');\n return;\n } catch (e: any) {\n console.warn(`⚠️ Capture flow failed (${e?.message ?? e}). Falling back to the legacy AI case writer.`);\n if (params.captureOnly) {\n return;\n }\n }\n }\n\n try {\n if (pageSummary === undefined && url) {\n const elementMap = await captureElements(url, {\n headless: true,\n verbose: false,\n });\n pageSummary = toPageSummary(elementMap);\n }\n\n markdown = await generateTcMarkdownWithAi({\n appDescription: appDescription || undefined,\n feature,\n url,\n pageSummary,\n prefix,\n startIndex: 1,\n numCases,\n });\n markdown = injectUrlMetadata(markdown, url);\n console.log('✅ AI generated test cases.');\n } catch (err: any) {\n console.warn(`⚠️ AI generation failed (${err?.message ?? err}). Falling back to manual templates.`);\n markdown = buildManualCasesMarkdown({ prefix, feature, url, numCases, startIndex: 1 });\n console.log('📝 Generated manual test case templates instead.');\n }\n } else {\n markdown = buildManualCasesMarkdown({ prefix, feature, url, numCases, startIndex: 1 });\n console.log('📝 Generated manual test case templates (pass --ai to use AI).');\n }\n\n writeFileSync(fullPath, markdown);\n\n console.log(`\\n✍️ Wrote ${numCases} case(s) → ${fullPath}`);\n console.log('\\nNext steps:');\n console.log(' ct normalize ./cases --and-gen --lang ts');\n console.log(' ct test');\n}\n\n// ─── Shared options factory ───────────────────────────────────────────────────\n\nfunction addSharedOptions(cmd: Command): Command {\n return cmd\n .option('--ai', 'Use AI to generate test cases (requires API key)')\n .option('--prefix <prefix>', 'Explicit ID prefix, e.g. AUTH, DASH, CART')\n .option('--feature <name>', 'Feature name — skips interactive prompt')\n .option('--desc <text>', 'App description — skips interactive prompt')\n .option('--count <n>', 'Number of cases (1–10)', parseInt);\n}\n\nfunction resolveCommanderOpts<T extends Record<string, unknown>>(opts: T | Command): T {\n if (typeof (opts as Command).optsWithGlobals === 'function') {\n return (opts as Command).optsWithGlobals<T>();\n }\n if (typeof (opts as Command).opts === 'function') {\n return (opts as Command).opts<T>();\n }\n return opts as T;\n}\n\n// ─── Command definition ───────────────────────────────────────────────────────\n\nexport function tcCmd() {\n const root = new Command('tc')\n .description('Create CementicTest case files (Markdown) under ./cases')\n .addHelpText('after', `\nExamples:\n $ ct tc --feature \"Login form\" --ai\n $ ct tc --feature \"Checkout\" --count 5 --prefix CHK\n $ ct tc url https://example.com/login --ai --feature \"Auth\"\n`);\n\n addSharedOptions(root).action(\n async (opts: { ai?: boolean; prefix?: string; feature?: string; desc?: string; count?: number } | Command) => {\n const resolvedOpts = resolveCommanderOpts<{ ai?: boolean; prefix?: string; feature?: string; desc?: string; count?: number }>(opts);\n await runTcInteractive({\n url: undefined,\n explicitPrefix: resolvedOpts.prefix,\n feature: resolvedOpts.feature,\n appDescription: resolvedOpts.desc,\n numCases: resolvedOpts.count,\n useAi: resolvedOpts.ai ?? false,\n });\n }\n );\n\n addSharedOptions(\n root\n .command('url')\n .argument('<url>', 'Page URL to use as live capture and AI context')\n .description('Generate test cases with capture-aware context from a specific live page')\n .option('--headed', 'Show the browser while capturing the page')\n .option('--capture-only', 'Run live page capture and save artifacts without generating cases')\n ).action(\n async (\n url: string,\n opts: { ai?: boolean; prefix?: string; feature?: string; desc?: string; count?: number; headed?: boolean; captureOnly?: boolean } | Command,\n command?: Command,\n ) => {\n const resolvedOpts = resolveCommanderOpts<{ ai?: boolean; prefix?: string; feature?: string; desc?: string; count?: number; headed?: boolean; captureOnly?: boolean }>(command ?? opts);\n await runTcInteractive({\n url,\n explicitPrefix: resolvedOpts.prefix,\n feature: resolvedOpts.feature,\n appDescription: resolvedOpts.desc,\n numCases: resolvedOpts.count,\n useAi: resolvedOpts.ai ?? false,\n headed: resolvedOpts.headed ?? false,\n captureOnly: resolvedOpts.captureOnly ?? false,\n });\n }\n );\n\n return root;\n}\n","// src/core/prefix.ts\n\nconst PREFIX_MAP: Array<{ keywords: string[]; prefix: string }> = [\n { keywords: ['login', 'sign in', 'signin', 'auth', 'authentication'], prefix: 'AUTH' },\n { keywords: ['dashboard', 'home'], prefix: 'DASH' },\n { keywords: ['profile', 'account'], prefix: 'PROF' },\n { keywords: ['cart', 'basket'], prefix: 'CART' },\n { keywords: ['checkout', 'payment', 'pay'], prefix: 'CHK' },\n { keywords: ['order', 'orders'], prefix: 'ORD' },\n { keywords: ['settings', 'preferences', 'config'], prefix: 'SET' },\n];\n\nfunction normalizeText(text: string): string {\n return text.toLowerCase().trim();\n}\n\nfunction deriveFromFreeText(text: string): string | undefined {\n const norm = normalizeText(text);\n for (const entry of PREFIX_MAP) {\n if (entry.keywords.some(k => norm.includes(k))) {\n return entry.prefix;\n }\n }\n // fallback: first word, first 4 alphanumerics\n const firstWord = norm.split(/\\s+/).find(w => /[a-z0-9]/.test(w));\n if (!firstWord) return undefined;\n return firstWord.replace(/[^a-z0-9]/gi, '').slice(0, 4).toUpperCase() || undefined;\n}\n\nfunction deriveFromUrl(url: string): string | undefined {\n try {\n const u = new URL(url);\n const segments = u.pathname.split('/').filter(Boolean);\n const last = segments[segments.length - 1] || '';\n if (!last) return undefined;\n return deriveFromFreeText(last);\n } catch {\n return undefined;\n }\n}\n\nexport function inferPrefix(params: {\n featureText?: string;\n url?: string;\n explicitPrefix?: string;\n}): string {\n // 1) explicit flag wins\n if (params.explicitPrefix) {\n return params.explicitPrefix.trim().toUpperCase();\n }\n\n // 2) feature text\n if (params.featureText) {\n const fromFeature = deriveFromFreeText(params.featureText);\n if (fromFeature) return fromFeature;\n }\n\n // 3) url\n if (params.url) {\n const fromUrl = deriveFromUrl(params.url);\n if (fromUrl) return fromUrl;\n }\n\n // 4) last-resort default\n return 'TC';\n}","import type { PageSummary } from './capture.js';\n\nexport type TcAiContext = {\n appDescription?: string;\n feature: string;\n url?: string;\n pageSummary?: PageSummary;\n prefix: string;\n startIndex: number;\n numCases: number;\n};\n\n// ─── Prompt builders ──────────────────────────────────────────────────────────\n\nfunction buildSystemMessage(): string {\n return `\nYou are a senior QA engineer and test automation specialist.\n\nYour job:\n- Receive context about a web feature or page\n- Generate high-quality, realistic UI test cases\n- Format them in strict Markdown that a downstream parser will process\n\nYou MUST follow this format exactly for each test case:\n\n# <ID> — <Short title> @<tag1> @<tag2>\n## Steps\n1. <user action>\n2. <user action>\n\n## Expected Results\n- <verifiable outcome>\n- <verifiable outcome>\n\nRules:\n- ID must be PREFIX-NNN where NNN is zero-padded 3 digits starting from startIndex\n- Use 1–3 tags: @smoke @regression @auth @ui @critical @happy-path @negative\n- Steps = concrete user actions (\"Click the 'Sign In' button\", \"Fill in email with 'user@example.com'\")\n- Expected Results = verifiable UI outcomes (\"Error message 'Invalid credentials' is visible\", \"Page redirects to /dashboard\")\n- Include both happy-path AND negative/edge-case tests\n- Do NOT explain or add preamble — output ONLY the test cases\n- No code blocks, no extra headings outside the pattern\n`.trim();\n}\n\nfunction buildUserMessage(ctx: TcAiContext): string {\n const lines: string[] = [];\n\n lines.push(`App / product description:`);\n lines.push(ctx.appDescription || 'N/A');\n lines.push('');\n\n lines.push(`Feature or page to test:`);\n lines.push(ctx.feature);\n lines.push('');\n\n if (ctx.url) {\n lines.push(`Page URL: ${ctx.url}`);\n lines.push('');\n }\n\n if (ctx.pageSummary) {\n const s = ctx.pageSummary;\n lines.push(`Live page analysis:`);\n\n if (s.title) lines.push(`- Page title: ${s.title}`);\n\n if (s.headings.length) {\n lines.push(`- Headings: ${s.headings.join(' | ')}`);\n }\n\n if (s.inputs.length) {\n const inputDescs = s.inputs.map(i => {\n const parts = [\n i.label && `label=\"${i.label}\"`,\n i.placeholder && `placeholder=\"${i.placeholder}\"`,\n i.name && `name=\"${i.name}\"`,\n i.type && i.type !== 'text' && `type=\"${i.type}\"`,\n i.testId && `data-testid=\"${i.testId}\"`,\n ].filter(Boolean);\n return parts.join(', ');\n });\n lines.push(`- Form inputs: ${inputDescs.join(' | ')}`);\n }\n\n if (s.buttons.length) {\n lines.push(`- Buttons: ${s.buttons.join(' | ')}`);\n }\n\n if (s.links.length) {\n lines.push(`- Links: ${s.links.slice(0, 20).join(' | ')}`);\n }\n\n lines.push('');\n }\n\n lines.push(`Test ID prefix: ${ctx.prefix}`);\n lines.push(`Start numbering from: ${String(ctx.startIndex).padStart(3, '0')}`);\n lines.push(`Number of test cases: ${ctx.numCases}`);\n lines.push('');\n lines.push(`Include a mix of: happy-path flows, validation/error cases, and edge cases.`);\n lines.push(`Output ONLY the test cases. No explanation, no preamble.`);\n\n return lines.join('\\n');\n}\n\n// ─── Provider detection ───────────────────────────────────────────────────────\n\ntype Provider = 'anthropic' | 'openai';\n\nfunction detectProvider(): {\n provider: Provider;\n apiKey: string;\n model: string;\n baseUrl: string;\n} {\n // Anthropic — first-class support\n const anthropicKey =\n process.env.ANTHROPIC_API_KEY ??\n process.env.CT_ANTHROPIC_API_KEY ??\n '';\n\n // OpenAI-compatible fallback\n const openaiKey =\n process.env.CT_LLM_API_KEY ??\n process.env.OPENAI_API_KEY ??\n '';\n\n // Explicit provider override\n const explicitProvider = (process.env.CT_LLM_PROVIDER ?? '').toLowerCase();\n\n if (explicitProvider === 'anthropic' || (anthropicKey && explicitProvider !== 'openai')) {\n if (!anthropicKey) {\n throw new Error(\n 'CT_LLM_PROVIDER=anthropic but no ANTHROPIC_API_KEY found.\\n' +\n 'Set ANTHROPIC_API_KEY=your-key or switch to OPENAI_API_KEY.'\n );\n }\n return {\n provider: 'anthropic',\n apiKey: anthropicKey,\n model: process.env.CT_LLM_MODEL ?? 'claude-sonnet-4-5',\n baseUrl: 'https://api.anthropic.com',\n };\n }\n\n if (openaiKey) {\n return {\n provider: 'openai',\n apiKey: openaiKey,\n model: process.env.CT_LLM_MODEL ?? 'gpt-4o-mini',\n baseUrl: process.env.CT_LLM_BASE_URL ?? 'https://api.openai.com/v1',\n };\n }\n\n throw new Error(\n 'No LLM API key found.\\n' +\n ' For Claude: export ANTHROPIC_API_KEY=your-key\\n' +\n ' For OpenAI: export OPENAI_API_KEY=your-key\\n' +\n ' Any OpenAI-compatible endpoint: set CT_LLM_API_KEY + CT_LLM_BASE_URL'\n );\n}\n\n// ─── Anthropic call ───────────────────────────────────────────────────────────\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n system: string,\n user: string\n): Promise<string> {\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n model,\n max_tokens: 4096,\n system,\n messages: [{ role: 'user', content: user }],\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Anthropic API error ${response.status}: ${text}`);\n }\n\n const json: any = await response.json();\n const content = json.content?.[0]?.text?.trim() ?? '';\n if (!content) throw new Error('Anthropic response had no content');\n return content;\n}\n\n// ─── OpenAI-compatible call ───────────────────────────────────────────────────\n\nasync function callOpenAi(\n apiKey: string,\n model: string,\n baseUrl: string,\n system: string,\n user: string\n): Promise<string> {\n const response = await fetch(`${baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n temperature: 0.2,\n messages: [\n { role: 'system', content: system },\n { role: 'user', content: user },\n ],\n }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`OpenAI API error ${response.status}: ${text}`);\n }\n\n const json: any = await response.json();\n const content = json.choices?.[0]?.message?.content?.trim() ?? '';\n if (!content) throw new Error('OpenAI response had no content');\n return content;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport async function generateTcMarkdownWithAi(ctx: TcAiContext): Promise<string> {\n const { provider, apiKey, model, baseUrl } = detectProvider();\n\n const system = buildSystemMessage();\n const user = buildUserMessage(ctx);\n\n console.log(`🤖 Using ${provider === 'anthropic' ? 'Claude' : 'OpenAI-compatible'} (${model})`);\n\n if (provider === 'anthropic') {\n return callAnthropic(apiKey, model, system, user);\n }\n return callOpenAi(apiKey, model, baseUrl, system, user);\n}\n","import type { ElementMap } from './capture.js';\n\ntype ProviderName = 'deepseek' | 'anthropic' | 'gemini' | 'qwen' | 'kimi' | 'openai';\n\ntype ProviderConfig = {\n provider: ProviderName;\n displayName: string;\n apiKey: string;\n model: string;\n baseUrl?: string;\n};\n\nexport type AnalysisStep = {\n action: 'navigate' | 'fill' | 'click' | 'select' | 'check' | 'keyboard' | 'hover';\n selector: string;\n value: string;\n human: string;\n};\n\nexport type AnalysisAssertion = {\n type: string;\n selector: string;\n expected: string;\n human: string;\n playwright: string;\n};\n\nexport type AnalysisScenario = {\n id: string;\n title: string;\n tags: string[];\n steps: AnalysisStep[];\n assertions: AnalysisAssertion[];\n narrator: string;\n codeLevel: 'beginner' | 'intermediate' | 'advanced';\n};\n\nexport type AnalysisResult = {\n url: string;\n feature: string;\n suggestedPrefix: string;\n scenarios: AnalysisScenario[];\n analysisNotes?: string;\n audioSummary?: string;\n};\n\nexport async function analyseElements(\n elementMap: ElementMap,\n options: { verbose?: boolean } = {},\n): Promise<AnalysisResult> {\n const { verbose = false } = options;\n const { provider, displayName, apiKey, model, baseUrl } = detectProvider();\n\n log(verbose, `\\n[analyse] Sending capture to ${displayName} (${model})`);\n\n const systemPrompt = buildSystemPrompt();\n const userPrompt = buildUserPrompt(elementMap);\n\n const rawResponse = provider === 'anthropic'\n ? await callAnthropic(apiKey, model, systemPrompt, userPrompt)\n : await callOpenAi(apiKey, model, baseUrl ?? 'https://api.openai.com/v1', systemPrompt, userPrompt);\n\n const parsed = parseAnalysisJson(rawResponse);\n return sanitizeAnalysis(parsed, elementMap);\n}\n\nfunction detectProvider(): ProviderConfig {\n const explicitProvider = (process.env.CT_LLM_PROVIDER ?? '').toLowerCase();\n\n const providers: Record<ProviderName, ProviderConfig> = {\n deepseek: {\n provider: 'deepseek',\n displayName: 'DeepSeek',\n apiKey: process.env.DEEPSEEK_API_KEY ?? process.env.CT_DEEPSEEK_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.DEEPSEEK_MODEL ?? 'deepseek-chat',\n baseUrl: process.env.DEEPSEEK_BASE_URL ?? 'https://api.deepseek.com/v1',\n },\n anthropic: {\n provider: 'anthropic',\n displayName: 'Claude',\n apiKey: process.env.ANTHROPIC_API_KEY ?? process.env.CT_ANTHROPIC_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.ANTHROPIC_MODEL ?? 'claude-sonnet-4-5',\n },\n gemini: {\n provider: 'gemini',\n displayName: 'Gemini',\n apiKey: process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.GEMINI_MODEL ?? 'gemini-2.5-flash',\n baseUrl: process.env.GEMINI_BASE_URL ?? 'https://generativelanguage.googleapis.com/v1beta/openai',\n },\n qwen: {\n provider: 'qwen',\n displayName: 'Qwen',\n apiKey: process.env.QWEN_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.QWEN_MODEL ?? 'qwen-plus',\n baseUrl: process.env.QWEN_BASE_URL ?? 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',\n },\n kimi: {\n provider: 'kimi',\n displayName: 'Kimi',\n apiKey: process.env.KIMI_API_KEY ?? process.env.MOONSHOT_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.KIMI_MODEL ?? 'moonshot-v1-8k',\n baseUrl: process.env.KIMI_BASE_URL ?? 'https://api.moonshot.ai/v1',\n },\n openai: {\n provider: 'openai',\n displayName: 'OpenAI-compatible',\n apiKey: process.env.OPENAI_API_KEY ?? process.env.CT_LLM_API_KEY ?? '',\n model: process.env.CT_LLM_MODEL ?? process.env.OPENAI_MODEL ?? 'gpt-4o-mini',\n baseUrl: process.env.CT_LLM_BASE_URL ?? 'https://api.openai.com/v1',\n },\n };\n\n if (explicitProvider) {\n if (!(explicitProvider in providers)) {\n throw new Error(`Unsupported CT_LLM_PROVIDER=\"${explicitProvider}\". Use deepseek, anthropic, gemini, qwen, kimi, or openai.`);\n }\n\n const selected = providers[explicitProvider as ProviderName];\n if (!selected.apiKey) {\n throw new Error(`CT_LLM_PROVIDER=${explicitProvider} is set but the matching API key is missing.`);\n }\n return selected;\n }\n\n for (const providerName of ['deepseek', 'anthropic', 'gemini', 'qwen', 'kimi', 'openai'] as const) {\n if (providers[providerName].apiKey) return providers[providerName];\n }\n\n throw new Error(\n 'No LLM API key found.\\n' +\n ' DeepSeek: export DEEPSEEK_API_KEY=your-key\\n' +\n ' Anthropic: export ANTHROPIC_API_KEY=your-key\\n' +\n ' Gemini: export GEMINI_API_KEY=your-key\\n' +\n ' Qwen: export QWEN_API_KEY=your-key\\n' +\n ' Kimi: export KIMI_API_KEY=your-key\\n' +\n ' OpenAI: export OPENAI_API_KEY=your-key'\n );\n}\n\nfunction buildSystemPrompt(): string {\n return `\nYou are a senior QA automation engineer and Playwright expert working on CementicTest.\n\nYour job is to analyse a structured map of interactive elements extracted from a live web page,\nthen produce a complete set of Playwright test scenarios.\n\nRULES:\n1. Use only selectors provided in the ElementMap.\n2. Every assertion must include an exact \"playwright\" field with a complete await expect(...) statement.\n3. Use clearly fake data like user@example.com.\n4. Include both happy-path and negative scenarios, but stay evidence-backed.\n5. Output only valid JSON matching the requested schema.\n6. Do not invent redirect targets, success pages, error text, password clearing, or security scenarios unless the capture explicitly supports them.\n7. If no status or alert region was captured, avoid scenarios that depend on unseen server-side validation messages.\n8. Prefer 3 to 5 realistic scenarios.\n\nOUTPUT SCHEMA:\n{\n \"url\": string,\n \"feature\": string,\n \"suggestedPrefix\": string,\n \"scenarios\": [\n {\n \"id\": string,\n \"title\": string,\n \"tags\": string[],\n \"steps\": [\n {\n \"action\": \"navigate\"|\"fill\"|\"click\"|\"select\"|\"check\"|\"keyboard\"|\"hover\",\n \"selector\": string,\n \"value\": string,\n \"human\": string\n }\n ],\n \"assertions\": [\n {\n \"type\": string,\n \"selector\": string,\n \"expected\": string,\n \"human\": string,\n \"playwright\": string\n }\n ],\n \"narrator\": string,\n \"codeLevel\": \"beginner\"|\"intermediate\"|\"advanced\"\n }\n ],\n \"analysisNotes\": string,\n \"audioSummary\": string\n}`.trim();\n}\n\nfunction buildUserPrompt(elementMap: ElementMap): string {\n const lines: string[] = [];\n\n lines.push('PAGE INFORMATION');\n lines.push(`URL: ${elementMap.url}`);\n lines.push(`Title: ${elementMap.title}`);\n lines.push(`Captured in: ${elementMap.mode} mode`);\n lines.push('');\n\n for (const category of ['input', 'button', 'link', 'heading', 'status'] as const) {\n const items = elementMap.elements.filter((element) => element.category === category);\n if (items.length === 0) continue;\n\n lines.push(`${category.toUpperCase()}S (${items.length} found):`);\n for (const item of items.slice(0, category === 'link' ? 10 : items.length)) {\n lines.push(` - [${item.confidence}] ${item.selector}`);\n lines.push(` Purpose: ${item.purpose}`);\n if (item.selectorAlt.length > 0) {\n lines.push(` Fallbacks: ${item.selectorAlt.slice(0, 2).join(' | ')}`);\n }\n }\n lines.push('');\n }\n\n if (elementMap.warnings.length > 0) {\n lines.push('CAPTURE WARNINGS:');\n for (const warning of elementMap.warnings) {\n lines.push(` - ${warning}`);\n }\n lines.push('');\n }\n\n const interactiveCount = elementMap.elements.filter((element) => (\n element.category === 'input' || element.category === 'button' || element.category === 'link'\n )).length;\n const statusCount = elementMap.elements.filter((element) => element.category === 'status').length;\n\n lines.push('EVIDENCE CONSTRAINTS:');\n lines.push(` - Interactive elements captured: ${interactiveCount}`);\n lines.push(` - Status or alert regions captured: ${statusCount}`);\n lines.push(' - If no redirect target is explicitly captured, do not assert a destination path.');\n lines.push(' - If no status region was captured, avoid exact server-side credential error claims.');\n lines.push('');\n lines.push('Generate only the JSON response.');\n\n return lines.join('\\n');\n}\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n systemPrompt: string,\n userPrompt: string,\n): Promise<string> {\n const response = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n model,\n max_tokens: 4096,\n system: systemPrompt,\n messages: [{ role: 'user', content: userPrompt }],\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Anthropic API ${response.status}: ${await response.text()}`);\n }\n\n const json: any = await response.json();\n const content = json.content?.[0]?.text?.trim() ?? '';\n if (!content) throw new Error('Anthropic returned empty content');\n return content;\n}\n\nasync function callOpenAi(\n apiKey: string,\n model: string,\n baseUrl: string,\n systemPrompt: string,\n userPrompt: string,\n): Promise<string> {\n const response = await fetch(`${baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n temperature: 0.1,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n }),\n });\n\n if (!response.ok) {\n throw new Error(`OpenAI API ${response.status}: ${await response.text()}`);\n }\n\n const json: any = await response.json();\n const content = json.choices?.[0]?.message?.content?.trim() ?? '';\n if (!content) throw new Error('OpenAI-compatible provider returned empty content');\n return content;\n}\n\nfunction parseAnalysisJson(raw: string): AnalysisResult {\n const stripped = raw\n .replace(/^```(?:json)?\\s*/m, '')\n .replace(/\\s*```\\s*$/m, '')\n .trim();\n\n try {\n return JSON.parse(stripped);\n } catch (error: any) {\n throw new Error(\n `Failed to parse LLM response as JSON.\\nParse error: ${error?.message ?? error}\\nRaw response:\\n${raw.slice(0, 500)}`\n );\n }\n}\n\nfunction sanitizeAnalysis(analysis: AnalysisResult, elementMap: ElementMap): AnalysisResult {\n const selectors = new Set(elementMap.elements.map((element) => element.selector));\n const rawScenarios = Array.isArray(analysis.scenarios) ? analysis.scenarios : [];\n const currentUrl = new URL(elementMap.url);\n const knownPaths = new Set([\n currentUrl.pathname,\n ...elementMap.elements\n .filter((element) => element.category === 'link')\n .map((element) => {\n const href = element.attributes?.href;\n if (typeof href !== 'string') return '';\n try {\n return new URL(href, elementMap.url).pathname;\n } catch {\n return href;\n }\n })\n .filter(Boolean),\n ]);\n\n const sanitizedScenarios = rawScenarios\n .map((scenario) => normalizeScenario(scenario, selectors))\n .filter((scenario) => scenario.steps.length > 0 && scenario.assertions.length > 0)\n .map((scenario) => ({\n ...scenario,\n tags: scenario.tags.map(normalizeTag),\n assertions: scenario.assertions.filter((assertion) => {\n if (assertion.type !== 'url') return true;\n const combined = `${assertion.expected} ${assertion.human} ${assertion.playwright}`;\n const pathMatch = combined.match(/\\/[a-z0-9/_-]+/i);\n if (!pathMatch) return true;\n return knownPaths.has(pathMatch[0]);\n }),\n }))\n .filter((scenario) => scenario.assertions.length > 0)\n .slice(0, 5);\n\n const finalScenarios = sanitizedScenarios.length > 0\n ? sanitizedScenarios\n : buildFallbackScenarios(elementMap, analysis.suggestedPrefix || 'FLOW');\n\n return {\n ...analysis,\n url: analysis.url || elementMap.url,\n feature: analysis.feature || inferFeatureName(elementMap),\n suggestedPrefix: (analysis.suggestedPrefix || inferPrefix(elementMap)).toUpperCase(),\n scenarios: finalScenarios,\n analysisNotes: [\n analysis.analysisNotes,\n `Sanitized to ${finalScenarios.length} evidence-backed scenario(s) from ${rawScenarios.length} raw scenario(s).`,\n ].filter(Boolean).join(' '),\n audioSummary: analysis.audioSummary || buildAudioSummary(analysis.feature || inferFeatureName(elementMap), finalScenarios),\n };\n}\n\nfunction normalizeScenario(candidate: any, selectors: Set<string>): AnalysisScenario {\n return {\n id: candidate?.id ?? 'FLOW-001',\n title: candidate?.title ?? 'Captured page flow',\n tags: Array.isArray(candidate?.tags) ? candidate.tags : [],\n steps: Array.isArray(candidate?.steps)\n ? candidate.steps.filter((step: any) => step?.selector === 'page' || selectors.has(step?.selector))\n : [],\n assertions: Array.isArray(candidate?.assertions)\n ? candidate.assertions.filter((assertion: any) => assertion?.selector === 'page' || selectors.has(assertion?.selector))\n : [],\n narrator: candidate?.narrator ?? 'Let us run this captured test flow.',\n codeLevel: candidate?.codeLevel ?? 'beginner',\n };\n}\n\nfunction buildFallbackScenarios(elementMap: ElementMap, prefix: string): AnalysisScenario[] {\n const heading = elementMap.elements.find((element) => element.category === 'heading');\n const emailInput = elementMap.elements.find((element) => (\n element.category === 'input' &&\n (element.attributes.type === 'email' || /email/.test(`${element.name ?? ''} ${String(element.attributes.label ?? '')}`.toLowerCase()))\n ));\n const passwordInput = elementMap.elements.find((element) => (\n element.category === 'input' && element.attributes.type === 'password'\n ));\n const submitButton = elementMap.elements.find((element) => (\n element.category === 'button' && /login|sign in|submit|continue/i.test(element.name ?? '')\n )) ?? elementMap.elements.find((element) => element.category === 'button');\n\n const scenarios: AnalysisScenario[] = [];\n const tag = (value: string) => normalizeTag(value);\n const nextId = (index: number) => `${prefix}-${String(900 + index).padStart(3, '0')}`;\n\n if (heading) {\n scenarios.push({\n id: nextId(scenarios.length + 1),\n title: 'Page loads with expected heading',\n tags: [tag('smoke'), tag('page-load')],\n steps: [\n {\n action: 'navigate',\n selector: 'page',\n value: elementMap.url,\n human: 'Navigate to the captured page',\n },\n ],\n assertions: [\n {\n type: 'visible',\n selector: heading.selector,\n expected: 'visible',\n human: `${heading.name || 'Primary heading'} is visible`,\n playwright: `await expect(page.${heading.selector}).toBeVisible();`,\n },\n ],\n narrator: 'We will first confirm that the expected page heading is visible.',\n codeLevel: 'beginner',\n });\n }\n\n if (emailInput && passwordInput && submitButton) {\n scenarios.push({\n id: nextId(scenarios.length + 1),\n title: 'Submitting without a password keeps the user on the form',\n tags: [tag('validation'), tag('negative')],\n steps: [\n {\n action: 'navigate',\n selector: 'page',\n value: elementMap.url,\n human: 'Navigate to the captured page',\n },\n {\n action: 'fill',\n selector: emailInput.selector,\n value: 'user@example.com',\n human: 'Fill in the email field',\n },\n {\n action: 'click',\n selector: submitButton.selector,\n value: '',\n human: 'Click the submit button',\n },\n ],\n assertions: [\n {\n type: 'url',\n selector: 'page',\n expected: new URL(elementMap.url).pathname,\n human: 'User remains on the same page',\n playwright: `await expect(page).toHaveURL('${elementMap.url}');`,\n },\n {\n type: 'visible',\n selector: passwordInput.selector,\n expected: 'visible',\n human: 'Password field stays visible for correction',\n playwright: `await expect(page.${passwordInput.selector}).toBeVisible();`,\n },\n ],\n narrator: 'Next we will leave the password blank and confirm the form does not advance.',\n codeLevel: 'beginner',\n });\n }\n\n return scenarios.slice(0, 5);\n}\n\nfunction inferFeatureName(elementMap: ElementMap): string {\n const heading = elementMap.elements.find((element) => element.category === 'heading' && element.name);\n return heading?.name || elementMap.title || 'Captured page';\n}\n\nfunction inferPrefix(elementMap: ElementMap): string {\n const source = `${elementMap.title} ${elementMap.url}`.toLowerCase();\n if (/\\blogin|sign in|auth/.test(source)) return 'AUTH';\n if (/\\bcheckout|cart|payment/.test(source)) return 'CHK';\n if (/\\bdashboard/.test(source)) return 'DASH';\n return 'FLOW';\n}\n\nfunction buildAudioSummary(feature: string, scenarios: AnalysisScenario[]): string {\n return `We finished validating ${feature || 'the captured page'} with ${scenarios.length} evidence-backed scenario${scenarios.length === 1 ? '' : 's'}.`;\n}\n\nfunction normalizeTag(value: string): string {\n const cleaned = String(value ?? '').trim().replace(/^@+/, '');\n return cleaned ? `@${cleaned}` : '@ui';\n}\n\nfunction log(verbose: boolean, message: string): void {\n if (verbose) console.log(message);\n}\n","export type InputDescriptor = {\n label?: string;\n placeholder?: string;\n name?: string;\n type?: string;\n testId?: string;\n};\n\nexport type PageSummary = {\n url: string;\n title?: string;\n headings: string[];\n buttons: string[];\n links: string[];\n inputs: InputDescriptor[];\n landmarks: string[];\n rawLength: number;\n};\n\nexport type CaptureMode = 'headless' | 'headed';\nexport type CaptureConfidence = 'high' | 'medium' | 'low';\nexport type CaptureCategory = 'input' | 'button' | 'link' | 'heading' | 'status';\n\nexport type CapturedElement = {\n category: CaptureCategory;\n role: string;\n name?: string;\n selector: string;\n selectorAlt: string[];\n purpose: string;\n confidence: CaptureConfidence;\n attributes: Record<string, unknown>;\n};\n\nexport type ElementMap = {\n url: string;\n title: string;\n timestamp: string;\n mode: CaptureMode;\n summary: {\n totalElements: number;\n byCategory: Partial<Record<CaptureCategory, number>>;\n };\n elements: CapturedElement[];\n warnings: string[];\n};\n\ntype CaptureOptions = {\n headless?: boolean;\n timeoutMs?: number;\n verbose?: boolean;\n userAgent?: string;\n};\n\ntype DomButton = {\n tag: string;\n text?: string;\n testId?: string;\n id?: string;\n type?: string;\n disabled: boolean;\n selector?: string | null;\n cssPath?: string | null;\n};\n\ntype DomInput = {\n tag: string;\n type: string;\n label?: string;\n placeholder?: string;\n name?: string;\n id?: string;\n testId?: string;\n required: boolean;\n selector?: string | null;\n cssPath?: string | null;\n};\n\ntype DomLink = {\n text: string;\n href?: string;\n testId?: string;\n external: boolean;\n selector: string;\n};\n\ntype DomHeading = {\n level: string;\n text: string;\n selector: string;\n};\n\ntype DomStatusRegion = {\n role: string;\n ariaLive?: string;\n text?: string;\n selector: string;\n};\n\ntype DomForm = {\n id?: string;\n label?: string;\n action?: string;\n method: string;\n fieldCount: number;\n index: number;\n};\n\ntype DomData = {\n buttons: DomButton[];\n inputs: DomInput[];\n links: DomLink[];\n headings: DomHeading[];\n landmarks: Array<{ role: string; label: string }>;\n statusRegions: DomStatusRegion[];\n forms: DomForm[];\n pageUrl: string;\n pageTitle: string;\n};\n\nconst SETTLE_MS = 1200;\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_PER_CATEGORY = 50;\n\nexport async function captureElements(url: string, options: CaptureOptions = {}): Promise<ElementMap> {\n const {\n headless = true,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n verbose = false,\n userAgent = 'Mozilla/5.0 (compatible; CementicTest/0.2.5 capture)',\n } = options;\n\n const chromium = await loadChromium();\n const mode: CaptureMode = headless ? 'headless' : 'headed';\n log(verbose, `\\n[capture] Starting ${mode} capture: ${url}`);\n\n const browser = await chromium.launch({\n headless,\n slowMo: headless ? 0 : 150,\n });\n\n const context = await browser.newContext({\n userAgent,\n viewport: { width: 1280, height: 800 },\n ignoreHTTPSErrors: true,\n });\n\n const page = await context.newPage();\n\n try {\n log(verbose, ` -> Navigating (timeout: ${timeoutMs}ms)`);\n await page.goto(url, {\n waitUntil: 'domcontentloaded',\n timeout: timeoutMs,\n });\n\n log(verbose, ` -> Waiting ${SETTLE_MS}ms for page settle`);\n await page.waitForTimeout(SETTLE_MS);\n\n log(verbose, ' -> Extracting accessibility snapshot');\n const a11ySnapshot = await getAccessibilitySnapshot(page, verbose);\n\n log(verbose, ' -> Extracting DOM data');\n const domData = await page.evaluate(extractDomData);\n\n const title = await page.title();\n const finalUrl = page.url();\n const elements = buildElementMap(domData);\n const warnings = buildWarnings(elements, domData, a11ySnapshot);\n const byCategory: Partial<Record<CaptureCategory, number>> = {};\n\n for (const element of elements) {\n byCategory[element.category] = (byCategory[element.category] ?? 0) + 1;\n }\n\n const result: ElementMap = {\n url: finalUrl,\n title,\n timestamp: new Date().toISOString(),\n mode,\n summary: {\n totalElements: elements.length,\n byCategory,\n },\n elements,\n warnings,\n };\n\n log(verbose, ` -> Captured ${elements.length} testable elements from \"${title}\"`);\n return result;\n } finally {\n await browser.close();\n }\n}\n\nexport function toPageSummary(elementMap: ElementMap): PageSummary {\n const inputs = elementMap.elements\n .filter((element): element is CapturedElement => element.category === 'input')\n .slice(0, 30)\n .map((element) => ({\n label: asString(element.attributes.label),\n placeholder: asString(element.attributes.placeholder),\n name: asString(element.attributes.name),\n type: asString(element.attributes.type),\n testId: asString(element.attributes.testId),\n }));\n\n return {\n url: elementMap.url,\n title: elementMap.title,\n headings: elementMap.elements\n .filter((element) => element.category === 'heading')\n .map((element) => element.name ?? '')\n .filter(Boolean)\n .slice(0, 20),\n buttons: elementMap.elements\n .filter((element) => element.category === 'button')\n .map((element) => element.name ?? '')\n .filter(Boolean)\n .slice(0, 30),\n links: elementMap.elements\n .filter((element) => element.category === 'link')\n .map((element) => element.name ?? '')\n .filter(Boolean)\n .slice(0, 50),\n inputs,\n landmarks: [],\n rawLength: elementMap.elements.length,\n };\n}\n\nasync function loadChromium(): Promise<any> {\n try {\n return (await import('playwright-core')).chromium;\n } catch {}\n\n try {\n return (await import('@playwright/test')).chromium;\n } catch {}\n\n throw new Error(\n 'Playwright is required for live page capture. Install it in the target project with \"npm install -D @playwright/test\" and \"npx playwright install chromium\".'\n );\n}\n\nfunction extractDomData(): DomData {\n const localMaxPerCategory = 50;\n const attr = (el: Element, name: string): string | undefined => el.getAttribute(name)?.trim() || undefined;\n const text = (el: Element | null): string | undefined => el?.textContent?.replace(/\\s+/g, ' ').trim() || undefined;\n const jsString = (value: string) => JSON.stringify(value);\n\n const findLabel = (el: Element): string | undefined => {\n const id = attr(el, 'id');\n\n if (id) {\n const labelEl = document.querySelector(`label[for=\"${id}\"]`);\n if (labelEl) return text(labelEl);\n }\n\n const ariaLabel = attr(el, 'aria-label');\n if (ariaLabel) return ariaLabel;\n\n const labelledBy = attr(el, 'aria-labelledby');\n if (labelledBy) {\n const labelEl = document.getElementById(labelledBy);\n if (labelEl) return text(labelEl);\n }\n\n const closestLabel = el.closest('label');\n if (closestLabel) {\n const raw = text(closestLabel) || '';\n const placeholder = attr(el, 'placeholder') || '';\n return raw.replace(placeholder, '').trim() || undefined;\n }\n\n const previous = el.previousElementSibling;\n if (previous?.tagName === 'LABEL') return text(previous);\n\n return undefined;\n };\n\n const buildSelector = (el: Element, labelText?: string, buttonText?: string): string | null => {\n const testId = attr(el, 'data-testid');\n if (testId) return `getByTestId(${jsString(testId)})`;\n\n const id = attr(el, 'id');\n if (id && !id.match(/^(ember|react|vue|ng|auto|rand)/i)) {\n return `locator(${jsString(`#${id}`)})`;\n }\n\n const ariaLabel = attr(el, 'aria-label');\n if (ariaLabel) {\n return `getByRole(${jsString(el.getAttribute('role') || el.tagName.toLowerCase())}, { name: ${jsString(ariaLabel)} })`;\n }\n\n if (labelText) {\n const tag = el.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || tag === 'select') {\n return `getByLabel(${jsString(labelText)})`;\n }\n }\n\n if (buttonText) return `getByRole('button', { name: ${jsString(buttonText)} })`;\n\n const name = attr(el, 'name');\n if (name) return `locator(${jsString(`[name=\"${name}\"]`)})`;\n\n return null;\n };\n\n const buttons: DomButton[] = [];\n document.querySelectorAll('button, [role=\"button\"], input[type=\"submit\"], input[type=\"button\"]').forEach((el) => {\n const buttonText = attr(el, 'aria-label') || text(el) || attr(el, 'value');\n const testId = attr(el, 'data-testid');\n const id = attr(el, 'id');\n const type = attr(el, 'type');\n const disabled = el.hasAttribute('disabled') || el.getAttribute('aria-disabled') === 'true';\n const selector = buildSelector(el, undefined, buttonText);\n\n if (buttonText || testId) {\n buttons.push({\n tag: el.tagName.toLowerCase(),\n text: buttonText,\n testId,\n id,\n type,\n disabled,\n selector,\n cssPath: testId ? `[data-testid=\"${testId}\"]` : (id ? `#${id}` : null),\n });\n }\n });\n\n const inputs: DomInput[] = [];\n document.querySelectorAll('input:not([type=\"hidden\"]):not([type=\"submit\"]):not([type=\"button\"]), textarea, select').forEach((el) => {\n const label = findLabel(el);\n const placeholder = attr(el, 'placeholder');\n const name = attr(el, 'name');\n const id = attr(el, 'id');\n const type = attr(el, 'type') || el.tagName.toLowerCase();\n const testId = attr(el, 'data-testid');\n const required = el.hasAttribute('required');\n const selector = buildSelector(el, label);\n\n if (label || placeholder || name || testId || id) {\n inputs.push({\n tag: el.tagName.toLowerCase(),\n type,\n label,\n placeholder,\n name,\n id,\n testId,\n required,\n selector,\n cssPath: testId ? `[data-testid=\"${testId}\"]` : (id ? `#${id}` : (name ? `[name=\"${name}\"]` : null)),\n });\n }\n });\n\n const links: DomLink[] = [];\n document.querySelectorAll('a[href]').forEach((el) => {\n const linkText = attr(el, 'aria-label') || text(el);\n const href = attr(el, 'href');\n const testId = attr(el, 'data-testid');\n\n if (!linkText || href === '#') return;\n\n links.push({\n text: linkText,\n href,\n testId,\n external: Boolean(href?.startsWith('http') && !href.includes(window.location.hostname)),\n selector: testId\n ? `getByTestId(${jsString(testId)})`\n : `getByRole('link', { name: ${jsString(linkText)} })`,\n });\n });\n\n const headings: DomHeading[] = [];\n document.querySelectorAll('h1, h2, h3').forEach((el) => {\n const headingText = text(el);\n if (headingText) {\n headings.push({\n level: el.tagName.toLowerCase(),\n text: headingText,\n selector: `getByRole('heading', { name: ${jsString(headingText)} })`,\n });\n }\n });\n\n const landmarks: Array<{ role: string; label: string }> = [];\n document.querySelectorAll('[role], main, nav, header, footer, aside, section[aria-label]').forEach((el) => {\n const role = attr(el, 'role') || el.tagName.toLowerCase();\n const label = attr(el, 'aria-label') || text(el)?.slice(0, 40);\n if (role && label) landmarks.push({ role, label });\n });\n\n const statusRegions: DomStatusRegion[] = [];\n document.querySelectorAll('[role=\"alert\"], [role=\"status\"], [aria-live]').forEach((el) => {\n const role = attr(el, 'role') || 'live';\n statusRegions.push({\n role,\n ariaLive: attr(el, 'aria-live'),\n text: text(el),\n selector: el.getAttribute('role')\n ? `getByRole(${jsString(role)})`\n : `locator('[aria-live]')`,\n });\n });\n\n const forms: DomForm[] = [];\n document.querySelectorAll('form').forEach((form, index) => {\n forms.push({\n id: attr(form, 'id'),\n label: attr(form, 'aria-label') || attr(form, 'aria-labelledby'),\n action: attr(form, 'action'),\n method: attr(form, 'method') || 'get',\n fieldCount: form.querySelectorAll('input, textarea, select').length,\n index,\n });\n });\n\n return {\n buttons: buttons.slice(0, localMaxPerCategory),\n inputs: inputs.slice(0, localMaxPerCategory),\n links: links.slice(0, localMaxPerCategory),\n headings: headings.slice(0, 20),\n landmarks,\n statusRegions,\n forms,\n pageUrl: window.location.href,\n pageTitle: document.title,\n };\n}\n\nfunction buildElementMap(domData: DomData): CapturedElement[] {\n const elements: CapturedElement[] = [];\n\n for (const input of domData.inputs) {\n const displayName = input.label || input.placeholder || input.name || input.testId;\n if (!displayName) continue;\n\n const selector = input.selector\n || (input.testId ? `getByTestId(${JSON.stringify(input.testId)})` : null)\n || (input.label ? `getByLabel(${JSON.stringify(input.label)})` : null)\n || (input.id ? `locator(${JSON.stringify(`#${input.id}`)})` : null)\n || (input.name ? `locator(${JSON.stringify(`[name=\"${input.name}\"]`)})` : null)\n || `locator(${JSON.stringify(`${input.tag}[placeholder=\"${input.placeholder ?? ''}\"]`)})`;\n\n const selectorAlt: string[] = [];\n if (input.id && !selector.includes(`#${input.id}`)) selectorAlt.push(`locator(${JSON.stringify(`#${input.id}`)})`);\n if (input.name && !selector.includes(input.name)) selectorAlt.push(`locator(${JSON.stringify(`[name=\"${input.name}\"]`)})`);\n if (input.testId && !selector.includes(input.testId)) selectorAlt.push(`getByTestId(${JSON.stringify(input.testId)})`);\n if (input.placeholder && !selector.includes(input.placeholder)) selectorAlt.push(`getByPlaceholder(${JSON.stringify(input.placeholder)})`);\n if (input.label && !selector.includes(input.label)) selectorAlt.push(`getByLabel(${JSON.stringify(input.label)})`);\n\n elements.push({\n category: 'input',\n role: input.type === 'checkbox' ? 'checkbox' : 'textbox',\n name: displayName,\n selector,\n selectorAlt,\n purpose: input.required\n ? `Required ${input.type} field - \"${displayName}\"`\n : `${input.type} field - \"${displayName}\"`,\n confidence: input.testId || input.label || input.id ? 'high' : (input.placeholder ? 'medium' : 'low'),\n attributes: {\n type: input.type,\n label: input.label,\n placeholder: input.placeholder,\n name: input.name,\n id: input.id,\n testId: input.testId,\n required: input.required,\n tag: input.tag,\n },\n });\n }\n\n for (const button of domData.buttons) {\n const displayName = button.text || button.testId;\n if (!displayName) continue;\n\n const selector = button.selector\n || (button.testId ? `getByTestId(${JSON.stringify(button.testId)})` : null)\n || (button.text ? `getByRole('button', { name: ${JSON.stringify(button.text)} })` : null)\n || (button.id ? `locator(${JSON.stringify(`#${button.id}`)})` : null)\n || `locator('button')`;\n\n const selectorAlt: string[] = [];\n if (button.id && !selector.includes(button.id)) selectorAlt.push(`locator(${JSON.stringify(`#${button.id}`)})`);\n if (button.testId && !selector.includes(button.testId)) selectorAlt.push(`getByTestId(${JSON.stringify(button.testId)})`);\n if (button.text && !selector.includes(button.text)) selectorAlt.push(`getByText(${JSON.stringify(button.text)})`);\n if (button.cssPath) selectorAlt.push(`locator(${JSON.stringify(button.cssPath)})`);\n\n elements.push({\n category: 'button',\n role: 'button',\n name: displayName,\n selector,\n selectorAlt,\n purpose: button.disabled\n ? `Disabled button - \"${displayName}\"`\n : button.type === 'submit'\n ? `Form submit button - \"${displayName}\"`\n : `Button - \"${displayName}\"`,\n confidence: button.testId || button.text ? 'high' : (button.id ? 'medium' : 'low'),\n attributes: {\n text: button.text,\n testId: button.testId,\n id: button.id,\n type: button.type,\n disabled: button.disabled,\n tag: button.tag,\n },\n });\n }\n\n for (const link of domData.links) {\n elements.push({\n category: 'link',\n role: 'link',\n name: link.text,\n selector: link.selector,\n selectorAlt: link.testId ? [`getByTestId(${JSON.stringify(link.testId)})`] : [],\n purpose: link.external\n ? `External link to \"${link.href}\" - \"${link.text}\"`\n : `Internal navigation link - \"${link.text}\" -> ${link.href}`,\n confidence: link.testId ? 'high' : 'medium',\n attributes: {\n text: link.text,\n href: link.href,\n testId: link.testId,\n external: link.external,\n },\n });\n }\n\n for (const heading of domData.headings) {\n elements.push({\n category: 'heading',\n role: 'heading',\n name: heading.text,\n selector: heading.selector,\n selectorAlt: [`getByText(${JSON.stringify(heading.text)})`],\n purpose: `Page ${heading.level} heading - use to assert the correct page or section loaded`,\n confidence: 'medium',\n attributes: {\n level: heading.level,\n text: heading.text,\n },\n });\n }\n\n for (const status of domData.statusRegions) {\n elements.push({\n category: 'status',\n role: status.role,\n name: status.text || status.role,\n selector: status.selector,\n selectorAlt: [],\n purpose: 'Live region - use to assert error messages, success toasts, and validation feedback',\n confidence: 'medium',\n attributes: {\n role: status.role,\n ariaLive: status.ariaLive,\n currentText: status.text,\n },\n });\n }\n\n return elements;\n}\n\nfunction buildWarnings(\n elements: CapturedElement[],\n domData: DomData,\n a11ySnapshot: unknown,\n): string[] {\n const warnings: string[] = [];\n\n if (!a11ySnapshot) {\n warnings.push('Playwright accessibility snapshot was unavailable. Capture continued using DOM extraction only.');\n }\n\n const lowConfidenceInputs = elements.filter((element) => element.category === 'input' && element.confidence === 'low');\n if (lowConfidenceInputs.length > 0) {\n warnings.push(\n `${lowConfidenceInputs.length} input(s) have low-confidence selectors. Consider adding data-testid attributes to: ${lowConfidenceInputs.map((element) => element.name).filter(Boolean).join(', ')}`\n );\n }\n\n if (domData.statusRegions.length === 0) {\n warnings.push('No ARIA alert or status regions detected. Error message assertions may need manual selector adjustments after generation.');\n }\n\n for (const form of domData.forms) {\n const hasSubmit = domData.buttons.some((button) => button.type === 'submit');\n if (form.fieldCount > 0 && !hasSubmit) {\n warnings.push(\n `Form ${form.id || `#${form.index}`} has ${form.fieldCount} field(s) but no detected submit button. It may use keyboard submit or a custom handler.`\n );\n }\n }\n\n if (domData.links.length >= MAX_PER_CATEGORY) {\n warnings.push(`Link count hit the ${MAX_PER_CATEGORY} capture limit. Generation will focus on forms and buttons.`);\n }\n\n const interactive = elements.filter((element) => (\n element.category === 'button' || element.category === 'input' || element.category === 'link'\n ));\n if (interactive.length === 0) {\n warnings.push('No interactive elements detected. The page may require authentication, render later than the current settle window, or be mostly static content.');\n }\n\n return warnings;\n}\n\nfunction log(verbose: boolean, message: string): void {\n if (verbose) console.log(message);\n}\n\nasync function getAccessibilitySnapshot(page: any, verbose: boolean): Promise<unknown> {\n if (!page.accessibility || typeof page.accessibility.snapshot !== 'function') {\n log(verbose, ' -> Accessibility snapshot API unavailable; continuing with DOM-only capture');\n return null;\n }\n\n try {\n return await page.accessibility.snapshot({ interestingOnly: false });\n } catch (error: any) {\n log(verbose, ` -> Accessibility snapshot failed (${error?.message ?? error}); continuing with DOM-only capture`);\n return null;\n }\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n","import { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { AnalysisResult } from './analyse.js';\nimport type { ElementMap } from './capture.js';\n\nexport function printCaptureReport(elementMap: ElementMap, analysis?: AnalysisResult | null): void {\n console.log('');\n console.log('='.repeat(60));\n console.log('CementicTest Capture Report');\n console.log('='.repeat(60));\n console.log(`URL: ${elementMap.url}`);\n console.log(`Title: ${elementMap.title}`);\n console.log(`Captured: ${elementMap.timestamp} (${elementMap.mode})`);\n console.log('');\n console.log('Elements:');\n\n for (const [category, count] of Object.entries(elementMap.summary.byCategory)) {\n console.log(` ${category}: ${count}`);\n }\n\n if (elementMap.warnings.length > 0) {\n console.log('');\n console.log('Warnings:');\n for (const warning of elementMap.warnings) {\n console.log(` - ${warning}`);\n }\n }\n\n if (analysis) {\n console.log('');\n console.log(`AI scenarios: ${analysis.scenarios.length}`);\n console.log(`Feature: ${analysis.feature}`);\n console.log(`Prefix: ${analysis.suggestedPrefix}`);\n }\n\n console.log('');\n}\n\nexport function saveCaptureJson(\n elementMap: ElementMap,\n analysis?: AnalysisResult | null,\n outputDir = '.cementic/capture',\n): string {\n mkdirSync(outputDir, { recursive: true });\n\n const fileName = `capture-${slugify(elementMap.url)}-${Date.now()}.json`;\n const filePath = join(outputDir, fileName);\n\n writeFileSync(filePath, JSON.stringify({\n _meta: {\n version: '0.2.5',\n generatedAt: new Date().toISOString(),\n tool: '@cementic/cementic-test',\n },\n elementMap,\n analysis: analysis ?? null,\n }, null, 2));\n\n return filePath;\n}\n\nexport function buildCasesMarkdown(analysis: AnalysisResult): string {\n const lines: string[] = [];\n\n for (const scenario of analysis.scenarios) {\n lines.push(`# ${scenario.id} — ${scenario.title} ${scenario.tags.map(normalizeTag).join(' ')}`.trim());\n lines.push(`<!-- ct:url ${analysis.url} -->`);\n lines.push(`<!-- ct:feature ${analysis.feature} -->`);\n lines.push(`<!-- ct:generated-by capture -->`);\n lines.push(`<!-- narrator: ${sanitizeComment(scenario.narrator)} -->`);\n lines.push(`<!-- code-level: ${scenario.codeLevel} -->`);\n lines.push('');\n lines.push('## Steps');\n\n scenario.steps.forEach((step, index) => {\n const hint = step.selector && step.selector !== 'page'\n ? ` <!-- selector: ${step.selector} -->`\n : '';\n lines.push(`${index + 1}. ${step.human}${hint}`);\n });\n\n lines.push('');\n lines.push('## Expected Results');\n\n scenario.assertions.forEach((assertion) => {\n const hint = assertion.playwright\n ? ` <!-- playwright: ${sanitizeComment(assertion.playwright)} -->`\n : '';\n lines.push(`- ${assertion.human}${hint}`);\n });\n\n lines.push('');\n lines.push('---');\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\nexport function saveCasesMarkdown(\n analysis: AnalysisResult,\n outputDir = 'cases',\n fileName?: string,\n): string {\n mkdirSync(outputDir, { recursive: true });\n\n const resolvedFileName = fileName ?? `${slugify(analysis.feature || analysis.url)}.md`;\n const filePath = join(outputDir, resolvedFileName);\n\n writeFileSync(filePath, buildCasesMarkdown(analysis));\n return filePath;\n}\n\nexport function saveSpecPreview(\n analysis: AnalysisResult,\n outputDir = 'tests/preview',\n): string | null {\n if (analysis.scenarios.length === 0) return null;\n\n mkdirSync(outputDir, { recursive: true });\n\n const fileName = `spec-preview-${slugify(analysis.url)}-${Date.now()}.spec.cjs`;\n const filePath = join(outputDir, fileName);\n const lines: string[] = [];\n\n lines.push('/**');\n lines.push(' * CementicTest Capture Preview');\n lines.push(` * Generated from: ${analysis.url}`);\n lines.push(` * Feature: ${analysis.feature}`);\n lines.push(' */');\n lines.push('');\n lines.push(`const { test, expect } = require('@playwright/test');`);\n lines.push('');\n\n for (const scenario of analysis.scenarios) {\n lines.push(`test(${JSON.stringify(`${scenario.id} — ${scenario.title}`)}, async ({ page }) => {`);\n for (const step of scenario.steps) {\n if (step.action === 'navigate') {\n lines.push(` await page.goto(${JSON.stringify(step.value)});`);\n continue;\n }\n\n if (step.selector === 'page') continue;\n const selector = `page.${step.selector}`;\n\n if (step.action === 'fill') lines.push(` await ${selector}.fill(${JSON.stringify(step.value)});`);\n if (step.action === 'click') lines.push(` await ${selector}.click();`);\n if (step.action === 'select') lines.push(` await ${selector}.selectOption(${JSON.stringify(step.value)});`);\n if (step.action === 'check') lines.push(` await ${selector}.check();`);\n if (step.action === 'keyboard') lines.push(` await page.keyboard.press(${JSON.stringify(step.value)});`);\n if (step.action === 'hover') lines.push(` await ${selector}.hover();`);\n }\n\n for (const assertion of scenario.assertions) {\n lines.push(` ${ensureStatement(assertion.playwright)}`);\n }\n\n lines.push('});');\n lines.push('');\n }\n\n writeFileSync(filePath, lines.join('\\n'));\n return filePath;\n}\n\nfunction slugify(value: string): string {\n return (value || 'capture')\n .replace(/^https?:\\/\\//, '')\n .replace(/[^a-zA-Z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n .toLowerCase()\n .slice(0, 60);\n}\n\nfunction normalizeTag(value: string): string {\n const cleaned = String(value ?? '').trim();\n if (!cleaned) return '@ui';\n return cleaned.startsWith('@') ? cleaned : `@${cleaned}`;\n}\n\nfunction sanitizeComment(value: string): string {\n return String(value ?? '').replace(/-->/g, '-- >');\n}\n\nfunction ensureStatement(value: string): string {\n const trimmed = String(value ?? '').trim();\n if (!trimmed) return '// TODO: add assertion';\n return trimmed.endsWith(';') ? trimmed : `${trimmed};`;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\n\nexport function reportCmd() {\n const cmd = new Command('report')\n .description('Open the Playwright HTML report')\n .action(() => {\n console.log('📊 Opening Playwright HTML report...');\n \n const child = spawn(\n 'npx',\n ['playwright', 'show-report'],\n {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport function serveCmd() {\n const cmd = new Command('serve')\n .description('Serve the Allure report')\n .action(() => {\n console.log('📊 Serving Allure report...');\n\n // Try to find the local allure binary first (more reliable than global/npx sometimes)\n const localAllureBin = join(process.cwd(), 'node_modules', 'allure-commandline', 'bin', 'allure');\n \n let executable = 'npx';\n let args = ['allure', 'serve', './allure-results'];\n\n // If we can find the direct binary, use it (node node_modules/.../allure)\n // This bypasses the \"require('../')\" issue in the .bin wrapper\n if (existsSync(localAllureBin)) {\n executable = 'node';\n args = [localAllureBin, 'serve', './allure-results'];\n }\n\n console.log(`> ${executable} ${args.join(' ')}`);\n\n const child = spawn(\n executable,\n args,\n {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n }\n );\n\n child.on('exit', (code) => {\n process.exit(code ?? 0);\n });\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { spawn } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nfunction runStep(cmd: string, args: string[], stepName: string): Promise<void> {\n return new Promise((resolve, reject) => {\n console.log(`\\n🌊 Flow Step: ${stepName}`);\n console.log(`> ${cmd} ${args.join(' ')}`);\n\n const child = spawn(cmd, args, {\n stdio: 'inherit',\n shell: process.platform === 'win32',\n });\n\n child.on('exit', (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`${stepName} failed with exit code ${code}`));\n }\n });\n });\n}\n\nexport function flowCmd() {\n const cmd = new Command('flow')\n .description('End-to-end flow: Normalize -> Generate -> Run Tests')\n .argument('[casesDir]', 'Directory containing test cases', './cases')\n .option('--lang <lang>', 'Target language (ts|js)', 'ts')\n .option('--no-run', 'Skip running tests')\n .action(async (casesDir, opts) => {\n const cliBin = resolve(process.argv[1]); // The current CLI executable\n\n try {\n // 1. Normalize\n await runStep(process.execPath, [cliBin, 'normalize', casesDir], 'Normalize Cases');\n\n // 2. Generate\n await runStep(process.execPath, [cliBin, 'gen', '--lang', opts.lang], 'Generate Tests');\n\n // 3. Test (unless skipped)\n if (opts.run) {\n await runStep(process.execPath, [cliBin, 'test'], 'Run Playwright Tests');\n } else {\n console.log('\\n⏭️ Skipping test execution (--no-run)');\n }\n\n console.log('\\n✅ Flow completed successfully!');\n } catch (err: any) {\n console.error(`\\n❌ Flow failed: ${err.message}`);\n process.exit(1);\n }\n });\n\n return cmd;\n}\n","import { Command } from 'commander';\nimport { mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst WORKFLOW_CONTENT = `name: Playwright Tests\non:\n push:\n branches: [ main, master ]\n pull_request:\n branches: [ main, master ]\njobs:\n test:\n timeout-minutes: 60\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: lts/*\n - name: Install dependencies\n run: npm ci\n - name: Install Playwright Browsers\n run: npx playwright install --with-deps\n - name: Run Playwright tests\n run: npx playwright test\n - uses: actions/upload-artifact@v4\n if: always()\n with:\n name: playwright-report\n path: playwright-report/\n retention-days: 30\n`;\n\nexport function ciCmd() {\n const cmd = new Command('ci')\n .description('Generate GitHub Actions workflow for CI')\n .action(() => {\n const githubDir = join(process.cwd(), '.github');\n const workflowsDir = join(githubDir, 'workflows');\n const workflowFile = join(workflowsDir, 'cementic.yml');\n\n console.log('🤖 Setting up CI/CD workflow...');\n\n if (!existsSync(workflowsDir)) {\n mkdirSync(workflowsDir, { recursive: true });\n console.log(`Created directory: ${workflowsDir}`);\n }\n\n if (existsSync(workflowFile)) {\n console.warn(`⚠️ Workflow file already exists at ${workflowFile}. Skipping.`);\n return;\n }\n\n writeFileSync(workflowFile, WORKFLOW_CONTENT.trim() + '\\n');\n console.log(`✅ CI workflow generated at: ${workflowFile}`);\n console.log('Next steps:');\n console.log('1. Commit and push the new file');\n console.log('2. Check the \"Actions\" tab in your GitHub repository');\n });\n\n return cmd;\n}\n"],"mappings":"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","names":["Command","Command","readFileSync","mkdirSync","writeFileSync","statSync","join","resolve","Command","Command","mkdirSync","writeFileSync","join","detectProvider","callAnthropic","callOpenAi","inferPrefix","log","input","mkdirSync","writeFileSync","join","normalizeTag","slugify","output","mkdirSync","join","writeFileSync","Command","Command","spawn","Command","spawn","existsSync","join","Command","spawn","resolve","Command","mkdirSync","writeFileSync","existsSync","join","require","Command"]}
|
|
@@ -1,13 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CementicTest JavaScript Template
|
|
2
2
|
|
|
3
|
-
This project
|
|
3
|
+
This scaffold is the starter project used by `ct new` in JavaScript mode. It keeps the Page Object Model structure explicit so generated specs have somewhere sensible to land.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Included pages
|
|
6
|
+
|
|
7
|
+
- `pages/BasePage.js`
|
|
8
|
+
- `pages/LoginPage.js`
|
|
9
|
+
- `pages/DashboardPage.js`
|
|
10
|
+
- `pages/FormPage.js`
|
|
11
|
+
|
|
12
|
+
## Included example specs
|
|
13
|
+
|
|
14
|
+
- `tests/login.spec.js`
|
|
15
|
+
- `tests/dashboard.spec.js`
|
|
16
|
+
|
|
17
|
+
## Typical workflow
|
|
6
18
|
|
|
7
19
|
```bash
|
|
20
|
+
# 1. write or generate cases
|
|
21
|
+
ct tc --feature "Login" --count 2
|
|
22
|
+
|
|
23
|
+
# 2. normalize and generate Playwright code
|
|
24
|
+
ct normalize ./cases --and-gen --lang js
|
|
25
|
+
|
|
26
|
+
# 3. run the suite
|
|
8
27
|
npx playwright test
|
|
9
28
|
```
|
|
10
29
|
|
|
11
|
-
##
|
|
30
|
+
## Useful commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm test
|
|
34
|
+
npm run test:login
|
|
35
|
+
npm run test:dashboard
|
|
36
|
+
npm run test:headed
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Notes
|
|
12
40
|
|
|
13
|
-
|
|
41
|
+
- Generated POM classes are written to `pages/`.
|
|
42
|
+
- Generated specs are written to `tests/generated/`.
|
|
43
|
+
- Existing page objects are not overwritten by `ct gen`.
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"test": "npx playwright test",
|
|
8
8
|
"test:debug": "npx playwright test --debug",
|
|
9
9
|
"test:headed": "npx playwright test --headed",
|
|
10
|
-
"test:
|
|
11
|
-
"test:
|
|
10
|
+
"test:login": "npx playwright test tests/login.spec.js",
|
|
11
|
+
"test:dashboard": "npx playwright test tests/dashboard.spec.js",
|
|
12
12
|
"report": "npx playwright show-report",
|
|
13
13
|
"allure:generate": "node node_modules/allure-commandline/bin/allure generate ./allure-results --clean -o ./allure-report",
|
|
14
14
|
"allure:open": "node node_modules/allure-commandline/bin/allure open ./allure-report",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"automation",
|
|
20
20
|
"testing",
|
|
21
21
|
"javascript",
|
|
22
|
+
"page-object-model",
|
|
22
23
|
"allure",
|
|
23
24
|
"education"
|
|
24
25
|
],
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class BasePage {
|
|
2
|
+
/**
|
|
3
|
+
* @param {import('@playwright/test').Page} page
|
|
4
|
+
*/
|
|
5
|
+
constructor(page) {
|
|
6
|
+
this.page = page;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} path
|
|
11
|
+
*/
|
|
12
|
+
async goto(path = '/') {
|
|
13
|
+
await this.page.goto(path);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async waitForLoad() {
|
|
17
|
+
await this.page.waitForLoadState('domcontentloaded');
|
|
18
|
+
}
|
|
19
|
+
}
|