@inherits SpecFlow.Plus.Runner.Reporting.CustomTemplateBase @using System @using System.Collections.Generic @using System.Linq @using System.Globalization @using TechTalk.SpecRun.Framework @using TechTalk.SpecRun.Framework.Results @using TechTalk.SpecRun.Framework.TestSuiteStructure @helper GetReportBar(TestItemExecutionResult test) {   } @helper GetTimelineBar(DateTime startTime, DateTime endTime, double msecPerPixel, TestItemExecutionResult test) { int endPixel = Math.Max((int)Math.Round((endTime - Model.ExecutionTime.StartTime).TotalMilliseconds / msecPerPixel), currentPixel + 4); int size = endPixel - currentPixel; currentPixel = endPixel;   } @helper GetSummaryHeader(string titleHeader, bool showDuration = false) { @if (titleHeader != null) { @titleHeader } Success rate Tests Succeeded Failed Pending Ignored Skipped @if (showDuration) { Duration } } @helper GetSummaryRow(TestCollectionResultSummary summary, string title, string href, TimeSpan? executionDuration = null) { @if (title != null) { @title } @RenderTestExecutionSummaryRowTail(summary, executionDuration) } @helper RenderBar(TestNodeResultType testNodeResultType, int count, int total, Func titleFactory) { } @helper RenderSummaryBars(IEnumerable> data, int total, Func titleFactory) { @foreach (var resultCount in data) { @RenderBar(resultCount.Key, resultCount.Value, total, titleFactory) }
} @helper RenderTotalSummaryRowTail(TestCollectionResultSummary summary, TimeSpan? executionDuration = null) { @RenderSummaryRowTail(summary.Total, summary.ResultCounts, summary.TotalMessage, summary.GetText, executionDuration) } @helper RenderTestExecutionSummaryRowTail(TestCollectionResultSummary summary, TimeSpan? executionDuration = null) { @RenderSummaryRowTail(summary.Total, summary.TestExecutionResultCounts, summary.TotalMessage, summary.GetText, executionDuration) } @helper RenderSummaryRowTail(int total, IDictionary resultCounts, string totalMessage, Func getText, TimeSpan? executionDuration = null) { int succeeded = resultCounts.Where(rc => rc.Key.IsInGroup(TestNodeResultTypeGroup.Success)).Sum(rc => rc.Value); int failed = resultCounts.Where(rc => rc.Key.IsInGroup(TestNodeResultTypeGroup.Failure)).Sum(rc => rc.Value); int pending = resultCounts.Where(rc => rc.Key.IsInGroup(TestNodeResultTypeGroup.Pending)).Sum(rc => rc.Value); int ignored = resultCounts.Where(rc => rc.Key.IsInGroup(TestNodeResultTypeGroup.Ignored)).Sum(rc => rc.Value); int skipped = resultCounts[TestNodeResultType.Skipped]; @if (succeeded + failed + pending > 0) { @:@GetRoundedSuccessPercentage(succeeded, failed, pending)% } else { @:n/a } @RenderSummaryBars(GetOrderedBarChartData(resultCounts), total, getText) @totalMessage @succeeded @failed @pending @ignored @skipped if (executionDuration != null) { @executionDuration.Value } } @helper TestItemLinks(TestItem testItem, int level) { if (level == 0) { @testItem.Type: @testItem.Title } else { @testItem.Title } var tiResult = GetTestItemResult(testItem); if (tiResult != null) { foreach (var retry in tiResult.Executions.Skip(1)) { retry #@retry.TestItemExecutionIndex } } } @helper TestNodeLinks(TestNode testNode, int level) { if (testNode is TestItem) { @TestItemLinks((TestItem)testNode, level) } if (testNode is TestCollection) { @testNode.Type: @testNode.Title } } @functions { double GetRoundedSuccessPercentage(int succeeded, int failed, int pending) { double absolute = succeeded + failed + pending; double percent = succeeded / absolute; double scaledRoundedPercent = Math.Round(percent * 100); return scaledRoundedPercent; } string GetFixtureTitle(TestNode fixtureNode) { return fixtureNode.IsDefaultTestTarget ? fixtureNode.Title : string.Format("{0} (target: {1})", fixtureNode.Title, fixtureNode.TestTarget); } TimeSpan CalculateDuration(TestNode testNode) { TimeSpan executionDuration = TimeSpan.Zero; var testNodeResults = Model.TestExecutionResults.Where(tr => tr.TestItemResult.TestNode == testNode); foreach (var testNodeResult in testNodeResults) { executionDuration += testNodeResult.ExecutionTime.Duration; } return executionDuration; } double GetPixelBarWidth(double total, double value) { return Math.Round((value * 200 / total) - 1); } IEnumerable> GetOrderedBarChartData(IEnumerable> source) { return source.Where(rc => rc.Value > 0).OrderByDescending(rc => rc.Key.GetGroup() == TestNodeResultTypeGroup.Success ? 1000 : (int)rc.Key); } } @section ProjectInformation {
  • Project: @Model.Configuration.ProjectName
  • Configuration: @Model.Configuration.Name
  • Test Assemblies: @string.Join(", ", Model.Configuration.TestAssemblyPaths)
  • Start Time: @Model.ExecutionTime.StartTime
  • Duration: @Model.ExecutionTime.Duration
  • Test Threads: @Model.TestThreads.Count
  • @if (Model.FrameworkError != null) {
  • Execution framework error: @(Model.FrameworkError.ToString())
  • }
} @section TestResultView {

Test Result View

Time Act Time Execution Result desc
Time Act Time
     
     
     
@foreach (var test in Model.TestExecutionResults.OrderBy(tr => tr.ResultType)) { @GetReportBar(test); } @for (int test10Index = 1; test10Index < Model.TestExecutionResults.Count() / 10; test10Index++) { }
  
   @(test10Index * 10)
} @section FeatureSummary {

Feature Summary

@GetSummaryHeader("Feature", true) @foreach (var fixtureNode in GetTextFixtures()) { var fixtureSummary = GetSummary(fixtureNode); string fixtureTitle = GetFixtureTitle(fixtureNode); string testNodeAnchor = GetTestNodeAnchor(fixtureNode, "f"); var executionDuration = fixtureNode.SubNodes.Aggregate(TimeSpan.Zero, (acc, testNode) => acc + CalculateDuration(testNode)); @RenderTestExecutionSummaryRowTail(fixtureSummary, executionDuration) }
@fixtureTitle
} @Model.Configuration.ProjectName Test Execution Report

@Model.Configuration.ProjectName Test Execution Report

@RenderSection("ProjectInformation")

Result: @Model.Summary.ConcludedResultMessage

@GetSummaryHeader(null) @RenderTotalSummaryRowTail(Model.Summary)

Test Timeline Summary

@{ double msecPerPixel = Model.ExecutionTime.DurationMilliseconds / (Model.TestExecutionResults.Count() * 7); var secScale = Math.Max(1.0, Math.Round((msecPerPixel / 1000 * 70) / 2) * 2); var scaleItemCount = (int)Math.Floor(Model.ExecutionTime.DurationSeconds / secScale) + 1; var pixelScale = secScale * 1000 / msecPerPixel; }
@foreach (var testThread in Model.TestThreads) { } @for (int scaleIndex = 0; scaleIndex < scaleItemCount - 1; scaleIndex++) { var width = (int)(Math.Round((scaleIndex + 1) * pixelScale) - Math.Round((scaleIndex) * pixelScale)); }
thread
#@testThread.ThreadId @{ currentPixel = 0; } @GetTimelineBar(Model.ExecutionTime.StartTime, testThread.ExecutionTime.StartTime, msecPerPixel, null) @foreach (var test in Model.TestExecutionResults.Where(tr => tr.ThreadId == testThread.ThreadId).OrderBy(tr => tr.ExecutionOrder)) { @GetTimelineBar(test.ExecutionTime.StartTime, test.ExecutionTime.EndTime, msecPerPixel, test) }
 @Math.Round(secScale * scaleIndex)s@Math.Round(secScale * (scaleItemCount - 1))s
@RenderSection("TestResultView") @RenderSection("FeatureSummary")

Error Summary

@GetSummaryHeader("Test") @foreach (var testResult in Model.Tests.Where(tr => tr.Result.GetGroup() == TestNodeResultTypeGroup.Failure)) { var testSummary = GetSummary(testResult.TestNode); @RenderTestExecutionSummaryRowTail(testSummary) if (!string.IsNullOrEmpty(testResult.Error)) { } }
@GetTestTitle(testResult) @foreach (var retry in testResult.Executions.Skip(1)) { retry #@retry.TestItemExecutionIndex }
Error: @(testResult.Error)

Scenario Summary

@foreach (var fixtureNode in GetTextFixtures()) {

@fixtureNode.Type: @GetFixtureTitle(fixtureNode)

if (!string.IsNullOrEmpty(fixtureNode.Description)) {
@fixtureNode.Description
} @GetSummaryHeader("Test", true) @foreach (var testNode in fixtureNode.SubNodes) { var testSummary = GetSummary(testNode); @RenderTestExecutionSummaryRowTail(testSummary, CalculateDuration(testNode)) }
@TestNodeLinks(testNode, 0)
}

Execution Details

@foreach (var test in Model.TestExecutionResults.OrderBy(tr => tr.ExecutionOrder)) { var testItem = test.TestItemResult.TestNode;

@testItem.Type: @GetTestTitle(test)

if (!string.IsNullOrEmpty(testItem.Description)) {
@testItem.Description
} if (testItem.Tags.Any()) {
tags: @string.Join(", ", testItem.Tags)
}
  • Status: @test.ResultType
  • Start time: @test.ExecutionTime.StartTime
  • Execution time (sec): @test.ExecutionTime.DurationSeconds
  • Thread: #@test.ThreadId
  • @if (!string.IsNullOrEmpty(test.Result.Error)) {
  • Error: @(test.Result.Error)
  • }
@foreach (var traceEvent in test.Result.TraceEvents) { if (!IsRelevant(traceEvent)) { continue; } var relatedNode = GetTestNode(traceEvent); }
Steps Trace Result
@(GetBusinessMessages(traceEvent))
@Raw(FormatTechMessages(traceEvent.TechMessages.TrimEnd()))
@if (!string.IsNullOrEmpty(traceEvent.Error)) {
@Raw(FormatTechMessages(traceEvent.Error))
@Raw(FormatTechMessages(traceEvent.StackTrace.TrimEnd()))
}
@traceEvent.ResultType in @GetSeconds(Math.Round(traceEvent.Duration.TotalSeconds, 3))s
}