UNPKG

donobu

Version:

Create browser automations with an LLM agent and replay them as Playwright scripts.

124 lines 5.91 kB
"use strict"; /** * @fileoverview Reconstructs a `DonobuReport` from live Playwright `Reporter` * events. Shared across all Donobu reporter classes (HTML, Markdown, Slack) * so they all agree on the canonical shape sent downstream to the renderers * and the auto-heal merge step. * * The output structure mirrors Playwright's native JSON reporter: * suites (one per file) → specs (one per test title) → tests (one per project) * plus the Donobu-specific `metadata` fields the merge + render steps rely on. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.buildDonobuReport = buildDonobuReport; const path_1 = require("path"); /** * Build a canonical `DonobuReport` from the per-test result accumulators that * each Donobu reporter maintains during a run. * * Callers are expected to fill in `metadata` (specifically the `donobuOutputs` * entry for their format) before persisting the result — this helper owns the * structure walk, not the output-path accounting. */ function buildDonobuReport(resultsByTest, rootDir) { // Group tests by file path, then by test title. // Multiple TestCase objects with the same (file, title) represent the same // spec running under different Playwright projects. // Paths are normalized to be relative to the Playwright rootDir (mirroring // the native JSON reporter) so GitHub Actions summaries and other consumers // don't show absolute CI runner paths. const byFile = new Map(); for (const test of resultsByTest.keys()) { const file = rootDir ? (0, path_1.relative)(rootDir, test.location.file) : test.location.file; if (!byFile.has(file)) { byFile.set(file, new Map()); } const byTitle = byFile.get(file); if (!byTitle.has(test.title)) { byTitle.set(test.title, []); } byTitle.get(test.title).push(test); } const suites = []; for (const [file, titleMap] of byFile) { const specs = []; for (const [title, tests] of titleMap) { const testEntries = tests.map((test) => { const results = resultsByTest.get(test) ?? []; return { // Playwright's stable per-(file, project, title) ID — used by the // merge step's `byId` index and by the HTML renderer for permalink // anchors (`index.html#?testId=<id>`) and the per-test redirect stubs. testId: test.id, annotations: test.annotations, tags: test.tags, projectName: getProjectName(test), // Signal "skipped" tests to the renderers the same way the JSON // reporter does. status: test.expectedStatus === 'skipped' ? 'skipped' : undefined, results: results.map((r) => ({ status: r.status, duration: r.duration, retry: r.retry, startTime: r.startTime?.toISOString() ?? null, // Pass errors through; cast to any to preserve runtime-only // fields (snippet, actual, expected) that Playwright adds to // assertion errors. errors: r.errors.map((e) => ({ message: e.message, stack: e.stack, snippet: e.snippet, actual: e.actual, expected: e.expected, location: e.location, })), // The markdown renderer reads `result.error` (singular) in addition // to `errors[]`; surface the first error there for back-compat. error: r.errors[0] ? { message: r.errors[0].message, stack: r.errors[0].stack, snippet: r.errors[0].snippet, } : undefined, attachments: r.attachments.map((a) => ({ name: a.name, contentType: a.contentType, path: a.path ?? null, // Playwright Reporter API provides body as a Buffer; the HTML // generator expects a base64-encoded string (matching JSON // format). When `path` is set the renderer prefers it, so skip // the base64 inflation — large image attachments end up as // sidecar files on disk rather than embedded in the report. body: a.path ? undefined : a.body ? a.body.toString('base64') : undefined, })), // TestResult.stderr is already Array<{text?: string; buffer?: string}>, // which is the same shape parseStderrSteps expects. stderr: r.stderr, })), }; }); specs.push({ title, tests: testEntries }); } suites.push({ file, specs }); } return { suites, metadata: {} }; } /** Walk up the suite chain to find the enclosing project suite's title. */ function getProjectName(test) { let suite = test.parent; while (suite) { if (suite.type === 'project') { return suite.title; } suite = suite.parent; } return ''; } //# sourceMappingURL=buildReport.js.map