cucumber 3.1.2 → 4.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -13
  3. data/CONTRIBUTING.md +1 -0
  4. data/bin/cucumber +1 -1
  5. data/lib/autotest/cucumber_mixin.rb +42 -39
  6. data/lib/cucumber/cli/configuration.rb +4 -4
  7. data/lib/cucumber/cli/main.rb +11 -12
  8. data/lib/cucumber/cli/options.rb +53 -62
  9. data/lib/cucumber/cli/profile_loader.rb +32 -20
  10. data/lib/cucumber/configuration.rb +20 -21
  11. data/lib/cucumber/constantize.rb +2 -5
  12. data/lib/cucumber/deprecate.rb +5 -5
  13. data/lib/cucumber/errors.rb +4 -6
  14. data/lib/cucumber/events.rb +1 -0
  15. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  16. data/lib/cucumber/events/step_activated.rb +2 -1
  17. data/lib/cucumber/file_specs.rb +6 -6
  18. data/lib/cucumber/filters/activate_steps.rb +5 -3
  19. data/lib/cucumber/filters/prepare_world.rb +5 -9
  20. data/lib/cucumber/filters/quit.rb +1 -3
  21. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  22. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  23. data/lib/cucumber/formatter/ast_lookup.rb +160 -0
  24. data/lib/cucumber/formatter/backtrace_filter.rb +5 -7
  25. data/lib/cucumber/formatter/console.rb +28 -59
  26. data/lib/cucumber/formatter/console_counts.rb +4 -9
  27. data/lib/cucumber/formatter/console_issues.rb +6 -3
  28. data/lib/cucumber/formatter/duration_extractor.rb +1 -1
  29. data/lib/cucumber/formatter/fanout.rb +2 -0
  30. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  31. data/lib/cucumber/formatter/interceptor.rb +5 -7
  32. data/lib/cucumber/formatter/io.rb +3 -3
  33. data/lib/cucumber/formatter/json.rb +92 -110
  34. data/lib/cucumber/formatter/junit.rb +55 -57
  35. data/lib/cucumber/formatter/pretty.rb +346 -152
  36. data/lib/cucumber/formatter/progress.rb +28 -32
  37. data/lib/cucumber/formatter/rerun.rb +22 -4
  38. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  39. data/lib/cucumber/formatter/steps.rb +2 -3
  40. data/lib/cucumber/formatter/summary.rb +16 -8
  41. data/lib/cucumber/formatter/unicode.rb +15 -17
  42. data/lib/cucumber/formatter/usage.rb +9 -8
  43. data/lib/cucumber/gherkin/data_table_parser.rb +8 -6
  44. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  45. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  46. data/lib/cucumber/gherkin/steps_parser.rb +7 -8
  47. data/lib/cucumber/glue/dsl.rb +1 -1
  48. data/lib/cucumber/glue/hook.rb +16 -9
  49. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  50. data/lib/cucumber/glue/proto_world.rb +14 -16
  51. data/lib/cucumber/glue/registry_and_more.rb +7 -9
  52. data/lib/cucumber/glue/snippet.rb +21 -20
  53. data/lib/cucumber/glue/step_definition.rb +14 -15
  54. data/lib/cucumber/glue/world_factory.rb +1 -1
  55. data/lib/cucumber/hooks.rb +11 -11
  56. data/lib/cucumber/multiline_argument.rb +4 -6
  57. data/lib/cucumber/multiline_argument/data_table.rb +88 -59
  58. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
  59. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  60. data/lib/cucumber/platform.rb +3 -3
  61. data/lib/cucumber/rake/task.rb +13 -16
  62. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  63. data/lib/cucumber/running_test_case.rb +2 -53
  64. data/lib/cucumber/runtime.rb +27 -57
  65. data/lib/cucumber/runtime/after_hooks.rb +3 -3
  66. data/lib/cucumber/runtime/before_hooks.rb +3 -3
  67. data/lib/cucumber/runtime/for_programming_languages.rb +3 -2
  68. data/lib/cucumber/runtime/step_hooks.rb +1 -1
  69. data/lib/cucumber/runtime/support_code.rb +10 -12
  70. data/lib/cucumber/runtime/user_interface.rb +4 -6
  71. data/lib/cucumber/step_definition_light.rb +4 -3
  72. data/lib/cucumber/step_match.rb +12 -11
  73. data/lib/cucumber/step_match_search.rb +2 -1
  74. data/lib/cucumber/term/ansicolor.rb +9 -9
  75. data/lib/cucumber/version +1 -1
  76. metadata +36 -29
  77. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  78. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  79. data/lib/cucumber/formatter/cucumber.css +0 -286
  80. data/lib/cucumber/formatter/cucumber.sass +0 -247
  81. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  82. data/lib/cucumber/formatter/html.rb +0 -611
  83. data/lib/cucumber/formatter/html_builder.rb +0 -121
  84. data/lib/cucumber/formatter/inline-js.js +0 -30
  85. data/lib/cucumber/formatter/jquery-min.js +0 -154
  86. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  87. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  88. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  89. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  90. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  91. data/lib/cucumber/step_argument.rb +0 -25
@@ -1,247 +0,0 @@
1
- /* cucumber.css is generated from cucumber.sass */
2
- /* Regenerate with rake sass */
3
-
4
- $step_left: 5px solid
5
- $step_bottom: 1px solid
6
-
7
- $failed: #fffbd3
8
- $failed_border: #c20000
9
- $failed_text: #c20000
10
-
11
- $passed: #dbffb4
12
- $passed_border: #65c400
13
- $passed_text: #3d7700
14
-
15
- $skipped: #e0ffff
16
- $skipped_border: aqua
17
- $skipped_text: #001111
18
-
19
- $pending: #fcfb98
20
- $pending_border: #faf834
21
- $pending_text: #131313
22
-
23
- $undefined: #fcfb98
24
- $undefined_border: #faf834
25
- $undefined_text: #131313
26
-
27
- $message: #e0ffff
28
- $message_border: aqua
29
- $message_text: #001111
30
-
31
- body
32
- font-size: 0px
33
- color: white
34
- margin: 0px
35
- padding: 0px
36
-
37
- .cucumber,td,th
38
- font: normal 11px "Lucida Grande", Helvetica, sans-serif
39
- background: white
40
- color: black
41
- #cucumber-header
42
- background: #65c400
43
- color: white
44
- height: 6em
45
- #expand-collapse
46
- p
47
- float: right
48
- margin: 0 0 0 10px
49
- .scenario
50
- h3
51
- font-size: 11px
52
- padding: 3px
53
- margin: 0
54
- background: #65c400
55
- color: white
56
- font-weight: bold
57
- h1
58
- margin: 0px 10px 0px 10px
59
- padding: 10px
60
- font-family: "Lucida Grande", Helvetica, sans-serif
61
- font-size: 2em
62
- position: absolute
63
- h4
64
- margin-bottom: 2px
65
- div.feature
66
- padding: 2px
67
- margin: 0px 10px 5px 10px
68
- div.examples
69
- padding: 0em 0em 0em 1em
70
- .stats
71
- margin: 2em
72
- .summary
73
- ul.features
74
- li
75
- display: inline
76
- .step_name
77
- float: left
78
- .step_file
79
- text-align: right
80
- color: #999999
81
- a
82
- color: #999999
83
- .scenario_file
84
- float: right
85
- color: #999999
86
- .tag
87
- font-weight: bold
88
- color: #246ac1
89
- .backtrace
90
- margin-top: 0
91
- margin-bottom: 0
92
- margin-left: 1em
93
- color: black
94
- a
95
- text-decoration: none
96
- color: #be5c00
97
- &:hover
98
- text-decoration: underline
99
- &:visited
100
- font-weight: normal
101
- div.examples
102
- margin: 5px 0px 5px 15px
103
- color: black
104
- .outline
105
- table
106
- margin: 0px 0px 5px 10px
107
- table
108
- border-collapse: collapse
109
- td
110
- padding: 3px 3px 3px 5px
111
- td.failed, td.passed, td.skipped, td.pending, td.undefined
112
- padding-left: 18px
113
- padding-right: 10px
114
- td.failed
115
- border-left: $step_left $failed_border
116
- border-bottom: $step_bottom $failed_border
117
- background: $failed
118
- color: $failed_text
119
- td.passed
120
- border-left: $step_left $passed_border
121
- border-bottom: $step_bottom $passed_border
122
- background: $passed
123
- color: $passed_text
124
- td.skipped
125
- border-left: $step_left $skipped_border
126
- border-bottom: $step_bottom $skipped_border
127
- background: $skipped
128
- color: $skipped_text
129
- td.pending
130
- border-left: $step_left $pending_border
131
- border-bottom: $step_bottom $pending_border
132
- background: $pending
133
- color: $pending_text
134
- td.undefined
135
- border-left: $step_left $undefined_border
136
- border-bottom: $step_bottom $undefined_border
137
- background: $undefined
138
- color: $undefined_text
139
- td.message
140
- border-left: $step_left $message_border
141
- border-bottom: $step_bottom $message_border
142
- background: $message
143
- color: $message_text
144
- ol
145
- list-style: none
146
- margin: 0px
147
- padding: 0px
148
- li.step
149
- padding: 3px 3px 3px 18px
150
- margin: 5px 0px 5px 5px
151
- li
152
- margin: 0em 0em 0em 1em
153
- padding: 0em 0em 0em 0.2em
154
- span.param
155
- font-weight: bold
156
- li.failed
157
- border-left: $step_left $failed_border
158
- border-bottom: $step_bottom $failed_border
159
- background: $failed
160
- color: $failed_text
161
- li.passed
162
- border-left: $step_left $passed_border
163
- border-bottom: $step_bottom $passed_border
164
- background: $passed
165
- color: $passed_text
166
- li.skipped
167
- border-left: $step_left $skipped_border
168
- border-bottom: $step_bottom $skipped_border
169
- background: $skipped
170
- color: $skipped_text
171
- li.pending
172
- border-left: $step_left $pending_border
173
- border-bottom: $step_bottom $pending_border
174
- background: $pending
175
- color: $pending_text
176
- li.undefined
177
- border-left: $step_left $undefined_border
178
- border-bottom: $step_bottom $undefined_border
179
- background: $undefined
180
- color: $undefined_text
181
- li.message
182
- border-left: $step_left $message_border
183
- border-bottom: $step_bottom $message_border
184
- background: $message
185
- color: $message_text
186
- margin-left: 10px
187
- #summary
188
- margin: 0px
189
- padding: 5px 10px
190
- text-align: right
191
- top: 0px
192
- right: 0px
193
- float: right
194
- p
195
- margin: 0 0 0 2px
196
- #totals
197
- font-size: 1.2em
198
-
199
- .ruby
200
- font-size: 12px
201
- font-family: monospace
202
- color: white
203
- background: black
204
- padding: 0.1em 0 0.2em 0
205
- .keyword
206
- color: #ff6600
207
- .constant
208
- color: #339999
209
- .attribute
210
- color: white
211
- .global
212
- color: white
213
- .module
214
- color: white
215
- .class
216
- color: white
217
- .string
218
- color: #66ff00
219
- .ident
220
- color: white
221
- .method
222
- color: #ffcc00
223
- .number
224
- color: white
225
- .char
226
- color: white
227
- .comment
228
- color: #9933cc
229
- .symbol
230
- color: white
231
- .regex
232
- color: #44b4cc
233
- .punct
234
- color: white
235
- .escape
236
- color: white
237
- .interp
238
- color: white
239
- .expr
240
- color: white
241
- .offending
242
- background: #333333
243
- .linenum
244
- width: 75px
245
- padding: 0.1em 1em 0.2em 0
246
- color: black
247
- background: #fffbd3
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Cucumber
4
- module Formatter
5
- class HookQueryVisitor
6
- attr_reader :type
7
-
8
- def initialize(test_step)
9
- @hook = false
10
- @type = :no_hook
11
- test_step.source.last.describe_to(self)
12
- end
13
-
14
- def hook?
15
- @hook
16
- end
17
-
18
- def step(*)
19
- end
20
-
21
- def before_hook(*)
22
- @hook = true
23
- @type = :before
24
- end
25
-
26
- def after_hook(*)
27
- @hook = true
28
- @type = :after
29
- end
30
-
31
- def after_step_hook(*)
32
- @hook = true
33
- @type = :after_step
34
- end
35
-
36
- def around_hook(*)
37
- @hook = true
38
- @type = :around
39
- end
40
- end
41
- end
42
- end
@@ -1,611 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'erb'
4
- require 'cucumber/formatter/duration'
5
- require 'cucumber/formatter/io'
6
- require 'cucumber/formatter/html_builder'
7
- require 'pathname'
8
-
9
- module Cucumber
10
- module Formatter
11
- class Html
12
- # TODO: remove coupling to types
13
- AST_CLASSES = {
14
- Cucumber::Core::Ast::Scenario => 'scenario',
15
- Cucumber::Core::Ast::ScenarioOutline => 'scenario outline'
16
- }
17
-
18
- AST_DATA_TABLE = LegacyApi::Ast::MultilineArg::DataTable
19
-
20
- include ERB::Util # for the #h method
21
- include Duration
22
- include Io
23
-
24
- attr_reader :builder
25
- private :builder
26
-
27
- def initialize(runtime, path_or_io, options)
28
- @io = ensure_io(path_or_io)
29
- @runtime = runtime
30
- @options = options
31
- @buffer = {}
32
- @builder = HtmlBuilder.new(target: @io, indent: 0)
33
- @feature_number = 0
34
- @scenario_number = 0
35
- @step_number = 0
36
- @header_red = nil
37
- @delayed_messages = []
38
- @inside_outline = false
39
- @previous_step_keyword = nil
40
- end
41
-
42
- def embed(src, mime_type, label)
43
- if image?(mime_type)
44
- src = src_is_file_or_data?(src) ? src : "data:#{standardize_mime_type(mime_type)},#{src}"
45
- builder.embed(type: :image, src: path(src), label: label, id: next_id(:img))
46
- else
47
- builder.embed(type: :text, src: src, label: label, id: next_id(:text))
48
- end
49
- end
50
-
51
- def path(src)
52
- if @io.respond_to?(:path) && File.file?(src)
53
- out_dir = Pathname.new(File.dirname(File.absolute_path(@io.path)))
54
- src = Pathname.new(File.absolute_path(src)).relative_path_from(out_dir)
55
- end
56
-
57
- src
58
- end
59
-
60
- def standardize_mime_type(mime_type)
61
- mime_type =~ /;base[0-9]+$/ ? mime_type : mime_type + ';base64'
62
- end
63
-
64
- def src_is_file_or_data?(src)
65
- File.file?(src) || src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
66
- end
67
-
68
- def image?(mime_type)
69
- mime_type =~ /^image\/(png|gif|jpg|jpeg)/
70
- end
71
-
72
- def before_features(features)
73
- @step_count = features && features.step_count || 0 # TODO: Make this work with core!
74
-
75
- builder.build_document!
76
- builder.format_features! features
77
- end
78
-
79
- def after_features(features)
80
- print_stats(features)
81
- builder << '</div>'
82
- builder << '</body>'
83
- builder << '</html>'
84
- end
85
-
86
- def before_feature(_feature)
87
- @exceptions = []
88
- builder << '<div class="feature">'
89
- end
90
-
91
- def after_feature(_feature)
92
- builder << '</div>'
93
- end
94
-
95
- def before_comment(_comment)
96
- builder << '<pre class="comment">'
97
- end
98
-
99
- def after_comment(_comment)
100
- builder << '</pre>'
101
- end
102
-
103
- def comment_line(comment_line)
104
- builder.text!(comment_line)
105
- builder.br
106
- end
107
-
108
- def after_tags(_tags)
109
- @tag_spacer = nil
110
- end
111
-
112
- def tag_name(tag_name)
113
- builder.text!(@tag_spacer) if @tag_spacer
114
- @tag_spacer = ' '
115
- builder.span(tag_name, :class => 'tag')
116
- end
117
-
118
- def feature_name(keyword, name)
119
- lines = name.split(/\r?\n/)
120
- return if lines.empty?
121
- builder.h2 do |h2|
122
- builder.span(keyword + ': ' + lines[0], :class => 'val')
123
- end
124
- builder.p(:class => 'narrative') do
125
- lines[1..-1].each do |line|
126
- builder.text!(line.strip)
127
- builder.br
128
- end
129
- end
130
- end
131
-
132
- def before_test_case(_test_case)
133
- @previous_step_keyword = nil
134
- end
135
-
136
- def before_background(_background)
137
- @in_background = true
138
- builder << '<div class="background">'
139
- end
140
-
141
- def after_background(_background)
142
- @in_background = nil
143
- builder << '</div>'
144
- end
145
-
146
- def background_name(keyword, name, _file_colon_line, _source_indent)
147
- @listing_background = true
148
- builder.h3(:id => "background_#{@scenario_number}") do |h3|
149
- builder.span(keyword, :class => 'keyword')
150
- builder.text!(' ')
151
- builder.span(name, :class => 'val')
152
- end
153
- end
154
-
155
- def before_feature_element(feature_element)
156
- @scenario_number += 1
157
- @scenario_red = false
158
- css_class = AST_CLASSES[feature_element.class]
159
- builder << "<div class='#{css_class}'>"
160
- @in_scenario_outline = feature_element.class == Cucumber::Core::Ast::ScenarioOutline
161
- end
162
-
163
- def after_feature_element(_feature_element)
164
- unless @in_scenario_outline
165
- print_messages
166
- builder << '</ol>'
167
- end
168
- builder << '</div>'
169
- @in_scenario_outline = nil
170
- end
171
-
172
- def scenario_name(keyword, name, file_colon_line, _source_indent)
173
- builder.span(:class => 'scenario_file') do
174
- builder << file_colon_line
175
- end
176
- @listing_background = false
177
- scenario_id = "scenario_#{@scenario_number}"
178
- if @inside_outline
179
- @outline_row += 1
180
- scenario_id += "_#{@outline_row}"
181
- @scenario_red = false
182
- end
183
- builder.h3(:id => scenario_id) do
184
- builder.span(keyword + ':', :class => 'keyword')
185
- builder.text!(' ')
186
- builder.span(name, :class => 'val')
187
- end
188
- end
189
-
190
- def before_outline_table(_outline_table)
191
- @inside_outline = true
192
- @outline_row = 0
193
- builder << '<table>'
194
- end
195
-
196
- def after_outline_table(_outline_table)
197
- builder << '</table>'
198
- @outline_row = nil
199
- @inside_outline = false
200
- end
201
-
202
- def before_examples(_examples)
203
- builder << '<div class="examples">'
204
- end
205
-
206
- def after_examples(_examples)
207
- builder << '</div>'
208
- end
209
-
210
- def examples_name(keyword, name)
211
- builder.h4 do
212
- builder.span(keyword, :class => 'keyword')
213
- builder.text!(' ')
214
- builder.span(name, :class => 'val')
215
- end
216
- end
217
-
218
- def before_steps(_steps)
219
- builder << '<ol>'
220
- end
221
-
222
- def after_steps(_steps)
223
- print_messages
224
- builder << '</ol>' if @in_background || @in_scenario_outline
225
- end
226
-
227
- def before_step(step)
228
- print_messages
229
- @step_id = step.dom_id
230
- @step_number += 1
231
- @step = step
232
- end
233
-
234
- def after_step(_step)
235
- move_progress
236
- end
237
-
238
- def before_step_result(_keyword, step_match, _multiline_arg, status, exception, _source_indent, background, _file_colon_line)
239
- @step_match = step_match
240
- @hide_this_step = false
241
- if exception
242
- if @exceptions.include?(exception)
243
- @hide_this_step = true
244
- return
245
- end
246
- @exceptions << exception
247
- end
248
- if status != :failed && @in_background ^ background
249
- @hide_this_step = true
250
- return
251
- end
252
- @status = status
253
- return if @hide_this_step
254
- scenario_color(status)
255
- builder << "<li id='#{@step_id}' class='step #{status}'>"
256
- end
257
-
258
- def after_step_result(keyword, step_match, _multiline_arg, status, _exception, _source_indent, _background, _file_colon_line)
259
- return if @hide_this_step
260
- # print snippet for undefined steps
261
- unless outline_step?(@step)
262
- keyword = @step.actual_keyword(@previous_step_keyword)
263
- @previous_step_keyword = keyword
264
- end
265
- if status == :undefined
266
- builder.pre do |pre|
267
- # TODO: snippet text should be an event sent to the formatter so we don't
268
- # have this couping to the runtime.
269
- pre << @runtime.snippet_text(keyword, step_match.instance_variable_get('@name') || '', @step.multiline_arg)
270
- end
271
- end
272
- builder << '</li>'
273
- print_messages
274
- end
275
-
276
- def step_name(keyword, step_match, status, _source_indent, background, _file_colon_line)
277
- background_in_scenario = background && !@listing_background
278
- @skip_step = background_in_scenario
279
-
280
- unless @skip_step
281
- build_step(keyword, step_match, status)
282
- end
283
- end
284
-
285
- def exception(exception, _status)
286
- return if @hide_this_step
287
- print_messages
288
- build_exception_detail(exception)
289
- end
290
-
291
- def extra_failure_content(file_colon_line)
292
- @snippet_extractor ||= SnippetExtractor.new
293
- "<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
294
- end
295
-
296
- def before_multiline_arg(multiline_arg)
297
- return if @hide_this_step || @skip_step
298
- if AST_DATA_TABLE === multiline_arg
299
- builder << '<table>'
300
- end
301
- end
302
-
303
- def after_multiline_arg(multiline_arg)
304
- return if @hide_this_step || @skip_step
305
- if AST_DATA_TABLE === multiline_arg
306
- builder << '</table>'
307
- end
308
- end
309
-
310
- def doc_string(string)
311
- return if @hide_this_step
312
- builder.pre(:class => 'val') do |pre|
313
- builder << h(string).gsub("\n", '&#x000A;')
314
- end
315
- end
316
-
317
- def before_table_row(table_row)
318
- @row_id = table_row.dom_id
319
- @col_index = 0
320
- return if @hide_this_step
321
- builder << "<tr class='step' id='#{@row_id}'>"
322
- end
323
-
324
- def after_table_row(table_row)
325
- return if @hide_this_step
326
- print_table_row_messages
327
- builder << '</tr>'
328
- if table_row.exception
329
- builder.tr do
330
- builder.td(:colspan => @col_index.to_s, :class => 'failed') do
331
- builder.pre do |pre|
332
- pre << h(format_exception(table_row.exception))
333
- end
334
- end
335
- end
336
- if table_row.exception.is_a? ::Cucumber::Pending
337
- set_scenario_color_pending
338
- else
339
- set_scenario_color_failed
340
- end
341
- end
342
- if @outline_row
343
- @outline_row += 1
344
- end
345
- @step_number += 1
346
- move_progress
347
- end
348
-
349
- def table_cell_value(value, status)
350
- return if @hide_this_step
351
-
352
- @cell_type = @outline_row == 0 ? :th : :td
353
- attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
354
- attributes[:class] += " #{status}" if status
355
- build_cell(@cell_type, value, attributes)
356
- scenario_color(status) if @inside_outline
357
- @col_index += 1
358
- end
359
-
360
- def puts(message)
361
- @delayed_messages << message
362
- end
363
-
364
- def print_messages
365
- return if @delayed_messages.empty?
366
-
367
- @delayed_messages.each do |ann|
368
- builder.li(:class => 'step message') do
369
- builder << ann
370
- end
371
- end
372
- empty_messages
373
- end
374
-
375
- def print_table_row_messages
376
- return if @delayed_messages.empty?
377
-
378
- builder.td(:class => 'message') do
379
- builder << @delayed_messages.join(', ')
380
- end
381
- empty_messages
382
- end
383
-
384
- def empty_messages
385
- @delayed_messages = []
386
- end
387
-
388
- def after_test_case(_test_case, result)
389
- if result.failed? && !@scenario_red
390
- set_scenario_color_failed
391
- end
392
- end
393
-
394
- protected
395
-
396
- def next_id(type)
397
- @indices ||= Hash.new { 0 }
398
- @indices[type] += 1
399
- "#{type}_#{@indices[type]}"
400
- end
401
-
402
- def build_exception_detail(exception)
403
- backtrace = Array.new
404
-
405
- builder.div(:class => 'message') do
406
- message = exception.message
407
-
408
- if defined?(RAILS_ROOT) && message.include?('Exception caught')
409
- matches = message.match(/Showing <i>(.+)<\/i>(?:.+) #(\d+)/)
410
- backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"] if matches
411
- matches = message.match(/<code>([^(\/)]+)<\//m)
412
- message = matches ? matches[1] : ''
413
- end
414
-
415
- unless exception.instance_of?(RuntimeError)
416
- message = "#{message} (#{exception.class})"
417
- end
418
-
419
- builder.pre do
420
- builder.text!(message)
421
- end
422
- end
423
-
424
- builder.div(:class => 'backtrace') do
425
- builder.pre do
426
- backtrace = exception.backtrace
427
- backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
428
- builder << backtrace_line(backtrace.join("\n"))
429
- end
430
- end
431
-
432
- extra = extra_failure_content(backtrace)
433
- builder << extra unless extra == ''
434
- end
435
-
436
- def scenario_color(status)
437
- if status.nil? || status == :undefined || status == :pending
438
- set_scenario_color_pending
439
- end
440
- if status == :failed
441
- set_scenario_color_failed
442
- end
443
- end
444
-
445
- def set_scenario_color_failed
446
- builder.script do
447
- builder.text!("makeRed('cucumber-header');") unless @header_red
448
- @header_red = true
449
- scenario_or_background = @in_background ? 'background' : 'scenario'
450
- builder.text!("makeRed('#{scenario_or_background}_#{@scenario_number}');") unless @scenario_red
451
- @scenario_red = true
452
- if @options[:expand] && @inside_outline
453
- builder.text!("makeRed('#{scenario_or_background}_#{@scenario_number}_#{@outline_row}');")
454
- end
455
- end
456
- end
457
-
458
- def set_scenario_color_pending
459
- builder.script do
460
- builder.text!("makeYellow('cucumber-header');") unless @header_red
461
- scenario_or_background = @in_background ? 'background' : 'scenario'
462
- builder.text!("makeYellow('#{scenario_or_background}_#{@scenario_number}');") unless @scenario_red
463
- end
464
- end
465
-
466
- def build_step(keyword, step_match, _status)
467
- step_name = step_match.format_args(lambda { |param| %{<span class="param">#{param}</span>} })
468
- builder.div(:class => 'step_name') do |div|
469
- builder.span(keyword, :class => 'keyword')
470
- builder.span(:class => 'step val') do |name|
471
- name << h(step_name).gsub(/&lt;span class=&quot;(.*?)&quot;&gt;/, '<span class="\1">').gsub(/&lt;\/span&gt;/, '</span>')
472
- end
473
- end
474
-
475
- step_file = step_match.file_colon_line
476
- step_file.gsub(/^([^:]*\.rb):(\d*)/) do
477
- if ENV['TM_PROJECT_DIRECTORY']
478
- step_file = "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
479
- end
480
- end
481
-
482
- builder.div(:class => 'step_file') do |div|
483
- builder.span do
484
- builder << step_file
485
- end
486
- end
487
- end
488
-
489
- def build_cell(cell_type, value, attributes)
490
- builder.__send__(cell_type, attributes) do
491
- builder.div do
492
- builder.span(value, :class => 'step param')
493
- end
494
- end
495
- end
496
-
497
- def move_progress
498
- builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
499
- end
500
-
501
- def percent_done
502
- result = 100.0
503
- if @step_count != 0
504
- result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
505
- end
506
- result
507
- end
508
-
509
- def format_exception(exception)
510
- ([exception.message.to_s] + exception.backtrace).join("\n")
511
- end
512
-
513
- def backtrace_line(line)
514
- if ENV['TM_PROJECT_DIRECTORY']
515
- line.gsub(/^([^:]*\.(?:rb|feature|haml)):(\d*).*$/) do
516
- "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
517
- end
518
- else
519
- line
520
- end
521
- end
522
-
523
- def print_stats(features)
524
- builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
525
- builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
526
- end
527
-
528
- def print_stat_string(_features)
529
- string = String.new
530
- string << dump_count(@runtime.scenarios.length, 'scenario')
531
- scenario_count = print_status_counts { |status| @runtime.scenarios(status) }
532
- string << scenario_count if scenario_count
533
- string << '<br />'
534
- string << dump_count(@runtime.steps.length, 'step')
535
- step_count = print_status_counts { |status| @runtime.steps(status) }
536
- string << step_count if step_count
537
- end
538
-
539
- def print_status_counts
540
- counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
541
- elements = yield status
542
- elements.any? ? "#{elements.length} #{status}" : nil
543
- end.compact
544
- return " (#{counts.join(', ')})" if counts.any?
545
- end
546
-
547
- def dump_count(count, what, state = nil)
548
- [count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(' ')
549
- end
550
-
551
- def outline_step?(_step)
552
- not @step.step.respond_to?(:actual_keyword)
553
- end
554
-
555
- class SnippetExtractor #:nodoc:
556
- class NullConverter; def convert(code, _pre); code; end; end #:nodoc:
557
-
558
- begin
559
- require 'syntax/convertors/html'
560
- @@converter = Syntax::Convertors::HTML.for_syntax 'ruby'
561
- rescue LoadError
562
- @@converter = NullConverter.new
563
- end
564
-
565
- def snippet(error)
566
- raw_code, line = snippet_for(error[0])
567
- highlighted = @@converter.convert(raw_code, false)
568
- highlighted += "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
569
- post_process(highlighted, line)
570
- end
571
-
572
- def snippet_for(error_line)
573
- if error_line =~ /(.*):(\d+)/
574
- file = $1
575
- line = $2.to_i
576
- [lines_around(file, line), line]
577
- else
578
- ["# Couldn't get snippet for #{error_line}", 1]
579
- end
580
- end
581
-
582
- def lines_around(file, line)
583
- if File.file?(file)
584
- begin
585
- lines = File.open(file).read.split("\n")
586
- rescue ArgumentError
587
- return "# Couldn't get snippet for #{file}"
588
- end
589
- min = [0, line - 3].max
590
- max = [line + 1, lines.length - 1].min
591
- selected_lines = []
592
- selected_lines.join("\n")
593
- lines[min..max].join("\n")
594
- else
595
- "# Couldn't get snippet for #{file}"
596
- end
597
- end
598
-
599
- def post_process(highlighted, offending_line)
600
- new_lines = []
601
- highlighted.split("\n").each_with_index do |line, i|
602
- new_line = "<span class=\"linenum\">#{offending_line + i - 2}</span>#{line}"
603
- new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
604
- new_lines << new_line
605
- end
606
- new_lines.join("\n")
607
- end
608
- end
609
- end
610
- end
611
- end