UNPKG

donobu

Version:

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

156 lines 6.72 kB
"use strict"; /** * @fileoverview Donobu Slack Reporter for Playwright. * * Writes a Slack Block Kit payload to disk and, when configured, POSTs it * directly to a Slack Incoming Webhook. During auto-heal the POST is deferred * to the orchestrator so the user sees exactly one message per run reflecting * the final (merged, possibly healed) outcome — not an initial failure * message followed by a healed one. * * @usage * The reporter takes no Slack-related options. All Slack configuration is * driven by environment variables (see below) — typically populated from CI * secrets and contextual run metadata. Wire it up in `playwright.config.ts` * with no arguments: * * ```ts * reporter: [ * ['donobu/reporter/slack'], * ], * ``` * * Optionally set `outputFile` to control the on-disk payload path: * * ```ts * reporter: [ * ['donobu/reporter/slack', { outputFile: 'test-results/slack-payload.json' }], * ], * ``` * * @env Configuration * * **`DONOBU_SLACK_WEBHOOK_URL`** *(required for posting; secret)* — the Slack * Incoming Webhook URL to POST the payload to. When unset, the reporter still * writes the payload file but performs no network call (the file is then * useful as a CI artifact or for piping to `curl` manually). This is treated * as a secret and is read only from the environment — never as a reporter * option — so it cannot accidentally end up checked into a config file. * * **`DONOBU_REPORT_URL`** *(optional; not secret)* — a URL to embed in the * Slack message that links back to the full report (e.g. the GitHub Actions * run URL, or a published HTML report). When unset, the message simply omits * the link section. Read from the environment for symmetry with the webhook * URL: both come from CI context, and a single configuration story keeps the * mental model simple. To override per-suite, set the env var differently * before invoking `donobu test`. * * **`DONOBU_AUTO_HEAL_ACTIVE`** / **`DONOBU_AUTO_HEAL_ORCHESTRATED`** * *(set internally; not user-facing)* — coordination flags the donobu CLI * sets so the reporter knows when to defer Slack posting to the orchestrator. * Users should never set these themselves. * * @CI A typical GitHub Actions wiring: * * ```yaml * env: * DONOBU_SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} * DONOBU_REPORT_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} * run: npx donobu test --auto-heal * ``` * * @auto-heal Per-run delivery semantics * * Every Donobu run produces exactly one Slack POST when a webhook is * configured, regardless of whether auto-heal triggers: * - **No auto-heal configured:** the reporter posts directly during `onEnd`. * - **Auto-heal configured, tests pass:** the reporter defers; the * orchestrator posts the initial payload after the early-return. * - **Auto-heal configured, no eligible plans:** same — orchestrator posts * the initial payload via the fallback path. * - **Auto-heal configured, heal runs and merges:** the reporter defers in * both the initial run and the heal rerun; the orchestrator posts the * merged payload after re-rendering. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.postSlackPayload = postSlackPayload; 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 renderSlack_1 = require("./renderSlack"); const stateFile_1 = require("./stateFile"); /** * POST a Slack Block Kit payload to an Incoming Webhook URL. Logs failures but * never throws — Slack delivery is a nice-to-have, not a test-run blocker. */ async function postSlackPayload(webhookUrl, payload) { try { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) { const body = await response.text().catch(() => '<no body>'); Logger_1.appLogger.warn(`Donobu Slack POST returned ${response.status}: ${body}`); } } catch (error) { Logger_1.appLogger.warn('Donobu Slack POST failed.', error); } } class DonobuSlackReporter { constructor(options = {}) { 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/slack-payload.json'); const outputDir = (0, path_1.dirname)(outputFile); const autoHealActive = envVars_1.env.data.DONOBU_AUTO_HEAL_ACTIVE === '1'; const autoHealOrchestrated = envVars_1.env.data.DONOBU_AUTO_HEAL_ORCHESTRATED === '1'; const reportUrl = envVars_1.env.data.DONOBU_REPORT_URL; const report = (0, buildReport_1.buildDonobuReport)(this.resultsByTest, this.rootDir); (0, stateFile_1.mergeStateFileEntry)(envVars_1.env.data.PLAYWRIGHT_JSON_OUTPUT_DIR, report, { slack: { outputFile }, }); // Payload file is always written — even during a heal rerun — so the // auto-heal orchestrator and any CI artifact upload can see the final state. const payload = (0, renderSlack_1.renderSlack)(report, { reportUrl }); (0, fs_1.mkdirSync)(outputDir, { recursive: true }); (0, fs_1.writeFileSync)(outputFile, JSON.stringify(payload, null, 2), 'utf8'); Logger_1.appLogger.info(`Donobu Slack payload written to ${outputFile}`); // Defer Slack POST to the orchestrator when either: // - this is the auto-heal rerun (orchestrator will re-render and post the // merged result itself), OR // - the outer `donobu test` invocation enabled auto-heal (orchestrator will // decide whether to post the pre-heal or merged payload once it knows // whether auto-heal actually triggered). if (autoHealActive || autoHealOrchestrated) { return; } const webhookUrl = envVars_1.env.data.DONOBU_SLACK_WEBHOOK_URL; if (webhookUrl) { await postSlackPayload(webhookUrl, payload); } } printsToStdio() { return false; } } exports.default = DonobuSlackReporter; //# sourceMappingURL=slack.js.map