Structured Diagnostics and Reporting
Designing what a framework captures on failure -- logs, screenshots, traces -- as structured, queryable diagnostic data, not just raw text, and what a genuinely useful test report communicates beyond pass/fail counts.
What you'll learn
- Design a structured failure record that captures enough context to begin diagnosing a failure without re-running it
- Explain why capturing diagnostics only on failure (not on every run) is the practical default for CI performance
- Explain what a genuinely useful test report communicates beyond a simple pass/fail count
Prerequisites
Explanation
No real trace, screenshot, or CI report is generated by this lesson's exercises -- they model structured-diagnostic-record design as data, using genuine JavaScript/TypeScript execution.
Capturing "some logs" on a test failure is a start, but a genuinely useful framework designs its failure output as structured, queryable data, not a wall of unstructured text — a failure record with clearly separated fields (test name, tags, the specific assertion that failed, the environment/browser it ran against, a timestamp, and a link or path to the trace/screenshot/video captured) can be filtered, searched, and aggregated across many CI runs — "show me every failure of this specific assertion across the last 20 runs" is a query a structured record supports, and unstructured console output effectively does not.
The practical default, matching what Playwright itself does, is to capture the expensive diagnostics — traces, screenshots, video — only on failure (or on the first retry), not on every single successful run: capturing a full trace for every passing test would meaningfully slow down CI and consume storage for artifacts that are, by definition, never actually needed for a test that passed. This is a deliberate performance/completeness tradeoff, not an oversight — the cases where those artifacts matter are overwhelmingly the failing ones.
A genuinely useful test report communicates meaningfully more than a bare pass/fail count. At minimum: which SPECIFIC tests failed (not just "12 failed"), the failure trend across recent runs (is this test newly broken, or has it been reliably failing for a week?), retry counts per test (a test that "passed" only after 2 retries is a very different signal from one that passed immediately, even though both show as green), and direct links to each failure's captured diagnostics, so investigating a failure doesn't require re-running the suite locally just to reproduce and capture the same information the CI run already had.
Example
Modeling a structured failure record's shape and the failure-only diagnostic-capture decision, as data.
function buildFailureRecord(test, error, tracePath) {
return {
testName: test.name,
tags: test.tags,
failedAssertion: error.assertionDescription,
environment: test.environment,
timestamp: test.timestamp,
tracePath, // a link/path to the captured artifact, not the raw trace data itself
};
}
const record = buildFailureRecord(
{ name: "checkout completes", tags: ["smoke", "checkout"], environment: "chromium", timestamp: "2026-08-03T10:00:00Z" },
{ assertionDescription: "expected order total to be $42.00" },
"traces/checkout-completes-run482.zip"
);
console.log(record.failedAssertion); // "expected order total to be $42.00" -- immediately queryable/searchable, not buried in prose
function shouldCaptureExpensiveDiagnostics(testOutcome) {
return testOutcome === "failed" || testOutcome === "flaky-retry";
}
console.log(shouldCaptureExpensiveDiagnostics("passed")); // false -- capturing a trace here would be pure, unneeded overhead
console.log(shouldCaptureExpensiveDiagnostics("failed")); // true -- exactly where the diagnostic artifact actually mattersTry it yourself
Call shouldCaptureExpensiveDiagnostics with 'flaky-retry', and confirm a retried-then-passed test is still correctly treated as worth capturing diagnostics for.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
This models building a structured failure record only -- no real trace or screenshot is captured. Write buildFailureRecord(testName, tags, failedAssertion, tracePath), returning an object with exactly those four fields, named testName, tags, failedAssertion, tracePath.
Checks: correctly preserves the test name · correctly preserves the full tags array · correctly preserves the trace path
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
This models a report's per-test signal beyond bare pass/fail only -- no real report is generated. Write reportSignal(outcome, retryCount): if outcome is 'failed', return 'needs-investigation'. Else if retryCount > 0, return 'passed-but-flaky'. Else return 'genuinely-stable-pass'.
Checks: correctly flags a failure for investigation · correctly distinguishes a retried pass from a genuinely stable one · correctly identifies a genuinely stable, immediate pass
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Capturing failure information as unstructured console text instead of a structured record with clearly separated fields -- this makes it effectively impossible to query, filter, or aggregate failures across many CI runs.
- Capturing full traces, screenshots, and video for every single test run, including passing ones -- this meaningfully slows down CI and wastes storage on artifacts that are never actually needed for a passing test.
- Reporting only a bare pass/fail count without retry counts or failure trends -- this hides the meaningful difference between a genuinely stable pass and one that only succeeded after retries, and between a newly broken test and a chronically flaky one.
Knowledge check
Takeaway
Design failure output as structured, queryable data with clearly separated fields, not unstructured text. Capture expensive diagnostics (traces, screenshots, video) only on failure, as a deliberate performance tradeoff. Report retry counts and failure trends, not just a bare pass/fail count -- a retried pass is a meaningfully different signal from an immediate one.
Summary
A structured failure record (test name, tags, failed assertion, environment, timestamp, trace path) supports filtering and aggregation across CI runs in a way unstructured console text does not. Capturing expensive diagnostics only on failure is a deliberate, practical performance tradeoff, since those artifacts are needed almost exclusively for failing tests. A genuinely useful report surfaces retry counts and failure trends, not just a bare pass/fail count, since a retried pass is a meaningfully different reliability signal from an immediate one.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.