1- using System . Text ;
1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Text ;
25
36namespace TestStack . BDDfy . Reporters . MarkDown
47{
58 public class MarkDownReportBuilder : IReportBuilder
69 {
10+ private readonly List < Exception > _exceptions = new List < Exception > ( ) ;
11+
712 public string CreateReport ( FileReportModel model )
813 {
914 var report = new StringBuilder ( ) ;
1015
1116 foreach ( var story in model . Stories )
1217 {
18+ _exceptions . Clear ( ) ;
1319 if ( story . Metadata != null )
1420 {
1521 report . AppendLine ( string . Format ( "## {0}{1}" , story . Metadata . TitlePrefix , story . Metadata . Title ) ) ;
@@ -22,19 +28,154 @@ public string CreateReport(FileReportModel model)
2228 }
2329
2430 report . AppendLine ( ) ; // separator
25-
26- foreach ( var scenario in story . Scenarios )
31+
32+ foreach ( var scenarioGroup in story . Scenarios . GroupBy ( s => s . Id ) )
2733 {
28- report . AppendLine ( string . Format ( "### {0}" , scenario . Title ) ) ;
34+ if ( scenarioGroup . Count ( ) > 1 )
35+ {
36+ // all scenarios in an example based scenario share the same header and narrative
37+ var exampleScenario = story . Scenarios . First ( ) ;
38+ report . AppendLine ( string . Format ( "### {0}" , exampleScenario . Title ) ) ;
39+
40+ if ( exampleScenario . Steps . Any ( ) )
41+ {
42+ foreach ( var step in exampleScenario . Steps . Where ( s => s . ShouldReport ) )
43+ report . AppendLine ( " " + step . Title + " " ) ;
44+ }
2945
30- foreach ( var step in scenario . Steps )
31- report . AppendLine ( " " + step . Title + " " ) ;
46+ report . AppendLine ( ) ; // separator
47+ WriteExamples ( report , exampleScenario , scenarioGroup ) ;
48+ }
49+ else
50+ {
51+ foreach ( var scenario in scenarioGroup )
52+ {
53+ report . AppendLine ( string . Format ( "### {0}" , scenario . Title ) ) ;
3254
33- report . AppendLine ( ) ; // separator
55+ foreach ( var step in scenario . Steps )
56+ report . AppendLine ( " " + step . Title + " " ) ;
57+
58+ report . AppendLine ( ) ; // separator
59+ }
60+ }
3461 }
62+
63+ ReportExceptions ( report ) ;
3564 }
65+ _exceptions . Clear ( ) ;
3666
3767 return report . ToString ( ) ;
3868 }
69+
70+ private void WriteExamples ( StringBuilder report , Scenario exampleScenario , IEnumerable < Scenario > scenarioGroup )
71+ {
72+ report . AppendLine ( "### Examples: " ) ;
73+ report . AppendLine ( ) ;
74+ var scenarios = scenarioGroup . ToArray ( ) ;
75+ var allPassed = scenarios . All ( s => s . Result == Result . Passed ) ;
76+ var exampleColumns = exampleScenario . Example . Headers . Length ;
77+ var numberColumns = allPassed ? exampleColumns : exampleColumns + 2 ;
78+ var maxWidth = new int [ numberColumns ] ;
79+ var rows = new List < string [ ] > ( ) ;
80+
81+ Action < IEnumerable < string > , string , string > addRow = ( cells , result , error ) =>
82+ {
83+ var row = new string [ numberColumns ] ;
84+ var index = 0 ;
85+
86+ foreach ( var cellText in cells )
87+ row [ index ++ ] = cellText ;
88+
89+ if ( ! allPassed )
90+ {
91+ row [ numberColumns - 2 ] = result ;
92+ row [ numberColumns - 1 ] = error ;
93+ }
94+
95+ for ( var i = 0 ; i < numberColumns ; i ++ )
96+ {
97+ var rowValue = row [ i ] ;
98+ if ( rowValue != null && rowValue . Length > maxWidth [ i ] )
99+ maxWidth [ i ] = rowValue . Length ;
100+ }
101+
102+ rows . Add ( row ) ;
103+ } ;
104+
105+ addRow ( exampleScenario . Example . Headers , "Result" , "Errors" ) ;
106+ foreach ( var scenario in scenarios )
107+ {
108+ var failingStep = scenario . Steps . FirstOrDefault ( s => s . Result == Result . Failed ) ;
109+ var error = failingStep == null
110+ ? null
111+ : string . Format ( "Step: {0} failed with exception: {1}" , failingStep . Title , CreateExceptionMessage ( failingStep ) ) ;
112+
113+ addRow ( scenario . Example . Values . Select ( e => e . GetValueAsString ( ) ) , scenario . Result . ToString ( ) , error ) ;
114+ }
115+
116+ foreach ( var row in rows )
117+ WriteExampleRow ( report , row , maxWidth ) ;
118+ }
119+
120+ private void WriteExampleRow ( StringBuilder report , string [ ] row , int [ ] maxWidth )
121+ {
122+ report . Append ( " " ) ;
123+ for ( int index = 0 ; index < row . Length ; index ++ )
124+ {
125+ var col = row [ index ] ;
126+ report . AppendFormat ( "| {0} " , ( col ?? string . Empty ) . Trim ( ) . PadRight ( maxWidth [ index ] ) ) ;
127+ }
128+ report . AppendLine ( "|" ) ;
129+ }
130+
131+ private string CreateExceptionMessage ( Step step )
132+ {
133+ _exceptions . Add ( step . Exception ) ;
134+
135+ var exceptionReference = string . Format ( "[Details at {0} below]" , _exceptions . Count ) ;
136+ if ( ! string . IsNullOrEmpty ( step . Exception . Message ) )
137+ return string . Format ( "[{0}] {1}" , FlattenExceptionMessage ( step . Exception . Message ) , exceptionReference ) ;
138+
139+ return string . Format ( "{0}" , exceptionReference ) ;
140+ }
141+
142+ void ReportExceptions ( StringBuilder report )
143+ {
144+ if ( _exceptions . Count == 0 )
145+ return ;
146+
147+ report . AppendLine ( ) ;
148+ report . Append ( "#### Exceptions:" ) ;
149+
150+ for ( int index = 0 ; index < _exceptions . Count ; index ++ )
151+ {
152+ var exception = _exceptions [ index ] ;
153+ report . AppendLine ( ) ;
154+ report . AppendFormat ( " {0}. " , index + 1 ) ;
155+
156+ if ( ! string . IsNullOrEmpty ( exception . Message ) )
157+ {
158+ report . AppendLine ( FlattenExceptionMessage ( exception . Message ) ) ;
159+ }
160+ else
161+ report . AppendLine ( ) ;
162+
163+ var stackTrace = string . Join ( Environment . NewLine , exception . StackTrace . Split ( new [ ] { Environment . NewLine } , StringSplitOptions . None )
164+ . Select ( s => " " + s ) ) ;
165+ report . AppendLine ( stackTrace ) ;
166+ }
167+
168+ report . AppendLine ( ) ;
169+ }
170+
171+ static string FlattenExceptionMessage ( string message )
172+ {
173+ return message
174+ . Replace ( "\t " , " " ) // replace tab with one space
175+ . Replace ( Environment . NewLine , ", " ) // replace new line with one space
176+ . Trim ( ) // trim starting and trailing spaces
177+ . Replace ( " " , " " )
178+ . TrimEnd ( ',' ) ; // chop any , from the end
179+ }
39180 }
40181}
0 commit comments