Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
03e82a6
feat: replace basic HTML reporter with rich always-on test report
thomhurst Mar 1, 2026
8110044
feat: correlate OpenTelemetry trace IDs with test results in HTML report
thomhurst Mar 1, 2026
2fd149f
fix: address code review feedback
thomhurst Mar 1, 2026
6433368
fix: scope trace timeline to current test's span and descendants
thomhurst Mar 1, 2026
7f55e04
feat: add execution timelines at global, class, and test levels
thomhurst Mar 1, 2026
dd0a3db
feat: collapse timelines by default with click-to-expand
thomhurst Mar 1, 2026
78e9bee
feat: include traceId and spanId in test search
thomhurst Mar 1, 2026
1deb8f4
fix: add diagnostic logging for GitHub artifact upload
thomhurst Mar 1, 2026
a929dc1
fix: address second round of code review feedback
thomhurst Mar 1, 2026
142ced1
fix: address remaining code review items
thomhurst Mar 1, 2026
32d3858
fix: cross-platform IsFileLocked, HttpClient timeout, parameter name
thomhurst Mar 1, 2026
976fbaf
fix: reliable GitHub Actions artifact upload via workflow step
thomhurst Mar 1, 2026
e4d2556
fix: diagnose and fix ACTIONS_RUNTIME_TOKEN availability
thomhurst Mar 1, 2026
5d710f3
fix: replace debug logging with actionable workaround instructions
thomhurst Mar 1, 2026
d2a3f9e
fix: only show artifact upload instructions when env vars are missing
thomhurst Mar 1, 2026
d07bf8e
fix: null-forgiving operator for nullable reference warning
thomhurst Mar 1, 2026
abf67e5
fix: restrict ActivityListener to TUnit sources, fix RetryAsync dead …
thomhurst Mar 1, 2026
658d3a7
fix: revert size to string for proto3 Twirp JSON, add error diagnostics
thomhurst Mar 1, 2026
223d3af
fix: use snake_case proto field names for Twirp JSON requests
thomhurst Mar 1, 2026
a2afff0
fix: use bare strings for protobuf StringValue wrapper types
thomhurst Mar 1, 2026
f6701d8
docs: move artifact upload options to docs, simplify inline messages
thomhurst Mar 1, 2026
85804d6
docs: use broader wildcard for upload-artifact path example
thomhurst Mar 1, 2026
eaee1f1
fix: address code review feedback and enrich timeline span names
thomhurst Mar 1, 2026
04621f5
feat: add OS/runtime to report filename and metadata, improve summary
thomhurst Mar 1, 2026
0a14467
feat: add visual polish to HTML test report
thomhurst Mar 1, 2026
8645329
feat: add 10 interactive UX enhancements to HTML test report
thomhurst Mar 1, 2026
54ff3f6
feat: add accessibility, mobile polish, and UX tweaks to HTML report
thomhurst Mar 1, 2026
9e09041
feat: replace hook timing steps with OTel activity spans
thomhurst Mar 1, 2026
cb976a2
fix: address code review feedback
thomhurst Mar 1, 2026
036a1da
feat: add OTel activity span for test body execution
thomhurst Mar 1, 2026
e59168c
fix: update PublicAPI snapshots for Obsolete Timing API attributes
thomhurst Mar 2, 2026
e371b18
fix: scope sort-button JS selectors to prevent cross-contamination wi…
thomhurst Mar 2, 2026
362ed73
fix: document span caps as approximate soft limits
thomhurst Mar 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/actions/execute-pipeline/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ runs:
DOTNET_ENVIRONMENT: ${{ inputs.environment }}
NuGet__ApiKey: ${{ inputs.nuget-apikey }}
NuGet__ShouldPublish: ${{ inputs.publish-packages }}
NET_VERSION: ${{ inputs.netversion }}
NET_VERSION: ${{ inputs.netversion }}
ACTIONS_RUNTIME_TOKEN: ${{ env.ACTIONS_RUNTIME_TOKEN }}
ACTIONS_RESULTS_URL: ${{ env.ACTIONS_RESULTS_URL }}
7 changes: 7 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ jobs:
- name: Publish AOT
run: dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --use-current-runtime -p:Aot=true -o TESTPROJECT_AOT --framework net10.0

- name: Expose GitHub Actions Runtime
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN']);
core.exportVariable('ACTIONS_RESULTS_URL', process.env['ACTIONS_RESULTS_URL']);

- name: Run Pipeline
uses: ./.github/actions/execute-pipeline
with:
Expand Down
2 changes: 2 additions & 0 deletions TUnit.Core/Interfaces/ITestOutput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public interface ITestOutput
/// Gets the collection of timing measurements recorded during test execution.
/// Useful for performance profiling and identifying bottlenecks.
/// </summary>
[Obsolete("Use OpenTelemetry activity spans instead. Hook timings are now automatically recorded as OTel child spans of the test activity.")]
IReadOnlyCollection<Timing> Timings { get; }

