Skip to content

Commit bc22ded

Browse files
committed
Merge pull request #126 from JakeGinnivan/MarkdownReport
Updated markdown report for examples
2 parents 290e874 + 7986985 commit bc22ded

6 files changed

Lines changed: 255 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Story: Account holder withdraws cash
2+
**As an account holder**
3+
**I want to withdraw cash**
4+
**So that I can get money when the bank is closed**
5+
6+
### Example Scenario
7+
Given a <sign> account balance
8+
When the account holder requests money
9+
Then money <action> dispensed
10+
11+
### Examples:
12+
13+
| sign | action | Result | Errors |
14+
| positive | is | Passed | |
15+
| negative | is not | Failed | Step: Then money <action> dispensed failed with exception: [Boom] [Details at 1 below] |
16+
17+
#### Exceptions:
18+
1. Boom
19+
at TestStack.BDDfy.Tests.Reporters.ReportTestData.GetScenarios(Boolean includeFailingScenario, Boolean includeExamples) in ...\ReportTestData.cs:line 113
20+
21+
## Story: Happiness
22+
**As a person**
23+
**I want ice cream**
24+
**So that I can be happy**
25+
26+
### Example Scenario
27+
Given a <sign> account balance
28+
When the account holder requests money
29+
Then money <action> dispensed
30+
31+
### Examples:
32+
33+
| sign | action |
34+
| positive | is |
35+
| negative | is not |

TestStack.BDDfy.Tests/Reporters/MarkDown/MarkDownReportBuilderTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,19 @@ public void ShouldProduceExpectedMarkdown()
1717
var sut = new MarkDownReportBuilder();
1818
ReportApprover.Approve(model, sut);
1919
}
20+
21+
[Test]
22+
[MethodImpl(MethodImplOptions.NoInlining)]
23+
public void ShouldProduceExpectedMarkdownWithExamples()
24+
{
25+
Func<FileReportModel> model = () =>
26+
new FileReportModel(new ReportTestData().CreateTwoStoriesEachWithOneFailingScenarioAndOnePassingScenarioWithThreeStepsOfFiveMillisecondsAndEachHasTwoExamples())
27+
{
28+
RunDate = new DateTime(2014, 3, 25, 11, 30, 5)
29+
};
30+
31+
var sut = new MarkDownReportBuilder();
32+
ReportApprover.Approve(model, sut);
33+
}
2034
}
2135
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Story: Happiness
2+
As a person
3+
I want ice cream
4+
So that I can be happy
5+
6+
Scenario: Example Scenario
7+
Given a <sign> account balance
8+
When the account holder requests money
9+
Then money <action> dispensed
10+
11+
Examples:
12+
| sign | action |
13+
| positive | is |
14+
| negative | is not |
15+
16+
17+
Story: Account holder withdraws cash
18+
As an account holder
19+
I want to withdraw cash
20+
So that I can get money when the bank is closed
21+
22+
Scenario: Example Scenario
23+
Given a <sign> account balance
24+
When the account holder requests money
25+
Then money <action> dispensed
26+
27+
Examples:
28+
| sign | action | Result | Errors |
29+
| positive | is | Passed | |
30+
| negative | is not | Failed | Step: Then money <action> dispensed failed with exception: [Boom] [Details at 1 below] |
31+
32+
Exceptions:
33+
1. Boom
34+
at TestStack.BDDfy.Tests.Reporters.ReportTestData.GetScenarios(Boolean includeFailingScenario, Boolean includeExamples) in ...\ReportTestData.cs
35+
36+

TestStack.BDDfy.Tests/Reporters/TextReporter/TextReporterTests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Runtime.CompilerServices;
1+
using System;
2+
using System.Runtime.CompilerServices;
23
using System.Text;
34
using ApprovalTests;
45
using NUnit.Framework;
@@ -25,5 +26,22 @@ public void ShouldProduceExpectedReport()
2526

