closer 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/HISTORY.md +7 -0
- data/closer.gemspec +3 -3
- data/lib/closer/formatter/closer_html.rb +2 -6
- data/lib/closer/formatter/html.rb +175 -252
- data/lib/closer/formatter/html_builder.rb +144 -0
- data/lib/closer/formatter/jquery-min.js +154 -4
- data/lib/closer/version.rb +1 -1
- metadata +9 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74f23eec764f2465f266ac3ccbd0c31c128d849de9775b9fac70cebe407e0f39
|
4
|
+
data.tar.gz: aee97eff5fec4ca6422878f3d67ed722d46e17feaaf649d549f5776dced70c03
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12a70b65bd851a1860ac84639e56a9f31c6c8f6ae18ccb74591c25b633f25615249d53d975d0dc1787bac78f1bce73fcaeca5fa4b71de2ea74f7b7aa604bd040
|
7
|
+
data.tar.gz: 5c28933a567304c82f8a99d90a9f97f163ba8681908e0fa9c81f26e63e004204015f50b78ad3e4de2c79359eb22092047c29a948c8caaa4d959645d715c730f7
|
data/HISTORY.md
CHANGED
data/closer.gemspec
CHANGED
@@ -17,13 +17,13 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features|user_stories)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.required_ruby_version = '~> 2.
|
20
|
+
spec.required_ruby_version = '~> 2.2'
|
21
21
|
|
22
|
-
spec.add_runtime_dependency 'cucumber', '>=
|
22
|
+
spec.add_runtime_dependency 'cucumber', '>= 3.0', '<= 4.0'
|
23
23
|
spec.add_runtime_dependency 'poltergeist', '>= 1.16'
|
24
24
|
spec.add_runtime_dependency 'selenium-webdriver', '~> 3.0'
|
25
25
|
|
26
|
-
spec.add_development_dependency 'bundler', '~> 1.
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
27
27
|
spec.add_development_dependency 'minitest', '~> 5.10'
|
28
28
|
spec.add_development_dependency 'rake', '~> 12.0'
|
29
29
|
end
|
@@ -12,7 +12,7 @@ module Closer
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def gem_dir
|
15
|
-
File.join(Gem.dir, 'gems')
|
15
|
+
::File.join(Gem.dir, 'gems')
|
16
16
|
end
|
17
17
|
|
18
18
|
def feature_id
|
@@ -38,10 +38,6 @@ module Closer
|
|
38
38
|
ret
|
39
39
|
end
|
40
40
|
|
41
|
-
def should_expand
|
42
|
-
['t', 'true'].include?(ENV['EXPAND'].to_s.downcase)
|
43
|
-
end
|
44
|
-
|
45
41
|
def magic_comment?(comment_line)
|
46
42
|
comment = comment_line.to_s
|
47
43
|
|
@@ -53,7 +49,7 @@ module Closer
|
|
53
49
|
end
|
54
50
|
|
55
51
|
def current_time_string
|
56
|
-
Time.now.instance_eval{ '%s%03d' % [strftime('%Y%m%d%H%M%S'), (usec / 1000.0).round] }
|
52
|
+
::Time.now.instance_eval{ '%s%03d' % [strftime('%Y%m%d%H%M%S'), (usec / 1000.0).round] }
|
57
53
|
end
|
58
54
|
|
59
55
|
def display_keyword(keyword)
|
@@ -1,163 +1,149 @@
|
|
1
1
|
require 'erb'
|
2
|
-
require 'builder'
|
3
2
|
require 'cucumber/formatter/duration'
|
4
3
|
require 'cucumber/formatter/io'
|
4
|
+
require 'cucumber/core/report/summary'
|
5
|
+
require 'cucumber/core/test/result'
|
5
6
|
require 'pathname'
|
7
|
+
require_relative 'html_builder'
|
6
8
|
require_relative 'closer_html'
|
7
9
|
|
8
10
|
module Closer
|
9
11
|
module Formatter
|
10
12
|
class Html
|
13
|
+
include CloserHtml
|
11
14
|
|
12
|
-
#
|
15
|
+
# TODO: remove coupling to types
|
13
16
|
AST_CLASSES = {
|
14
17
|
Cucumber::Core::Ast::Scenario => 'scenario',
|
15
18
|
Cucumber::Core::Ast::ScenarioOutline => 'scenario outline'
|
16
19
|
}
|
20
|
+
|
17
21
|
AST_DATA_TABLE = ::Cucumber::Formatter::LegacyApi::Ast::MultilineArg::DataTable
|
18
22
|
|
19
23
|
include ERB::Util # for the #h method
|
20
24
|
include ::Cucumber::Formatter::Duration
|
21
25
|
include ::Cucumber::Formatter::Io
|
22
|
-
|
26
|
+
|
27
|
+
attr_reader :builder
|
28
|
+
private :builder
|
23
29
|
|
24
30
|
def initialize(runtime, path_or_io, options)
|
25
31
|
@io = ensure_io(path_or_io)
|
26
32
|
@runtime = runtime
|
27
33
|
@options = options
|
28
34
|
@buffer = {}
|
29
|
-
@builder =
|
35
|
+
@builder = HtmlBuilder.new(target: @io, indent: 0)
|
30
36
|
@feature_number = 0
|
31
37
|
@scenario_number = 0
|
32
38
|
@step_number = 0
|
33
39
|
@header_red = nil
|
34
40
|
@delayed_messages = []
|
35
|
-
@
|
36
|
-
@text_id = 0
|
37
|
-
@inside_outline = false
|
41
|
+
@inside_outline = false
|
38
42
|
@previous_step_keyword = nil
|
43
|
+
@summary = ::Cucumber::Core::Report::Summary.new(runtime.configuration.event_bus)
|
39
44
|
end
|
40
45
|
|
41
46
|
def before_features(features)
|
42
47
|
@step_count = features && features.step_count || 0 #TODO: Make this work with core!
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
@builder << '<html>'
|
47
|
-
@builder.head do
|
48
|
-
@builder.meta('http-equiv' => 'Content-Type', :content => 'text/html;charset=utf-8')
|
49
|
-
@builder.title 'Cucumber'
|
50
|
-
inline_css
|
51
|
-
inline_js
|
52
|
-
end
|
53
|
-
@builder << '<body>'
|
54
|
-
@builder << "<!-- Step count #{@step_count}-->"
|
55
|
-
@builder << '<div class="cucumber">'
|
56
|
-
@builder.div(:id => 'cucumber-header') do
|
57
|
-
@builder.div(:id => 'label') do
|
58
|
-
@builder.h1('Cucumber Features')
|
59
|
-
end
|
60
|
-
@builder.div(:id => 'summary') do
|
61
|
-
@builder.p('',:id => 'totals')
|
62
|
-
@builder.p('',:id => 'duration')
|
63
|
-
@builder.div(:id => 'expand-collapse') do
|
64
|
-
@builder.p('Expand All', :id => 'expander')
|
65
|
-
@builder.p('Collapse All', :id => 'collapser')
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
49
|
+
builder.build_document!
|
50
|
+
builder.format_features! features
|
69
51
|
end
|
70
52
|
|
71
53
|
def after_features(features)
|
72
54
|
print_stats(features)
|
73
|
-
|
74
|
-
|
75
|
-
|
55
|
+
builder << '</div>'
|
56
|
+
builder << '</body>'
|
57
|
+
builder << '</html>'
|
76
58
|
end
|
77
59
|
|
78
|
-
def before_feature(
|
79
|
-
dir = feature_dir(
|
60
|
+
def before_feature(_feature)
|
61
|
+
dir = feature_dir(_feature)
|
80
62
|
unless dir.empty?
|
81
63
|
if @feature_dir != dir
|
82
|
-
|
83
|
-
|
84
|
-
|
64
|
+
builder << '<div class="feature_dir"><span class="val" onclick="toggle_feature_dir(this);">'
|
65
|
+
builder << dir
|
66
|
+
builder << '</span></div>'
|
85
67
|
end
|
86
68
|
|
87
69
|
@feature_dir = dir
|
88
70
|
end
|
89
71
|
|
90
|
-
@feature =
|
72
|
+
@feature = _feature
|
91
73
|
@exceptions = []
|
92
|
-
|
74
|
+
builder << "<div id=\"#{feature_id}\" class=\"feature\">"
|
93
75
|
end
|
94
76
|
|
95
|
-
def after_feature(
|
96
|
-
|
77
|
+
def after_feature(_feature)
|
78
|
+
builder << '</div>'
|
97
79
|
end
|
98
80
|
|
99
|
-
def before_comment(
|
100
|
-
|
81
|
+
def before_comment(_comment)
|
82
|
+
return if magic_comment?(_comment)
|
83
|
+
|
84
|
+
builder << '<pre class="comment">'
|
101
85
|
end
|
102
86
|
|
103
|
-
def after_comment(
|
104
|
-
|
87
|
+
def after_comment(_comment)
|
88
|
+
return if magic_comment?(_comment)
|
89
|
+
|
90
|
+
builder << '</pre>'
|
105
91
|
end
|
106
92
|
|
107
93
|
def comment_line(comment_line)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
94
|
+
return if magic_comment?(comment_line)
|
95
|
+
|
96
|
+
builder.text!(comment_line)
|
97
|
+
builder.br
|
112
98
|
end
|
113
99
|
|
114
|
-
def after_tags(
|
100
|
+
def after_tags(_tags)
|
115
101
|
@tag_spacer = nil
|
116
102
|
end
|
117
103
|
|
118
104
|
def tag_name(tag_name)
|
119
|
-
|
105
|
+
builder.text!(@tag_spacer) if @tag_spacer
|
120
106
|
@tag_spacer = ' '
|
121
|
-
|
107
|
+
builder.span(tag_name, :class => 'tag')
|
122
108
|
end
|
123
109
|
|
124
110
|
def feature_name(keyword, name)
|
125
111
|
title = feature_dir(@feature, true) + @feature.file.split('/').last.gsub(/\.feature/, '')
|
126
112
|
lines = name.split(/\r?\n/)
|
127
113
|
return if lines.empty?
|
128
|
-
|
129
|
-
|
130
|
-
|
114
|
+
builder.h2 do |h2|
|
115
|
+
builder.span(:class => 'val') do
|
116
|
+
builder << title
|
131
117
|
end
|
132
118
|
end
|
133
119
|
|
134
120
|
if lines.size > 1
|
135
|
-
|
136
|
-
|
121
|
+
builder.div(:class => 'narrative') do
|
122
|
+
builder << lines[1..-1].join("\n")
|
137
123
|
end
|
138
124
|
end
|
139
125
|
end
|
140
126
|
|
141
|
-
def before_test_case(
|
127
|
+
def before_test_case(_test_case)
|
142
128
|
@previous_step_keyword = nil
|
143
129
|
end
|
144
130
|
|
145
|
-
def before_background(
|
131
|
+
def before_background(_background)
|
146
132
|
@in_background = true
|
147
|
-
|
133
|
+
builder << '<div class="background">'
|
148
134
|
end
|
149
135
|
|
150
|
-
def after_background(
|
136
|
+
def after_background(_background)
|
151
137
|
@in_background = nil
|
152
|
-
|
138
|
+
builder << '</div>'
|
153
139
|
end
|
154
140
|
|
155
|
-
def background_name(keyword, name,
|
141
|
+
def background_name(keyword, name, _file_colon_line, _source_indent)
|
156
142
|
@listing_background = true
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
143
|
+
builder.h3(:id => "background_#{@scenario_number}") do |h3|
|
144
|
+
builder.span(keyword, :class => 'keyword')
|
145
|
+
builder.text!(' ')
|
146
|
+
builder.span(name, :class => 'val')
|
161
147
|
end
|
162
148
|
end
|
163
149
|
|
@@ -165,22 +151,22 @@ module Closer
|
|
165
151
|
@scenario_number+=1
|
166
152
|
@scenario_red = false
|
167
153
|
css_class = AST_CLASSES[feature_element.class]
|
168
|
-
|
154
|
+
builder << "<div class='#{css_class}'>"
|
169
155
|
@in_scenario_outline = feature_element.class == Cucumber::Core::Ast::ScenarioOutline
|
170
156
|
end
|
171
157
|
|
172
|
-
def after_feature_element(
|
158
|
+
def after_feature_element(_feature_element)
|
173
159
|
unless @in_scenario_outline
|
174
160
|
print_messages
|
175
|
-
|
161
|
+
builder << '</ol>'
|
176
162
|
end
|
177
|
-
|
163
|
+
builder << '</div>'
|
178
164
|
@in_scenario_outline = nil
|
179
165
|
end
|
180
166
|
|
181
|
-
def scenario_name(keyword, name, file_colon_line,
|
182
|
-
|
183
|
-
|
167
|
+
def scenario_name(keyword, name, file_colon_line, _source_indent)
|
168
|
+
builder.span(:class => 'scenario_file hidden') do
|
169
|
+
builder << file_colon_line
|
184
170
|
end
|
185
171
|
@listing_background = false
|
186
172
|
scenario_id = "scenario_#{@scenario_number}"
|
@@ -192,53 +178,53 @@ module Closer
|
|
192
178
|
|
193
179
|
lines = name.split("\n")
|
194
180
|
title = lines.shift
|
195
|
-
|
196
|
-
|
181
|
+
builder.h3(:id => scenario_id) do
|
182
|
+
builder.span(title, :class => 'val pointer')
|
197
183
|
end
|
198
184
|
|
199
185
|
if lines.size > 0
|
200
|
-
|
186
|
+
builder.pre(:class => 'narrative hidden') do
|
201
187
|
trim_size = indent_size(lines.first)
|
202
|
-
|
188
|
+
builder << lines.map{|line| line[trim_size..-1] }.join("\n")
|
203
189
|
end
|
204
190
|
end
|
205
191
|
end
|
206
192
|
|
207
|
-
def before_outline_table(
|
193
|
+
def before_outline_table(_outline_table)
|
208
194
|
@inside_outline = true
|
209
195
|
@outline_row = 0
|
210
|
-
|
196
|
+
builder << '<table>'
|
211
197
|
end
|
212
198
|
|
213
|
-
def after_outline_table(
|
214
|
-
|
199
|
+
def after_outline_table(_outline_table)
|
200
|
+
builder << '</table>'
|
215
201
|
@outline_row = nil
|
216
202
|
@inside_outline = false
|
217
203
|
end
|
218
204
|
|
219
|
-
def before_examples(
|
220
|
-
|
205
|
+
def before_examples(_examples)
|
206
|
+
builder << '<div class="examples">'
|
221
207
|
end
|
222
208
|
|
223
|
-
def after_examples(
|
224
|
-
|
209
|
+
def after_examples(_examples)
|
210
|
+
builder << '</div>'
|
225
211
|
end
|
226
212
|
|
227
213
|
def examples_name(keyword, name)
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
214
|
+
builder.h4 do
|
215
|
+
builder.span(keyword, :class => 'keyword')
|
216
|
+
builder.text!(' ')
|
217
|
+
builder.span(name, :class => 'val')
|
232
218
|
end
|
233
219
|
end
|
234
220
|
|
235
|
-
def before_steps(
|
236
|
-
|
221
|
+
def before_steps(_steps)
|
222
|
+
builder << '<ol class="hidden">'
|
237
223
|
end
|
238
224
|
|
239
|
-
def after_steps(
|
225
|
+
def after_steps(_steps)
|
240
226
|
print_messages
|
241
|
-
|
227
|
+
builder << '</ol>' if @in_background || @in_scenario_outline
|
242
228
|
end
|
243
229
|
|
244
230
|
def before_step(step)
|
@@ -248,10 +234,10 @@ module Closer
|
|
248
234
|
@step = step
|
249
235
|
end
|
250
236
|
|
251
|
-
def after_step(
|
237
|
+
def after_step(_step)
|
252
238
|
end
|
253
239
|
|
254
|
-
def before_step_result(
|
240
|
+
def before_step_result(_keyword, step_match, _multiline_arg, status, exception, _source_indent, background, _file_colon_line)
|
255
241
|
@step_match = step_match
|
256
242
|
@hide_this_step = false
|
257
243
|
if exception
|
@@ -270,13 +256,13 @@ module Closer
|
|
270
256
|
set_scenario_color(status)
|
271
257
|
|
272
258
|
if ! @delayed_messages.empty? and status == :passed
|
273
|
-
|
259
|
+
builder << "<li id='#{@step_id}' class='step #{status} expand'>"
|
274
260
|
else
|
275
|
-
|
261
|
+
builder << "<li id='#{@step_id}' class='step #{status}'>"
|
276
262
|
end
|
277
263
|
end
|
278
264
|
|
279
|
-
def after_step_result(keyword, step_match,
|
265
|
+
def after_step_result(keyword, step_match, _multiline_arg, status, _exception, _source_indent, _background, _file_colon_line)
|
280
266
|
return if @hide_this_step
|
281
267
|
# print snippet for undefined steps
|
282
268
|
unless outline_step?(@step)
|
@@ -284,13 +270,13 @@ module Closer
|
|
284
270
|
@previous_step_keyword = keyword
|
285
271
|
end
|
286
272
|
if status == :undefined
|
287
|
-
|
288
|
-
# TODO: snippet text should be an event sent to the formatter so we don't
|
273
|
+
builder.pre do |pre|
|
274
|
+
# TODO: snippet text should be an event sent to the formatter so we don't
|
289
275
|
# have this couping to the runtime.
|
290
|
-
pre << @runtime.snippet_text(keyword,step_match.instance_variable_get(
|
276
|
+
pre << @runtime.snippet_text(keyword,step_match.instance_variable_get('@name') || '', @step.multiline_arg)
|
291
277
|
end
|
292
278
|
end
|
293
|
-
|
279
|
+
builder << '</li>'
|
294
280
|
|
295
281
|
unless status == :undefined
|
296
282
|
step_file = step_match.file_colon_line
|
@@ -306,13 +292,13 @@ module Closer
|
|
306
292
|
end
|
307
293
|
end
|
308
294
|
step_contents << "</pre></div>"
|
309
|
-
|
295
|
+
builder << step_contents
|
310
296
|
end
|
311
297
|
|
312
298
|
print_messages
|
313
299
|
end
|
314
300
|
|
315
|
-
def step_name(keyword, step_match, status,
|
301
|
+
def step_name(keyword, step_match, status, _source_indent, background, _file_colon_line)
|
316
302
|
background_in_scenario = background && !@listing_background
|
317
303
|
@skip_step = background_in_scenario
|
318
304
|
|
@@ -321,7 +307,7 @@ module Closer
|
|
321
307
|
end
|
322
308
|
end
|
323
309
|
|
324
|
-
def exception(exception,
|
310
|
+
def exception(exception, _status)
|
325
311
|
return if @hide_this_step
|
326
312
|
print_messages
|
327
313
|
build_exception_detail(exception)
|
@@ -335,21 +321,21 @@ module Closer
|
|
335
321
|
def before_multiline_arg(multiline_arg)
|
336
322
|
return if @hide_this_step || @skip_step
|
337
323
|
if AST_DATA_TABLE === multiline_arg
|
338
|
-
|
324
|
+
builder << '<table>'
|
339
325
|
end
|
340
326
|
end
|
341
327
|
|
342
328
|
def after_multiline_arg(multiline_arg)
|
343
329
|
return if @hide_this_step || @skip_step
|
344
330
|
if AST_DATA_TABLE === multiline_arg
|
345
|
-
|
331
|
+
builder << '</table>'
|
346
332
|
end
|
347
333
|
end
|
348
334
|
|
349
335
|
def doc_string(string)
|
350
336
|
return if @hide_this_step
|
351
|
-
|
352
|
-
|
337
|
+
builder.pre(:class => 'val') do |pre|
|
338
|
+
builder << h(string).gsub("\n", '
')
|
353
339
|
end
|
354
340
|
end
|
355
341
|
|
@@ -357,17 +343,17 @@ module Closer
|
|
357
343
|
@row_id = table_row.dom_id
|
358
344
|
@col_index = 0
|
359
345
|
return if @hide_this_step
|
360
|
-
|
346
|
+
builder << "<tr class='step' id='#{@row_id}'>"
|
361
347
|
end
|
362
348
|
|
363
349
|
def after_table_row(table_row)
|
364
350
|
return if @hide_this_step
|
365
351
|
print_table_row_messages
|
366
|
-
|
352
|
+
builder << '</tr>'
|
367
353
|
if table_row.exception
|
368
|
-
|
369
|
-
|
370
|
-
|
354
|
+
builder.tr do
|
355
|
+
builder.td(:colspan => @col_index.to_s, :class => 'failed') do
|
356
|
+
builder.pre do |pre|
|
371
357
|
pre << h(format_exception(table_row.exception))
|
372
358
|
end
|
373
359
|
end
|
@@ -397,16 +383,16 @@ module Closer
|
|
397
383
|
|
398
384
|
def puts(message)
|
399
385
|
@delayed_messages << message
|
400
|
-
|
386
|
+
#builder.pre(message, :class => 'message')
|
401
387
|
end
|
402
388
|
|
403
389
|
def print_messages
|
404
390
|
return if @delayed_messages.empty?
|
405
391
|
|
406
|
-
|
392
|
+
#builder.ol do
|
407
393
|
@delayed_messages.each do |ann|
|
408
|
-
|
409
|
-
|
394
|
+
builder.li(:class => 'message hidden') do
|
395
|
+
builder << ann
|
410
396
|
end
|
411
397
|
end
|
412
398
|
#end
|
@@ -416,8 +402,8 @@ module Closer
|
|
416
402
|
def print_table_row_messages
|
417
403
|
return if @delayed_messages.empty?
|
418
404
|
|
419
|
-
|
420
|
-
|
405
|
+
builder.td(:class => 'message') do
|
406
|
+
builder << @delayed_messages.join(', ')
|
421
407
|
end
|
422
408
|
empty_messages
|
423
409
|
end
|
@@ -426,8 +412,8 @@ module Closer
|
|
426
412
|
@delayed_messages = []
|
427
413
|
end
|
428
414
|
|
429
|
-
def after_test_case(
|
430
|
-
if result.failed?
|
415
|
+
def after_test_case(_test_case, result)
|
416
|
+
if result.failed? && !@scenario_red
|
431
417
|
set_scenario_color_failed
|
432
418
|
end
|
433
419
|
end
|
@@ -436,36 +422,40 @@ module Closer
|
|
436
422
|
|
437
423
|
def build_exception_detail(exception)
|
438
424
|
backtrace = Array.new
|
439
|
-
|
425
|
+
|
426
|
+
builder.div(:class => 'message') do
|
440
427
|
message = exception.message
|
428
|
+
|
441
429
|
if defined?(RAILS_ROOT) && message.include?('Exception caught')
|
442
430
|
matches = message.match(/Showing <i>(.+)<\/i>(?:.+) #(\d+)/)
|
443
431
|
backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"] if matches
|
444
432
|
matches = message.match(/<code>([^(\/)]+)<\//m)
|
445
|
-
message = matches ? matches[1] :
|
433
|
+
message = matches ? matches[1] : ''
|
446
434
|
end
|
447
435
|
|
448
436
|
unless exception.instance_of?(RuntimeError)
|
449
437
|
message = "#{message} (#{exception.class})"
|
450
438
|
end
|
451
439
|
|
452
|
-
|
453
|
-
|
440
|
+
builder.pre do
|
441
|
+
builder.text!(message)
|
454
442
|
end
|
455
443
|
end
|
456
|
-
|
457
|
-
|
444
|
+
|
445
|
+
builder.div(:class => 'backtrace') do
|
446
|
+
builder.pre do
|
458
447
|
backtrace = exception.backtrace
|
459
448
|
backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
|
460
|
-
|
449
|
+
builder << backtrace_line(backtrace.join("\n"))
|
461
450
|
end
|
462
451
|
end
|
452
|
+
|
463
453
|
extra = extra_failure_content(backtrace)
|
464
|
-
|
454
|
+
builder << extra unless extra == ''
|
465
455
|
end
|
466
456
|
|
467
457
|
def set_scenario_color(status)
|
468
|
-
if status.nil?
|
458
|
+
if status.nil? || status == :undefined || status == :pending
|
469
459
|
set_scenario_color_pending
|
470
460
|
end
|
471
461
|
if status == :failed
|
@@ -474,32 +464,32 @@ module Closer
|
|
474
464
|
end
|
475
465
|
|
476
466
|
def set_scenario_color_failed
|
477
|
-
|
478
|
-
|
467
|
+
builder.script do
|
468
|
+
builder.text!("makeRed('cucumber-header');") unless @header_red
|
479
469
|
@header_red = true
|
480
|
-
scenario_or_background = @in_background ?
|
481
|
-
|
470
|
+
scenario_or_background = @in_background ? 'background' : 'scenario'
|
471
|
+
builder.text!("makeRed('#{scenario_or_background}_#{@scenario_number}');") unless @scenario_red
|
482
472
|
@scenario_red = true
|
483
|
-
if @options[:expand]
|
484
|
-
|
473
|
+
if @options[:expand] && @inside_outline
|
474
|
+
builder.text!("makeRed('#{scenario_or_background}_#{@scenario_number}_#{@outline_row}');")
|
485
475
|
end
|
486
476
|
end
|
487
477
|
end
|
488
478
|
|
489
479
|
def set_scenario_color_pending
|
490
|
-
|
491
|
-
|
492
|
-
scenario_or_background = @in_background ?
|
493
|
-
|
480
|
+
builder.script do
|
481
|
+
builder.text!("makeYellow('cucumber-header');") unless @header_red
|
482
|
+
scenario_or_background = @in_background ? 'background' : 'scenario'
|
483
|
+
builder.text!("makeYellow('#{scenario_or_background}_#{@scenario_number}');") unless @scenario_red
|
494
484
|
end
|
495
485
|
end
|
496
486
|
|
497
|
-
def build_step(keyword, step_match,
|
487
|
+
def build_step(keyword, step_match, _status)
|
498
488
|
keyword = display_keyword(keyword)
|
499
489
|
step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
|
500
|
-
|
501
|
-
|
502
|
-
|
490
|
+
builder.div(:class => 'step_name') do |div|
|
491
|
+
builder.span(keyword, :class => 'keyword')
|
492
|
+
builder.span(:class => 'step val') do |name|
|
503
493
|
name << h(step_name).gsub(/<span class="(.*?)">/, '<span class="\1">').gsub(/<\/span>/, '</span>')
|
504
494
|
end
|
505
495
|
end
|
@@ -508,97 +498,24 @@ module Closer
|
|
508
498
|
step_file.gsub(/^([^:]*\.rb):(\d*)/) do
|
509
499
|
step_file = "<span class=\"pointer\" onclick=\"toggle_step_file(this); return false;\">#{step_file}</span>"
|
510
500
|
|
511
|
-
|
512
|
-
|
513
|
-
|
501
|
+
builder.div(:class => 'step_file') do |div|
|
502
|
+
builder.span do
|
503
|
+
builder << step_file
|
514
504
|
end
|
515
505
|
end
|
516
506
|
end
|
517
507
|
end
|
518
508
|
|
519
509
|
def build_cell(cell_type, value, attributes)
|
520
|
-
|
521
|
-
|
522
|
-
|
510
|
+
builder.__send__(cell_type, attributes) do
|
511
|
+
builder.div do
|
512
|
+
builder.span(value,:class => 'step param')
|
523
513
|
end
|
524
514
|
end
|
525
515
|
end
|
526
516
|
|
527
|
-
def inline_css
|
528
|
-
@builder.style do
|
529
|
-
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
530
|
-
@builder << File.read(File.dirname(__FILE__) + '/closer.css')
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
|
-
def inline_js
|
535
|
-
@builder.script do
|
536
|
-
@builder << inline_jquery
|
537
|
-
@builder << inline_js_content
|
538
|
-
@builder << inline_closer
|
539
|
-
end
|
540
|
-
end
|
541
|
-
|
542
|
-
def inline_jquery
|
543
|
-
File.read(File.dirname(__FILE__) + '/jquery-min.js')
|
544
|
-
end
|
545
|
-
|
546
|
-
def inline_closer
|
547
|
-
ret = ''
|
548
|
-
ret << File.read(File.join(File.dirname(__FILE__), 'closer.js'))
|
549
|
-
ret << File.read(File.join(File.dirname(__FILE__), 'screenshot.js'))
|
550
|
-
|
551
|
-
if should_expand
|
552
|
-
ret << %w{
|
553
|
-
$(document).ready(function() {
|
554
|
-
$('#expander').click();
|
555
|
-
});
|
556
|
-
}.join(' ')
|
557
|
-
end
|
558
|
-
|
559
|
-
ret
|
560
|
-
end
|
561
|
-
|
562
|
-
def inline_js_content
|
563
|
-
<<-EOF
|
564
|
-
|
565
|
-
SCENARIOS = "h3[id^='scenario_'],h3[id^=background_]";
|
566
|
-
|
567
|
-
$(document).ready(function() {
|
568
|
-
$(SCENARIOS).delegate('.val', 'click', function() {
|
569
|
-
$(this).parent().siblings().toggle(100);
|
570
|
-
});
|
571
|
-
|
572
|
-
$("#collapser").css('cursor', 'pointer');
|
573
|
-
$("#collapser").click(function() {
|
574
|
-
$(SCENARIOS).siblings().addClass('hidden');
|
575
|
-
$('li.message').addClass('hidden');
|
576
|
-
});
|
577
|
-
|
578
|
-
$("#expander").css('cursor', 'pointer');
|
579
|
-
$("#expander").click(function() {
|
580
|
-
$(SCENARIOS).siblings().removeClass('hidden');
|
581
|
-
$('li.message').removeClass('hidden');
|
582
|
-
});
|
583
|
-
})
|
584
|
-
|
585
|
-
function moveProgressBar(percentDone) {
|
586
|
-
$("cucumber-header").css('width', percentDone +"%");
|
587
|
-
}
|
588
|
-
function makeRed(element_id) {
|
589
|
-
$('#'+element_id).css('background', '#C40D0D');
|
590
|
-
$('#'+element_id).css('color', '#FFFFFF');
|
591
|
-
}
|
592
|
-
function makeYellow(element_id) {
|
593
|
-
$('#'+element_id).css('background', '#FAF834');
|
594
|
-
$('#'+element_id).css('color', '#000000');
|
595
|
-
}
|
596
|
-
|
597
|
-
EOF
|
598
|
-
end
|
599
|
-
|
600
517
|
def format_exception(exception)
|
601
|
-
([
|
518
|
+
([exception.message.to_s] + exception.backtrace).join("\n")
|
602
519
|
end
|
603
520
|
|
604
521
|
def backtrace_line(line)
|
@@ -608,44 +525,50 @@ module Closer
|
|
608
525
|
end
|
609
526
|
|
610
527
|
def print_stats(features)
|
611
|
-
|
612
|
-
|
528
|
+
builder << "<script>document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
|
529
|
+
builder << "<script>document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
|
613
530
|
end
|
614
531
|
|
615
|
-
def print_stat_string(
|
532
|
+
def print_stat_string(_features)
|
616
533
|
string = String.new
|
617
|
-
string << dump_count(@
|
618
|
-
scenario_count =
|
534
|
+
string << dump_count(@summary.test_cases.total, 'scenario')
|
535
|
+
scenario_count = status_counts(@summary.test_cases)
|
619
536
|
string << scenario_count if scenario_count
|
620
|
-
string <<
|
621
|
-
string << dump_count(@
|
622
|
-
step_count =
|
537
|
+
string << '<br />'
|
538
|
+
string << dump_count(@summary.test_steps.total, 'step')
|
539
|
+
step_count = status_counts(@summary.test_steps)
|
623
540
|
string << step_count if step_count
|
624
541
|
end
|
625
542
|
|
626
|
-
def
|
627
|
-
counts =
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
543
|
+
def status_counts(summary)
|
544
|
+
counts = ::Cucumber::Core::Test::Result::TYPES.map { |status|
|
545
|
+
count = summary.total(status)
|
546
|
+
[status, count]
|
547
|
+
}.select { |status, count|
|
548
|
+
count > 0
|
549
|
+
}.map { |status, count|
|
550
|
+
"#{count} #{status}"
|
551
|
+
}
|
552
|
+
"(#{counts.join(", ")})" if counts.any?
|
632
553
|
end
|
633
554
|
|
634
555
|
def dump_count(count, what, state=nil)
|
635
|
-
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(
|
636
|
-
end
|
637
|
-
|
638
|
-
def create_builder(io)
|
639
|
-
Builder::XmlMarkup.new(:target => io, :indent => 0)
|
556
|
+
[count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(' ')
|
640
557
|
end
|
641
558
|
|
642
|
-
def outline_step?(
|
559
|
+
def outline_step?(_step)
|
643
560
|
not @step.step.respond_to?(:actual_keyword)
|
644
561
|
end
|
645
562
|
|
646
563
|
class SnippetExtractor #:nodoc:
|
647
|
-
class NullConverter; def convert(code,
|
648
|
-
|
564
|
+
class NullConverter; def convert(code, _pre); code; end; end #:nodoc:
|
565
|
+
|
566
|
+
begin
|
567
|
+
require 'syntax/convertors/html'
|
568
|
+
@@converter = Syntax::Convertors::HTML.for_syntax 'ruby'
|
569
|
+
rescue LoadError
|
570
|
+
@@converter = NullConverter.new
|
571
|
+
end
|
649
572
|
|
650
573
|
def snippet(error)
|
651
574
|
raw_code, line = snippet_for(error[0])
|
@@ -694,4 +617,4 @@ module Closer
|
|
694
617
|
end
|
695
618
|
end
|
696
619
|
end
|
697
|
-
end
|
620
|
+
end
|