Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
feat: HtmlReporter publishes SessionFileArtifact after writing report
Implements IDataProducer on HtmlReporter with DataTypesProduced=[SessionFileArtifact]
and calls PublishArtifactAsync after writing the HTML file so the platform can
surface the report as a session artifact. Adds unit tests to verify the interface
and DataTypesProduced contract.
  • Loading branch information
thomhurst committed Mar 27, 2026
commit ce60f8622a296fedae9fad8feee4ad298bf14bec
35 changes: 35 additions & 0 deletions TUnit.Engine.Tests/HtmlReporterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma warning disable TPEXP

using Microsoft.Testing.Platform.Extensions;
using Microsoft.Testing.Platform.Extensions.Messages;
using Shouldly;
using TUnit.Engine.Reporters.Html;

namespace TUnit.Engine.Tests;

public class HtmlReporterTests
{
private sealed class MockExtension : IExtension
{
public string Uid => "MockExtension";
public string DisplayName => "Mock";
public string Version => "1.0.0";
public string Description => "Mock Extension";
public Task<bool> IsEnabledAsync() => Task.FromResult(true);
}

[Test]
public void HtmlReporter_Implements_IDataProducer()
{
var reporter = new HtmlReporter(new MockExtension());
reporter.ShouldBeAssignableTo<Microsoft.Testing.Platform.Extensions.Messages.IDataProducer>();
}

[Test]
public void HtmlReporter_DataTypesProduced_Contains_SessionFileArtifact()
{
var reporter = new HtmlReporter(new MockExtension());
var producer = (Microsoft.Testing.Platform.Extensions.Messages.IDataProducer)reporter;
producer.DataTypesProduced.ShouldContain(typeof(SessionFileArtifact));
}
}
3 changes: 2 additions & 1 deletion TUnit.Engine.Tests/TUnit.Engine.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Testing.Extensions.CrashDump" />
<PackageReference Include="Microsoft.Testing.Platform" />
<PackageReference Include="Microsoft.Testing.Extensions.CrashDump" />
<PackageReference Include="Microsoft.Testing.Extensions.HangDump" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
<PackageReference Include="CliWrap" />
Expand Down
28 changes: 25 additions & 3 deletions TUnit.Engine/Reporters/Html/HtmlReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@

namespace TUnit.Engine.Reporters.Html;

internal sealed class HtmlReporter(IExtension extension) : IDataConsumer, ITestHostApplicationLifetime, IFilterReceiver, IDisposable
public sealed class HtmlReporter(IExtension extension) : IDataConsumer, IDataProducer, ITestHostApplicationLifetime, IFilterReceiver, IDisposable
{
private string? _outputPath;
#pragma warning disable CS0414 // Field assigned but value never used — consumed in Task 2
private (IMessageBus MessageBus, SessionUid SessionUid)? _sessionContext;
#pragma warning restore CS0414
private readonly ConcurrentDictionary<string, ConcurrentQueue<TestNodeUpdateMessage>> _updates = [];

#if NET
Expand Down Expand Up @@ -61,6 +59,8 @@ public Task ConsumeAsync(IDataProducer dataProducer, IData value, CancellationTo

public Type[] DataTypesConsumed { get; } = [typeof(TestNodeUpdateMessage)];

public Type[] DataTypesProduced { get; } = [typeof(SessionFileArtifact)];

public Task BeforeRunAsync(CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(_outputPath))
Expand Down Expand Up @@ -112,6 +112,8 @@ public async Task AfterRunAsync(int exitCode, CancellationToken cancellation)

await WriteFileAsync(_outputPath!, html, cancellation);

await PublishArtifactAsync(_outputPath!, cancellation);

// GitHub Actions integration (artifact upload + step summary)
await TryGitHubIntegrationAsync(_outputPath!, cancellation);
}
Expand All @@ -121,6 +123,26 @@ public async Task AfterRunAsync(int exitCode, CancellationToken cancellation)
}
}

private async Task PublishArtifactAsync(string outputPath, CancellationToken cancellationToken)
{
if (_sessionContext is not { } ctx)
{
return;
}

var file = new FileInfo(outputPath);
if (!file.Exists)
{
return;
}

await ctx.MessageBus.PublishAsync(this, new SessionFileArtifact(
ctx.SessionUid,
file,
"HTML Test Report",
"TUnit HTML test results report"));
}

public void Dispose()
{
#if NET
Expand Down