2627
Approvals.Verify(actual.ToString(), StackTraceScrubber.Scrub);
2728
}
29+
30+
[Test]
31+
[MethodImpl(MethodImplOptions.NoInlining)]
32+
public void ShouldProduceExpectedMarkdownWithExamples()
33+
{
34+
var stories = new ReportTestData().CreateTwoStoriesEachWithOneFailingScenarioAndOnePassingScenarioWithThreeStepsOfFiveMillisecondsAndEachHasTwoExamples();
35+
var actual = new StringBuilder();
36+
37+
foreach (var story in stories)
38+
{
39+
var textReporter = new TextReporter();
40+
textReporter.Process(story);
41+
actual.AppendLine(textReporter.ToString());
42+
}
43+
44+
Approvals.Verify(actual.ToString(), StackTraceScrubber.Scrub);
45+
}
2846
}
2947
}

TestStack.BDDfy.Tests/TestStack.BDDfy.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@
174174
<Content Include="Reporters\Html\ClassicReportBuilderTests.ShouldProduceExpectedHtmlWithExamples.approved.txt" />
175175
<Content Include="Reporters\Html\MetroReportBuilderTests.ShouldProduceExpectedHtmlWithExamples.approved.txt" />
176176
<Content Include="Reporters\MarkDown\MarkDownReportBuilderTests.ShouldProduceExpectedMarkdown.approved.txt" />
177+
<Content Include="Reporters\MarkDown\MarkDownReportBuilderTests.ShouldProduceExpectedMarkdownWithExamples.approved.txt" />
178+
<Content Include="Reporters\TextReporter\TextReporterTests.ShouldProduceExpectedMarkdownWithExamples.approved.txt" />
177179
<Content Include="Reporters\TextReporter\TextReporterTests.ShouldProduceExpectedReport.approved.txt" />
178180
</ItemGroup>
179181
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
1-
using System.Text;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Web;
26

