UNPKG

donobu

Version:

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

146 lines 6.03 kB
"use strict"; /** * @fileoverview Donobu HTML Reporter for Playwright * * A Playwright reporter that generates a Donobu-branded HTML report directly, * without requiring a separate JSON post-processing step. * * @usage * ```ts * // playwright.config.ts * import { defineConfig } from 'donobu'; * export default defineConfig({ * reporter: [ * ['donobu/reporter/html', { outputFile: 'test-results/index.html' }], * ], * }); * ``` * * Optionally enrich the report with Donobu AI triage data: * ```ts * reporter: [ * ['donobu/reporter/html', { * outputFile: 'test-results/index.html', * triageDir: process.env.DONOBU_TRIAGE_DIR, * }], * ], * ``` * * During an auto-heal rerun (`DONOBU_AUTO_HEAL_ACTIVE=1`) the reporter skips * HTML generation. It always serializes its `DonobuReport` to a state file in * the Playwright JSON output directory so the orchestrator can pick it up, * merge it with the initial run's state, and re-render the HTML once. */ Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const path_1 = require("path"); const envVars_1 = require("../envVars"); const Logger_1 = require("../utils/Logger"); const buildReport_1 = require("./buildReport"); const render_1 = require("./render"); const stateFile_1 = require("./stateFile"); class DonobuHtmlReporter { constructor(options = {}) { /** Accumulates all TestResult objects per TestCase (one per retry attempt). */ this.resultsByTest = new Map(); this.options = options; } onBegin(config, _suite) { this.rootDir = config.rootDir; } onTestEnd(test, result) { const existing = this.resultsByTest.get(test); if (existing) { existing.push(result); } else { this.resultsByTest.set(test, [result]); } } async onEnd(_result) { const outputFile = (0, path_1.resolve)(this.options.outputFile ?? 'test-results/index.html'); const outputDir = (0, path_1.dirname)(outputFile); const autoHealActive = envVars_1.env.data.DONOBU_AUTO_HEAL_ACTIVE === '1'; const report = (0, buildReport_1.buildDonobuReport)(this.resultsByTest, this.rootDir); // Persist the full report + this reporter's output entry to the shared // state file so other reporters and the auto-heal orchestrator can find // it. When multiple reporters run in the same process this merges each // entry in without clobbering siblings. (0, stateFile_1.mergeStateFileEntry)(envVars_1.env.data.PLAYWRIGHT_JSON_OUTPUT_DIR, report, { html: { outputFile }, }); // During an auto-heal rerun the orchestrator re-renders the HTML from the // merged report; writing it here would overwrite the initial HTML with // incomplete data. if (autoHealActive) { return; } const triage = this.options.triageDir ? (0, render_1.loadTriageData)((0, path_1.resolve)(this.options.triageDir)) : { plans: [], evidence: [] }; const html = (0, render_1.renderHtml)(report, triage, outputDir); (0, fs_1.mkdirSync)(outputDir, { recursive: true }); (0, fs_1.writeFileSync)(outputFile, html, 'utf8'); Logger_1.appLogger.info(`Donobu report written to ${outputFile}`); this.writePerTestStubs(outputFile, outputDir); } printsToStdio() { return false; } /** * Drop a tiny redirect `index.html` into each Playwright-managed per-test * directory under `outputDir`. The stub points back at the combined report's * `#?testId=<id>` deep link, giving every test a stable URL inside its own * directory without duplicating any of the rendered HTML. * * Directories are discovered from `dirname(attachment.path)` — Donobu does * not invent any directory naming or layout. Tests with no attachments (and * therefore no Playwright-created directory) get no stub. */ writePerTestStubs(outputFile, outputDir) { for (const [test, results] of this.resultsByTest) { if (!test.id) { continue; } const testDirs = new Set(); for (const result of results) { for (const att of result.attachments) { if (!att.path) { continue; } const attDir = (0, path_1.dirname)((0, path_1.resolve)(att.path)); const rel = (0, path_1.relative)(outputDir, attDir); if (!rel || rel.startsWith('..')) { // Attachment lives outside the report's output directory — skip; // we shouldn't be writing into arbitrary paths. continue; } testDirs.add(attDir); } } if (testDirs.size === 0) { continue; } const fileBase = test.location.file.split('/').pop() ?? test.location.file; const title = `${fileBase}${test.title}`; for (const testDir of testDirs) { const stubPath = (0, path_1.resolve)(testDir, 'index.html'); const relPathToReport = (0, path_1.relative)(testDir, outputFile); const stub = (0, render_1.renderPerTestStub)({ testId: test.id, title, relPathToReport, }); try { (0, fs_1.mkdirSync)(testDir, { recursive: true }); (0, fs_1.writeFileSync)(stubPath, stub, 'utf8'); } catch (err) { Logger_1.appLogger.warn(`Failed to write per-test redirect stub at ${stubPath}: ${err.message}`); } } } } } exports.default = DonobuHtmlReporter; //# sourceMappingURL=html.js.map