1 /++
2   A module containing the SpecStepsReporter
3 
4   This is an example of how this reporter looks
5   <script type="text/javascript" src="https://asciinema.org/a/bsk6do8t4zay9k9vznvh8yn71.js" id="asciicast-bsk6do8t4zay9k9vznvh8yn71" async></script>
6 
7   Copyright: © 2017 Szabo Bogdan
8   License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
9   Authors: Szabo Bogdan
10 +/
11 module trial.reporters.specsteps;
12 
13 import trial.interfaces;
14 import trial.reporters.spec;
15 import trial.reporters.writer;
16 import trial.settings;
17 
18 import std.datetime;
19 import std.conv;
20 
21 /// A structure containing the glyphs used for the spec steps reporter
22 struct SpecStepsGlyphs {
23   version(Windows) {
24     ///
25     string testBegin = "/";
26 
27     ///
28     string testEnd = "\\";
29 
30     ///
31     string step = "|";
32   } else {
33     ///
34     string testBegin = "┌";
35 
36     ///
37     string testEnd = "└";
38 
39     ///
40     string step = "│";
41   }
42 }
43 
44 ///
45 string specStepsGlyphsToCode(SpecStepsGlyphs glyphs) {
46   return "SpecStepsGlyphs(`" ~ glyphs.testBegin ~ "`, `" ~ glyphs.testEnd ~ "`, `" ~ glyphs.step ~ "`)";
47 }
48 
49 /// A flavour of the "spec" reporter that show the tests and the steps of your tests.
50 class SpecStepsReporter : SpecReporter, ISuiteLifecycleListener, IStepLifecycleListener
51 {
52   private {
53     size_t indents;
54     size_t stepIndents;
55 
56     Settings settings;
57   }
58 
59 
60   this(Settings settings)
61   {
62     super(settings);
63     this.settings = settings;
64   }
65 
66   this(ReportWriter writer)
67   {
68     super(writer);
69   }
70 
71   void begin(ref SuiteResult suite)
72   {
73     indents = printSuite(suite.name);
74   }
75 
76   void end(ref SuiteResult) { }
77 
78   override
79   {
80     void begin(string suite, ref TestResult test)
81     {
82       stepIndents = 0;
83       write!(Type.none)(settings.glyphs.specSteps.testBegin ~ " " ~ test.name ~ "\n", indents);
84     }
85 
86     void end(string suite, ref TestResult test)
87     {
88       write!(Type.none)(settings.glyphs.specSteps.testEnd ~ " ", indents);
89 
90       if(test.status == TestResult.Status.success) {
91         write!(Type.success)("Success", 0);
92       } else if(test.status == TestResult.Status.failure) {
93         write!(Type.failure)("Failure", 0);
94         failedTests++;
95       } else if(test.status == TestResult.Status.pending) {
96         write!(Type.pending)("Pending", 0);
97       } else {
98         write!(Type.none)("Unknown", 0);
99       }
100 
101       auto timeDiff = (test.end - test.begin).total!"msecs";
102 
103       if(timeDiff >= settings.warningTestDuration && timeDiff < settings.dangerTestDuration) {
104         write!(Type.warning)(" (" ~ timeDiff.to!string ~ "ms)", 0);
105       }
106 
107       if(timeDiff >= settings.dangerTestDuration) {
108         write!(Type.danger)(" (" ~ timeDiff.to!string ~ "ms)", 0);
109       }
110 
111       write!(Type.none)("\n", 0);
112     }
113   }
114 
115   void begin(string suite, string test, ref StepResult s)
116   {
117     stepIndents++;
118     write!(Type.none)(settings.glyphs.specSteps.step, indents);
119     write!(Type.none)(" " ~ s.name ~ "\n", stepIndents);
120   }
121 
122   void end(string suite, string test, ref StepResult)
123   {
124     stepIndents--;
125   }
126 }
127 
128 version (unittest)
129 {
130   version(Have_fluent_asserts) {
131     import fluent.asserts;
132   }
133 }
134 
135 @("it should format the steps for a success test")
136 unittest
137 {
138   auto writer = new BufferedWriter;
139   auto reporter = new SpecStepsReporter(writer);
140 
141   auto suite = SuiteResult("some suite");
142   auto test = new TestResult("some test");
143   test.status = TestResult.Status.success;
144 
145   auto step = new StepResult();
146   step.name = "some step";
147 
148   reporter.begin(suite);
149   reporter.begin("some suite", test);
150 
151   reporter.begin("some suite", "some test", step);
152   reporter.begin("some suite", "some test", step);
153   reporter.end("some suite", "some test", step);
154   reporter.end("some suite", "some test", step);
155   reporter.begin("some suite", "some test", step);
156   reporter.end("some suite", "some test", step);
157 
158   reporter.end("some suite", test);
159 
160   reporter.begin("some suite", test);
161   reporter.end("some suite", test);
162 
163   reporter.end(suite);
164 
165   writer.buffer.should.equal("\n" ~
166         "  some suite\n" ~
167         "    ┌ some test\n" ~
168         "    │   some step\n" ~
169         "    │     some step\n" ~
170         "    │   some step\n" ~
171         "    └ ✓ Success\n" ~
172         "    ┌ some test\n" ~
173         "    └ ✓ Success\n");
174 }
175 
176 @("it should format a pending test")
177 unittest
178 {
179   auto writer = new BufferedWriter;
180   auto reporter = new SpecStepsReporter(writer);
181 
182   auto suite = SuiteResult("some suite");
183   auto test = new TestResult("some test");
184   test.status = TestResult.Status.pending;
185 
186   reporter.begin(suite);
187   reporter.begin("some suite", test);
188   reporter.end("some suite", test);
189 
190   reporter.end(suite);
191 
192   writer.buffer.should.equal("\n" ~
193         "  some suite\n" ~
194         "    ┌ some test\n" ~
195         "    └ - Pending\n");
196 }
197 
198 
199 @("it should print the duration of a long test")
200 unittest
201 {
202   auto writer = new BufferedWriter;
203   auto reporter = new SpecStepsReporter(writer);
204 
205   auto suite = SuiteResult("some suite");
206   auto test = new TestResult("some test");
207   test.status = TestResult.Status.success;
208   test.end = Clock.currTime;
209   test.begin = test.end - 200.msecs;
210 
211   reporter.begin(suite);
212   reporter.begin("some suite", test);
213   reporter.end("some suite", test);
214 
215   reporter.end(suite);
216 
217   writer.buffer.should.equal("\n" ~
218         "  some suite\n" ~
219         "    ┌ some test\n" ~
220         "    └ ✓ Success (200ms)\n");
221 }
222 
223 
224 @("it should format the steps for a failing test")
225 unittest
226 {
227   auto writer = new BufferedWriter;
228   auto reporter = new SpecStepsReporter(writer);
229 
230   auto suite = SuiteResult("some suite");
231   auto test = new TestResult("some test");
232   test.status = TestResult.Status.failure;
233 
234   auto step = new StepResult();
235   step.name = "some step";
236 
237   reporter.begin(suite);
238   reporter.begin("some suite", test);
239 
240   reporter.begin("some suite", "some test", step);
241   reporter.begin("some suite", "some test", step);
242   reporter.end("some suite", "some test", step);
243   reporter.end("some suite", "some test", step);
244   reporter.begin("some suite", "some test", step);
245   reporter.end("some suite", "some test", step);
246 
247   reporter.end("some suite", test);
248 
249   reporter.begin("some suite", test);
250   reporter.end("some suite", test);
251 
252   reporter.end(suite);
253 
254   writer.buffer.should.equal("\n" ~
255       "  some suite\n" ~
256       "    ┌ some test\n" ~
257       "    │   some step\n" ~
258       "    │     some step\n" ~
259       "    │   some step\n" ~
260       "    └ 0) Failure\n" ~
261       "    ┌ some test\n" ~
262       "    └ 1) Failure\n");
263 }