actir 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,417 @@
1
+ require 'erb'
2
+
3
+ module Actir
4
+ module ParallelTests
5
+ class HtmlFormatter
6
+
7
+ include ERB::Util # For the #h method.
8
+ def initialize(output)
9
+ @output = output
10
+ end
11
+
12
+ def print_html_start
13
+ @output.puts HTML_HEADER
14
+ @output.puts REPORT_HEADER
15
+ end
16
+
17
+ def print_example_group_end
18
+ @output.puts " </dl>"
19
+ @output.puts "</div>"
20
+ end
21
+
22
+ def print_example_group_start(group_id, description, number_of_parents)
23
+ @output.puts "<div id=\"div_group_#{group_id}\" class=\"example_group passed\">"
24
+ @output.puts " <dl #{indentation_style(number_of_parents)}>"
25
+ @output.puts " <dt id=\"example_group_#{group_id}\" class=\"passed\">#{h(description)}</dt>"
26
+ end
27
+
28
+ def print_example_passed(description, run_time)
29
+ formatted_run_time = "%.5f" % run_time
30
+ @output.puts " <dd class=\"example passed\">" \
31
+ "<span class=\"passed_spec_name\">#{h(description)}</span>" \
32
+ "<span class='duration'>#{formatted_run_time}s</span></dd>"
33
+ end
34
+
35
+ # rubocop:disable Style/ParameterLists
36
+ def print_example_failed(pending_fixed, description, run_time, failure_id,
37
+ exception, extra_content, escape_backtrace=false)
38
+ # rubocop:enable Style/ParameterLists
39
+ formatted_run_time = "%.5f" % run_time
40
+
41
+ @output.puts " <dd class=\"example #{pending_fixed ? 'pending_fixed' : 'failed'}\">"
42
+ @output.puts " <span class=\"failed_spec_name\">#{h(description)}</span>"
43
+ @output.puts " <span class=\"duration\">#{formatted_run_time}s</span>"
44
+ @output.puts " <div class=\"failure\" id=\"failure_#{failure_id}\">"
45
+ if exception
46
+ @output.puts " <div class=\"message\"><pre>#{h(exception[:message])}</pre></div>"
47
+ if escape_backtrace
48
+ @output.puts " <div class=\"backtrace\"><pre>#{h exception[:backtrace]}</pre></div>"
49
+ else
50
+ @output.puts " <div class=\"backtrace\"><pre>#{exception[:backtrace]}</pre></div>"
51
+ end
52
+ end
53
+ @output.puts extra_content if extra_content
54
+ @output.puts " </div>"
55
+ @output.puts " </dd>"
56
+ end
57
+
58
+ def print_example_pending(description, pending_message)
59
+ @output.puts " <dd class=\"example not_implemented\">" \
60
+ "<span class=\"not_implemented_spec_name\">#{h(description)} " \
61
+ "(PENDING: #{h(pending_message)})</span></dd>"
62
+ end
63
+
64
+ def print_summary(duration, example_count, failure_count, pending_count)
65
+ totals = "#{example_count} example#{'s' unless example_count == 1}, "
66
+ totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
67
+ totals << ", #{pending_count} pending" if pending_count > 0
68
+
69
+ formatted_duration = "%.5f" % duration
70
+
71
+ @output.puts "<script type=\"text/javascript\">" \
72
+ "document.getElementById('duration').innerHTML = \"Finished in " \
73
+ "<strong>#{formatted_duration} seconds</strong>\";</script>"
74
+ @output.puts "<script type=\"text/javascript\">" \
75
+ "document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
76
+ @output.puts "</div>"
77
+ @output.puts "</div>"
78
+ @output.puts "</body>"
79
+ @output.puts "</html>"
80
+ end
81
+
82
+ def flush
83
+ @output.flush
84
+ end
85
+
86
+ def move_progress(percent_done)
87
+ @output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
88
+ @output.flush
89
+ end
90
+
91
+ def make_header_red
92
+ @output.puts " <script type=\"text/javascript\">makeRed('test-header');</script>"
93
+ end
94
+
95
+ def make_header_yellow
96
+ @output.puts " <script type=\"text/javascript\">makeYellow('test-header');</script>"
97
+ end
98
+
99
+ def make_example_group_header_red(group_id)
100
+ @output.puts " <script type=\"text/javascript\">" \
101
+ "makeRed('div_group_#{group_id}');</script>"
102
+ @output.puts " <script type=\"text/javascript\">" \
103
+ "makeRed('example_group_#{group_id}');</script>"
104
+ end
105
+
106
+ def make_example_group_header_yellow(group_id)
107
+ @output.puts " <script type=\"text/javascript\">" \
108
+ "makeYellow('div_group_#{group_id}');</script>"
109
+ @output.puts " <script type=\"text/javascript\">" \
110
+ "makeYellow('example_group_#{group_id}');</script>"
111
+ end
112
+
113
+ private
114
+
115
+ def indentation_style(number_of_parents)
116
+ "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
117
+ end
118
+
119
+ # rubocop:disable LineLength
120
+ REPORT_HEADER = <<-EOF
121
+ <div class="test-report">
122
+
123
+ <div id="test-header">
124
+ <div id="label">
125
+ <h1>test Code Examples</h1>
126
+ </div>
127
+
128
+ <div id="display-filters">
129
+ <input id="passed_checkbox" name="passed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> <label for="passed_checkbox">Passed</label>
130
+ <input id="failed_checkbox" name="failed_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> <label for="failed_checkbox">Failed</label>
131
+ <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> <label for="pending_checkbox">Pending</label>
132
+ </div>
133
+
134
+ <div id="summary">
135
+ <p id="totals">&#160;</p>
136
+ <p id="duration">&#160;</p>
137
+ </div>
138
+ </div>
139
+
140
+
141
+ <div class="results">
142
+ EOF
143
+ # rubocop:enable LineLength
144
+
145
+ # rubocop:disable LineLength
146
+ GLOBAL_SCRIPTS = <<-EOF
147
+
148
+ function addClass(element_id, classname) {
149
+ document.getElementById(element_id).className += (" " + classname);
150
+ }
151
+
152
+ function removeClass(element_id, classname) {
153
+ var elem = document.getElementById(element_id);
154
+ var classlist = elem.className.replace(classname,'');
155
+ elem.className = classlist;
156
+ }
157
+
158
+ function moveProgressBar(percentDone) {
159
+ document.getElementById("test-header").style.width = percentDone +"%";
160
+ }
161
+
162
+ function makeRed(element_id) {
163
+ removeClass(element_id, 'passed');
164
+ removeClass(element_id, 'not_implemented');
165
+ addClass(element_id,'failed');
166
+ }
167
+
168
+ function makeYellow(element_id) {
169
+ var elem = document.getElementById(element_id);
170
+ if (elem.className.indexOf("failed") == -1) { // class doesn't includes failed
171
+ if (elem.className.indexOf("not_implemented") == -1) { // class doesn't include not_implemented
172
+ removeClass(element_id, 'passed');
173
+ addClass(element_id,'not_implemented');
174
+ }
175
+ }
176
+ }
177
+
178
+ function apply_filters() {
179
+ var passed_filter = document.getElementById('passed_checkbox').checked;
180
+ var failed_filter = document.getElementById('failed_checkbox').checked;
181
+ var pending_filter = document.getElementById('pending_checkbox').checked;
182
+
183
+ assign_display_style("example passed", passed_filter);
184
+ assign_display_style("example failed", failed_filter);
185
+ assign_display_style("example not_implemented", pending_filter);
186
+
187
+ assign_display_style_for_group("example_group passed", passed_filter);
188
+ assign_display_style_for_group("example_group not_implemented", pending_filter, pending_filter || passed_filter);
189
+ assign_display_style_for_group("example_group failed", failed_filter, failed_filter || pending_filter || passed_filter);
190
+ }
191
+
192
+ function get_display_style(display_flag) {
193
+ var style_mode = 'none';
194
+ if (display_flag == true) {
195
+ style_mode = 'block';
196
+ }
197
+ return style_mode;
198
+ }
199
+
200
+ function assign_display_style(classname, display_flag) {
201
+ var style_mode = get_display_style(display_flag);
202
+ var elems = document.getElementsByClassName(classname)
203
+ for (var i=0; i<elems.length;i++) {
204
+ elems[i].style.display = style_mode;
205
+ }
206
+ }
207
+
208
+ function assign_display_style_for_group(classname, display_flag, subgroup_flag) {
209
+ var display_style_mode = get_display_style(display_flag);
210
+ var subgroup_style_mode = get_display_style(subgroup_flag);
211
+ var elems = document.getElementsByClassName(classname)
212
+ for (var i=0; i<elems.length;i++) {
213
+ var style_mode = display_style_mode;
214
+ if ((display_flag != subgroup_flag) && (elems[i].getElementsByTagName('dt')[0].innerHTML.indexOf(", ") != -1)) {
215
+ elems[i].style.display = subgroup_style_mode;
216
+ } else {
217
+ elems[i].style.display = display_style_mode;
218
+ }
219
+ }
220
+ }
221
+ EOF
222
+ # rubocop:enable LineLength
223
+
224
+ GLOBAL_STYLES = <<-EOF
225
+ #test-header {
226
+ background: #65C400; color: #fff; height: 4em;
227
+ }
228
+
229
+ .test-report h1 {
230
+ margin: 0px 10px 0px 10px;
231
+ padding: 10px;
232
+ font-family: "Lucida Grande", Helvetica, sans-serif;
233
+ font-size: 1.8em;
234
+ position: absolute;
235
+ }
236
+
237
+ #label {
238
+ float:left;
239
+ }
240
+
241
+ #display-filters {
242
+ float:left;
243
+ padding: 28px 0 0 40%;
244
+ font-family: "Lucida Grande", Helvetica, sans-serif;
245
+ }
246
+
247
+ #summary {
248
+ float:right;
249
+ padding: 5px 10px;
250
+ font-family: "Lucida Grande", Helvetica, sans-serif;
251
+ text-align: right;
252
+ }
253
+
254
+ #summary p {
255
+ margin: 0 0 0 2px;
256
+ }
257
+
258
+ #summary #totals {
259
+ font-size: 1.2em;
260
+ }
261
+
262
+ .example_group {
263
+ margin: 0 10px 5px;
264
+ background: #fff;
265
+ }
266
+
267
+ dl {
268
+ margin: 0; padding: 0 0 5px;
269
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
270
+ }
271
+
272
+ dt {
273
+ padding: 3px;
274
+ background: #65C400;
275
+ color: #fff;
276
+ font-weight: bold;
277
+ }
278
+
279
+ dd {
280
+ margin: 5px 0 5px 5px;
281
+ padding: 3px 3px 3px 18px;
282
+ }
283
+
284
+ dd .duration {
285
+ padding-left: 5px;
286
+ text-align: right;
287
+ right: 0px;
288
+ float:right;
289
+ }
290
+
291
+ dd.example.passed {
292
+ border-left: 5px solid #65C400;
293
+ border-bottom: 1px solid #65C400;
294
+ background: #DBFFB4; color: #3D7700;
295
+ }
296
+
297
+ dd.example.not_implemented {
298
+ border-left: 5px solid #FAF834;
299
+ border-bottom: 1px solid #FAF834;
300
+ background: #FCFB98; color: #131313;
301
+ }
302
+
303
+ dd.example.pending_fixed {
304
+ border-left: 5px solid #0000C2;
305
+ border-bottom: 1px solid #0000C2;
306
+ color: #0000C2; background: #D3FBFF;
307
+ }
308
+
309
+ dd.example.failed {
310
+ border-left: 5px solid #C20000;
311
+ border-bottom: 1px solid #C20000;
312
+ color: #C20000; background: #FFFBD3;
313
+ }
314
+
315
+
316
+ dt.not_implemented {
317
+ color: #000000; background: #FAF834;
318
+ }
319
+
320
+ dt.pending_fixed {
321
+ color: #FFFFFF; background: #C40D0D;
322
+ }
323
+
324
+ dt.failed {
325
+ color: #FFFFFF; background: #C40D0D;
326
+ }
327
+
328
+
329
+ #test-header.not_implemented {
330
+ color: #000000; background: #FAF834;
331
+ }
332
+
333
+ #test-header.pending_fixed {
334
+ color: #FFFFFF; background: #C40D0D;
335
+ }
336
+
337
+ #test-header.failed {
338
+ color: #FFFFFF; background: #C40D0D;
339
+ }
340
+
341
+
342
+ .backtrace {
343
+ color: #000;
344
+ font-size: 12px;
345
+ }
346
+
347
+ a {
348
+ color: #BE5C00;
349
+ }
350
+
351
+ /* Ruby code, style similar to vibrant ink */
352
+ .ruby {
353
+ font-size: 12px;
354
+ font-family: monospace;
355
+ color: white;
356
+ background-color: black;
357
+ padding: 0.1em 0 0.2em 0;
358
+ }
359
+
360
+ .ruby .keyword { color: #FF6600; }
361
+ .ruby .constant { color: #339999; }
362
+ .ruby .attribute { color: white; }
363
+ .ruby .global { color: white; }
364
+ .ruby .module { color: white; }
365
+ .ruby .class { color: white; }
366
+ .ruby .string { color: #66FF00; }
367
+ .ruby .ident { color: white; }
368
+ .ruby .method { color: #FFCC00; }
369
+ .ruby .number { color: white; }
370
+ .ruby .char { color: white; }
371
+ .ruby .comment { color: #9933CC; }
372
+ .ruby .symbol { color: white; }
373
+ .ruby .regex { color: #44B4CC; }
374
+ .ruby .punct { color: white; }
375
+ .ruby .escape { color: white; }
376
+ .ruby .interp { color: white; }
377
+ .ruby .expr { color: white; }
378
+
379
+ .ruby .offending { background-color: gray; }
380
+ .ruby .linenum {
381
+ width: 75px;
382
+ padding: 0.1em 1em 0.2em 0;
383
+ color: #000000;
384
+ background-color: #FFFBD3;
385
+ }
386
+ EOF
387
+
388
+ HTML_HEADER = <<-EOF
389
+ <!DOCTYPE html>
390
+ <html lang='en'>
391
+ <head>
392
+ <title>Test results</title>
393
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
394
+ <meta http-equiv="Expires" content="-1" />
395
+ <meta http-equiv="Pragma" content="no-cache" />
396
+ <style type="text/css">
397
+ body {
398
+ margin: 0;
399
+ padding: 0;
400
+ background: #fff;
401
+ font-size: 80%;
402
+ }
403
+ </style>
404
+ <script type="text/javascript">
405
+ // <![CDATA[
406
+ #{GLOBAL_SCRIPTS}
407
+ // ]]>
408
+ </script>
409
+ <style type="text/css">
410
+ #{GLOBAL_STYLES}
411
+ </style>
412
+ </head>
413
+ <body>
414
+ EOF
415
+ end
416
+ end
417
+ end
@@ -0,0 +1,145 @@
1
+ module Actir
2
+ module ParallelTests
3
+ class HtmlReport
4
+
5
+ def initialize(output)
6
+ super(output)
7
+ @failed_examples = []
8
+ @example_group_number = 0
9
+ @example_number = 0
10
+ @header_red = nil
11
+ @printer = HtmlPrinter.new(output)
12
+ end
13
+
14
+ def start(notification)
15
+ super
16
+ @printer.print_html_start
17
+ @printer.flush
18
+ end
19
+
20
+ def example_group_started(notification)
21
+ super
22
+ @example_group_red = false
23
+ @example_group_number += 1
24
+
25
+ @printer.print_example_group_end unless example_group_number == 1
26
+ @printer.print_example_group_start(example_group_number,
27
+ notification.group.description,
28
+ notification.group.parent_groups.size)
29
+ @printer.flush
30
+ end
31
+
32
+ def start_dump(_notification)
33
+ @printer.print_example_group_end
34
+ @printer.flush
35
+ end
36
+
37
+ def example_started(_notification)
38
+ @example_number += 1
39
+ end
40
+
41
+ def example_passed(passed)
42
+ @printer.move_progress(percent_done)
43
+ @printer.print_example_passed(passed.example.description, passed.example.execution_result.run_time)
44
+ @printer.flush
45
+ end
46
+
47
+ def example_failed(failure)
48
+ @failed_examples << failure.example
49
+ unless @header_red
50
+ @header_red = true
51
+ @printer.make_header_red
52
+ end
53
+
54
+ unless @example_group_red
55
+ @example_group_red = true
56
+ @printer.make_example_group_header_red(example_group_number)
57
+ end
58
+
59
+ @printer.move_progress(percent_done)
60
+
61
+ example = failure.example
62
+
63
+ exception = failure.exception
64
+ exception_details = if exception
65
+ {
66
+ :message => exception.message,
67
+ :backtrace => failure.formatted_backtrace.join("\n")
68
+ }
69
+ else
70
+ false
71
+ end
72
+ extra = extra_failure_content(failure)
73
+
74
+ @printer.print_example_failed(
75
+ example.execution_result.pending_fixed,
76
+ example.description,
77
+ example.execution_result.run_time,
78
+ @failed_examples.size,
79
+ exception_details,
80
+ (extra == "") ? false : extra,
81
+ true
82
+ )
83
+ @printer.flush
84
+ end
85
+
86
+ def example_pending(pending)
87
+ example = pending.example
88
+
89
+ @printer.make_header_yellow unless @header_red
90
+ @printer.make_example_group_header_yellow(example_group_number) unless @example_group_red
91
+ @printer.move_progress(percent_done)
92
+ @printer.print_example_pending(example.description, example.execution_result.pending_message)
93
+ @printer.flush
94
+ end
95
+
96
+ def dump_summary(summary)
97
+ @printer.print_summary(
98
+ summary.duration,
99
+ summary.example_count,
100
+ summary.failure_count,
101
+ summary.pending_count
102
+ )
103
+ @printer.flush
104
+ end
105
+
106
+ private
107
+
108
+ # If these methods are declared with attr_reader Ruby will issue a
109
+ # warning because they are private.
110
+ # rubocop:disable Style/TrivialAccessors
111
+
112
+ # The number of the currently running example_group.
113
+ def example_group_number
114
+ @example_group_number
115
+ end
116
+
117
+ # The number of the currently running example (a global counter).
118
+ def example_number
119
+ @example_number
120
+ end
121
+ # rubocop:enable Style/TrivialAccessors
122
+
123
+ def percent_done
124
+ result = 100.0
125
+ if @example_count > 0
126
+ result = (((example_number).to_f / @example_count.to_f * 1000).to_i / 10.0).to_f
127
+ end
128
+ result
129
+ end
130
+
131
+ # Override this method if you wish to output extra HTML for a failed
132
+ # spec. For example, you could output links to images or other files
133
+ # produced during the specs.
134
+ def extra_failure_content(failure)
135
+ RSpec::Support.require_rspec_core "formatters/snippet_extractor"
136
+ backtrace = failure.exception.backtrace.map do |line|
137
+ RSpec.configuration.backtrace_formatter.backtrace_line(line)
138
+ end
139
+ backtrace.compact!
140
+ @snippet_extractor ||= SnippetExtractor.new
141
+ " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
142
+ end
143
+ end
144
+ end
145
+ end