Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
37 changes: 37 additions & 0 deletions TUnit.Engine.Tests/HtmlReporterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,43 @@ public void GenerateHtml_RoundTrips_ClassTimeline_CustomProperty_OnTest()
embedded.ShouldContain("\"value\":\"FullExecution\"");
}

[Test]
public void ExtractTestResult_SortsTestMetadataProperty_Into_Categories_And_CustomProperties()
{
// Regression: Microsoft.Testing.Platform's VSTestBridge convention emits categories as
// TestMetadataProperty(name, "") — the category name lives in Key, Value is empty.
// Traits/custom properties use the (key, value) form with a non-empty Value.
// Earlier code keyed off IsNullOrEmpty(Key), which inverted both branches and silently
// misclassified every category as a custom property — leaving the HTML report's
// category-pill UI permanently empty.
var node = new TestNode
{
Uid = new TestNodeUid("extract-1"),
DisplayName = "Test",
Properties = new PropertyBag(
PassedTestNodeStateProperty.CachedInstance,
new TestMethodIdentifierProperty(
@namespace: "TestNamespace",
assemblyFullName: "TestAssembly",
typeName: "SampleTests",
methodName: "Test",
parameterTypeFullNames: [],
returnTypeFullName: "System.Void",
methodArity: 0),
new TestMetadataProperty("Async", string.Empty),
new TestMetadataProperty("Integration", string.Empty),
new TestMetadataProperty("Owner", "TeamA"))
};

var result = HtmlReporter.ExtractTestResult("extract-1", node, traceId: null, spanId: null, retryAttempt: 0, additionalTraceIds: null);

result.Categories.ShouldBe(["Async", "Integration"], ignoreOrder: true);
result.CustomProperties.ShouldNotBeNull();
result.CustomProperties!.Length.ShouldBe(1);
result.CustomProperties[0].Key.ShouldBe("Owner");
result.CustomProperties[0].Value.ShouldBe("TeamA");
}

private static ReportTestResult CreateTestResultWithStartTime(string displayName, string? startTime) => new()
{
Id = displayName,
Expand Down
9 changes: 6 additions & 3 deletions TUnit.Engine/Reporters/Html/HtmlReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ private static DateTimeOffset ParseStartTimeForSort(string? raw)
: DateTimeOffset.MaxValue;
}

private static ReportTestResult ExtractTestResult(string testId, TestNode testNode, string? traceId, string? spanId, int retryAttempt, string[]? additionalTraceIds)
internal static ReportTestResult ExtractTestResult(string testId, TestNode testNode, string? traceId, string? spanId, int retryAttempt, string[]? additionalTraceIds)
{
IProperty? stateProperty = null;
TestMethodIdentifierProperty? testMethodIdentifier = null;
Expand Down Expand Up @@ -518,10 +518,13 @@ private static ReportTestResult ExtractTestResult(string testId, TestNode testNo
stdErr = TruncateOutput(e.StandardError);
break;
case TestMetadataProperty meta:
if (string.IsNullOrEmpty(meta.Key))
// MTP convention (matches Microsoft.Testing.Extensions.VSTestBridge): categories are
// emitted as TestMetadataProperty(category, "") — name in Key, empty Value. Traits/
// custom properties use (key, value) with a non-empty Value.
if (string.IsNullOrEmpty(meta.Value))
{
categories ??= [];
categories.Add(meta.Value);
categories.Add(meta.Key);
}
else
{
Expand Down
Loading