/// <summary>
Expand All @@ -37,6 +38,7 @@ public interface ITestOutput
/// Thread-safe for concurrent calls.
/// </summary>
/// <param name="timing">The timing information to record</param>
[Obsolete("Use OpenTelemetry activity spans instead. Hook timings are now automatically recorded as OTel child spans of the test activity.")]
void RecordTiming(Timing timing);

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions TUnit.Core/TestContext.Output.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace TUnit.Core;
/// </summary>
public partial class TestContext
{
#pragma warning disable CS0618 // Obsolete Timing API — internal backing for interface implementation
// Internal backing fields and properties
internal ConcurrentBag<Timing> Timings { get; } = [];
private readonly ConcurrentBag<Artifact> _artifactsBag = new();
Expand All @@ -20,12 +21,15 @@ public partial class TestContext
TextWriter ITestOutput.StandardOutput => OutputWriter;
TextWriter ITestOutput.ErrorOutput => ErrorOutputWriter;
IReadOnlyCollection<Timing> ITestOutput.Timings => Timings;
#pragma warning restore CS0618
IReadOnlyCollection<Artifact> ITestOutput.Artifacts => Artifacts;

#pragma warning disable CS0618 // Obsolete Timing API — internal backing for interface implementation
void ITestOutput.RecordTiming(Timing timing)
{
Timings.Add(timing);
}
#pragma warning restore CS0618

void ITestOutput.AttachArtifact(Artifact artifact)
{
Expand Down
1 change: 1 addition & 0 deletions TUnit.Core/Timing.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace TUnit.Core;

[Obsolete("Use OpenTelemetry activity spans instead. Hook timings are now automatically recorded as OTel child spans of the test activity.")]
public record Timing(string StepName, DateTimeOffset Start, DateTimeOffset End)
{
public TimeSpan Duration => End - Start;
Expand Down
13 changes: 13 additions & 0 deletions TUnit.Engine/Configuration/EnvironmentConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ internal static class EnvironmentConstants
// TUnit-specific: Reporters
public const string DisableGithubReporter = "TUNIT_DISABLE_GITHUB_REPORTER";
public const string DisableJUnitReporter = "TUNIT_DISABLE_JUNIT_REPORTER";
public const string DisableHtmlReporter = "TUNIT_DISABLE_HTML_REPORTER";
public const string EnableJUnitReporter = "TUNIT_ENABLE_JUNIT_REPORTER";
public const string GitHubReporterStyle = "TUNIT_GITHUB_REPORTER_STYLE";

Expand All @@ -28,4 +29,16 @@ internal static class EnvironmentConstants
public const string GitHubStepSummary = "GITHUB_STEP_SUMMARY";
public const string GitLabCi = "GITLAB_CI";
public const string CiServer = "CI_SERVER";

// GitHub Actions runtime (for artifact upload)
public const string ActionsRuntimeToken = "ACTIONS_RUNTIME_TOKEN";
public const string ActionsResultsUrl = "ACTIONS_RESULTS_URL";
public const string GitHubRepository = "GITHUB_REPOSITORY";
public const string GitHubRunId = "GITHUB_RUN_ID";

// GitHub Actions context (for CI metadata in reports)
public const string GitHubSha = "GITHUB_SHA";
public const string GitHubRef = "GITHUB_REF";
public const string GitHubHeadRef = "GITHUB_HEAD_REF";
public const string GitHubEventName = "GITHUB_EVENT_NAME";
}
11 changes: 5 additions & 6 deletions TUnit.Engine/Extensions/TestApplicationBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static void AddTUnit(this ITestApplicationBuilder testApplicationBuilder)
var junitReporter = new JUnitReporter(extension);
var junitReporterCommandProvider = new JUnitReporterCommandProvider(extension);

var htmlReporter = new HtmlReporter(extension);
var htmlReporter = new Reporters.Html.HtmlReporter(extension);
var htmlReporterCommandProvider = new HtmlReporterCommandProvider(extension);

testApplicationBuilder.RegisterTestFramework(
Expand Down Expand Up @@ -87,16 +87,15 @@ public static void AddTUnit(this ITestApplicationBuilder testApplicationBuilder)
{
var commandLineOptions = serviceProvider.GetRequiredService<ICommandLineOptions>();

// Enable if --report-html flag is set or --report-html-filename is provided
if (commandLineOptions.IsOptionSet(HtmlReporterCommandProvider.ReportHtml)
|| commandLineOptions.TryGetOptionArgumentList(HtmlReporterCommandProvider.ReportHtmlFilename, out _))
// Deprecated: --report-html is now a no-op (reporter is always-on)
if (commandLineOptions.IsOptionSet(HtmlReporterCommandProvider.ReportHtml))
{
htmlReporter.Enable();
Console.WriteLine("Warning: --report-html is deprecated. The HTML report is now generated by default. Use TUNIT_DISABLE_HTML_REPORTER=true to disable.");
}

if (commandLineOptions.TryGetOptionArgumentList(HtmlReporterCommandProvider.ReportHtmlFilename, out var pathArgs))
{
htmlReporter.SetOutputPath(pathArgs[0]);
htmlReporter.SetOutputPath(Helpers.PathValidator.ValidateAndNormalizePath(pathArgs[0], HtmlReporterCommandProvider.ReportHtmlFilename));
}

return htmlReporter;
Expand Down
2 changes: 2 additions & 0 deletions TUnit.Engine/Extensions/TestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ private static bool IsTrxEnabled(TestContext testContext)
return _cachedIsTrxEnabled.Value;
}

#pragma warning disable CS0618 // Obsolete Timing API — still needed for TimingProperty reporting
private static TimingProperty GetTimingProperty(TestContext testContext, DateTimeOffset overallStart)
{
if (overallStart == default(DateTimeOffset))
Expand All @@ -309,6 +310,7 @@ private static TimingProperty GetTimingProperty(TestContext testContext, DateTim

return new TimingProperty(new TimingInfo(overallStart, end, end - overallStart), stepTimings);
}
#pragma warning restore CS0618

private static IEnumerable<TrxMessage> GetTrxMessages(TestContext testContext, string? standardOutput, string? standardError)
{
Expand Down
57 changes: 0 additions & 57 deletions TUnit.Engine/Helpers/Timings.cs

This file was deleted.

Loading
Loading