37
namespace TestStack.BDDfy.Reporters.MarkDown
48
{
59
public class MarkDownReportBuilder : IReportBuilder
610
{
11+
private readonly List<Exception> _exceptions = new List<Exception>();
12+
713
public string CreateReport(FileReportModel model)
814
{
915
var report = new StringBuilder();
1016

1117
foreach (var story in model.Stories)
1218
{
19+
_exceptions.Clear();
1320
if (story.Metadata != null)
1421
{
1522
report.AppendLine(string.Format("## {0}{1}", story.Metadata.TitlePrefix, story.Metadata.Title));
@@ -22,19 +29,154 @@ public string CreateReport(FileReportModel model)
2229
}
2330

2431
report.AppendLine(); // separator
25-
26-
foreach (var scenario in story.Scenarios)
32+
33+
foreach (var scenarioGroup in story.Scenarios.GroupBy(s => s.Id))
2734
{
28-
report.AppendLine(string.Format("### {0}", scenario.Title));
35+
if (scenarioGroup.Count() > 1)
36+
{
37+
// all scenarios in an example based scenario share the same header and narrative
38+
var exampleScenario = story.Scenarios.First();
39+
report.AppendLine(string.Format("### {0}", exampleScenario.Title));
40+
41+
if (exampleScenario.Steps.Any())
42+
{
43+
foreach (var step in exampleScenario.Steps.Where(s => s.ShouldReport))
44+
report.AppendLine(" " + HttpUtility.HtmlEncode(step.Title) + " ");
45+
}
2946

30-
foreach (var step in scenario.Steps)
31-
report.AppendLine(" " + step.Title + " ");
47+
report.AppendLine(); // separator
48+
WriteExamples(report, exampleScenario, scenarioGroup);
49+
}
50+
else
51+
{
52+
foreach (var scenario in scenarioGroup)
53+
{
54+
report.AppendLine(string.Format("### {0}", scenario.Title));
3255

33-
report.AppendLine(); // separator
56+
foreach (var step in scenario.Steps)
57+
report.AppendLine(" " + HttpUtility.HtmlEncode(step.Title) + " ");
58+
59+
report.AppendLine(); // separator
60+
}
61+
}
3462
}
63+
64+
ReportExceptions(report);
3565
}
66+
_exceptions.Clear();
3667

3768
return report.ToString();
3869
}
70+
71+
private void WriteExamples(StringBuilder report, Scenario exampleScenario, IEnumerable<Scenario> scenarioGroup)
72+
{
73+
report.AppendLine("### Examples: ");
74+
report.AppendLine();
75+
var scenarios = scenarioGroup.ToArray();
76+
var allPassed = scenarios.All(s => s.Result == Result.Passed);
77+
var exampleColumns = exampleScenario.Example.Headers.Length;
78+
var numberColumns = allPassed ? exampleColumns : exampleColumns + 2;
79+
var maxWidth = new int[numberColumns];
80+
var rows = new List<string[]>();
81+
82+
Action<IEnumerable<string>, string, string> addRow = (cells, result, error) =>
83+
{
84+
var row = new string[numberColumns];
85+
var index = 0;
86+
87+
foreach (var cellText in cells)
88+
row[index++] = cellText;
89+
90+
if (!allPassed)
91+
{
92+
row[numberColumns - 2] = result;
93+
row[numberColumns - 1] = error;
94+
}
95+
96+
for (var i = 0; i < numberColumns; i++)
97+
{
98+
var rowValue = row[i];
99+
if (rowValue != null && rowValue.Length > maxWidth[i])
100+
maxWidth[i] = rowValue.Length;
101+
}
102+
103+
rows.Add(row);
104+
};
105+
106+
addRow(exampleScenario.Example.Headers, "Result", "Errors");
107+
foreach (var scenario in scenarios)
108+
{
109+
var failingStep = scenario.Steps.FirstOrDefault(s => s.Result == Result.Failed);
110+
var error = failingStep == null
111+
? null
112+
: string.Format("Step: {0} failed with exception: {1}", HttpUtility.HtmlEncode(failingStep.Title), CreateExceptionMessage(failingStep));
113+
114+
addRow(scenario.Example.Values.Select(e => e.GetValueAsString()), scenario.Result.ToString(), error);
115+
}
116+
117+
foreach (var row in rows)
118+
WriteExampleRow(report, row, maxWidth);
119+
}
120+
121+
private void WriteExampleRow(StringBuilder report, string[] row, int[] maxWidth)
122+
{
123+
report.Append(" ");
124+
for (int index = 0; index < row.Length; index++)
125+
{
126+
var col = row[index];
127+
report.AppendFormat("| {0} ", (col ?? string.Empty).Trim().PadRight(maxWidth[index]));
128+
}
129+
report.AppendLine("|");
130+
}
131+
132+
private string CreateExceptionMessage(Step step)
133+
{
134+
_exceptions.Add(step.Exception);
135+
136+
var exceptionReference = string.Format("[Details at {0} below]", _exceptions.Count);
137+
if (!string.IsNullOrEmpty(step.Exception.Message))
138+
return string.Format("[{0}] {1}", FlattenExceptionMessage(step.Exception.Message), exceptionReference);
139+
140+
return string.Format("{0}", exceptionReference);
141+
}
142+
143+
void ReportExceptions(StringBuilder report)
144+
{
145+
if (_exceptions.Count == 0)
146+
return;
147+
148+
report.AppendLine();
149+
report.Append("#### Exceptions:");
150+
151+
for (int index = 0; index < _exceptions.Count; index++)
152+
{
153+
var exception = _exceptions[index];
154+
report.AppendLine();
155+
report.AppendFormat(" {0}. ", index + 1);
156+
157+
if (!string.IsNullOrEmpty(exception.Message))
158+
{
159+
report.AppendLine(FlattenExceptionMessage(exception.Message));
160+
}
161+
else
162+
report.AppendLine();
163+
164+
var stackTrace = string.Join(Environment.NewLine, exception.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
165+
.Select(s => " " + s));
166+
report.AppendLine(stackTrace);
167+
}
168+
169+
report.AppendLine();
170+
}
171+
172+
static string FlattenExceptionMessage(string message)
173+
{
174+
return message
175+
.Replace("\t", " ") // replace tab with one space
176+
.Replace(Environment.NewLine, ", ") // replace new line with one space
177+
.Trim() // trim starting and trailing spaces
178+
.Replace(" ", " ")
179+
.TrimEnd(','); // chop any , from the end
180+
}
39181
}
40182
}

0 commit comments

Comments
 (0)