diff --git a/GitHubActionsTestLogger.Tests/InitializationSpecs.cs b/GitHubActionsTestLogger.Tests/InitializationSpecs.cs index 46fc2d0..fa80f00 100644 --- a/GitHubActionsTestLogger.Tests/InitializationSpecs.cs +++ b/GitHubActionsTestLogger.Tests/InitializationSpecs.cs @@ -34,7 +34,6 @@ public void I_can_use_the_logger_with_a_custom_configuration() { ["annotations.titleFormat"] = "TitleFormat", ["annotations.messageFormat"] = "MessageFormat", - ["summary.compactLayout"] = "true", ["summary.includePassedTests"] = "true", ["summary.includeSkippedTests"] = "true" }; @@ -46,7 +45,6 @@ public void I_can_use_the_logger_with_a_custom_configuration() logger.Context.Should().NotBeNull(); logger.Context?.Options.AnnotationTitleFormat.Should().Be("TitleFormat"); logger.Context?.Options.AnnotationMessageFormat.Should().Be("MessageFormat"); - logger.Context?.Options.SummaryCompactLayout.Should().BeTrue(); logger.Context?.Options.SummaryIncludePassedTests.Should().BeTrue(); logger.Context?.Options.SummaryIncludeSkippedTests.Should().BeTrue(); } diff --git a/GitHubActionsTestLogger/Templates/MarkdownRazorTemplate.cs b/GitHubActionsTestLogger/Templates/MarkdownRazorTemplate.cs deleted file mode 100644 index 76c608b..0000000 --- a/GitHubActionsTestLogger/Templates/MarkdownRazorTemplate.cs +++ /dev/null @@ -1,45 +0,0 @@ -using RazorBlade; - -namespace GitHubActionsTestLogger.Templates; - -internal abstract class MarkdownRazorTemplate : PlainTextTemplate -{ - protected MarkdownRazorTemplate(T model) - : base(model) - { - } - - // In order to produce HTML that's also valid Markdown, we need to - // remove some whitespace inside literals. - public new void WriteLiteral(string? literal) - { - if (!string.IsNullOrEmpty(literal)) - { - base.WriteLiteral( - literal - // Remove indentation - .Replace(" ", "") - // Remove linebreaks - .Replace("\r", "").Replace("\n", "") - ); - } - else - { - base.WriteLiteral(literal); - } - } - - // Using params here to write multiple lines as a workaround - // for the fact that Razor does not support raw string literals. - protected void WriteMarkdown(params string?[] lines) - { - // Two line breaks are required to separate markdown from HTML - base.WriteLiteral("\n\n"); - - foreach (var line in lines) - { - base.WriteLiteral(line); - base.WriteLiteral("\n"); - } - } -} \ No newline at end of file diff --git a/GitHubActionsTestLogger/Templates/TestSummaryContext.cs b/GitHubActionsTestLogger/Templates/TestSummaryContext.cs deleted file mode 100644 index 1831bd1..0000000 --- a/GitHubActionsTestLogger/Templates/TestSummaryContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace GitHubActionsTestLogger.Templates; - -internal class TestSummaryContext -{ - public required TestLoggerOptions Options { get; init; } - - public required string TestSuite { get; init; } - - public required string TargetFramework { get; init; } - - public required TestRunResult TestRunResult { get; init; } -} \ No newline at end of file diff --git a/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cs b/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cs deleted file mode 100644 index 5529c2f..0000000 --- a/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace GitHubActionsTestLogger.Templates; - -// Workaround for: -// https://github.com/ltrzesniewski/RazorBlade/issues/10 -internal partial class TestSummaryDetailsTemplate -{ - public TestSummaryDetailsTemplate(TestSummaryContext context) - : base(context) - { - } -} \ No newline at end of file diff --git a/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cshtml b/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cshtml deleted file mode 100644 index 494a6c8..0000000 --- a/GitHubActionsTestLogger/Templates/TestSummaryDetailsTemplate.cshtml +++ /dev/null @@ -1,134 +0,0 @@ -@using System -@using System.Linq -@using GitHubActionsTestLogger.Utils.Extensions -@using Microsoft.VisualStudio.TestPlatform.ObjectModel - -@inherits MarkdownRazorTemplate - -@{ - string FormatTestOutcome(TestOutcome outcome) => outcome switch - { - TestOutcome.Passed => "🟩", - TestOutcome.Failed => "🟥", - _ => "🟨" - }; -} - - - - - - - - - - - - - - -
✓  Passed✘  Failed↷  Skipped∑  Total⧗  Elapsed
- @(Model.TestRunResult.PassedTestCount > 0 - ? Model.TestRunResult.PassedTestCount.ToString() - : "—") - - @(Model.TestRunResult.FailedTestCount > 0 - ? Model.TestRunResult.FailedTestCount.ToString() - : "—") - - @(Model.TestRunResult.SkippedTestCount > 0 - ? Model.TestRunResult.SkippedTestCount.ToString() - : "—") - - @Model.TestRunResult.TotalTestCount - - @Model.TestRunResult.OverallDuration.ToHumanString() -
- -@{ - var testResults = Model - .TestRunResult - .TestResults - .Where(r => - r.Outcome == TestOutcome.Failed || - r.Outcome == TestOutcome.Passed && Model.Options.SummaryIncludePassedTests || - r.Outcome == TestOutcome.Skipped && Model.Options.SummaryIncludeSkippedTests - ); - - var testResultGroups = testResults - .GroupBy(r => r.TestCase.GetTypeFullyQualifiedName(), StringComparer.Ordinal) - .Select(g => new - { - TypeFullyQualifiedName = g.Key, - TypeName = g.First().TestCase.GetTypeMinimallyQualifiedName(), - TestResults = g - .OrderByDescending(r => r.Outcome == TestOutcome.Failed) - .ThenByDescending(r => r.Outcome == TestOutcome.Passed) - .ThenBy(r => r.TestCase.DisplayName, StringComparer.Ordinal) - .ToArray() - }) - .OrderByDescending(g => g.TestResults.Any(r => r.Outcome == TestOutcome.Failed)) - .ThenByDescending(g => g.TestResults.Any(r => r.Outcome == TestOutcome.Passed)) - .ThenBy(g => g.TypeName, StringComparer.Ordinal); -} - -@foreach (var testResultGroup in testResultGroups) -{ - var failedTestCount = testResultGroup.TestResults.Count(r => r.Outcome == TestOutcome.Failed); - -
- - @testResultGroup.TypeName - - @if (failedTestCount > 0) - { - @(" ")(@failedTestCount failed) - } - - - @* This adds a margin that is smaller than
*@ -

- -
    - @foreach (var testResult in testResultGroup.TestResults) - { - // Use display name if it's different from the fully qualified name, - // otherwise use the minimally qualified name. - var testName = !string.Equals( - testResult.TestCase.DisplayName, - testResult.TestCase.FullyQualifiedName, - StringComparison.Ordinal) - ? testResult.TestCase.DisplayName - : testResult.TestCase.GetMinimallyQualifiedName(); - - // Test source permalink - var filePath = testResult.TryGetSourceFilePath(); - var fileLine = testResult.TryGetSourceLine(); - var url = filePath?.Pipe(p => GitHubWorkflow.TryGenerateFilePermalink(p, fileLine)); - -
  • - @FormatTestOutcome(testResult.Outcome) - - @if (!string.IsNullOrWhiteSpace(url)) - { - @testName - } - else - { - @testName - } - - @if (!string.IsNullOrWhiteSpace(testResult.ErrorMessage)) - { - WriteMarkdown( - "```yml", - testResult.ErrorMessage, - testResult.ErrorStackTrace, - "```" - ); - } -
  • - } -
-
-} \ No newline at end of file diff --git a/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cs b/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cs deleted file mode 100644 index b1084ed..0000000 --- a/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace GitHubActionsTestLogger.Templates; - -// Workaround for: -// https://github.com/ltrzesniewski/RazorBlade/issues/10 -internal partial class TestSummaryTemplate -{ - public TestSummaryTemplate(TestSummaryContext context) - : base(context) - { - } -} \ No newline at end of file diff --git a/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cshtml b/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cshtml deleted file mode 100644 index fe395f7..0000000 --- a/GitHubActionsTestLogger/Templates/TestSummaryTemplate.cshtml +++ /dev/null @@ -1,37 +0,0 @@ -@using Microsoft.VisualStudio.TestPlatform.ObjectModel - -@inherits MarkdownRazorTemplate - -@{ - string FormatTestOutcome(TestOutcome outcome) => outcome switch - { - TestOutcome.Passed => "🟢", - TestOutcome.Failed => "🔴", - _ => "🟡" - }; -} - -@if (Model.Options.SummaryCompactLayout) -{ - // Have to yield raw HTML -
- - @FormatTestOutcome(Model.TestRunResult.OverallOutcome) @Model.TestSuite (@Model.TargetFramework) - - - @* This adds a margin that is smaller than
*@ -

- - @(new TestSummaryDetailsTemplate(Model)) -
-} -else -{ -

- @FormatTestOutcome(Model.TestRunResult.OverallOutcome) @Model.TestSuite (@Model.TargetFramework) -

- - @(new TestSummaryDetailsTemplate(Model)) - -
-} \ No newline at end of file diff --git a/GitHubActionsTestLogger/TestLoggerContext.cs b/GitHubActionsTestLogger/TestLoggerContext.cs index 496e0a3..64100a6 100644 --- a/GitHubActionsTestLogger/TestLoggerContext.cs +++ b/GitHubActionsTestLogger/TestLoggerContext.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using System.Text; -using GitHubActionsTestLogger.Templates; using GitHubActionsTestLogger.Utils.Extensions; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; @@ -90,7 +89,7 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs args) { lock (_lock) { - var context = new TestSummaryContext + var template = new TestSummaryTemplate { Options = Options, @@ -105,7 +104,7 @@ public void HandleTestRunComplete(TestRunCompleteEventArgs args) TestRunResult = new TestRunResult(_testResults, args.ElapsedTimeInRunningTests) }; - _github.CreateSummary(new TestSummaryTemplate(context).Render()); + _github.CreateSummary(template.Render()); } } } \ No newline at end of file diff --git a/GitHubActionsTestLogger/TestLoggerOptions.cs b/GitHubActionsTestLogger/TestLoggerOptions.cs index f1dd587..69c88ed 100644 --- a/GitHubActionsTestLogger/TestLoggerOptions.cs +++ b/GitHubActionsTestLogger/TestLoggerOptions.cs @@ -9,8 +9,6 @@ public partial class TestLoggerOptions public string AnnotationMessageFormat { get; init; } = "$error"; - public bool SummaryCompactLayout { get; init; } - public bool SummaryIncludePassedTests { get; init; } public bool SummaryIncludeSkippedTests { get; init; } @@ -30,10 +28,6 @@ public partial class TestLoggerOptions parameters.GetValueOrDefault("annotations.messageFormat") ?? Default.AnnotationMessageFormat, - SummaryCompactLayout = - parameters.GetValueOrDefault("summary.compactLayout")?.Pipe(bool.Parse) ?? - Default.SummaryCompactLayout, - SummaryIncludePassedTests = parameters.GetValueOrDefault("summary.includePassedTests")?.Pipe(bool.Parse) ?? Default.SummaryIncludePassedTests, diff --git a/GitHubActionsTestLogger/TestSummaryTemplate.cshtml b/GitHubActionsTestLogger/TestSummaryTemplate.cshtml new file mode 100644 index 0000000..29edd66 --- /dev/null +++ b/GitHubActionsTestLogger/TestSummaryTemplate.cshtml @@ -0,0 +1,196 @@ +@using System +@using System.Collections.Generic +@using System.Linq +@using GitHubActionsTestLogger.Utils.Extensions +@using Microsoft.VisualStudio.TestPlatform.ObjectModel + +@inherits RazorBlade.PlainTextTemplate + +@functions +{ + public required TestLoggerOptions Options { get; init; } + + public required string TestSuite { get; init; } + + public required string TargetFramework { get; init; } + + public required TestRunResult TestRunResult { get; init; } +} + +
+ @{ + var overallOutcomeEmoji = TestRunResult.OverallOutcome switch + { + TestOutcome.Passed => "🟢", + TestOutcome.Failed => "🔴", + _ => "🟡" + }; + } + + +

