Skip to content

feat: rich HTML test reports#5044

Merged
thomhurst merged 33 commits intomainfrom
002-html-test-report
Mar 2, 2026
Merged

feat: rich HTML test reports#5044
thomhurst merged 33 commits intomainfrom
002-html-test-report

Conversation

@thomhurst
Copy link
Owner

Summary

  • Replaces the basic table-based HTML reporter with a feature-rich, always-on HTML test report
  • New "Dark Observatory" dark theme with animated SVG donut chart, glass-morphism cards, expandable test details, type-ahead search, status filters, and class-based grouping
  • Adds OpenTelemetry trace timeline for Activity spans (.NET 8+), GitHub Actions artifact upload via Twirp protocol, and AOT-compatible JSON serialization
  • Report is always generated by default; disable with TUNIT_DISABLE_HTML_REPORTER=true; --report-html flag now shows deprecation warning

New Files

File Purpose
Html/HtmlReporter.cs Main reporter implementing IDataConsumer, ITestHostApplicationLifetime, IFilterReceiver
Html/HtmlReportGenerator.cs Self-contained HTML/CSS/JS template builder (~840 lines)
Html/HtmlReportDataModel.cs POCO types with [JsonPropertyName] for camelCase serialization
Html/HtmlReportJsonContext.cs [JsonSerializable] source-gen context for AOT compatibility
Html/ActivityCollector.cs ActivityListener for OTel span capture (guarded with #if NET)
Html/GitHubArtifactUploader.cs Twirp protocol artifact upload with exponential backoff retry
docs/guides/html-report.md User-facing documentation

Modified Files

  • EnvironmentConstants.cs — Added TUNIT_DISABLE_HTML_REPORTER and GitHub Actions runtime env vars
  • TestApplicationBuilderExtensions.cs — Updated registration for new reporter, deprecation warning for --report-html
  • docs/reference/environment-variables.md — Added TUNIT_DISABLE_HTML_REPORTER documentation
  • Deleted old Reporters/HtmlReporter.cs

Test plan

  • Builds on all targets: net8.0, net9.0, net10.0, netstandard2.0
  • End-to-end: report generates correctly with passing tests (27 tests)
  • End-to-end: report generates correctly with failing tests (error details, stack traces)
  • Retry badge only shows for genuinely retried tests (not for normal 2-update cycle)
  • Verify GitHub Actions artifact upload in a CI workflow run
  • Verify TUNIT_DISABLE_HTML_REPORTER=true suppresses report generation
  • Verify --report-html-filename custom path still works

Replace the basic table-based HTML reporter with a feature-rich,
always-on HTML test report with a modern dark theme ("Dark Observatory").

New features:
- Always-on by default (disable via TUNIT_DISABLE_HTML_REPORTER)
- Summary dashboard with animated SVG donut chart
- Expandable test details with exception, stack trace, stdout/stderr
- Type-ahead search and status filter pills
- Class-based grouping with collapsible sections
- OpenTelemetry trace timeline (Activity spans on .NET 8+)
- GitHub Actions artifact upload via Twirp protocol
- Retry badge display for genuinely retried tests
- Dark theme with glass-morphism cards and CSS animations
- Responsive layout with print styles
- AOT-compatible JSON serialization via source generators
- Deprecation warning for --report-html flag

New files:
- Html/HtmlReporter.cs — Main reporter (IDataConsumer, ITestHostApplicationLifetime)
- Html/HtmlReportGenerator.cs — Self-contained HTML/CSS/JS template builder
- Html/HtmlReportDataModel.cs — POCO types with JsonPropertyName attributes
- Html/HtmlReportJsonContext.cs — JsonSerializable source-gen context
- Html/ActivityCollector.cs — ActivityListener for OTel span capture
- Html/GitHubArtifactUploader.cs — Twirp artifact upload with retry
- docs/guides/html-report.md — User documentation
@thomhurst thomhurst linked an issue Mar 1, 2026 that may be closed by this pull request
1 task
claude[bot]

This comment was marked as outdated.

Add tunit.test.node_uid activity tag so the ActivityCollector can map
each "test case" span back to its TestNode UID. The HtmlReporter now
populates TraceId and SpanId on each ReportTestResult, enabling the
trace timeline to be linked directly to individual tests.

Changes:
- TestExecutor: add tunit.test.node_uid tag to test case Activity
- ActivityCollector: add GetTestSpanLookup() mapping node UID → (traceId, spanId)
- HtmlReporter: populate TraceId/SpanId on test results during BuildReportData
- HtmlReportDataModel: change TraceId/SpanId from init to set
claude[bot]

This comment was marked as outdated.

claude[bot]

This comment was marked as outdated.

- Fix race condition: change _updates from List to ConcurrentBag for
  thread-safe concurrent Add() calls in ConsumeAsync
- Move _outputPath initialization from IsEnabledAsync to BeforeRunAsync
  to remove side effect from a query method
- Extract AccumulateStatus helper to deduplicate summary counting logic
- Add global span cap (50K) to ActivityCollector to prevent unbounded
  memory growth from non-TUnit activity sources
- Log warning on retry exhaustion in GitHubArtifactUploader
- Improve last-update selection to explicitly find final-state updates
  instead of relying on bag ordering
Each test's trace timeline was rendering all spans sharing the same
traceId (e.g., the entire class-level trace). Now renderTrace filters
to only the test's own span (identified by spanId) plus its descendant
spans, making the timeline relevant and scalable for large suites.
claude[bot]

This comment was marked as outdated.

claude[bot]

This comment was marked as outdated.

- Global "Execution Timeline" below the dashboard shows session,
  assembly, and test suite spans as a high-level overview
- "Class Timeline" inside each group shows the test suite span and
  its non-test-case children (hooks, setup, teardown)
- Per-test trace continues to show only the test case span + descendants
- Refactored renderTrace into reusable renderSpanRows/getDescendants
  helpers shared across all three levels
Wrap global, class, and per-test timelines in collapsible sections
that start collapsed. Click the header arrow to expand. Keeps the
report compact on first load, especially for large test suites.
@thomhurst thomhurst enabled auto-merge (squash) March 2, 2026 01:53
@thomhurst thomhurst merged commit a0ea3a3 into main Mar 2, 2026
11 of 14 checks passed
@thomhurst thomhurst deleted the 002-html-test-report branch March 2, 2026 01:58
This was referenced Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: A "Super-Fancy" HTML Report

2 participants