@cementic/cementic-test 0.2.16 → 0.2.17
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/CHANGELOG.md +8 -0
- package/README.md +12 -2
- package/dist/capture.js +1 -1
- package/dist/{chunk-PYKYHIO3.js → chunk-A4IHRXON.js} +148 -17
- package/dist/chunk-A4IHRXON.js.map +1 -0
- package/dist/{chunk-WUGSOKKY.js → chunk-JWGYAQ3O.js} +2 -2
- package/dist/{chunk-WUGSOKKY.js.map → chunk-JWGYAQ3O.js.map} +1 -1
- package/dist/cli.js +62 -19
- package/dist/cli.js.map +1 -1
- package/dist/gen-J4HZWS5T.js +12 -0
- package/package.json +1 -1
- package/dist/chunk-PYKYHIO3.js.map +0 -1
- package/dist/gen-WYG3JOGJ.js +0 -10
- /package/dist/{gen-WYG3JOGJ.js.map → gen-J4HZWS5T.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/playwright.ts","../src/core/capture.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\n\ntype PlaywrightPackageName = '@playwright/test' | 'playwright-core';\n\nexport type PlaywrightResolution = {\n packageName: PlaywrightPackageName;\n resolvedPath: string;\n searchRoot: string;\n chromium: any;\n};\n\nconst require = createRequire(import.meta.url);\nconst moduleDir = dirname(fileURLToPath(import.meta.url));\n\nexport async function resolvePlaywrightChromium(cwd = process.cwd()): Promise<PlaywrightResolution> {\n const searchRoots = buildSearchRoots(cwd);\n\n for (const packageName of ['@playwright/test', 'playwright-core'] as const) {\n for (const searchRoot of searchRoots) {\n const resolvedPath = resolveFromRoot(packageName, searchRoot);\n if (!resolvedPath) continue;\n\n const imported = await import(pathToFileURL(resolvedPath).href);\n const chromium = imported.chromium ?? imported.default?.chromium;\n\n if (chromium) {\n return {\n packageName,\n resolvedPath,\n searchRoot,\n chromium,\n };\n }\n }\n }\n\n throw new Error(\n 'Playwright runtime not found in this project.\\n' +\n 'Try:\\n' +\n ' npm install\\n' +\n ' npx playwright install chromium'\n );\n}\n\nfunction buildSearchRoots(cwd: string): string[] {\n const roots = [resolve(cwd)];\n const projectRoot = findNearestProjectRoot(cwd);\n\n if (projectRoot && projectRoot !== roots[0]) {\n roots.push(projectRoot);\n }\n\n roots.push(moduleDir);\n return Array.from(new Set(roots));\n}\n\nfunction findNearestProjectRoot(startDir: string): string | undefined {\n let currentDir = resolve(startDir);\n\n while (true) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return undefined;\n currentDir = parentDir;\n }\n}\n\nfunction resolveFromRoot(packageName: PlaywrightPackageName, searchRoot: string): string | undefined {\n try {\n return require.resolve(packageName, { paths: [searchRoot] });\n } catch {\n return undefined;\n }\n}\n","import { resolvePlaywrightChromium } from './playwright.js';\n\nexport 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';\nexport type CaptureErrorCode = 'PLAYWRIGHT_NOT_FOUND' | 'BROWSER_NOT_INSTALLED' | 'PAGE_LOAD_FAILED' | 'CAPTURE_FAILED';\nexport type InteractiveElementRole = 'button' | 'link' | 'textbox' | 'checkbox';\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 role: InteractiveElementRole;\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 tag: string;\n role: InteractiveElementRole;\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\ntype DomElementLike = {\n tagName: string;\n textContent?: string | null;\n previousElementSibling?: DomElementLike | null;\n getAttribute(name: string): string | null;\n hasAttribute(name: string): boolean;\n closest(selector: string): DomElementLike | null;\n querySelectorAll?(selector: string): ArrayLike<DomElementLike>;\n};\n\ntype DomDocumentLike = {\n title?: string;\n querySelector(selector: string): DomElementLike | null;\n querySelectorAll(selector: string): ArrayLike<DomElementLike>;\n getElementById(id: string): DomElementLike | null;\n};\n\ntype DomWindowLike = {\n location: {\n hostname: string;\n href: string;\n };\n};\n\nconst SETTLE_MS = 1200;\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_PER_CATEGORY = 50;\n\nexport class CaptureRuntimeError extends Error {\n code: CaptureErrorCode;\n\n constructor(code: CaptureErrorCode, message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = 'CaptureRuntimeError';\n this.code = code;\n if (options?.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = options.cause;\n }\n }\n}\n\nexport function inferInteractiveRole(\n tagName: string,\n attrs: { role?: string; href?: string; type?: string } = {},\n): InteractiveElementRole | undefined {\n const tag = String(tagName ?? '').trim().toLowerCase();\n const explicitRole = String(attrs.role ?? '').trim().toLowerCase();\n const type = String(attrs.type ?? '').trim().toLowerCase();\n const href = String(attrs.href ?? '').trim();\n\n if (explicitRole === 'button') return 'button';\n if (explicitRole === 'link') return 'link';\n if (explicitRole === 'textbox') return 'textbox';\n if (explicitRole === 'checkbox') return 'checkbox';\n\n if (tag === 'a' && href) return 'link';\n if (tag === 'button') return 'button';\n if (tag === 'textarea') return 'textbox';\n if (tag === 'select') return 'textbox';\n if (tag === 'input') {\n if (type === 'submit' || type === 'button' || type === 'reset' || type === 'image') return 'button';\n if (type === 'checkbox') return 'checkbox';\n return 'textbox';\n }\n\n return undefined;\n}\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.16 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 let browser: any;\n\n try {\n browser = await chromium.launch({\n headless,\n slowMo: headless ? 0 : 150,\n });\n } catch (error: any) {\n throw classifyCaptureError(error);\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 try {\n await page.goto(url, {\n waitUntil: 'domcontentloaded',\n timeout: timeoutMs,\n });\n } catch (error: any) {\n throw new CaptureRuntimeError(\n 'PAGE_LOAD_FAILED',\n `Page load failed for ${url}. ${error?.message ?? error}`,\n { cause: error },\n );\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 } catch (error: any) {\n throw classifyCaptureError(error);\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 const resolution = await resolvePlaywrightChromium(process.cwd());\n return resolution.chromium;\n } catch (error: any) {\n throw new CaptureRuntimeError(\n 'PLAYWRIGHT_NOT_FOUND',\n error?.message ??\n 'Playwright runtime not found in this project.\\nTry:\\n npm install\\n npx playwright install chromium',\n { cause: error },\n );\n }\n}\n\nexport function extractDomDataFromEnvironment(doc: DomDocumentLike, win: DomWindowLike): DomData {\n const localMaxPerCategory = 50;\n const attr = (el: DomElementLike, name: string): string | undefined => el.getAttribute(name)?.trim() || undefined;\n const text = (el: DomElementLike | null): string | undefined => el?.textContent?.replace(/\\s+/g, ' ').trim() || undefined;\n const jsString = (value: string) => JSON.stringify(value);\n const semanticRole = (el: DomElementLike): InteractiveElementRole | undefined => inferInteractiveRole(el.tagName, {\n role: attr(el, 'role'),\n href: attr(el, 'href'),\n type: attr(el, 'type'),\n });\n\n const findLabel = (el: DomElementLike): string | undefined => {\n const id = attr(el, 'id');\n\n if (id) {\n const labelEl = doc.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 = doc.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: DomElementLike, 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 const role = semanticRole(el);\n if (role === 'textbox') return `getByLabel(${jsString(ariaLabel)})`;\n if (role) {\n return `getByRole(${jsString(role)}, { name: ${jsString(ariaLabel)} })`;\n }\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) {\n const role = semanticRole(el);\n if (role === 'button' || role === 'link') {\n return `getByRole(${jsString(role)}, { name: ${jsString(buttonText)} })`;\n }\n }\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 Array.from(doc.querySelectorAll('button, [role=\"button\"], input[type=\"submit\"], input[type=\"button\"]')).forEach((el) => {\n const role = semanticRole(el);\n if (role !== 'button') return;\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 role,\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 Array.from(doc.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 Array.from(doc.querySelectorAll('a[href]')).forEach((el) => {\n const role = semanticRole(el);\n if (role !== 'link') return;\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 tag: el.tagName.toLowerCase(),\n role,\n text: linkText,\n href,\n testId,\n external: Boolean(href?.startsWith('http') && !href.includes(win.location.hostname)),\n selector: testId\n ? `getByTestId(${jsString(testId)})`\n : `getByRole('link', { name: ${jsString(linkText)} })`,\n });\n });\n\n const headings: DomHeading[] = [];\n Array.from(doc.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 Array.from(doc.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 Array.from(doc.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 Array.from(doc.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: Array.from(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: win.location.href,\n pageTitle: doc.title ?? '',\n };\n}\n\nfunction extractDomData(): DomData {\n return extractDomDataFromEnvironment(\n document as unknown as DomDocumentLike,\n window as unknown as DomWindowLike,\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 buttonRole = button.role || 'button';\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(${JSON.stringify(buttonRole)}, { name: ${JSON.stringify(button.text)} })` : null)\n || (button.id ? `locator(${JSON.stringify(`#${button.id}`)})` : null)\n || (button.tag === 'button' ? `locator('button')` : `locator('[role=\"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: buttonRole,\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 role: buttonRole,\n },\n });\n }\n\n for (const link of domData.links) {\n const linkRole = link.role || 'link';\n elements.push({\n category: 'link',\n role: linkRole,\n name: link.text,\n selector: link.selector,\n selectorAlt: [\n ...(link.testId ? [`getByTestId(${JSON.stringify(link.testId)})`] : []),\n `getByText(${JSON.stringify(link.text)})`,\n ],\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 tag: link.tag,\n role: linkRole,\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\nexport function formatCaptureFailure(error: unknown): string[] {\n const resolved = classifyCaptureError(error);\n\n if (resolved.code === 'PLAYWRIGHT_NOT_FOUND') {\n return [\n 'Playwright not resolvable.',\n 'Playwright runtime not found in this project.',\n 'Try:',\n ' npm install',\n ' npx playwright install chromium',\n ];\n }\n\n if (resolved.code === 'BROWSER_NOT_INSTALLED') {\n return [\n 'Browser not installed.',\n 'Playwright resolved, but Chromium is not installed for this project.',\n 'Try:',\n ' npx playwright install chromium',\n ];\n }\n\n if (resolved.code === 'PAGE_LOAD_FAILED') {\n return [\n 'Page load failed.',\n resolved.message,\n ];\n }\n\n return [\n 'Capture failed.',\n resolved.message,\n ];\n}\n\nfunction classifyCaptureError(error: unknown): CaptureRuntimeError {\n if (error instanceof CaptureRuntimeError) {\n return error;\n }\n\n const message = errorMessage(error);\n\n if (isBrowserInstallError(message)) {\n return new CaptureRuntimeError(\n 'BROWSER_NOT_INSTALLED',\n 'Playwright resolved, but Chromium is not installed for this project.',\n { cause: error },\n );\n }\n\n if (isPlaywrightNotFoundError(message)) {\n return new CaptureRuntimeError(\n 'PLAYWRIGHT_NOT_FOUND',\n 'Playwright runtime not found in this project.',\n { cause: error },\n );\n }\n\n return new CaptureRuntimeError('CAPTURE_FAILED', message || 'Unknown capture failure.', { cause: error });\n}\n\nfunction isBrowserInstallError(message: string): boolean {\n return /Executable doesn't exist|browserType\\.launch: Executable doesn't exist|Please run the following command|playwright install/i.test(message);\n}\n\nfunction isPlaywrightNotFoundError(message: string): boolean {\n return /Playwright runtime not found|Cannot find package ['\"](?:@playwright\\/test|playwright-core)['\"]/i.test(message);\n}\n\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error ?? '');\n}\n"],"mappings":"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","names":["require"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/playwright.ts","../src/core/capture.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { createRequire } from 'node:module';\nimport { fileURLToPath, pathToFileURL } from 'node:url';\n\ntype PlaywrightPackageName = '@playwright/test' | 'playwright-core';\n\nexport type PlaywrightResolution = {\n packageName: PlaywrightPackageName;\n resolvedPath: string;\n searchRoot: string;\n chromium: any;\n};\n\nconst require = createRequire(import.meta.url);\nconst moduleDir = dirname(fileURLToPath(import.meta.url));\n\nexport async function resolvePlaywrightChromium(cwd = process.cwd()): Promise<PlaywrightResolution> {\n const searchRoots = buildSearchRoots(cwd);\n\n for (const packageName of ['@playwright/test', 'playwright-core'] as const) {\n for (const searchRoot of searchRoots) {\n const resolvedPath = resolveFromRoot(packageName, searchRoot);\n if (!resolvedPath) continue;\n\n const imported = await import(pathToFileURL(resolvedPath).href);\n const chromium = imported.chromium ?? imported.default?.chromium;\n\n if (chromium) {\n return {\n packageName,\n resolvedPath,\n searchRoot,\n chromium,\n };\n }\n }\n }\n\n throw new Error(\n 'Playwright runtime not found in this project.\\n' +\n 'Try:\\n' +\n ' npm install\\n' +\n ' npx playwright install chromium'\n );\n}\n\nfunction buildSearchRoots(cwd: string): string[] {\n const roots = [resolve(cwd)];\n const projectRoot = findNearestProjectRoot(cwd);\n\n if (projectRoot && projectRoot !== roots[0]) {\n roots.push(projectRoot);\n }\n\n roots.push(moduleDir);\n return Array.from(new Set(roots));\n}\n\nfunction findNearestProjectRoot(startDir: string): string | undefined {\n let currentDir = resolve(startDir);\n\n while (true) {\n if (existsSync(join(currentDir, 'package.json'))) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) return undefined;\n currentDir = parentDir;\n }\n}\n\nfunction resolveFromRoot(packageName: PlaywrightPackageName, searchRoot: string): string | undefined {\n try {\n return require.resolve(packageName, { paths: [searchRoot] });\n } catch {\n return undefined;\n }\n}\n","import { resolvePlaywrightChromium } from './playwright.js';\n\nexport 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';\nexport type CaptureErrorCode = 'PLAYWRIGHT_NOT_FOUND' | 'BROWSER_NOT_INSTALLED' | 'PAGE_LOAD_FAILED' | 'CAPTURE_FAILED';\nexport type InteractiveElementRole = 'button' | 'link' | 'textbox' | 'checkbox';\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 role: InteractiveElementRole;\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 tag: string;\n role: InteractiveElementRole;\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\ntype DomElementLike = {\n tagName: string;\n textContent?: string | null;\n previousElementSibling?: DomElementLike | null;\n getAttribute(name: string): string | null;\n hasAttribute(name: string): boolean;\n closest(selector: string): DomElementLike | null;\n querySelectorAll?(selector: string): ArrayLike<DomElementLike>;\n};\n\ntype DomDocumentLike = {\n title?: string;\n querySelector(selector: string): DomElementLike | null;\n querySelectorAll(selector: string): ArrayLike<DomElementLike>;\n getElementById(id: string): DomElementLike | null;\n};\n\ntype DomWindowLike = {\n location: {\n hostname: string;\n href: string;\n };\n};\n\nconst SETTLE_MS = 1200;\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_PER_CATEGORY = 50;\n\nexport class CaptureRuntimeError extends Error {\n code: CaptureErrorCode;\n\n constructor(code: CaptureErrorCode, message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = 'CaptureRuntimeError';\n this.code = code;\n if (options?.cause !== undefined) {\n (this as Error & { cause?: unknown }).cause = options.cause;\n }\n }\n}\n\nexport function inferInteractiveRole(\n tagName: string,\n attrs: { role?: string; href?: string; type?: string } = {},\n): InteractiveElementRole | undefined {\n const tag = String(tagName ?? '').trim().toLowerCase();\n const explicitRole = String(attrs.role ?? '').trim().toLowerCase();\n const type = String(attrs.type ?? '').trim().toLowerCase();\n const href = String(attrs.href ?? '').trim();\n\n if (explicitRole === 'button') return 'button';\n if (explicitRole === 'link') return 'link';\n if (explicitRole === 'textbox') return 'textbox';\n if (explicitRole === 'checkbox') return 'checkbox';\n\n if (tag === 'a' && href) return 'link';\n if (tag === 'button') return 'button';\n if (tag === 'textarea') return 'textbox';\n if (tag === 'select') return 'textbox';\n if (tag === 'input') {\n if (type === 'submit' || type === 'button' || type === 'reset' || type === 'image') return 'button';\n if (type === 'checkbox') return 'checkbox';\n return 'textbox';\n }\n\n return undefined;\n}\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.17 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 let browser: any;\n\n try {\n browser = await chromium.launch({\n headless,\n slowMo: headless ? 0 : 150,\n });\n } catch (error: any) {\n throw classifyCaptureError(error);\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 try {\n await page.goto(url, {\n waitUntil: 'domcontentloaded',\n timeout: timeoutMs,\n });\n } catch (error: any) {\n throw new CaptureRuntimeError(\n 'PAGE_LOAD_FAILED',\n `Page load failed for ${url}. ${error?.message ?? error}`,\n { cause: error },\n );\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 } catch (error: any) {\n throw classifyCaptureError(error);\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 const resolution = await resolvePlaywrightChromium(process.cwd());\n return resolution.chromium;\n } catch (error: any) {\n throw new CaptureRuntimeError(\n 'PLAYWRIGHT_NOT_FOUND',\n error?.message ??\n 'Playwright runtime not found in this project.\\nTry:\\n npm install\\n npx playwright install chromium',\n { cause: error },\n );\n }\n}\n\nexport function extractDomDataFromEnvironment(doc: DomDocumentLike, win: DomWindowLike): DomData {\n const localMaxPerCategory = 50;\n const attr = (el: DomElementLike, name: string): string | undefined => el.getAttribute(name)?.trim() || undefined;\n const text = (el: DomElementLike | null): string | undefined => el?.textContent?.replace(/\\s+/g, ' ').trim() || undefined;\n const jsString = (value: string) => JSON.stringify(value);\n const semanticRole = (el: DomElementLike): InteractiveElementRole | undefined => inferInteractiveRole(el.tagName, {\n role: attr(el, 'role'),\n href: attr(el, 'href'),\n type: attr(el, 'type'),\n });\n\n const findLabel = (el: DomElementLike): string | undefined => {\n const id = attr(el, 'id');\n\n if (id) {\n const labelEl = doc.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 = doc.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: DomElementLike, 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 const role = semanticRole(el);\n if (role === 'textbox') return `getByLabel(${jsString(ariaLabel)})`;\n if (role) {\n return `getByRole(${jsString(role)}, { name: ${jsString(ariaLabel)} })`;\n }\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) {\n const role = semanticRole(el);\n if (role === 'button' || role === 'link') {\n return `getByRole(${jsString(role)}, { name: ${jsString(buttonText)} })`;\n }\n }\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 Array.from(doc.querySelectorAll('button, [role=\"button\"], input[type=\"submit\"], input[type=\"button\"]')).forEach((el) => {\n const role = semanticRole(el);\n if (role !== 'button') return;\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 role,\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 Array.from(doc.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 Array.from(doc.querySelectorAll('a[href]')).forEach((el) => {\n const role = semanticRole(el);\n if (role !== 'link') return;\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 tag: el.tagName.toLowerCase(),\n role,\n text: linkText,\n href,\n testId,\n external: Boolean(href?.startsWith('http') && !href.includes(win.location.hostname)),\n selector: testId\n ? `getByTestId(${jsString(testId)})`\n : `getByRole('link', { name: ${jsString(linkText)} })`,\n });\n });\n\n const headings: DomHeading[] = [];\n Array.from(doc.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 Array.from(doc.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 Array.from(doc.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 Array.from(doc.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: Array.from(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: win.location.href,\n pageTitle: doc.title ?? '',\n };\n}\n\nfunction extractDomData(): DomData {\n return extractDomDataFromEnvironment(\n document as unknown as DomDocumentLike,\n window as unknown as DomWindowLike,\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 buttonRole = button.role || 'button';\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(${JSON.stringify(buttonRole)}, { name: ${JSON.stringify(button.text)} })` : null)\n || (button.id ? `locator(${JSON.stringify(`#${button.id}`)})` : null)\n || (button.tag === 'button' ? `locator('button')` : `locator('[role=\"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: buttonRole,\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 role: buttonRole,\n },\n });\n }\n\n for (const link of domData.links) {\n const linkRole = link.role || 'link';\n elements.push({\n category: 'link',\n role: linkRole,\n name: link.text,\n selector: link.selector,\n selectorAlt: [\n ...(link.testId ? [`getByTestId(${JSON.stringify(link.testId)})`] : []),\n `getByText(${JSON.stringify(link.text)})`,\n ],\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 tag: link.tag,\n role: linkRole,\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\nexport function formatCaptureFailure(error: unknown): string[] {\n const resolved = classifyCaptureError(error);\n\n if (resolved.code === 'PLAYWRIGHT_NOT_FOUND') {\n return [\n 'Playwright not resolvable.',\n 'Playwright runtime not found in this project.',\n 'Try:',\n ' npm install',\n ' npx playwright install chromium',\n ];\n }\n\n if (resolved.code === 'BROWSER_NOT_INSTALLED') {\n return [\n 'Browser not installed.',\n 'Playwright resolved, but Chromium is not installed for this project.',\n 'Try:',\n ' npx playwright install chromium',\n ];\n }\n\n if (resolved.code === 'PAGE_LOAD_FAILED') {\n return [\n 'Page load failed.',\n resolved.message,\n ];\n }\n\n return [\n 'Capture failed.',\n resolved.message,\n ];\n}\n\nfunction classifyCaptureError(error: unknown): CaptureRuntimeError {\n if (error instanceof CaptureRuntimeError) {\n return error;\n }\n\n const message = errorMessage(error);\n\n if (isBrowserInstallError(message)) {\n return new CaptureRuntimeError(\n 'BROWSER_NOT_INSTALLED',\n 'Playwright resolved, but Chromium is not installed for this project.',\n { cause: error },\n );\n }\n\n if (isPlaywrightNotFoundError(message)) {\n return new CaptureRuntimeError(\n 'PLAYWRIGHT_NOT_FOUND',\n 'Playwright runtime not found in this project.',\n { cause: error },\n );\n }\n\n return new CaptureRuntimeError('CAPTURE_FAILED', message || 'Unknown capture failure.', { cause: error });\n}\n\nfunction isBrowserInstallError(message: string): boolean {\n return /Executable doesn't exist|browserType\\.launch: Executable doesn't exist|Please run the following command|playwright install/i.test(message);\n}\n\nfunction isPlaywrightNotFoundError(message: string): boolean {\n return /Playwright runtime not found|Cannot find package ['\"](?:@playwright\\/test|playwright-core)['\"]/i.test(message);\n}\n\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error ?? '');\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAC9B,SAAS,eAAe,qBAAqB;AAW7C,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,eAAsB,0BAA0B,MAAM,QAAQ,IAAI,GAAkC;AAClG,QAAM,cAAc,iBAAiB,GAAG;AAExC,aAAW,eAAe,CAAC,oBAAoB,iBAAiB,GAAY;AAC1E,eAAW,cAAc,aAAa;AACpC,YAAM,eAAe,gBAAgB,aAAa,UAAU;AAC5D,UAAI,CAAC,aAAc;AAEnB,YAAM,WAAW,MAAM,OAAO,cAAc,YAAY,EAAE;AAC1D,YAAM,WAAW,SAAS,YAAY,SAAS,SAAS;AAExD,UAAI,UAAU;AACZ,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EAIF;AACF;AAEA,SAAS,iBAAiB,KAAuB;AAC/C,QAAM,QAAQ,CAAC,QAAQ,GAAG,CAAC;AAC3B,QAAM,cAAc,uBAAuB,GAAG;AAE9C,MAAI,eAAe,gBAAgB,MAAM,CAAC,GAAG;AAC3C,UAAM,KAAK,WAAW;AAAA,EACxB;AAEA,QAAM,KAAK,SAAS;AACpB,SAAO,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC;AAClC;AAEA,SAAS,uBAAuB,UAAsC;AACpE,MAAI,aAAa,QAAQ,QAAQ;AAEjC,SAAO,MAAM;AACX,QAAI,WAAW,KAAK,YAAY,cAAc,CAAC,GAAG;AAChD,aAAO;AAAA,IACT;AAEA,UAAM,YAAY,QAAQ,UAAU;AACpC,QAAI,cAAc,WAAY,QAAO;AACrC,iBAAa;AAAA,EACf;AACF;AAEA,SAAS,gBAAgB,aAAoC,YAAwC;AACnG,MAAI;AACF,WAAOA,SAAQ,QAAQ,aAAa,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACwEA,IAAM,YAAY;AAClB,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AAElB,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAG7C,YAAY,MAAwB,SAAiB,SAA+B;AAClF,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,QAAI,SAAS,UAAU,QAAW;AAChC,MAAC,KAAqC,QAAQ,QAAQ;AAAA,IACxD;AAAA,EACF;AACF;AAEO,SAAS,qBACd,SACA,QAAyD,CAAC,GACtB;AACpC,QAAM,MAAM,OAAO,WAAW,EAAE,EAAE,KAAK,EAAE,YAAY;AACrD,QAAM,eAAe,OAAO,MAAM,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY;AACjE,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAE,KAAK,EAAE,YAAY;AACzD,QAAM,OAAO,OAAO,MAAM,QAAQ,EAAE,EAAE,KAAK;AAE3C,MAAI,iBAAiB,SAAU,QAAO;AACtC,MAAI,iBAAiB,OAAQ,QAAO;AACpC,MAAI,iBAAiB,UAAW,QAAO;AACvC,MAAI,iBAAiB,WAAY,QAAO;AAExC,MAAI,QAAQ,OAAO,KAAM,QAAO;AAChC,MAAI,QAAQ,SAAU,QAAO;AAC7B,MAAI,QAAQ,WAAY,QAAO;AAC/B,MAAI,QAAQ,SAAU,QAAO;AAC7B,MAAI,QAAQ,SAAS;AACnB,QAAI,SAAS,YAAY,SAAS,YAAY,SAAS,WAAW,SAAS,QAAS,QAAO;AAC3F,QAAI,SAAS,WAAY,QAAO;AAChC,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,eAAsB,gBAAgB,KAAa,UAA0B,CAAC,GAAwB;AACpG,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,EACd,IAAI;AAEJ,QAAM,WAAW,MAAM,aAAa;AACpC,QAAM,OAAoB,WAAW,aAAa;AAClD,MAAI,SAAS;AAAA,qBAAwB,IAAI,aAAa,GAAG,EAAE;AAE3D,MAAI;AAEJ,MAAI;AACF,cAAU,MAAM,SAAS,OAAO;AAAA,MAC9B;AAAA,MACA,QAAQ,WAAW,IAAI;AAAA,IACzB,CAAC;AAAA,EACH,SAAS,OAAY;AACnB,UAAM,qBAAqB,KAAK;AAAA,EAClC;AAEA,QAAM,UAAU,MAAM,QAAQ,WAAW;AAAA,IACvC;AAAA,IACA,UAAU,EAAE,OAAO,MAAM,QAAQ,IAAI;AAAA,IACrC,mBAAmB;AAAA,EACrB,CAAC;AAED,QAAM,OAAO,MAAM,QAAQ,QAAQ;AAEnC,MAAI;AACF,QAAI,SAAS,6BAA6B,SAAS,KAAK;AACxD,QAAI;AACF,YAAM,KAAK,KAAK,KAAK;AAAA,QACnB,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,OAAY;AACnB,YAAM,IAAI;AAAA,QACR;AAAA,QACA,wBAAwB,GAAG,KAAK,OAAO,WAAW,KAAK;AAAA,QACvD,EAAE,OAAO,MAAM;AAAA,MACjB;AAAA,IACF;AAEA,QAAI,SAAS,gBAAgB,SAAS,oBAAoB;AAC1D,UAAM,KAAK,eAAe,SAAS;AAEnC,QAAI,SAAS,wCAAwC;AACrD,UAAM,eAAe,MAAM,yBAAyB,MAAM,OAAO;AAEjE,QAAI,SAAS,0BAA0B;AACvC,UAAM,UAAU,MAAM,KAAK,SAAS,cAAc;AAElD,UAAM,QAAQ,MAAM,KAAK,MAAM;AAC/B,UAAM,WAAW,KAAK,IAAI;AAC1B,UAAM,WAAW,gBAAgB,OAAO;AACxC,UAAM,WAAW,cAAc,UAAU,SAAS,YAAY;AAC9D,UAAM,aAAuD,CAAC;AAE9D,eAAW,WAAW,UAAU;AAC9B,iBAAW,QAAQ,QAAQ,KAAK,WAAW,QAAQ,QAAQ,KAAK,KAAK;AAAA,IACvE;AAEA,UAAM,SAAqB;AAAA,MACzB,KAAK;AAAA,MACL;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA,SAAS;AAAA,QACP,eAAe,SAAS;AAAA,QACxB;AAAA,MACF;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,SAAS,iBAAiB,SAAS,MAAM,4BAA4B,KAAK,GAAG;AACjF,WAAO;AAAA,EACT,SAAS,OAAY;AACnB,UAAM,qBAAqB,KAAK;AAAA,EAClC,UAAE;AACA,UAAM,SAAS,MAAM;AAAA,EACvB;AACF;AAEO,SAAS,cAAc,YAAqC;AACjE,QAAM,SAAS,WAAW,SACvB,OAAO,CAAC,YAAwC,QAAQ,aAAa,OAAO,EAC5E,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,aAAa;AAAA,IACjB,OAAO,SAAS,QAAQ,WAAW,KAAK;AAAA,IACxC,aAAa,SAAS,QAAQ,WAAW,WAAW;AAAA,IACpD,MAAM,SAAS,QAAQ,WAAW,IAAI;AAAA,IACtC,MAAM,SAAS,QAAQ,WAAW,IAAI;AAAA,IACtC,QAAQ,SAAS,QAAQ,WAAW,MAAM;AAAA,EAC5C,EAAE;AAEJ,SAAO;AAAA,IACL,KAAK,WAAW;AAAA,IAChB,OAAO,WAAW;AAAA,IAClB,UAAU,WAAW,SAClB,OAAO,CAAC,YAAY,QAAQ,aAAa,SAAS,EAClD,IAAI,CAAC,YAAY,QAAQ,QAAQ,EAAE,EACnC,OAAO,OAAO,EACd,MAAM,GAAG,EAAE;AAAA,IACd,SAAS,WAAW,SACjB,OAAO,CAAC,YAAY,QAAQ,aAAa,QAAQ,EACjD,IAAI,CAAC,YAAY,QAAQ,QAAQ,EAAE,EACnC,OAAO,OAAO,EACd,MAAM,GAAG,EAAE;AAAA,IACd,OAAO,WAAW,SACf,OAAO,CAAC,YAAY,QAAQ,aAAa,MAAM,EAC/C,IAAI,CAAC,YAAY,QAAQ,QAAQ,EAAE,EACnC,OAAO,OAAO,EACd,MAAM,GAAG,EAAE;AAAA,IACd;AAAA,IACA,WAAW,CAAC;AAAA,IACZ,WAAW,WAAW,SAAS;AAAA,EACjC;AACF;AAEA,eAAe,eAA6B;AAC1C,MAAI;AACF,UAAM,aAAa,MAAM,0BAA0B,QAAQ,IAAI,CAAC;AAChE,WAAO,WAAW;AAAA,EACpB,SAAS,OAAY;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,OAAO,WACL;AAAA,MACF,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AACF;AAEO,SAAS,8BAA8B,KAAsB,KAA6B;AAC/F,QAAM,sBAAsB;AAC5B,QAAM,OAAO,CAAC,IAAoB,SAAqC,GAAG,aAAa,IAAI,GAAG,KAAK,KAAK;AACxG,QAAM,OAAO,CAAC,OAAkD,IAAI,aAAa,QAAQ,QAAQ,GAAG,EAAE,KAAK,KAAK;AAChH,QAAM,WAAW,CAAC,UAAkB,KAAK,UAAU,KAAK;AACxD,QAAM,eAAe,CAAC,OAA2D,qBAAqB,GAAG,SAAS;AAAA,IAChH,MAAM,KAAK,IAAI,MAAM;AAAA,IACrB,MAAM,KAAK,IAAI,MAAM;AAAA,IACrB,MAAM,KAAK,IAAI,MAAM;AAAA,EACvB,CAAC;AAED,QAAM,YAAY,CAAC,OAA2C;AAC5D,UAAM,KAAK,KAAK,IAAI,IAAI;AAExB,QAAI,IAAI;AACN,YAAM,UAAU,IAAI,cAAc,cAAc,EAAE,IAAI;AACtD,UAAI,QAAS,QAAO,KAAK,OAAO;AAAA,IAClC;AAEA,UAAM,YAAY,KAAK,IAAI,YAAY;AACvC,QAAI,UAAW,QAAO;AAEtB,UAAM,aAAa,KAAK,IAAI,iBAAiB;AAC7C,QAAI,YAAY;AACd,YAAM,UAAU,IAAI,eAAe,UAAU;AAC7C,UAAI,QAAS,QAAO,KAAK,OAAO;AAAA,IAClC;AAEA,UAAM,eAAe,GAAG,QAAQ,OAAO;AACvC,QAAI,cAAc;AAChB,YAAM,MAAM,KAAK,YAAY,KAAK;AAClC,YAAM,cAAc,KAAK,IAAI,aAAa,KAAK;AAC/C,aAAO,IAAI,QAAQ,aAAa,EAAE,EAAE,KAAK,KAAK;AAAA,IAChD;AAEA,UAAM,WAAW,GAAG;AACpB,QAAI,UAAU,YAAY,QAAS,QAAO,KAAK,QAAQ;AAEvD,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,CAAC,IAAoB,WAAoB,eAAuC;AACpG,UAAM,SAAS,KAAK,IAAI,aAAa;AACrC,QAAI,OAAQ,QAAO,eAAe,SAAS,MAAM,CAAC;AAElD,UAAM,KAAK,KAAK,IAAI,IAAI;AACxB,QAAI,MAAM,CAAC,GAAG,MAAM,kCAAkC,GAAG;AACvD,aAAO,WAAW,SAAS,IAAI,EAAE,EAAE,CAAC;AAAA,IACtC;AAEA,UAAM,YAAY,KAAK,IAAI,YAAY;AACvC,QAAI,WAAW;AACb,YAAM,OAAO,aAAa,EAAE;AAC5B,UAAI,SAAS,UAAW,QAAO,cAAc,SAAS,SAAS,CAAC;AAChE,UAAI,MAAM;AACR,eAAO,aAAa,SAAS,IAAI,CAAC,aAAa,SAAS,SAAS,CAAC;AAAA,MACpE;AAAA,IACF;AAEA,QAAI,WAAW;AACb,YAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,UAAI,QAAQ,WAAW,QAAQ,cAAc,QAAQ,UAAU;AAC7D,eAAO,cAAc,SAAS,SAAS,CAAC;AAAA,MAC1C;AAAA,IACF;AAEA,QAAI,YAAY;AACd,YAAM,OAAO,aAAa,EAAE;AAC5B,UAAI,SAAS,YAAY,SAAS,QAAQ;AACxC,eAAO,aAAa,SAAS,IAAI,CAAC,aAAa,SAAS,UAAU,CAAC;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,KAAM,QAAO,WAAW,SAAS,UAAU,IAAI,IAAI,CAAC;AAExD,WAAO;AAAA,EACT;AAEA,QAAM,UAAuB,CAAC;AAC9B,QAAM,KAAK,IAAI,iBAAiB,qEAAqE,CAAC,EAAE,QAAQ,CAAC,OAAO;AACtH,UAAM,OAAO,aAAa,EAAE;AAC5B,QAAI,SAAS,SAAU;AACvB,UAAM,aAAa,KAAK,IAAI,YAAY,KAAK,KAAK,EAAE,KAAK,KAAK,IAAI,OAAO;AACzE,UAAM,SAAS,KAAK,IAAI,aAAa;AACrC,UAAM,KAAK,KAAK,IAAI,IAAI;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,WAAW,GAAG,aAAa,UAAU,KAAK,GAAG,aAAa,eAAe,MAAM;AACrF,UAAM,WAAW,cAAc,IAAI,QAAW,UAAU;AAExD,QAAI,cAAc,QAAQ;AACxB,cAAQ,KAAK;AAAA,QACX,KAAK,GAAG,QAAQ,YAAY;AAAA,QAC5B;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,SAAS,iBAAiB,MAAM,OAAQ,KAAK,IAAI,EAAE,KAAK;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,SAAqB,CAAC;AAC5B,QAAM,KAAK,IAAI,iBAAiB,wFAAwF,CAAC,EAAE,QAAQ,CAAC,OAAO;AACzI,UAAM,QAAQ,UAAU,EAAE;AAC1B,UAAM,cAAc,KAAK,IAAI,aAAa;AAC1C,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,KAAK,KAAK,IAAI,IAAI;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM,KAAK,GAAG,QAAQ,YAAY;AACxD,UAAM,SAAS,KAAK,IAAI,aAAa;AACrC,UAAM,WAAW,GAAG,aAAa,UAAU;AAC3C,UAAM,WAAW,cAAc,IAAI,KAAK;AAExC,QAAI,SAAS,eAAe,QAAQ,UAAU,IAAI;AAChD,aAAO,KAAK;AAAA,QACV,KAAK,GAAG,QAAQ,YAAY;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,SAAS,iBAAiB,MAAM,OAAQ,KAAK,IAAI,EAAE,KAAM,OAAO,UAAU,IAAI,OAAO;AAAA,MAChG,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,QAAmB,CAAC;AAC1B,QAAM,KAAK,IAAI,iBAAiB,SAAS,CAAC,EAAE,QAAQ,CAAC,OAAO;AAC1D,UAAM,OAAO,aAAa,EAAE;AAC5B,QAAI,SAAS,OAAQ;AACrB,UAAM,WAAW,KAAK,IAAI,YAAY,KAAK,KAAK,EAAE;AAClD,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,SAAS,KAAK,IAAI,aAAa;AAErC,QAAI,CAAC,YAAY,SAAS,IAAK;AAE/B,UAAM,KAAK;AAAA,MACT,KAAK,GAAG,QAAQ,YAAY;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACE;AAAA,MACA,UAAU,QAAQ,MAAM,WAAW,MAAM,KAAK,CAAC,KAAK,SAAS,IAAI,SAAS,QAAQ,CAAC;AAAA,MACnF,UAAU,SACN,eAAe,SAAS,MAAM,CAAC,MAC/B,6BAA6B,SAAS,QAAQ,CAAC;AAAA,IACvD,CAAC;AAAA,EACH,CAAC;AAED,QAAM,WAAyB,CAAC;AAChC,QAAM,KAAK,IAAI,iBAAiB,YAAY,CAAC,EAAE,QAAQ,CAAC,OAAO;AAC7D,UAAM,cAAc,KAAK,EAAE;AAC3B,QAAI,aAAa;AACf,eAAS,KAAK;AAAA,QACZ,OAAO,GAAG,QAAQ,YAAY;AAAA,QAC9B,MAAM;AAAA,QACN,UAAU,gCAAgC,SAAS,WAAW,CAAC;AAAA,MACjE,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,YAAoD,CAAC;AAC3D,QAAM,KAAK,IAAI,iBAAiB,+DAA+D,CAAC,EAAE,QAAQ,CAAC,OAAO;AAChH,UAAM,OAAO,KAAK,IAAI,MAAM,KAAK,GAAG,QAAQ,YAAY;AACxD,UAAM,QAAQ,KAAK,IAAI,YAAY,KAAK,KAAK,EAAE,GAAG,MAAM,GAAG,EAAE;AAC7D,QAAI,QAAQ,MAAO,WAAU,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,EACnD,CAAC;AAED,QAAM,gBAAmC,CAAC;AAC1C,QAAM,KAAK,IAAI,iBAAiB,8CAA8C,CAAC,EAAE,QAAQ,CAAC,OAAO;AAC/F,UAAM,OAAO,KAAK,IAAI,MAAM,KAAK;AACjC,kBAAc,KAAK;AAAA,MACjB;AAAA,MACA,UAAU,KAAK,IAAI,WAAW;AAAA,MAC9B,MAAM,KAAK,EAAE;AAAA,MACb,UAAU,GAAG,aAAa,MAAM,IAC5B,aAAa,SAAS,IAAI,CAAC,MAC3B;AAAA,IACN,CAAC;AAAA,EACH,CAAC;AAED,QAAM,QAAmB,CAAC;AAC1B,QAAM,KAAK,IAAI,iBAAiB,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,UAAU;AAChE,UAAM,KAAK;AAAA,MACT,IAAI,KAAK,MAAM,IAAI;AAAA,MACnB,OAAO,KAAK,MAAM,YAAY,KAAK,KAAK,MAAM,iBAAiB;AAAA,MAC/D,QAAQ,KAAK,MAAM,QAAQ;AAAA,MAC3B,QAAQ,KAAK,MAAM,QAAQ,KAAK;AAAA,MAChC,YAAY,MAAM,KAAK,KAAK,mBAAmB,yBAAyB,KAAK,CAAC,CAAC,EAAE;AAAA,MACjF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL,SAAS,QAAQ,MAAM,GAAG,mBAAmB;AAAA,IAC7C,QAAQ,OAAO,MAAM,GAAG,mBAAmB;AAAA,IAC3C,OAAO,MAAM,MAAM,GAAG,mBAAmB;AAAA,IACzC,UAAU,SAAS,MAAM,GAAG,EAAE;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,IAAI,SAAS;AAAA,IACtB,WAAW,IAAI,SAAS;AAAA,EAC1B;AACF;AAEA,SAAS,iBAA0B;AACjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,SAAqC;AAC5D,QAAM,WAA8B,CAAC;AAErC,aAAW,SAAS,QAAQ,QAAQ;AAClC,UAAM,cAAc,MAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,MAAM;AAC5E,QAAI,CAAC,YAAa;AAElB,UAAM,WAAW,MAAM,aACjB,MAAM,SAAS,eAAe,KAAK,UAAU,MAAM,MAAM,CAAC,MAAM,UAChE,MAAM,QAAQ,cAAc,KAAK,UAAU,MAAM,KAAK,CAAC,MAAM,UAC7D,MAAM,KAAK,WAAW,KAAK,UAAU,IAAI,MAAM,EAAE,EAAE,CAAC,MAAM,UAC1D,MAAM,OAAO,WAAW,KAAK,UAAU,UAAU,MAAM,IAAI,IAAI,CAAC,MAAM,SACvE,WAAW,KAAK,UAAU,GAAG,MAAM,GAAG,iBAAiB,MAAM,eAAe,EAAE,IAAI,CAAC;AAExF,UAAM,cAAwB,CAAC;AAC/B,QAAI,MAAM,MAAM,CAAC,SAAS,SAAS,IAAI,MAAM,EAAE,EAAE,EAAG,aAAY,KAAK,WAAW,KAAK,UAAU,IAAI,MAAM,EAAE,EAAE,CAAC,GAAG;AACjH,QAAI,MAAM,QAAQ,CAAC,SAAS,SAAS,MAAM,IAAI,EAAG,aAAY,KAAK,WAAW,KAAK,UAAU,UAAU,MAAM,IAAI,IAAI,CAAC,GAAG;AACzH,QAAI,MAAM,UAAU,CAAC,SAAS,SAAS,MAAM,MAAM,EAAG,aAAY,KAAK,eAAe,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG;AACrH,QAAI,MAAM,eAAe,CAAC,SAAS,SAAS,MAAM,WAAW,EAAG,aAAY,KAAK,oBAAoB,KAAK,UAAU,MAAM,WAAW,CAAC,GAAG;AACzI,QAAI,MAAM,SAAS,CAAC,SAAS,SAAS,MAAM,KAAK,EAAG,aAAY,KAAK,cAAc,KAAK,UAAU,MAAM,KAAK,CAAC,GAAG;AAEjH,aAAS,KAAK;AAAA,MACZ,UAAU;AAAA,MACV,MAAM,MAAM,SAAS,aAAa,aAAa;AAAA,MAC/C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,SAAS,MAAM,WACX,YAAY,MAAM,IAAI,aAAa,WAAW,MAC9C,GAAG,MAAM,IAAI,aAAa,WAAW;AAAA,MACzC,YAAY,MAAM,UAAU,MAAM,SAAS,MAAM,KAAK,SAAU,MAAM,cAAc,WAAW;AAAA,MAC/F,YAAY;AAAA,QACV,MAAM,MAAM;AAAA,QACZ,OAAO,MAAM;AAAA,QACb,aAAa,MAAM;AAAA,QACnB,MAAM,MAAM;AAAA,QACZ,IAAI,MAAM;AAAA,QACV,QAAQ,MAAM;AAAA,QACd,UAAU,MAAM;AAAA,QAChB,KAAK,MAAM;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,UAAU,QAAQ,SAAS;AACpC,UAAM,aAAa,OAAO,QAAQ;AAClC,UAAM,cAAc,OAAO,QAAQ,OAAO;AAC1C,QAAI,CAAC,YAAa;AAElB,UAAM,WAAW,OAAO,aAClB,OAAO,SAAS,eAAe,KAAK,UAAU,OAAO,MAAM,CAAC,MAAM,UAClE,OAAO,OAAO,aAAa,KAAK,UAAU,UAAU,CAAC,aAAa,KAAK,UAAU,OAAO,IAAI,CAAC,QAAQ,UACrG,OAAO,KAAK,WAAW,KAAK,UAAU,IAAI,OAAO,EAAE,EAAE,CAAC,MAAM,UAC5D,OAAO,QAAQ,WAAW,sBAAsB;AAEtD,UAAM,cAAwB,CAAC;AAC/B,QAAI,OAAO,MAAM,CAAC,SAAS,SAAS,OAAO,EAAE,EAAG,aAAY,KAAK,WAAW,KAAK,UAAU,IAAI,OAAO,EAAE,EAAE,CAAC,GAAG;AAC9G,QAAI,OAAO,UAAU,CAAC,SAAS,SAAS,OAAO,MAAM,EAAG,aAAY,KAAK,eAAe,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG;AACxH,QAAI,OAAO,QAAQ,CAAC,SAAS,SAAS,OAAO,IAAI,EAAG,aAAY,KAAK,aAAa,KAAK,UAAU,OAAO,IAAI,CAAC,GAAG;AAChH,QAAI,OAAO,QAAS,aAAY,KAAK,WAAW,KAAK,UAAU,OAAO,OAAO,CAAC,GAAG;AAEjF,aAAS,KAAK;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,SAAS,OAAO,WACZ,sBAAsB,WAAW,MACjC,OAAO,SAAS,WACd,yBAAyB,WAAW,MACpC,aAAa,WAAW;AAAA,MAC9B,YAAY,OAAO,UAAU,OAAO,OAAO,SAAU,OAAO,KAAK,WAAW;AAAA,MAC5E,YAAY;AAAA,QACV,MAAM,OAAO;AAAA,QACb,QAAQ,OAAO;AAAA,QACf,IAAI,OAAO;AAAA,QACX,MAAM,OAAO;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,KAAK,OAAO;AAAA,QACZ,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,QAAQ,QAAQ,OAAO;AAChC,UAAM,WAAW,KAAK,QAAQ;AAC9B,aAAS,KAAK;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,QACX,GAAI,KAAK,SAAS,CAAC,eAAe,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,IAAI,CAAC;AAAA,QACrE,aAAa,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,MACxC;AAAA,MACA,SAAS,KAAK,WACV,qBAAqB,KAAK,IAAI,QAAQ,KAAK,IAAI,MAC/C,+BAA+B,KAAK,IAAI,QAAQ,KAAK,IAAI;AAAA,MAC7D,YAAY,KAAK,SAAS,SAAS;AAAA,MACnC,YAAY;AAAA,QACV,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,KAAK,KAAK;AAAA,QACV,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,WAAW,QAAQ,UAAU;AACtC,aAAS,KAAK;AAAA,MACZ,UAAU;AAAA,MACV,MAAM;AAAA,MACN,MAAM,QAAQ;AAAA,MACd,UAAU,QAAQ;AAAA,MAClB,aAAa,CAAC,aAAa,KAAK,UAAU,QAAQ,IAAI,CAAC,GAAG;AAAA,MAC1D,SAAS,QAAQ,QAAQ,KAAK;AAAA,MAC9B,YAAY;AAAA,MACZ,YAAY;AAAA,QACV,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,UAAU,QAAQ,eAAe;AAC1C,aAAS,KAAK;AAAA,MACZ,UAAU;AAAA,MACV,MAAM,OAAO;AAAA,MACb,MAAM,OAAO,QAAQ,OAAO;AAAA,MAC5B,UAAU,OAAO;AAAA,MACjB,aAAa,CAAC;AAAA,MACd,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,YAAY;AAAA,QACV,MAAM,OAAO;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,aAAa,OAAO;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,cACP,UACA,SACA,cACU;AACV,QAAM,WAAqB,CAAC;AAE5B,MAAI,CAAC,cAAc;AACjB,aAAS,KAAK,iGAAiG;AAAA,EACjH;AAEA,QAAM,sBAAsB,SAAS,OAAO,CAAC,YAAY,QAAQ,aAAa,WAAW,QAAQ,eAAe,KAAK;AACrH,MAAI,oBAAoB,SAAS,GAAG;AAClC,aAAS;AAAA,MACP,GAAG,oBAAoB,MAAM,uFAAuF,oBAAoB,IAAI,CAAC,YAAY,QAAQ,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,IACnM;AAAA,EACF;AAEA,MAAI,QAAQ,cAAc,WAAW,GAAG;AACtC,aAAS,KAAK,2HAA2H;AAAA,EAC3I;AAEA,aAAW,QAAQ,QAAQ,OAAO;AAChC,UAAM,YAAY,QAAQ,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,QAAQ;AAC3E,QAAI,KAAK,aAAa,KAAK,CAAC,WAAW;AACrC,eAAS;AAAA,QACP,QAAQ,KAAK,MAAM,IAAI,KAAK,KAAK,EAAE,QAAQ,KAAK,UAAU;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,MAAM,UAAU,kBAAkB;AAC5C,aAAS,KAAK,sBAAsB,gBAAgB,6DAA6D;AAAA,EACnH;AAEA,QAAM,cAAc,SAAS,OAAO,CAAC,YACnC,QAAQ,aAAa,YAAY,QAAQ,aAAa,WAAW,QAAQ,aAAa,MACvF;AACD,MAAI,YAAY,WAAW,GAAG;AAC5B,aAAS,KAAK,kJAAkJ;AAAA,EAClK;AAEA,SAAO;AACT;AAEA,SAAS,IAAI,SAAkB,SAAuB;AACpD,MAAI,QAAS,SAAQ,IAAI,OAAO;AAClC;AAEA,eAAe,yBAAyB,MAAW,SAAoC;AACrF,MAAI,CAAC,KAAK,iBAAiB,OAAO,KAAK,cAAc,aAAa,YAAY;AAC5E,QAAI,SAAS,+EAA+E;AAC5F,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,MAAM,KAAK,cAAc,SAAS,EAAE,iBAAiB,MAAM,CAAC;AAAA,EACrE,SAAS,OAAY;AACnB,QAAI,SAAS,uCAAuC,OAAO,WAAW,KAAK,qCAAqC;AAChH,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,OAAoC;AACpD,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEO,SAAS,qBAAqB,OAA0B;AAC7D,QAAM,WAAW,qBAAqB,KAAK;AAE3C,MAAI,SAAS,SAAS,wBAAwB;AAC5C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,SAAS,yBAAyB;AAC7C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,SAAS,SAAS,oBAAoB;AACxC,WAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,SAAS,qBAAqB,OAAqC;AACjE,MAAI,iBAAiB,qBAAqB;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,aAAa,KAAK;AAElC,MAAI,sBAAsB,OAAO,GAAG;AAClC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI,0BAA0B,OAAO,GAAG;AACtC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,IAAI,oBAAoB,kBAAkB,WAAW,4BAA4B,EAAE,OAAO,MAAM,CAAC;AAC1G;AAEA,SAAS,sBAAsB,SAA0B;AACvD,SAAO,8HAA8H,KAAK,OAAO;AACnJ;AAEA,SAAS,0BAA0B,SAA0B;AAC3D,SAAO,kGAAkG,KAAK,OAAO;AACvH;AAEA,SAAS,aAAa,OAAwB;AAC5C,MAAI,iBAAiB,MAAO,QAAO,MAAM;AACzC,SAAO,OAAO,SAAS,EAAE;AAC3B;","names":["require"]}
|
package/dist/cli.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
captureElements,
|
|
4
4
|
formatCaptureFailure,
|
|
5
5
|
toPageSummary
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JWGYAQ3O.js";
|
|
7
7
|
import {
|
|
8
8
|
genCmd
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-A4IHRXON.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
12
12
|
import { Command as Command9 } from "commander";
|
|
@@ -370,7 +370,7 @@ Examples:
|
|
|
370
370
|
}
|
|
371
371
|
console.log(`\u2705 Normalized ${index.summary.parsed} case(s). Output \u2192 .cementic/normalized/`);
|
|
372
372
|
if (opts.andGen) {
|
|
373
|
-
const { gen } = await import("./gen-
|
|
373
|
+
const { gen } = await import("./gen-J4HZWS5T.js");
|
|
374
374
|
await gen({ lang: opts.lang || "ts", out: "tests/generated" });
|
|
375
375
|
}
|
|
376
376
|
});
|
|
@@ -748,7 +748,7 @@ FORMS:
|
|
|
748
748
|
AUTH:
|
|
749
749
|
"login with valid credentials"
|
|
750
750
|
-> fill(CT_VAR_USERNAME) + fill(CT_VAR_PASSWORD) + click login
|
|
751
|
-
+ await expect(page).toHaveURL(/dashboard
|
|
751
|
+
+ await expect(page).toHaveURL(/dashboard/) // or the exact target named in the intent, e.g. /secure/
|
|
752
752
|
|
|
753
753
|
"login fails / invalid credentials"
|
|
754
754
|
-> fill bad values + click login
|
|
@@ -903,6 +903,10 @@ RULE 6 - NEGATIVE CASES
|
|
|
903
903
|
Generate negative scenarios only when the intent implies interaction:
|
|
904
904
|
- Form testing: always include an invalid-input scenario
|
|
905
905
|
- Authentication: always include a wrong-credentials scenario
|
|
906
|
+
- For field-specific auth failures, make the field under test explicitly wrong:
|
|
907
|
+
invalid email -> 'invalid-not-an-email'
|
|
908
|
+
invalid username -> 'nonexistent-user'
|
|
909
|
+
invalid password -> 'wrong-password-123'
|
|
906
910
|
- Presence checks: no negative case needed
|
|
907
911
|
- Button click: negative case only if the button can be disabled
|
|
908
912
|
|
|
@@ -917,6 +921,7 @@ Never use the literal word 'value' as a placeholder.
|
|
|
917
921
|
When filling a field:
|
|
918
922
|
- If the user context lists a matching CT_VAR_* variable, write it exactly as '\${CT_VAR_FIELDNAME}` + `'
|
|
919
923
|
- Otherwise use a field-specific fallback such as 'test-username', 'test-email@example.com', 'test-password', or 'test-search'
|
|
924
|
+
- For redirect or stay-on-page assertions, derive the URL regex from the exact intent text or explicit path; do not invent generic alternates like dashboard|success|home
|
|
920
925
|
|
|
921
926
|
RULE 9 - SCOPE DISCIPLINE
|
|
922
927
|
Match scenario count to intent complexity:
|
|
@@ -1304,7 +1309,7 @@ FORMS:
|
|
|
1304
1309
|
AUTH:
|
|
1305
1310
|
"login with valid credentials"
|
|
1306
1311
|
-> fill(CT_VAR_USERNAME) + fill(CT_VAR_PASSWORD) + click login
|
|
1307
|
-
+ await expect(page).toHaveURL(/dashboard
|
|
1312
|
+
+ await expect(page).toHaveURL(/dashboard/) // or the exact target named in the intent, e.g. /secure/
|
|
1308
1313
|
|
|
1309
1314
|
"login fails / invalid credentials"
|
|
1310
1315
|
-> fill bad values + click login
|
|
@@ -1468,11 +1473,13 @@ RULES:
|
|
|
1468
1473
|
9. For presence-only intents such as "verify X" or "X is present", only assert visibility. Do not click and do not assert outcomes after clicking.
|
|
1469
1474
|
${RULE_10_PLAYWRIGHT_KNOWLEDGE_BASE2}
|
|
1470
1475
|
11. For fill steps, never use the literal word "value". If matching CT_VAR_* variables are listed in the prompt, use them in human-readable step text and use a realistic non-generic runtime value in the JSON value field. Otherwise use a field-specific fallback such as test-username or test-password.
|
|
1471
|
-
12.
|
|
1472
|
-
13.
|
|
1473
|
-
14.
|
|
1474
|
-
15.
|
|
1475
|
-
16.
|
|
1476
|
+
12. For field-specific auth failures, make the field under test explicitly wrong: invalid email -> invalid-not-an-email, invalid username -> nonexistent-user, invalid password -> wrong-password-123.
|
|
1477
|
+
13. When redirect or stay-on-page assertions are needed, derive the URL regex from the exact intent text or explicit path. Do not invent generic alternates such as dashboard|success|home.
|
|
1478
|
+
14. When a CTA could realistically be a button or a link, prefer a resilient selector such as getByRole('button', { name: /Join Now/i }).or(page.getByRole('link', { name: /Join Now/i })).
|
|
1479
|
+
15. The "selector" field must be a raw selector expression such as locator("#username") or getByRole('button', { name: "Login" }). Never wrap selectors in page. or page.locator("...") inside JSON, except inside Locator.or(...) where the alternate locator must be page-scoped.
|
|
1480
|
+
16. Tie-breaker for multiple matching elements: prefer highest confidence, then interactive elements, then the first visible-looking candidate.
|
|
1481
|
+
17. If a page clearly contains login or auth fields, do not create submit scenarios that only click the button. Include realistic fill steps for captured username/email and password inputs when the intent asks for auth interaction.
|
|
1482
|
+
18. "verify title" means the main visible heading on the page, not the browser tab title, unless the intent explicitly says browser title, tab title, or page title.
|
|
1476
1483
|
|
|
1477
1484
|
OUTPUT SCHEMA:
|
|
1478
1485
|
{
|
|
@@ -1892,7 +1899,10 @@ function buildAuthFallbackScenarios(elementMap, prefix, authElements, feature) {
|
|
|
1892
1899
|
const alert = findAlertElement(elementMap);
|
|
1893
1900
|
const wantsNavigation = /\b(valid credentials|correct credentials|successful login|log in successfully|login succeeds|sign in succeeds)\b/.test(normalizedFeature) || /\b(redirect|redirects|redirected|secure area|dashboard|redirected to|goes to|navigates? to)\b/.test(normalizedFeature);
|
|
1894
1901
|
const wantsError = /\b(wrong password|wrong credentials|invalid|incorrect|error message|shows an error|alert)\b/.test(normalizedFeature);
|
|
1902
|
+
const invalidAuthTarget = inferInvalidAuthTarget(normalizedFeature);
|
|
1895
1903
|
const successUrlPattern = deriveSuccessUrlPattern(elementMap, normalizedFeature);
|
|
1904
|
+
const invalidUsernameValue = invalidAuthTarget === "email" ? "invalid-not-an-email" : invalidAuthTarget === "username" ? "nonexistent-user" : usernameValue;
|
|
1905
|
+
const invalidPasswordValue = invalidAuthTarget === "password" ? "wrong-password-123" : "wrong-password-123";
|
|
1896
1906
|
if (wantsNavigation) {
|
|
1897
1907
|
return [{
|
|
1898
1908
|
id: `${prefix}-001`,
|
|
@@ -1952,14 +1962,14 @@ function buildAuthFallbackScenarios(elementMap, prefix, authElements, feature) {
|
|
|
1952
1962
|
{
|
|
1953
1963
|
action: "fill",
|
|
1954
1964
|
selector: usernameInput.selector,
|
|
1955
|
-
value:
|
|
1956
|
-
human: buildFillHuman("Fill in the username field", usernameInput.selector,
|
|
1965
|
+
value: invalidUsernameValue,
|
|
1966
|
+
human: buildFillHuman("Fill in the username field", usernameInput.selector, invalidUsernameValue)
|
|
1957
1967
|
},
|
|
1958
1968
|
{
|
|
1959
1969
|
action: "fill",
|
|
1960
1970
|
selector: passwordInput.selector,
|
|
1961
|
-
value:
|
|
1962
|
-
human: buildFillHuman("Fill in the password field", passwordInput.selector,
|
|
1971
|
+
value: invalidPasswordValue,
|
|
1972
|
+
human: buildFillHuman("Fill in the password field", passwordInput.selector, invalidPasswordValue)
|
|
1963
1973
|
},
|
|
1964
1974
|
{
|
|
1965
1975
|
action: "click",
|
|
@@ -2340,11 +2350,44 @@ function detectAuthElements(elementMap) {
|
|
|
2340
2350
|
function findAlertElement(elementMap) {
|
|
2341
2351
|
return elementMap.elements.find((element) => element.role === "alert" || element.category === "status" || String(element.attributes.role ?? "").toLowerCase() === "alert");
|
|
2342
2352
|
}
|
|
2343
|
-
function
|
|
2344
|
-
|
|
2353
|
+
function sanitizeUrlIntentPhrase(value) {
|
|
2354
|
+
return value.replace(/`+/g, " ").replace(/^\/+/, " ").replace(/\/[dgimsuvy]*$/i, " ").replace(/["']/g, " ").replace(/\b(?:the|a|an|user|same|current)\b/gi, " ").replace(/\b(?:page|screen|view|area|section|route|path|url|form)\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
2355
|
+
}
|
|
2356
|
+
function urlPatternFromIntentText(value) {
|
|
2357
|
+
const sanitized = value.replace(/`+/g, "").trim();
|
|
2358
|
+
const explicitPath = sanitized.match(/\/[a-z0-9/_-]+/i)?.[0] ?? sanitized.match(/https?:\/\/[^\s'"]+/i)?.[0];
|
|
2345
2359
|
if (explicitPath) {
|
|
2346
|
-
|
|
2360
|
+
const parsed = explicitPath.startsWith("/") ? explicitPath : (() => {
|
|
2361
|
+
try {
|
|
2362
|
+
return new URL(explicitPath).pathname;
|
|
2363
|
+
} catch {
|
|
2364
|
+
return "";
|
|
2365
|
+
}
|
|
2366
|
+
})();
|
|
2367
|
+
if (parsed) {
|
|
2368
|
+
return parsed.replace(/^\/+/, "").split("/").map(escapeForRegex).join("\\/");
|
|
2369
|
+
}
|
|
2347
2370
|
}
|
|
2371
|
+
const phraseMatch = sanitized.match(/\b(?:redirect(?:s|ed)?|navigat(?:e|es|ed)|go(?:es|ne)?|route|path|url)\b(?:\s+(?:to|on|contains))?\s+(?:the\s+)?(.+?)(?:[.?!]|$)/i) ?? sanitized.match(/\b(?:remain|remains|stays|stay|still)\s+(?:on|at)\s+(?:the\s+)?(.+?)(?:[.?!]|$)/i);
|
|
2372
|
+
const phrase = sanitizeUrlIntentPhrase(phraseMatch?.[1] ?? "");
|
|
2373
|
+
if (!phrase) return void 0;
|
|
2374
|
+
return phrase.split(/[\s/-]+/).filter(Boolean).map(escapeForRegex).join("[-_\\/]?");
|
|
2375
|
+
}
|
|
2376
|
+
function inferInvalidAuthTarget(normalizedFeature) {
|
|
2377
|
+
if (/\b(?:invalid|wrong|incorrect|bad|not an email)\s+(?:e-?mail|email)\b|\b(?:e-?mail|email)\b.{0,24}\b(?:invalid|wrong|incorrect|bad|not an email)\b/.test(normalizedFeature)) {
|
|
2378
|
+
return "email";
|
|
2379
|
+
}
|
|
2380
|
+
if (/\b(?:invalid|wrong|incorrect|bad|nonexistent)\s+(?:user(?:name)?|login)\b|\b(?:user(?:name)?|login)\b.{0,24}\b(?:invalid|wrong|incorrect|bad|nonexistent)\b/.test(normalizedFeature)) {
|
|
2381
|
+
return "username";
|
|
2382
|
+
}
|
|
2383
|
+
if (/\b(?:invalid|wrong|incorrect|bad)\s+password\b|\bpassword\b.{0,24}\b(?:invalid|wrong|incorrect|bad)\b/.test(normalizedFeature)) {
|
|
2384
|
+
return "password";
|
|
2385
|
+
}
|
|
2386
|
+
return void 0;
|
|
2387
|
+
}
|
|
2388
|
+
function deriveSuccessUrlPattern(elementMap, normalizedFeature) {
|
|
2389
|
+
const intentPattern = urlPatternFromIntentText(normalizedFeature);
|
|
2390
|
+
if (intentPattern) return intentPattern;
|
|
2348
2391
|
const authLink = elementMap.elements.find((element) => {
|
|
2349
2392
|
if (element.category !== "link") return false;
|
|
2350
2393
|
const href2 = String(element.attributes.href ?? "");
|
|
@@ -2355,7 +2398,7 @@ function deriveSuccessUrlPattern(elementMap, normalizedFeature) {
|
|
|
2355
2398
|
if (href.startsWith("/")) {
|
|
2356
2399
|
return href.replace(/^\/+/, "").split("/").map(escapeForRegex).join("\\/");
|
|
2357
2400
|
}
|
|
2358
|
-
return "secure
|
|
2401
|
+
return "secure";
|
|
2359
2402
|
}
|
|
2360
2403
|
function buildDeterministicAnalysis(elementMap, requestedFeature) {
|
|
2361
2404
|
const intentProfile = buildIntentProfile(requestedFeature);
|
|
@@ -2596,7 +2639,7 @@ function saveCaptureJson(elementMap, analysis, outputDir = ".cementic/capture")
|
|
|
2596
2639
|
const filePath = join3(outputDir, fileName);
|
|
2597
2640
|
writeFileSync3(filePath, JSON.stringify({
|
|
2598
2641
|
_meta: {
|
|
2599
|
-
version: "0.2.
|
|
2642
|
+
version: "0.2.17",
|
|
2600
2643
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2601
2644
|
tool: "@cementic/cementic-test"
|
|
2602
2645
|
},
|