@overallOutcomeEmoji @TestSuite (@TargetFramework)

+
+ + @* This adds a margin that is smaller than
*@ +

+ + + + + + + + + + + + + + +
✓  Passed✘  Failed↷  Skipped∑  Total⧗  Elapsed
+ @(TestRunResult.PassedTestCount > 0 + ? TestRunResult.PassedTestCount.ToString() + : "—") + + @(TestRunResult.FailedTestCount > 0 + ? TestRunResult.FailedTestCount.ToString() + : "—") + + @(TestRunResult.SkippedTestCount > 0 + ? TestRunResult.SkippedTestCount.ToString() + : "—") + + @TestRunResult.TotalTestCount + + @TestRunResult.OverallDuration.ToHumanString() +
+ + @{ + var testResultGroups = TestRunResult + .TestResults + .Where(r => + r.Outcome == TestOutcome.Failed || + r.Outcome == TestOutcome.Passed && Options.SummaryIncludePassedTests || + r.Outcome == TestOutcome.Skipped && Options.SummaryIncludeSkippedTests + ) + .GroupBy(r => r.TestCase.GetTypeFullyQualifiedName(), StringComparer.Ordinal) + .Select(g => new + { + TypeFullyQualifiedName = g.Key, + TypeName = g.First().TestCase.GetTypeMinimallyQualifiedName(), + TestResults = g + .OrderByDescending(r => r.Outcome == TestOutcome.Failed) + .ThenByDescending(r => r.Outcome == TestOutcome.Passed) + .ThenBy(r => r.TestCase.DisplayName, StringComparer.Ordinal) + .ToArray() + }) + .OrderByDescending(g => g.TestResults.Any(r => r.Outcome == TestOutcome.Failed)) + .ThenByDescending(g => g.TestResults.Any(r => r.Outcome == TestOutcome.Passed)) + .ThenBy(g => g.TypeName, StringComparer.Ordinal); + } + + @foreach (var testResultGroup in testResultGroups) + { + var failedTestCount = testResultGroup.TestResults.Count(r => r.Outcome == TestOutcome.Failed); + +
+ + @testResultGroup.TypeName + + @if (failedTestCount > 0) + { + @(" ")(@failedTestCount failed) + } + + + @* This adds a margin that is smaller than
*@ +

+ +
    + @foreach (var testResult in testResultGroup.TestResults) + { + var outcomeEmoji = testResult.Outcome switch + { + TestOutcome.Passed => "🟩", + TestOutcome.Failed => "🟥", + _ => "🟨" + }; + + // Use display name if it's different from the fully qualified name, + // otherwise use the minimally qualified name. + var testName = !string.Equals( + testResult.TestCase.DisplayName, + testResult.TestCase.FullyQualifiedName, + StringComparison.Ordinal) + ? testResult.TestCase.DisplayName + : testResult.TestCase.GetMinimallyQualifiedName(); + + // Test source permalink + var filePath = testResult.TryGetSourceFilePath(); + var fileLine = testResult.TryGetSourceLine(); + var url = filePath?.Pipe(p => GitHubWorkflow.TryGenerateFilePermalink(p, fileLine)); + +
  • + @outcomeEmoji + + @if (!string.IsNullOrWhiteSpace(url)) + { + @testName + } + else + { + @testName + } + + @if (!string.IsNullOrWhiteSpace(testResult.ErrorMessage)) + { + WriteMarkdown( + "```yml", + testResult.ErrorMessage, + testResult.ErrorStackTrace, + "```" + ); + } +
  • + } +
+
+ } +
+ +@functions +{ + // In order to produce HTML that's also valid Markdown, we need to + // remove some whitespace inside literals. + public new void WriteLiteral(string? literal) + { + if (!string.IsNullOrEmpty(literal)) + { + base.WriteLiteral( + literal + // Remove indentation + .Replace(" ", "") + // Remove linebreaks + .Replace("\r", "").Replace("\n", "") + ); + } + else + { + base.WriteLiteral(literal); + } + } + + // Using params here to write multiple lines as a workaround + // for the fact that Razor does not support raw string literals. + private void WriteMarkdown(params string?[] lines) + { + // Two line breaks are required to separate markdown from HTML + base.WriteLiteral("\n\n"); + + foreach (var line in lines) + { + base.WriteLiteral(line); + base.WriteLiteral("\n"); + } + } +} \ No newline at end of file diff --git a/Readme.md b/Readme.md index 59ca548..42cbaa9 100644 --- a/Readme.md +++ b/Readme.md @@ -112,13 +112,6 @@ Supports the same replacement tokens as [`annotations.titleFormat`](#custom-anno - `$error` → `AssertionException: Expected 'true' but found 'false'` - `$error\n$trace` → `AssertionException: Expected 'true' but found 'false'`, followed by stacktrace on the next line -#### Compact summary layout - -Use the `summary.compactLayout` option to enable a more compact layout for the test summary. -This layout is better suited for solutions that contain a large number of test projects. - -**Default**: `false`. - #### Include passed tests in summary Use the `summary.includePassedTests` option to specify whether passed tests should be included in the summary.