@muuktest/amikoo-playwright 2.0.0 → 2.0.1
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/dist/helpers/failure-analyzer.cjs +1 -1
- package/dist/helpers/failure-analyzer.cjs.map +1 -1
- package/dist/helpers/failure-analyzer.d.cts +1 -1
- package/dist/helpers/failure-analyzer.d.ts +1 -1
- package/dist/helpers/failure-analyzer.js +1 -1
- package/dist/helpers/failure-analyzer.js.map +1 -1
- package/package.json +1 -1
|
@@ -126,6 +126,7 @@ async function analyzeFailure(page, testInfo, consoleLogs, networkFailures, arti
|
|
|
126
126
|
error: errorFirstLine,
|
|
127
127
|
location,
|
|
128
128
|
context,
|
|
129
|
+
pw_console: snippet,
|
|
129
130
|
// v3 enriched
|
|
130
131
|
test: {
|
|
131
132
|
title: testInfo.title,
|
|
@@ -154,7 +155,6 @@ async function analyzeFailure(page, testInfo, consoleLogs, networkFailures, arti
|
|
|
154
155
|
errorDetails: {
|
|
155
156
|
message: errorMessage,
|
|
156
157
|
stack: errorStack,
|
|
157
|
-
pw_console: snippet,
|
|
158
158
|
isTimeout,
|
|
159
159
|
timeoutMs,
|
|
160
160
|
causes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/failure-analyzer.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Page, TestInfo } from '@playwright/test';\n\n// ─── Exported Interfaces ───────────────────────────────────────────────────\n\nexport interface ConsoleEntry {\n type: string;\n text: string;\n timestamp: string;\n location?: { url?: string; lineNumber?: number; columnNumber?: number };\n}\n\nexport interface NetworkFailure {\n url: string;\n method: string;\n status: number;\n statusText: string;\n resourceType: string;\n timestamp: string;\n}\n\nexport interface FailureSelector {\n raw: string;\n method: string;\n value: string;\n}\n\nexport interface ErrorCause {\n message: string;\n stack: string;\n}\n\nexport interface ErrorEntry {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n}\n\nexport interface FailureAnalysis {\n // Backward-compatible original 5 fields\n selector: string;\n action: string;\n error: string;\n location: string;\n context: string;\n\n // Enriched fields (v2+)\n test: {\n title: string;\n titlePath: string[];\n file: string;\n line: number | null;\n project: string;\n retries: number;\n retry: number;\n duration: number;\n status: string | undefined;\n expectedStatus: string;\n tags: string[];\n };\n page: {\n url: string;\n title: string | null;\n viewport: { width: number; height: number } | null;\n };\n selectorDetails: FailureSelector | null;\n consoleLogs: ConsoleEntry[];\n consoleErrors: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n errorDetails: {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n };\n errors: ErrorEntry[];\n timing: {\n testStart: string;\n failureCapture: string;\n duration: number;\n };\n artifacts: {\n domJsonPath: string;\n screenshotPath: string;\n failureInfoPath: string;\n };\n _version: 3;\n}\n\n// ─── Page Listener Setup ───────────────────────────────────────────────────\n\nexport function setupPageListeners(page: Page): {\n consoleLogs: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n} {\n const consoleLogs: ConsoleEntry[] = [];\n const networkFailures: NetworkFailure[] = [];\n const MAX_CONSOLE_ENTRIES = 500;\n\n page.on('console', (msg) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n const loc = msg.location();\n consoleLogs.push({\n type: msg.type(),\n text: msg.text(),\n timestamp: new Date().toISOString(),\n location: {\n url: loc.url,\n lineNumber: loc.lineNumber,\n columnNumber: loc.columnNumber,\n },\n });\n });\n\n page.on('pageerror', (err) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n consoleLogs.push({\n type: 'pageerror',\n text: err.message,\n timestamp: new Date().toISOString(),\n });\n });\n\n page.on('response', (response) => {\n if (response.status() >= 400) {\n networkFailures.push({\n url: response.url(),\n method: response.request().method(),\n status: response.status(),\n statusText: response.statusText(),\n resourceType: response.request().resourceType(),\n timestamp: new Date().toISOString(),\n });\n }\n });\n\n page.on('requestfailed', (request) => {\n networkFailures.push({\n url: request.url(),\n method: request.method(),\n status: 0,\n statusText: request.failure()?.errorText ?? 'Request failed',\n resourceType: request.resourceType(),\n timestamp: new Date().toISOString(),\n });\n });\n\n return { consoleLogs, networkFailures };\n}\n\n// ─── Failure Analysis ──────────────────────────────────────────────────────\n\nexport async function analyzeFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[],\n artifacts: { jsonPath: string; screenshotPath: string; failureInfoPath: string }\n): Promise<FailureAnalysis> {\n const error = (testInfo.error ?? testInfo.errors?.[0] ?? {}) as { message?: string; stack?: string };\n const errorMessage = error.message ?? '';\n const errorStack = error.stack ?? '';\n\n const now = new Date();\n const duration = testInfo.duration ?? 0;\n const testStart = new Date(now.getTime() - duration).toISOString();\n const failureCapture = now.toISOString();\n\n // Original 5 fields (backward compat)\n const selector = extractSelector(errorMessage);\n const action = extractAction(errorMessage);\n const errorFirstLine = errorMessage.split('\\n')[0];\n const location = extractLocation(errorStack, testInfo);\n const context = errorMessage;\n\n // Page state\n let pageTitle: string | null = null;\n try {\n pageTitle = await page.title();\n } catch {\n // page may be closed\n }\n\n // Timeout detection\n const isTimeout = /timeout/i.test(errorMessage);\n const timeoutMs = extractTimeoutMs(errorMessage);\n\n // Snippet & cause chain for primary error\n const snippet = generateSnippet(errorStack, testInfo);\n const causes = extractCauseChain(error);\n\n // Build errors array from all test errors\n const rawErrors = testInfo.errors ?? [];\n const errors: ErrorEntry[] = rawErrors.map((err) => {\n const msg = (err as { message?: string }).message ?? '';\n const stk = (err as { stack?: string }).stack ?? '';\n return {\n message: msg,\n stack: stk,\n pw_console: generateSnippet(stk, testInfo),\n isTimeout: /timeout/i.test(msg),\n timeoutMs: extractTimeoutMs(msg),\n causes: extractCauseChain(err),\n };\n });\n\n const analysis: FailureAnalysis = {\n // Backward-compatible\n selector,\n action,\n error: errorFirstLine,\n location,\n context,\n\n // v3 enriched\n test: {\n title: testInfo.title,\n titlePath: testInfo.titlePath,\n file: testInfo.file,\n line: (testInfo as unknown as { line?: number }).line ?? null,\n project: testInfo.project.name,\n retries: testInfo.project.retries,\n retry: testInfo.retry,\n duration,\n status: testInfo.status,\n expectedStatus: testInfo.expectedStatus,\n tags: (testInfo as unknown as { tags?: string[] }).tags ?? [],\n },\n page: {\n url: page.url(),\n title: pageTitle,\n viewport: page.viewportSize(),\n },\n selectorDetails: extractSelectorDetails(errorMessage),\n consoleLogs,\n consoleErrors: consoleLogs.filter(\n (e) => e.type === 'error' || e.type === 'pageerror'\n ),\n networkFailures,\n errorDetails: {\n message: errorMessage,\n stack: errorStack,\n pw_console: snippet,\n isTimeout,\n timeoutMs,\n causes,\n },\n errors,\n timing: {\n testStart,\n failureCapture,\n duration,\n },\n artifacts: {\n domJsonPath: artifacts.jsonPath,\n screenshotPath: artifacts.screenshotPath,\n failureInfoPath: artifacts.failureInfoPath,\n },\n _version: 3,\n };\n\n fs.writeFileSync(artifacts.failureInfoPath, JSON.stringify(analysis, null, 2));\n\n return analysis;\n}\n\n// ─── Snippet / Cause / Timeout Helpers ────────────────────────────────────\n\nfunction generateSnippet(stack: string, testInfo: TestInfo): string | null {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (!match) return null;\n\n const lineNumber = parseInt(match[1], 10);\n let lines: string[];\n try {\n lines = fs.readFileSync(testInfo.file, 'utf-8').split('\\n');\n } catch {\n return null;\n }\n\n const start = Math.max(0, lineNumber - 5); // 4 lines before (0-indexed: lineNumber-1 is the error line)\n const end = Math.min(lines.length, lineNumber + 3); // 3 lines after\n const slice = lines.slice(start, end);\n\n const maxLineNum = end;\n const gutterWidth = String(maxLineNum).length;\n\n return slice\n .map((text, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? '>' : ' ';\n const padded = String(num).padStart(gutterWidth);\n return `${marker} ${padded} | ${text}`;\n })\n .join('\\n');\n}\n\nfunction extractCauseChain(error: unknown): ErrorCause[] {\n const causes: ErrorCause[] = [];\n const seen = new WeakSet<object>();\n let current = error;\n\n for (let depth = 0; depth < 10; depth++) {\n if (current == null || typeof current !== 'object') break;\n const cause = (current as { cause?: unknown }).cause;\n if (cause == null || typeof cause !== 'object') break;\n if (seen.has(cause as object)) break;\n seen.add(cause as object);\n\n const causeErr = cause as { message?: string; stack?: string };\n causes.push({\n message: causeErr.message ?? String(cause),\n stack: causeErr.stack ?? '',\n });\n current = cause;\n }\n\n return causes;\n}\n\nfunction extractTimeoutMs(msg: string): number | null {\n if (!/timeout/i.test(msg)) return null;\n const match = msg.match(/(\\d+)\\s*ms/);\n return match ? parseInt(match[1], 10) : null;\n}\n\n// ─── Private Helpers ───────────────────────────────────────────────────────\n\nfunction extractSelector(msg: string): string {\n const match =\n msg.match(/locator\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByRole\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByText\\(['\"](.+?)['\"]\\)/);\n return match ? match[1] : '';\n}\n\nfunction extractAction(msg: string): string {\n if (msg.includes('.click')) return 'click';\n if (msg.includes('.fill')) return 'fill';\n if (msg.includes('.type')) return 'type';\n if (msg.includes('.hover')) return 'hover';\n if (msg.includes('.check')) return 'check';\n if (msg.includes('.uncheck')) return 'uncheck';\n if (msg.includes('.select')) return 'select';\n if (msg.includes('.press')) return 'press';\n if (msg.includes('.scroll')) return 'scroll';\n if (msg.includes('.drag')) return 'drag';\n if (/timeout/i.test(msg)) return 'wait';\n return '';\n}\n\nfunction extractLocation(stack: string, testInfo: TestInfo): string {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (match) {\n return `${path.basename(testInfo.file)}:${match[1]}`;\n }\n return '';\n}\n\nfunction extractSelectorDetails(msg: string): FailureSelector | null {\n const patterns: Array<{ method: string; regex: RegExp }> = [\n { method: 'locator', regex: /locator\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByRole', regex: /getByRole\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByText', regex: /getByText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTestId', regex: /getByTestId\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByLabel', regex: /getByLabel\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByPlaceholder', regex: /getByPlaceholder\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByAltText', regex: /getByAltText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTitle', regex: /getByTitle\\(['\"](.+?)['\"]\\)/ },\n { method: 'frameLocator', regex: /frameLocator\\(['\"](.+?)['\"]\\)/ },\n ];\n\n for (const { method, regex } of patterns) {\n const match = msg.match(regex);\n if (match) {\n return { raw: match[0], method, value: match[1] };\n }\n }\n return null;\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/failure-analyzer.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Page, TestInfo } from '@playwright/test';\n\n// ─── Exported Interfaces ───────────────────────────────────────────────────\n\nexport interface ConsoleEntry {\n type: string;\n text: string;\n timestamp: string;\n location?: { url?: string; lineNumber?: number; columnNumber?: number };\n}\n\nexport interface NetworkFailure {\n url: string;\n method: string;\n status: number;\n statusText: string;\n resourceType: string;\n timestamp: string;\n}\n\nexport interface FailureSelector {\n raw: string;\n method: string;\n value: string;\n}\n\nexport interface ErrorCause {\n message: string;\n stack: string;\n}\n\nexport interface ErrorEntry {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n}\n\nexport interface FailureAnalysis {\n // Backward-compatible original 5 fields\n selector: string;\n action: string;\n error: string;\n location: string;\n context: string;\n pw_console: string | null;\n\n // Enriched fields (v2+)\n test: {\n title: string;\n titlePath: string[];\n file: string;\n line: number | null;\n project: string;\n retries: number;\n retry: number;\n duration: number;\n status: string | undefined;\n expectedStatus: string;\n tags: string[];\n };\n page: {\n url: string;\n title: string | null;\n viewport: { width: number; height: number } | null;\n };\n selectorDetails: FailureSelector | null;\n consoleLogs: ConsoleEntry[];\n consoleErrors: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n errorDetails: {\n message: string;\n stack: string;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n };\n errors: ErrorEntry[];\n timing: {\n testStart: string;\n failureCapture: string;\n duration: number;\n };\n artifacts: {\n domJsonPath: string;\n screenshotPath: string;\n failureInfoPath: string;\n };\n _version: 3;\n}\n\n// ─── Page Listener Setup ───────────────────────────────────────────────────\n\nexport function setupPageListeners(page: Page): {\n consoleLogs: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n} {\n const consoleLogs: ConsoleEntry[] = [];\n const networkFailures: NetworkFailure[] = [];\n const MAX_CONSOLE_ENTRIES = 500;\n\n page.on('console', (msg) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n const loc = msg.location();\n consoleLogs.push({\n type: msg.type(),\n text: msg.text(),\n timestamp: new Date().toISOString(),\n location: {\n url: loc.url,\n lineNumber: loc.lineNumber,\n columnNumber: loc.columnNumber,\n },\n });\n });\n\n page.on('pageerror', (err) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n consoleLogs.push({\n type: 'pageerror',\n text: err.message,\n timestamp: new Date().toISOString(),\n });\n });\n\n page.on('response', (response) => {\n if (response.status() >= 400) {\n networkFailures.push({\n url: response.url(),\n method: response.request().method(),\n status: response.status(),\n statusText: response.statusText(),\n resourceType: response.request().resourceType(),\n timestamp: new Date().toISOString(),\n });\n }\n });\n\n page.on('requestfailed', (request) => {\n networkFailures.push({\n url: request.url(),\n method: request.method(),\n status: 0,\n statusText: request.failure()?.errorText ?? 'Request failed',\n resourceType: request.resourceType(),\n timestamp: new Date().toISOString(),\n });\n });\n\n return { consoleLogs, networkFailures };\n}\n\n// ─── Failure Analysis ──────────────────────────────────────────────────────\n\nexport async function analyzeFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[],\n artifacts: { jsonPath: string; screenshotPath: string; failureInfoPath: string }\n): Promise<FailureAnalysis> {\n const error = (testInfo.error ?? testInfo.errors?.[0] ?? {}) as { message?: string; stack?: string };\n const errorMessage = error.message ?? '';\n const errorStack = error.stack ?? '';\n\n const now = new Date();\n const duration = testInfo.duration ?? 0;\n const testStart = new Date(now.getTime() - duration).toISOString();\n const failureCapture = now.toISOString();\n\n // Original 5 fields (backward compat)\n const selector = extractSelector(errorMessage);\n const action = extractAction(errorMessage);\n const errorFirstLine = errorMessage.split('\\n')[0];\n const location = extractLocation(errorStack, testInfo);\n const context = errorMessage;\n\n // Page state\n let pageTitle: string | null = null;\n try {\n pageTitle = await page.title();\n } catch {\n // page may be closed\n }\n\n // Timeout detection\n const isTimeout = /timeout/i.test(errorMessage);\n const timeoutMs = extractTimeoutMs(errorMessage);\n\n // Snippet & cause chain for primary error\n const snippet = generateSnippet(errorStack, testInfo);\n const causes = extractCauseChain(error);\n\n // Build errors array from all test errors\n const rawErrors = testInfo.errors ?? [];\n const errors: ErrorEntry[] = rawErrors.map((err) => {\n const msg = (err as { message?: string }).message ?? '';\n const stk = (err as { stack?: string }).stack ?? '';\n return {\n message: msg,\n stack: stk,\n pw_console: generateSnippet(stk, testInfo),\n isTimeout: /timeout/i.test(msg),\n timeoutMs: extractTimeoutMs(msg),\n causes: extractCauseChain(err),\n };\n });\n\n const analysis: FailureAnalysis = {\n // Backward-compatible\n selector,\n action,\n error: errorFirstLine,\n location,\n context,\n pw_console: snippet,\n\n // v3 enriched\n test: {\n title: testInfo.title,\n titlePath: testInfo.titlePath,\n file: testInfo.file,\n line: (testInfo as unknown as { line?: number }).line ?? null,\n project: testInfo.project.name,\n retries: testInfo.project.retries,\n retry: testInfo.retry,\n duration,\n status: testInfo.status,\n expectedStatus: testInfo.expectedStatus,\n tags: (testInfo as unknown as { tags?: string[] }).tags ?? [],\n },\n page: {\n url: page.url(),\n title: pageTitle,\n viewport: page.viewportSize(),\n },\n selectorDetails: extractSelectorDetails(errorMessage),\n consoleLogs,\n consoleErrors: consoleLogs.filter(\n (e) => e.type === 'error' || e.type === 'pageerror'\n ),\n networkFailures,\n errorDetails: {\n message: errorMessage,\n stack: errorStack,\n isTimeout,\n timeoutMs,\n causes,\n },\n errors,\n timing: {\n testStart,\n failureCapture,\n duration,\n },\n artifacts: {\n domJsonPath: artifacts.jsonPath,\n screenshotPath: artifacts.screenshotPath,\n failureInfoPath: artifacts.failureInfoPath,\n },\n _version: 3,\n };\n\n fs.writeFileSync(artifacts.failureInfoPath, JSON.stringify(analysis, null, 2));\n\n return analysis;\n}\n\n// ─── Snippet / Cause / Timeout Helpers ────────────────────────────────────\n\nfunction generateSnippet(stack: string, testInfo: TestInfo): string | null {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (!match) return null;\n\n const lineNumber = parseInt(match[1], 10);\n let lines: string[];\n try {\n lines = fs.readFileSync(testInfo.file, 'utf-8').split('\\n');\n } catch {\n return null;\n }\n\n const start = Math.max(0, lineNumber - 5); // 4 lines before (0-indexed: lineNumber-1 is the error line)\n const end = Math.min(lines.length, lineNumber + 3); // 3 lines after\n const slice = lines.slice(start, end);\n\n const maxLineNum = end;\n const gutterWidth = String(maxLineNum).length;\n\n return slice\n .map((text, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? '>' : ' ';\n const padded = String(num).padStart(gutterWidth);\n return `${marker} ${padded} | ${text}`;\n })\n .join('\\n');\n}\n\nfunction extractCauseChain(error: unknown): ErrorCause[] {\n const causes: ErrorCause[] = [];\n const seen = new WeakSet<object>();\n let current = error;\n\n for (let depth = 0; depth < 10; depth++) {\n if (current == null || typeof current !== 'object') break;\n const cause = (current as { cause?: unknown }).cause;\n if (cause == null || typeof cause !== 'object') break;\n if (seen.has(cause as object)) break;\n seen.add(cause as object);\n\n const causeErr = cause as { message?: string; stack?: string };\n causes.push({\n message: causeErr.message ?? String(cause),\n stack: causeErr.stack ?? '',\n });\n current = cause;\n }\n\n return causes;\n}\n\nfunction extractTimeoutMs(msg: string): number | null {\n if (!/timeout/i.test(msg)) return null;\n const match = msg.match(/(\\d+)\\s*ms/);\n return match ? parseInt(match[1], 10) : null;\n}\n\n// ─── Private Helpers ───────────────────────────────────────────────────────\n\nfunction extractSelector(msg: string): string {\n const match =\n msg.match(/locator\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByRole\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByText\\(['\"](.+?)['\"]\\)/);\n return match ? match[1] : '';\n}\n\nfunction extractAction(msg: string): string {\n if (msg.includes('.click')) return 'click';\n if (msg.includes('.fill')) return 'fill';\n if (msg.includes('.type')) return 'type';\n if (msg.includes('.hover')) return 'hover';\n if (msg.includes('.check')) return 'check';\n if (msg.includes('.uncheck')) return 'uncheck';\n if (msg.includes('.select')) return 'select';\n if (msg.includes('.press')) return 'press';\n if (msg.includes('.scroll')) return 'scroll';\n if (msg.includes('.drag')) return 'drag';\n if (/timeout/i.test(msg)) return 'wait';\n return '';\n}\n\nfunction extractLocation(stack: string, testInfo: TestInfo): string {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (match) {\n return `${path.basename(testInfo.file)}:${match[1]}`;\n }\n return '';\n}\n\nfunction extractSelectorDetails(msg: string): FailureSelector | null {\n const patterns: Array<{ method: string; regex: RegExp }> = [\n { method: 'locator', regex: /locator\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByRole', regex: /getByRole\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByText', regex: /getByText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTestId', regex: /getByTestId\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByLabel', regex: /getByLabel\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByPlaceholder', regex: /getByPlaceholder\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByAltText', regex: /getByAltText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTitle', regex: /getByTitle\\(['\"](.+?)['\"]\\)/ },\n { method: 'frameLocator', regex: /frameLocator\\(['\"](.+?)['\"]\\)/ },\n ];\n\n for (const { method, regex } of patterns) {\n const match = msg.match(regex);\n if (match) {\n return { raw: match[0], method, value: match[1] };\n }\n }\n return null;\n}\n"],"mappings":"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","names":["fs","path"]}
|
|
@@ -41,6 +41,7 @@ interface FailureAnalysis {
|
|
|
41
41
|
error: string;
|
|
42
42
|
location: string;
|
|
43
43
|
context: string;
|
|
44
|
+
pw_console: string | null;
|
|
44
45
|
test: {
|
|
45
46
|
title: string;
|
|
46
47
|
titlePath: string[];
|
|
@@ -69,7 +70,6 @@ interface FailureAnalysis {
|
|
|
69
70
|
errorDetails: {
|
|
70
71
|
message: string;
|
|
71
72
|
stack: string;
|
|
72
|
-
pw_console: string | null;
|
|
73
73
|
isTimeout: boolean;
|
|
74
74
|
timeoutMs: number | null;
|
|
75
75
|
causes: ErrorCause[];
|
|
@@ -41,6 +41,7 @@ interface FailureAnalysis {
|
|
|
41
41
|
error: string;
|
|
42
42
|
location: string;
|
|
43
43
|
context: string;
|
|
44
|
+
pw_console: string | null;
|
|
44
45
|
test: {
|
|
45
46
|
title: string;
|
|
46
47
|
titlePath: string[];
|
|
@@ -69,7 +70,6 @@ interface FailureAnalysis {
|
|
|
69
70
|
errorDetails: {
|
|
70
71
|
message: string;
|
|
71
72
|
stack: string;
|
|
72
|
-
pw_console: string | null;
|
|
73
73
|
isTimeout: boolean;
|
|
74
74
|
timeoutMs: number | null;
|
|
75
75
|
causes: ErrorCause[];
|
|
@@ -92,6 +92,7 @@ async function analyzeFailure(page, testInfo, consoleLogs, networkFailures, arti
|
|
|
92
92
|
error: errorFirstLine,
|
|
93
93
|
location,
|
|
94
94
|
context,
|
|
95
|
+
pw_console: snippet,
|
|
95
96
|
// v3 enriched
|
|
96
97
|
test: {
|
|
97
98
|
title: testInfo.title,
|
|
@@ -120,7 +121,6 @@ async function analyzeFailure(page, testInfo, consoleLogs, networkFailures, arti
|
|
|
120
121
|
errorDetails: {
|
|
121
122
|
message: errorMessage,
|
|
122
123
|
stack: errorStack,
|
|
123
|
-
pw_console: snippet,
|
|
124
124
|
isTimeout,
|
|
125
125
|
timeoutMs,
|
|
126
126
|
causes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/helpers/failure-analyzer.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Page, TestInfo } from '@playwright/test';\n\n// ─── Exported Interfaces ───────────────────────────────────────────────────\n\nexport interface ConsoleEntry {\n type: string;\n text: string;\n timestamp: string;\n location?: { url?: string; lineNumber?: number; columnNumber?: number };\n}\n\nexport interface NetworkFailure {\n url: string;\n method: string;\n status: number;\n statusText: string;\n resourceType: string;\n timestamp: string;\n}\n\nexport interface FailureSelector {\n raw: string;\n method: string;\n value: string;\n}\n\nexport interface ErrorCause {\n message: string;\n stack: string;\n}\n\nexport interface ErrorEntry {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n}\n\nexport interface FailureAnalysis {\n // Backward-compatible original 5 fields\n selector: string;\n action: string;\n error: string;\n location: string;\n context: string;\n\n // Enriched fields (v2+)\n test: {\n title: string;\n titlePath: string[];\n file: string;\n line: number | null;\n project: string;\n retries: number;\n retry: number;\n duration: number;\n status: string | undefined;\n expectedStatus: string;\n tags: string[];\n };\n page: {\n url: string;\n title: string | null;\n viewport: { width: number; height: number } | null;\n };\n selectorDetails: FailureSelector | null;\n consoleLogs: ConsoleEntry[];\n consoleErrors: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n errorDetails: {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n };\n errors: ErrorEntry[];\n timing: {\n testStart: string;\n failureCapture: string;\n duration: number;\n };\n artifacts: {\n domJsonPath: string;\n screenshotPath: string;\n failureInfoPath: string;\n };\n _version: 3;\n}\n\n// ─── Page Listener Setup ───────────────────────────────────────────────────\n\nexport function setupPageListeners(page: Page): {\n consoleLogs: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n} {\n const consoleLogs: ConsoleEntry[] = [];\n const networkFailures: NetworkFailure[] = [];\n const MAX_CONSOLE_ENTRIES = 500;\n\n page.on('console', (msg) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n const loc = msg.location();\n consoleLogs.push({\n type: msg.type(),\n text: msg.text(),\n timestamp: new Date().toISOString(),\n location: {\n url: loc.url,\n lineNumber: loc.lineNumber,\n columnNumber: loc.columnNumber,\n },\n });\n });\n\n page.on('pageerror', (err) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n consoleLogs.push({\n type: 'pageerror',\n text: err.message,\n timestamp: new Date().toISOString(),\n });\n });\n\n page.on('response', (response) => {\n if (response.status() >= 400) {\n networkFailures.push({\n url: response.url(),\n method: response.request().method(),\n status: response.status(),\n statusText: response.statusText(),\n resourceType: response.request().resourceType(),\n timestamp: new Date().toISOString(),\n });\n }\n });\n\n page.on('requestfailed', (request) => {\n networkFailures.push({\n url: request.url(),\n method: request.method(),\n status: 0,\n statusText: request.failure()?.errorText ?? 'Request failed',\n resourceType: request.resourceType(),\n timestamp: new Date().toISOString(),\n });\n });\n\n return { consoleLogs, networkFailures };\n}\n\n// ─── Failure Analysis ──────────────────────────────────────────────────────\n\nexport async function analyzeFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[],\n artifacts: { jsonPath: string; screenshotPath: string; failureInfoPath: string }\n): Promise<FailureAnalysis> {\n const error = (testInfo.error ?? testInfo.errors?.[0] ?? {}) as { message?: string; stack?: string };\n const errorMessage = error.message ?? '';\n const errorStack = error.stack ?? '';\n\n const now = new Date();\n const duration = testInfo.duration ?? 0;\n const testStart = new Date(now.getTime() - duration).toISOString();\n const failureCapture = now.toISOString();\n\n // Original 5 fields (backward compat)\n const selector = extractSelector(errorMessage);\n const action = extractAction(errorMessage);\n const errorFirstLine = errorMessage.split('\\n')[0];\n const location = extractLocation(errorStack, testInfo);\n const context = errorMessage;\n\n // Page state\n let pageTitle: string | null = null;\n try {\n pageTitle = await page.title();\n } catch {\n // page may be closed\n }\n\n // Timeout detection\n const isTimeout = /timeout/i.test(errorMessage);\n const timeoutMs = extractTimeoutMs(errorMessage);\n\n // Snippet & cause chain for primary error\n const snippet = generateSnippet(errorStack, testInfo);\n const causes = extractCauseChain(error);\n\n // Build errors array from all test errors\n const rawErrors = testInfo.errors ?? [];\n const errors: ErrorEntry[] = rawErrors.map((err) => {\n const msg = (err as { message?: string }).message ?? '';\n const stk = (err as { stack?: string }).stack ?? '';\n return {\n message: msg,\n stack: stk,\n pw_console: generateSnippet(stk, testInfo),\n isTimeout: /timeout/i.test(msg),\n timeoutMs: extractTimeoutMs(msg),\n causes: extractCauseChain(err),\n };\n });\n\n const analysis: FailureAnalysis = {\n // Backward-compatible\n selector,\n action,\n error: errorFirstLine,\n location,\n context,\n\n // v3 enriched\n test: {\n title: testInfo.title,\n titlePath: testInfo.titlePath,\n file: testInfo.file,\n line: (testInfo as unknown as { line?: number }).line ?? null,\n project: testInfo.project.name,\n retries: testInfo.project.retries,\n retry: testInfo.retry,\n duration,\n status: testInfo.status,\n expectedStatus: testInfo.expectedStatus,\n tags: (testInfo as unknown as { tags?: string[] }).tags ?? [],\n },\n page: {\n url: page.url(),\n title: pageTitle,\n viewport: page.viewportSize(),\n },\n selectorDetails: extractSelectorDetails(errorMessage),\n consoleLogs,\n consoleErrors: consoleLogs.filter(\n (e) => e.type === 'error' || e.type === 'pageerror'\n ),\n networkFailures,\n errorDetails: {\n message: errorMessage,\n stack: errorStack,\n pw_console: snippet,\n isTimeout,\n timeoutMs,\n causes,\n },\n errors,\n timing: {\n testStart,\n failureCapture,\n duration,\n },\n artifacts: {\n domJsonPath: artifacts.jsonPath,\n screenshotPath: artifacts.screenshotPath,\n failureInfoPath: artifacts.failureInfoPath,\n },\n _version: 3,\n };\n\n fs.writeFileSync(artifacts.failureInfoPath, JSON.stringify(analysis, null, 2));\n\n return analysis;\n}\n\n// ─── Snippet / Cause / Timeout Helpers ────────────────────────────────────\n\nfunction generateSnippet(stack: string, testInfo: TestInfo): string | null {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (!match) return null;\n\n const lineNumber = parseInt(match[1], 10);\n let lines: string[];\n try {\n lines = fs.readFileSync(testInfo.file, 'utf-8').split('\\n');\n } catch {\n return null;\n }\n\n const start = Math.max(0, lineNumber - 5); // 4 lines before (0-indexed: lineNumber-1 is the error line)\n const end = Math.min(lines.length, lineNumber + 3); // 3 lines after\n const slice = lines.slice(start, end);\n\n const maxLineNum = end;\n const gutterWidth = String(maxLineNum).length;\n\n return slice\n .map((text, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? '>' : ' ';\n const padded = String(num).padStart(gutterWidth);\n return `${marker} ${padded} | ${text}`;\n })\n .join('\\n');\n}\n\nfunction extractCauseChain(error: unknown): ErrorCause[] {\n const causes: ErrorCause[] = [];\n const seen = new WeakSet<object>();\n let current = error;\n\n for (let depth = 0; depth < 10; depth++) {\n if (current == null || typeof current !== 'object') break;\n const cause = (current as { cause?: unknown }).cause;\n if (cause == null || typeof cause !== 'object') break;\n if (seen.has(cause as object)) break;\n seen.add(cause as object);\n\n const causeErr = cause as { message?: string; stack?: string };\n causes.push({\n message: causeErr.message ?? String(cause),\n stack: causeErr.stack ?? '',\n });\n current = cause;\n }\n\n return causes;\n}\n\nfunction extractTimeoutMs(msg: string): number | null {\n if (!/timeout/i.test(msg)) return null;\n const match = msg.match(/(\\d+)\\s*ms/);\n return match ? parseInt(match[1], 10) : null;\n}\n\n// ─── Private Helpers ───────────────────────────────────────────────────────\n\nfunction extractSelector(msg: string): string {\n const match =\n msg.match(/locator\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByRole\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByText\\(['\"](.+?)['\"]\\)/);\n return match ? match[1] : '';\n}\n\nfunction extractAction(msg: string): string {\n if (msg.includes('.click')) return 'click';\n if (msg.includes('.fill')) return 'fill';\n if (msg.includes('.type')) return 'type';\n if (msg.includes('.hover')) return 'hover';\n if (msg.includes('.check')) return 'check';\n if (msg.includes('.uncheck')) return 'uncheck';\n if (msg.includes('.select')) return 'select';\n if (msg.includes('.press')) return 'press';\n if (msg.includes('.scroll')) return 'scroll';\n if (msg.includes('.drag')) return 'drag';\n if (/timeout/i.test(msg)) return 'wait';\n return '';\n}\n\nfunction extractLocation(stack: string, testInfo: TestInfo): string {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (match) {\n return `${path.basename(testInfo.file)}:${match[1]}`;\n }\n return '';\n}\n\nfunction extractSelectorDetails(msg: string): FailureSelector | null {\n const patterns: Array<{ method: string; regex: RegExp }> = [\n { method: 'locator', regex: /locator\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByRole', regex: /getByRole\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByText', regex: /getByText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTestId', regex: /getByTestId\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByLabel', regex: /getByLabel\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByPlaceholder', regex: /getByPlaceholder\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByAltText', regex: /getByAltText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTitle', regex: /getByTitle\\(['\"](.+?)['\"]\\)/ },\n { method: 'frameLocator', regex: /frameLocator\\(['\"](.+?)['\"]\\)/ },\n ];\n\n for (const { method, regex } of patterns) {\n const match = msg.match(regex);\n if (match) {\n return { raw: match[0], method, value: match[1] };\n }\n }\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/failure-analyzer.ts"],"sourcesContent":["import fs from 'fs';\nimport path from 'path';\nimport type { Page, TestInfo } from '@playwright/test';\n\n// ─── Exported Interfaces ───────────────────────────────────────────────────\n\nexport interface ConsoleEntry {\n type: string;\n text: string;\n timestamp: string;\n location?: { url?: string; lineNumber?: number; columnNumber?: number };\n}\n\nexport interface NetworkFailure {\n url: string;\n method: string;\n status: number;\n statusText: string;\n resourceType: string;\n timestamp: string;\n}\n\nexport interface FailureSelector {\n raw: string;\n method: string;\n value: string;\n}\n\nexport interface ErrorCause {\n message: string;\n stack: string;\n}\n\nexport interface ErrorEntry {\n message: string;\n stack: string;\n pw_console: string | null;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n}\n\nexport interface FailureAnalysis {\n // Backward-compatible original 5 fields\n selector: string;\n action: string;\n error: string;\n location: string;\n context: string;\n pw_console: string | null;\n\n // Enriched fields (v2+)\n test: {\n title: string;\n titlePath: string[];\n file: string;\n line: number | null;\n project: string;\n retries: number;\n retry: number;\n duration: number;\n status: string | undefined;\n expectedStatus: string;\n tags: string[];\n };\n page: {\n url: string;\n title: string | null;\n viewport: { width: number; height: number } | null;\n };\n selectorDetails: FailureSelector | null;\n consoleLogs: ConsoleEntry[];\n consoleErrors: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n errorDetails: {\n message: string;\n stack: string;\n isTimeout: boolean;\n timeoutMs: number | null;\n causes: ErrorCause[];\n };\n errors: ErrorEntry[];\n timing: {\n testStart: string;\n failureCapture: string;\n duration: number;\n };\n artifacts: {\n domJsonPath: string;\n screenshotPath: string;\n failureInfoPath: string;\n };\n _version: 3;\n}\n\n// ─── Page Listener Setup ───────────────────────────────────────────────────\n\nexport function setupPageListeners(page: Page): {\n consoleLogs: ConsoleEntry[];\n networkFailures: NetworkFailure[];\n} {\n const consoleLogs: ConsoleEntry[] = [];\n const networkFailures: NetworkFailure[] = [];\n const MAX_CONSOLE_ENTRIES = 500;\n\n page.on('console', (msg) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n const loc = msg.location();\n consoleLogs.push({\n type: msg.type(),\n text: msg.text(),\n timestamp: new Date().toISOString(),\n location: {\n url: loc.url,\n lineNumber: loc.lineNumber,\n columnNumber: loc.columnNumber,\n },\n });\n });\n\n page.on('pageerror', (err) => {\n if (consoleLogs.length >= MAX_CONSOLE_ENTRIES) return;\n consoleLogs.push({\n type: 'pageerror',\n text: err.message,\n timestamp: new Date().toISOString(),\n });\n });\n\n page.on('response', (response) => {\n if (response.status() >= 400) {\n networkFailures.push({\n url: response.url(),\n method: response.request().method(),\n status: response.status(),\n statusText: response.statusText(),\n resourceType: response.request().resourceType(),\n timestamp: new Date().toISOString(),\n });\n }\n });\n\n page.on('requestfailed', (request) => {\n networkFailures.push({\n url: request.url(),\n method: request.method(),\n status: 0,\n statusText: request.failure()?.errorText ?? 'Request failed',\n resourceType: request.resourceType(),\n timestamp: new Date().toISOString(),\n });\n });\n\n return { consoleLogs, networkFailures };\n}\n\n// ─── Failure Analysis ──────────────────────────────────────────────────────\n\nexport async function analyzeFailure(\n page: Page,\n testInfo: TestInfo,\n consoleLogs: ConsoleEntry[],\n networkFailures: NetworkFailure[],\n artifacts: { jsonPath: string; screenshotPath: string; failureInfoPath: string }\n): Promise<FailureAnalysis> {\n const error = (testInfo.error ?? testInfo.errors?.[0] ?? {}) as { message?: string; stack?: string };\n const errorMessage = error.message ?? '';\n const errorStack = error.stack ?? '';\n\n const now = new Date();\n const duration = testInfo.duration ?? 0;\n const testStart = new Date(now.getTime() - duration).toISOString();\n const failureCapture = now.toISOString();\n\n // Original 5 fields (backward compat)\n const selector = extractSelector(errorMessage);\n const action = extractAction(errorMessage);\n const errorFirstLine = errorMessage.split('\\n')[0];\n const location = extractLocation(errorStack, testInfo);\n const context = errorMessage;\n\n // Page state\n let pageTitle: string | null = null;\n try {\n pageTitle = await page.title();\n } catch {\n // page may be closed\n }\n\n // Timeout detection\n const isTimeout = /timeout/i.test(errorMessage);\n const timeoutMs = extractTimeoutMs(errorMessage);\n\n // Snippet & cause chain for primary error\n const snippet = generateSnippet(errorStack, testInfo);\n const causes = extractCauseChain(error);\n\n // Build errors array from all test errors\n const rawErrors = testInfo.errors ?? [];\n const errors: ErrorEntry[] = rawErrors.map((err) => {\n const msg = (err as { message?: string }).message ?? '';\n const stk = (err as { stack?: string }).stack ?? '';\n return {\n message: msg,\n stack: stk,\n pw_console: generateSnippet(stk, testInfo),\n isTimeout: /timeout/i.test(msg),\n timeoutMs: extractTimeoutMs(msg),\n causes: extractCauseChain(err),\n };\n });\n\n const analysis: FailureAnalysis = {\n // Backward-compatible\n selector,\n action,\n error: errorFirstLine,\n location,\n context,\n pw_console: snippet,\n\n // v3 enriched\n test: {\n title: testInfo.title,\n titlePath: testInfo.titlePath,\n file: testInfo.file,\n line: (testInfo as unknown as { line?: number }).line ?? null,\n project: testInfo.project.name,\n retries: testInfo.project.retries,\n retry: testInfo.retry,\n duration,\n status: testInfo.status,\n expectedStatus: testInfo.expectedStatus,\n tags: (testInfo as unknown as { tags?: string[] }).tags ?? [],\n },\n page: {\n url: page.url(),\n title: pageTitle,\n viewport: page.viewportSize(),\n },\n selectorDetails: extractSelectorDetails(errorMessage),\n consoleLogs,\n consoleErrors: consoleLogs.filter(\n (e) => e.type === 'error' || e.type === 'pageerror'\n ),\n networkFailures,\n errorDetails: {\n message: errorMessage,\n stack: errorStack,\n isTimeout,\n timeoutMs,\n causes,\n },\n errors,\n timing: {\n testStart,\n failureCapture,\n duration,\n },\n artifacts: {\n domJsonPath: artifacts.jsonPath,\n screenshotPath: artifacts.screenshotPath,\n failureInfoPath: artifacts.failureInfoPath,\n },\n _version: 3,\n };\n\n fs.writeFileSync(artifacts.failureInfoPath, JSON.stringify(analysis, null, 2));\n\n return analysis;\n}\n\n// ─── Snippet / Cause / Timeout Helpers ────────────────────────────────────\n\nfunction generateSnippet(stack: string, testInfo: TestInfo): string | null {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (!match) return null;\n\n const lineNumber = parseInt(match[1], 10);\n let lines: string[];\n try {\n lines = fs.readFileSync(testInfo.file, 'utf-8').split('\\n');\n } catch {\n return null;\n }\n\n const start = Math.max(0, lineNumber - 5); // 4 lines before (0-indexed: lineNumber-1 is the error line)\n const end = Math.min(lines.length, lineNumber + 3); // 3 lines after\n const slice = lines.slice(start, end);\n\n const maxLineNum = end;\n const gutterWidth = String(maxLineNum).length;\n\n return slice\n .map((text, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? '>' : ' ';\n const padded = String(num).padStart(gutterWidth);\n return `${marker} ${padded} | ${text}`;\n })\n .join('\\n');\n}\n\nfunction extractCauseChain(error: unknown): ErrorCause[] {\n const causes: ErrorCause[] = [];\n const seen = new WeakSet<object>();\n let current = error;\n\n for (let depth = 0; depth < 10; depth++) {\n if (current == null || typeof current !== 'object') break;\n const cause = (current as { cause?: unknown }).cause;\n if (cause == null || typeof cause !== 'object') break;\n if (seen.has(cause as object)) break;\n seen.add(cause as object);\n\n const causeErr = cause as { message?: string; stack?: string };\n causes.push({\n message: causeErr.message ?? String(cause),\n stack: causeErr.stack ?? '',\n });\n current = cause;\n }\n\n return causes;\n}\n\nfunction extractTimeoutMs(msg: string): number | null {\n if (!/timeout/i.test(msg)) return null;\n const match = msg.match(/(\\d+)\\s*ms/);\n return match ? parseInt(match[1], 10) : null;\n}\n\n// ─── Private Helpers ───────────────────────────────────────────────────────\n\nfunction extractSelector(msg: string): string {\n const match =\n msg.match(/locator\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByRole\\(['\"](.+?)['\"]\\)/) ||\n msg.match(/getByText\\(['\"](.+?)['\"]\\)/);\n return match ? match[1] : '';\n}\n\nfunction extractAction(msg: string): string {\n if (msg.includes('.click')) return 'click';\n if (msg.includes('.fill')) return 'fill';\n if (msg.includes('.type')) return 'type';\n if (msg.includes('.hover')) return 'hover';\n if (msg.includes('.check')) return 'check';\n if (msg.includes('.uncheck')) return 'uncheck';\n if (msg.includes('.select')) return 'select';\n if (msg.includes('.press')) return 'press';\n if (msg.includes('.scroll')) return 'scroll';\n if (msg.includes('.drag')) return 'drag';\n if (/timeout/i.test(msg)) return 'wait';\n return '';\n}\n\nfunction extractLocation(stack: string, testInfo: TestInfo): string {\n const escaped = testInfo.file.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const match = stack.match(new RegExp(`${escaped}:(\\\\d+):(\\\\d+)`));\n if (match) {\n return `${path.basename(testInfo.file)}:${match[1]}`;\n }\n return '';\n}\n\nfunction extractSelectorDetails(msg: string): FailureSelector | null {\n const patterns: Array<{ method: string; regex: RegExp }> = [\n { method: 'locator', regex: /locator\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByRole', regex: /getByRole\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByText', regex: /getByText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTestId', regex: /getByTestId\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByLabel', regex: /getByLabel\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByPlaceholder', regex: /getByPlaceholder\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByAltText', regex: /getByAltText\\(['\"](.+?)['\"]\\)/ },\n { method: 'getByTitle', regex: /getByTitle\\(['\"](.+?)['\"]\\)/ },\n { method: 'frameLocator', regex: /frameLocator\\(['\"](.+?)['\"]\\)/ },\n ];\n\n for (const { method, regex } of patterns) {\n const match = msg.match(regex);\n if (match) {\n return { raw: match[0], method, value: match[1] };\n }\n }\n return null;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED