Class: Cucumber::Formatter::Html

Inherits:
Object
  • Object
show all
Includes:
Duration, Io, ERB::Util
Defined in:
lib/cucumber/formatter/html.rb

Defined Under Namespace

Classes: SnippetExtractor

Constant Summary collapse

AST_CLASSES =

TODO: remove coupling to types

{
  Cucumber::Core::Ast::Scenario        => 'scenario',
  Cucumber::Core::Ast::ScenarioOutline => 'scenario outline'
}
AST_DATA_TABLE =
LegacyApi::Ast::MultilineArg::DataTable

Instance Method Summary collapse

Methods included from Io

ensure_dir, ensure_file, ensure_io

Methods included from Duration

#format_duration

Constructor Details

#initialize(runtime, path_or_io, options) ⇒ Html

Returns a new instance of Html



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/cucumber/formatter/html.rb', line 27

def initialize(runtime, path_or_io, options)
  @io = ensure_io(path_or_io)
  @runtime = runtime
  @options = options
  @buffer = {}
  @builder = HtmlBuilder.new(target: @io, indent: 0)
  @feature_number = 0
  @scenario_number = 0
  @step_number = 0
  @header_red = nil
  @delayed_messages = []
  @inside_outline        = false
  @previous_step_keyword = nil
end

Instance Method Details

#after_background(_background) ⇒ Object



141
142
143
144
# File 'lib/cucumber/formatter/html.rb', line 141

def after_background(_background)
  @in_background = nil
  builder << '</div>'
end

#after_comment(_comment) ⇒ Object



99
100
101
# File 'lib/cucumber/formatter/html.rb', line 99

def after_comment(_comment)
  builder << '</pre>'
end

#after_examples(_examples) ⇒ Object



206
207
208
# File 'lib/cucumber/formatter/html.rb', line 206

def after_examples(_examples)
  builder << '</div>'
end

#after_feature(_feature) ⇒ Object



91
92
93
# File 'lib/cucumber/formatter/html.rb', line 91

def after_feature(_feature)
  builder << '</div>'
end

#after_feature_element(_feature_element) ⇒ Object



163
164
165
166
167
168
169
170
# File 'lib/cucumber/formatter/html.rb', line 163

def after_feature_element(_feature_element)
  unless @in_scenario_outline
    print_messages
    builder << '</ol>'
  end
  builder << '</div>'
  @in_scenario_outline = nil
end

#after_features(features) ⇒ Object



79
80
81
82
83
84
# File 'lib/cucumber/formatter/html.rb', line 79

def after_features(features)
  print_stats(features)
  builder << '</div>'
  builder << '</body>'
  builder << '</html>'
end

#after_multiline_arg(multiline_arg) ⇒ Object



303
304
305
306
307
308
# File 'lib/cucumber/formatter/html.rb', line 303

def after_multiline_arg(multiline_arg)
  return if @hide_this_step || @skip_step
  if AST_DATA_TABLE === multiline_arg
    builder << '</table>'
  end
end

#after_outline_table(_outline_table) ⇒ Object



196
197
198
199
200
# File 'lib/cucumber/formatter/html.rb', line 196

def after_outline_table(_outline_table)
  builder << '</table>'
  @outline_row = nil
  @inside_outline = false
end

#after_step(_step) ⇒ Object



234
235
236
# File 'lib/cucumber/formatter/html.rb', line 234

def after_step(_step)
  move_progress
end

#after_step_result(keyword, step_match, _multiline_arg, status, _exception, _source_indent, _background, _file_colon_line) ⇒ Object



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/cucumber/formatter/html.rb', line 258

def after_step_result(keyword, step_match, _multiline_arg, status, _exception, _source_indent, _background, _file_colon_line)
  return if @hide_this_step
  # print snippet for undefined steps
  unless outline_step?(@step)
    keyword = @step.actual_keyword(@previous_step_keyword)
    @previous_step_keyword = keyword
  end
  if status == :undefined
    builder.pre do |pre|
      # TODO: snippet text should be an event sent to the formatter so we don't
      # have this couping to the runtime.
      pre << @runtime.snippet_text(keyword, step_match.instance_variable_get('@name') || '', @step.multiline_arg)
    end
  end
  builder << '</li>'
  print_messages
end

#after_steps(_steps) ⇒ Object



222
223
224
225
# File 'lib/cucumber/formatter/html.rb', line 222

def after_steps(_steps)
  print_messages
  builder << '</ol>' if @in_background || @in_scenario_outline
end

#after_table_row(table_row) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/cucumber/formatter/html.rb', line 324

def after_table_row(table_row)
  return if @hide_this_step
  print_table_row_messages
  builder << '</tr>'
  if table_row.exception
    builder.tr do
      builder.td(:colspan => @col_index.to_s, :class => 'failed') do
        builder.pre do |pre|
          pre << h(format_exception(table_row.exception))
        end
      end
    end
    if table_row.exception.is_a? ::Cucumber::Pending
      set_scenario_color_pending
    else
      set_scenario_color_failed
    end
  end
  if @outline_row
    @outline_row += 1
  end
  @step_number += 1
  move_progress
end

#after_tags(_tags) ⇒ Object



108
109
110
# File 'lib/cucumber/formatter/html.rb', line 108

def after_tags(_tags)
  @tag_spacer = nil
end

#after_test_case(_test_case, result) ⇒ Object



388
389
390
391
392
# File 'lib/cucumber/formatter/html.rb', line 388

def after_test_case(_test_case, result)
  if result.failed? && !@scenario_red
    set_scenario_color_failed
  end
end

#background_name(keyword, name, _file_colon_line, _source_indent) ⇒ Object



146
147
148
149
150
151
152
153
# File 'lib/cucumber/formatter/html.rb', line 146

def background_name(keyword, name, _file_colon_line, _source_indent)
  @listing_background = true
  builder.h3(:id => "background_#{@scenario_number}") do |h3|
    builder.span(keyword, :class => 'keyword')
    builder.text!(' ')
    builder.span(name, :class => 'val')
  end
end

#before_background(_background) ⇒ Object



136
137
138
139
# File 'lib/cucumber/formatter/html.rb', line 136

def before_background(_background)
  @in_background = true
  builder << '<div class="background">'
end

#before_comment(_comment) ⇒ Object



95
96
97
# File 'lib/cucumber/formatter/html.rb', line 95

def before_comment(_comment)
  builder << '<pre class="comment">'
end

#before_examples(_examples) ⇒ Object



202
203
204
# File 'lib/cucumber/formatter/html.rb', line 202

def before_examples(_examples)
  builder << '<div class="examples">'
end

#before_feature(_feature) ⇒ Object



86
87
88
89
# File 'lib/cucumber/formatter/html.rb', line 86

def before_feature(_feature)
  @exceptions = []
  builder << '<div class="feature">'
end

#before_feature_element(feature_element) ⇒ Object



155
156
157
158
159
160
161
# File 'lib/cucumber/formatter/html.rb', line 155

def before_feature_element(feature_element)
  @scenario_number += 1
  @scenario_red = false
  css_class = AST_CLASSES[feature_element.class]
  builder << "<div class='#{css_class}'>"
  @in_scenario_outline = feature_element.class == Cucumber::Core::Ast::ScenarioOutline
end

#before_features(features) ⇒ Object



72
73
74
75
76
77
# File 'lib/cucumber/formatter/html.rb', line 72

def before_features(features)
  @step_count = features && features.step_count || 0 # TODO: Make this work with core!

  builder.build_document!
  builder.format_features! features
end

#before_multiline_arg(multiline_arg) ⇒ Object



296
297
298
299
300
301
# File 'lib/cucumber/formatter/html.rb', line 296

def before_multiline_arg(multiline_arg)
  return if @hide_this_step || @skip_step
  if AST_DATA_TABLE === multiline_arg
    builder << '<table>'
  end
end

#before_outline_table(_outline_table) ⇒ Object



190
191
192
193
194
# File 'lib/cucumber/formatter/html.rb', line 190

def before_outline_table(_outline_table)
  @inside_outline = true
  @outline_row = 0
  builder << '<table>'
end

#before_step(step) ⇒ Object



227
228
229
230
231
232
# File 'lib/cucumber/formatter/html.rb', line 227

def before_step(step)
  print_messages
  @step_id = step.dom_id
  @step_number += 1
  @step = step
end

#before_step_result(_keyword, step_match, _multiline_arg, status, exception, _source_indent, background, _file_colon_line) ⇒ Object



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/cucumber/formatter/html.rb', line 238

def before_step_result(_keyword, step_match, _multiline_arg, status, exception, _source_indent, background, _file_colon_line)
  @step_match = step_match
  @hide_this_step = false
  if exception
    if @exceptions.include?(exception)
      @hide_this_step = true
      return
    end
    @exceptions << exception
  end
  if status != :failed && @in_background ^ background
    @hide_this_step = true
    return
  end
  @status = status
  return if @hide_this_step
  scenario_color(status)
  builder << "<li id='#{@step_id}' class='step #{status}'>"
end

#before_steps(_steps) ⇒ Object



218
219
220
# File 'lib/cucumber/formatter/html.rb', line 218

def before_steps(_steps)
  builder << '<ol>'
end

#before_table_row(table_row) ⇒ Object



317
318
319
320
321
322
# File 'lib/cucumber/formatter/html.rb', line 317

def before_table_row(table_row)
  @row_id = table_row.dom_id
  @col_index = 0
  return if @hide_this_step
  builder << "<tr class='step' id='#{@row_id}'>"
end

#before_test_case(_test_case) ⇒ Object



132
133
134
# File 'lib/cucumber/formatter/html.rb', line 132

def before_test_case(_test_case)
  @previous_step_keyword = nil
end

#comment_line(comment_line) ⇒ Object



103
104
105
106
# File 'lib/cucumber/formatter/html.rb', line 103

def comment_line(comment_line)
  builder.text!(comment_line)
  builder.br
end

#doc_string(string) ⇒ Object



310
311
312
313
314
315
# File 'lib/cucumber/formatter/html.rb', line 310

def doc_string(string)
  return if @hide_this_step
  builder.pre(:class => 'val') do |pre|
    builder << h(string).gsub("\n", '&#x000A;')
  end
end

#embed(src, mime_type, label) ⇒ Object



42
43
44
45
46
47
48
49
# File 'lib/cucumber/formatter/html.rb', line 42

def embed(src, mime_type, label)
  if image?(mime_type)
    src = src_is_file_or_data?(src) ? src : "data:#{standardize_mime_type(mime_type)},#{src}"
    builder.embed(type: :image, src: path(src), label: label, id: next_id(:img))
  else
    builder.embed(type: :text, src: src, label: label, id: next_id(:text))
  end
end

#empty_messagesObject



384
385
386
# File 'lib/cucumber/formatter/html.rb', line 384

def empty_messages
  @delayed_messages = []
end

#examples_name(keyword, name) ⇒ Object



210
211
212
213
214
215
216
# File 'lib/cucumber/formatter/html.rb', line 210

def examples_name(keyword, name)
  builder.h4 do
    builder.span(keyword, :class => 'keyword')
    builder.text!(' ')
    builder.span(name, :class => 'val')
  end
end

#exception(exception, _status) ⇒ Object



285
286
287
288
289
# File 'lib/cucumber/formatter/html.rb', line 285

def exception(exception, _status)
  return if @hide_this_step
  print_messages
  build_exception_detail(exception)
end

#extra_failure_content(file_colon_line) ⇒ Object



291
292
293
294
# File 'lib/cucumber/formatter/html.rb', line 291

def extra_failure_content(file_colon_line)
  @snippet_extractor ||= SnippetExtractor.new
  "<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
end

#feature_name(keyword, name) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cucumber/formatter/html.rb', line 118

def feature_name(keyword, name)
  lines = name.split(/\r?\n/)
  return if lines.empty?
  builder.h2 do |h2|
    builder.span(keyword + ': ' + lines[0], :class => 'val')
  end
  builder.p(:class => 'narrative') do
    lines[1..-1].each do |line|
      builder.text!(line.strip)
      builder.br
    end
  end
end

#image?(mime_type) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/cucumber/formatter/html.rb', line 68

def image?(mime_type)
  mime_type =~ /^image\/(png|gif|jpg|jpeg)/
end

#path(src) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/cucumber/formatter/html.rb', line 51

def path(src)
  if @io.respond_to?(:path) && File.file?(src)
    out_dir = Pathname.new(File.dirname(File.absolute_path(@io.path)))
    src = Pathname.new(File.absolute_path(src)).relative_path_from(out_dir)
  end

  src
end


364
365
366
367
368
369
370
371
372
373
# File 'lib/cucumber/formatter/html.rb', line 364

def print_messages
  return if @delayed_messages.empty?

  @delayed_messages.each do |ann|
    builder.li(:class => 'step message') do
      builder << ann
    end
  end
  empty_messages
end


375
376
377
378
379
380
381
382
# File 'lib/cucumber/formatter/html.rb', line 375

def print_table_row_messages
  return if @delayed_messages.empty?

  builder.td(:class => 'message') do
    builder << @delayed_messages.join(', ')
  end
  empty_messages
end

#puts(message) ⇒ Object



360
361
362
# File 'lib/cucumber/formatter/html.rb', line 360

def puts(message)
  @delayed_messages << message
end

#scenario_name(keyword, name, file_colon_line, _source_indent) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/cucumber/formatter/html.rb', line 172

def scenario_name(keyword, name, file_colon_line, _source_indent)
  builder.span(:class => 'scenario_file') do
    builder << file_colon_line
  end
  @listing_background = false
  scenario_id = "scenario_#{@scenario_number}"
  if @inside_outline
    @outline_row += 1
    scenario_id += "_#{@outline_row}"
    @scenario_red = false
  end
  builder.h3(:id => scenario_id) do
    builder.span(keyword + ':', :class => 'keyword')
    builder.text!(' ')
    builder.span(name, :class => 'val')
  end
end

#src_is_file_or_data?(src) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
# File 'lib/cucumber/formatter/html.rb', line 64

def src_is_file_or_data?(src)
  File.file?(src) || src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
end

#standardize_mime_type(mime_type) ⇒ Object



60
61
62
# File 'lib/cucumber/formatter/html.rb', line 60

def standardize_mime_type(mime_type)
  mime_type =~ /;base[0-9]+$/ ? mime_type : mime_type + ';base64'
end

#step_name(keyword, step_match, status, _source_indent, background, _file_colon_line) ⇒ Object



276
277
278
279
280
281
282
283
# File 'lib/cucumber/formatter/html.rb', line 276

def step_name(keyword, step_match, status, _source_indent, background, _file_colon_line)
  background_in_scenario = background && !@listing_background
  @skip_step = background_in_scenario

  unless @skip_step
    build_step(keyword, step_match, status)
  end
end

#table_cell_value(value, status) ⇒ Object



349
350
351
352
353
354
355
356
357
358
# File 'lib/cucumber/formatter/html.rb', line 349

def table_cell_value(value, status)
  return if @hide_this_step

  @cell_type = @outline_row == 0 ? :th : :td
  attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
  attributes[:class] += " #{status}" if status
  build_cell(@cell_type, value, attributes)
  scenario_color(status) if @inside_outline
  @col_index += 1
end

#tag_name(tag_name) ⇒ Object



112
113
114
115
116
# File 'lib/cucumber/formatter/html.rb', line 112

def tag_name(tag_name)
  builder.text!(@tag_spacer) if @tag_spacer
  @tag_spacer = ' '
  builder.span(tag_name, :class => 'tag')
end