pretty_face 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,87 +0,0 @@
1
- require 'cucumber/formatter/io'
2
- require 'gherkin/formatter/argument'
3
-
4
- module Cucumber
5
- module Formatter
6
- # Adapts Cucumber formatter events to Gherkin formatter events
7
- # This class will disappear when Cucumber is based on Gherkin's model.
8
- class GherkinFormatterAdapter
9
- def initialize(gherkin_formatter, print_emtpy_match)
10
- @gf = gherkin_formatter
11
- @print_emtpy_match = print_emtpy_match
12
- end
13
-
14
- def before_feature(feature)
15
- @gf.uri(feature.file)
16
- @gf.feature(feature.gherkin_statement)
17
- end
18
-
19
- def before_background(background)
20
- @outline = false
21
- @gf.background(background.gherkin_statement)
22
- end
23
-
24
- def before_feature_element(feature_element)
25
- case(feature_element)
26
- when Ast::Scenario
27
- @outline = false
28
- @gf.scenario(feature_element.gherkin_statement)
29
- when Ast::ScenarioOutline
30
- @outline = true
31
- @gf.scenario_outline(feature_element.gherkin_statement)
32
- else
33
- raise "Bad type: #{feature_element.class}"
34
- end
35
- end
36
-
37
- def before_step(step)
38
- @gf.step(step.gherkin_statement)
39
- if @print_emtpy_match
40
- if(@outline)
41
- match = Gherkin::Formatter::Model::Match.new(step.gherkin_statement.outline_args, nil)
42
- else
43
- match = Gherkin::Formatter::Model::Match.new([], nil)
44
- end
45
- @gf.match(match)
46
- end
47
- end
48
-
49
- def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
50
- arguments = step_match.step_arguments.map{|a| Gherkin::Formatter::Argument.new(a.offset, a.val)}
51
- location = step_match.file_colon_line
52
- match = Gherkin::Formatter::Model::Match.new(arguments, location)
53
- if @print_emtpy_match
54
- # Trick the formatter to believe that's what was printed previously so we get arg highlights on #result
55
- @gf.instance_variable_set('@match', match)
56
- else
57
- @gf.match(match)
58
- end
59
-
60
- error_message = exception ? "#{exception.message} (#{exception.class})\n#{exception.backtrace.join("\n")}" : nil
61
- unless @outline
62
- @gf.result(Gherkin::Formatter::Model::Result.new(status, nil, error_message))
63
- end
64
- end
65
-
66
- def before_examples(examples)
67
- @gf.examples(examples.gherkin_statement)
68
- end
69
-
70
- def after_feature(feature)
71
- @gf.eof
72
- end
73
-
74
- def after_features(features)
75
- @gf.done
76
- end
77
-
78
- def embed(file, mime_type, label)
79
- data = File.read(file)
80
- if defined?(JRUBY_VERSION)
81
- data = data.to_java_bytes
82
- end
83
- @gf.embedding(mime_type, data)
84
- end
85
- end
86
- end
87
- end
@@ -1,655 +0,0 @@
1
- require 'erb'
2
- require 'cucumber/formatter/ordered_xml_markup'
3
- require 'cucumber/formatter/duration'
4
- require 'cucumber/formatter/io'
5
-
6
- module Cucumber
7
- module Formatter
8
- class Html
9
- include ERB::Util # for the #h method
10
- include Duration
11
- include Io
12
-
13
- def initialize(step_mother, path_or_io, options)
14
- @io = ensure_io(path_or_io, "html")
15
- @step_mother = step_mother
16
- @options = options
17
- @buffer = {}
18
- @builder = create_builder(@io)
19
- @feature_number = 0
20
- @scenario_number = 0
21
- @step_number = 0
22
- @header_red = nil
23
- @delayed_messages = []
24
- @img_id = 0
25
- end
26
-
27
- def embed(src, mime_type, label)
28
- case(mime_type)
29
- when /^image\/(png|gif|jpg|jpeg)/
30
- embed_image(src, label)
31
- end
32
- end
33
-
34
- def embed_image(src, label)
35
- id = "img_#{@img_id}"
36
- @img_id += 1
37
- @builder.span(:class => 'embed') do |pre|
38
- pre << %{<a href="" onclick="img=document.getElementById('#{id}'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false">#{label}</a><br>&nbsp;
39
- <img id="#{id}" style="display: none" src="#{src}"/>}
40
- end
41
- end
42
-
43
-
44
- def before_features(features)
45
- @step_count = get_step_count(features)
46
-
47
- # <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
48
- @builder.declare!(
49
- :DOCTYPE,
50
- :html,
51
- :PUBLIC,
52
- '-//W3C//DTD XHTML 1.0 Strict//EN',
53
- 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
54
- )
55
-
56
- @builder << '<html xmlns ="http://www.w3.org/1999/xhtml">'
57
- @builder.head do
58
- @builder.meta(:content => 'text/html;charset=utf-8')
59
- @builder.title 'Cucumber'
60
- inline_css
61
- inline_js
62
- end
63
- @builder << '<body>'
64
- @builder << "<!-- Step count #{@step_count}-->"
65
- @builder << '<div class="cucumber">'
66
- @builder.div(:id => 'cucumber-header') do
67
- @builder.div(:id => 'label') do
68
- @builder.h1('Cucumber Features')
69
- end
70
- @builder.div(:id => 'summary') do
71
- @builder.p('',:id => 'totals')
72
- @builder.p('',:id => 'duration')
73
- @builder.div(:id => 'expand-collapse') do
74
- @builder.p('Expand All', :id => 'expander')
75
- @builder.p('Collapse All', :id => 'collapser')
76
- end
77
- end
78
- end
79
- end
80
-
81
- def after_features(features)
82
- print_stats(features)
83
- @builder << '</div>'
84
- @builder << '</body>'
85
- @builder << '</html>'
86
- end
87
-
88
- def before_feature(feature)
89
- @exceptions = []
90
- @builder << '<div class="feature">'
91
- end
92
-
93
- def after_feature(feature)
94
- @builder << '</div>'
95
- end
96
-
97
- def before_comment(comment)
98
- @builder << '<pre class="comment">'
99
- end
100
-
101
- def after_comment(comment)
102
- @builder << '</pre>'
103
- end
104
-
105
- def comment_line(comment_line)
106
- @builder.text!(comment_line)
107
- @builder.br
108
- end
109
-
110
- def after_tags(tags)
111
- @tag_spacer = nil
112
- end
113
-
114
- def tag_name(tag_name)
115
- @builder.text!(@tag_spacer) if @tag_spacer
116
- @tag_spacer = ' '
117
- @builder.span(tag_name, :class => 'tag')
118
- end
119
-
120
- def feature_name(keyword, name)
121
- lines = name.split(/\r?\n/)
122
- return if lines.empty?
123
- @builder.h2 do |h2|
124
- @builder.span(keyword + ': ' + lines[0], :class => 'val')
125
- end
126
- @builder.p(:class => 'narrative') do
127
- lines[1..-1].each do |line|
128
- @builder.text!(line.strip)
129
- @builder.br
130
- end
131
- end
132
- end
133
-
134
- def before_background(background)
135
- @in_background = true
136
- @builder << '<div class="background">'
137
- end
138
-
139
- def after_background(background)
140
- @in_background = nil
141
- @builder << '</div>'
142
- end
143
-
144
- def background_name(keyword, name, file_colon_line, source_indent)
145
- @listing_background = true
146
- @builder.h3(:id => "background_#{@scenario_number}") do |h3|
147
- @builder.span(keyword, :class => 'keyword')
148
- @builder.text!(' ')
149
- @builder.span(name, :class => 'val')
150
- end
151
- end
152
-
153
- def before_feature_element(feature_element)
154
- @scenario_number+=1
155
- @scenario_red = false
156
- css_class = {
157
- Ast::Scenario => 'scenario',
158
- Ast::ScenarioOutline => 'scenario outline'
159
- }[feature_element.class]
160
- @builder << "<div class='#{css_class}'>"
161
- end
162
-
163
- def after_feature_element(feature_element)
164
- @builder << '</div>'
165
- @open_step_list = true
166
- end
167
-
168
- def scenario_name(keyword, name, file_colon_line, source_indent)
169
- @builder.span(:class => 'scenario_file') do
170
- @builder << file_colon_line
171
- end
172
- @listing_background = false
173
- @builder.h3(:id => "scenario_#{@scenario_number}") do
174
- @builder.span(keyword + ':', :class => 'keyword')
175
- @builder.text!(' ')
176
- @builder.span(name, :class => 'val')
177
- end
178
- end
179
-
180
- def before_outline_table(outline_table)
181
- @outline_row = 0
182
- @builder << '<table>'
183
- end
184
-
185
- def after_outline_table(outline_table)
186
- @builder << '</table>'
187
- @outline_row = nil
188
- end
189
-
190
- def before_examples(examples)
191
- @builder << '<div class="examples">'
192
- end
193
-
194
- def after_examples(examples)
195
- @builder << '</div>'
196
- end
197
-
198
- def examples_name(keyword, name)
199
- @builder.h4 do
200
- @builder.span(keyword, :class => 'keyword')
201
- @builder.text!(' ')
202
- @builder.span(name, :class => 'val')
203
- end
204
- end
205
-
206
- def before_steps(steps)
207
- @builder << '<ol>'
208
- end
209
-
210
- def after_steps(steps)
211
- @builder << '</ol>'
212
- end
213
-
214
- def before_step(step)
215
- @step_id = step.dom_id
216
- @step_number += 1
217
- @step = step
218
- end
219
-
220
- def after_step(step)
221
- move_progress
222
- end
223
-
224
- def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
225
- @step_match = step_match
226
- @hide_this_step = false
227
- if exception
228
- if @exceptions.include?(exception)
229
- @hide_this_step = true
230
- return
231
- end
232
- @exceptions << exception
233
- end
234
- if status != :failed && @in_background ^ background
235
- @hide_this_step = true
236
- return
237
- end
238
- @status = status
239
- return if @hide_this_step
240
- set_scenario_color(status)
241
- @builder << "<li id='#{@step_id}' class='step #{status}'>"
242
- end
243
-
244
- def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
245
- return if @hide_this_step
246
- # print snippet for undefined steps
247
- if status == :undefined
248
- step_multiline_class = @step.multiline_arg ? @step.multiline_arg.class : nil
249
- @builder.pre do |pre|
250
- pre << @step_mother.snippet_text(@step.actual_keyword,step_match.instance_variable_get("@name") || '',step_multiline_class)
251
- end
252
- end
253
- @builder << '</li>'
254
- print_messages
255
- end
256
-
257
- def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
258
- @step_matches ||= []
259
- background_in_scenario = background && !@listing_background
260
- @skip_step = @step_matches.index(step_match) || background_in_scenario
261
- @step_matches << step_match
262
-
263
- unless @skip_step
264
- build_step(keyword, step_match, status)
265
- end
266
- end
267
-
268
- def exception(exception, status)
269
- build_exception_detail(exception)
270
- end
271
-
272
- def extra_failure_content(file_colon_line)
273
- @snippet_extractor ||= SnippetExtractor.new
274
- "<pre class=\"ruby\"><code>#{@snippet_extractor.snippet(file_colon_line)}</code></pre>"
275
- end
276
-
277
- def before_multiline_arg(multiline_arg)
278
- return if @hide_this_step || @skip_step
279
- if Ast::Table === multiline_arg
280
- @builder << '<table>'
281
- end
282
- end
283
-
284
- def after_multiline_arg(multiline_arg)
285
- return if @hide_this_step || @skip_step
286
- if Ast::Table === multiline_arg
287
- @builder << '</table>'
288
- end
289
- end
290
-
291
- def doc_string(string)
292
- return if @hide_this_step
293
- @builder.pre(:class => 'val') do |pre|
294
- @builder << h(string).gsub("\n", '&#x000A;')
295
- end
296
- end
297
-
298
-
299
- def before_table_row(table_row)
300
- @row_id = table_row.dom_id
301
- @col_index = 0
302
- return if @hide_this_step
303
- @builder << "<tr class='step' id='#{@row_id}'>"
304
- end
305
-
306
- def after_table_row(table_row)
307
- return if @hide_this_step
308
- print_table_row_messages
309
- @builder << '</tr>'
310
- if table_row.exception
311
- @builder.tr do
312
- @builder.td(:colspan => @col_index.to_s, :class => 'failed') do
313
- @builder.pre do |pre|
314
- pre << h(format_exception(table_row.exception))
315
- end
316
- end
317
- end
318
- set_scenario_color_failed
319
- end
320
- if @outline_row
321
- @outline_row += 1
322
- end
323
- @step_number += 1
324
- move_progress
325
- end
326
-
327
- def table_cell_value(value, status)
328
- return if @hide_this_step
329
-
330
- @cell_type = @outline_row == 0 ? :th : :td
331
- attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'step'}
332
- attributes[:class] += " #{status}" if status
333
- build_cell(@cell_type, value, attributes)
334
- set_scenario_color(status)
335
- @col_index += 1
336
- end
337
-
338
- def puts(message)
339
- @delayed_messages << message
340
- #@builder.pre(message, :class => 'message')
341
- end
342
-
343
- def print_messages
344
- return if @delayed_messages.empty?
345
-
346
- #@builder.ol do
347
- @delayed_messages.each do |ann|
348
- @builder.li(:class => 'step message') do
349
- @builder << ann
350
- end
351
- end
352
- #end
353
- empty_messages
354
- end
355
-
356
- def print_table_row_messages
357
- return if @delayed_messages.empty?
358
-
359
- @builder.td(:class => 'message') do
360
- @builder << @delayed_messages.join(", ")
361
- end
362
- empty_messages
363
- end
364
-
365
- def empty_messages
366
- @delayed_messages = []
367
- end
368
-
369
- protected
370
-
371
- def build_exception_detail(exception)
372
- backtrace = Array.new
373
- @builder.div(:class => 'message') do
374
- message = exception.message
375
- if defined?(RAILS_ROOT) && message.include?('Exception caught')
376
- matches = message.match(/Showing <i>(.+)<\/i>(?:.+) #(\d+)/)
377
- backtrace += ["#{RAILS_ROOT}/#{matches[1]}:#{matches[2]}"] if matches
378
- matches = message.match(/<code>([^(\/)]+)<\//m)
379
- message = matches ? matches[1] : ""
380
- end
381
-
382
- unless exception.instance_of?(RuntimeError)
383
- message = "#{message} (#{exception.class})"
384
- end
385
-
386
- @builder.pre do
387
- @builder.text!(message)
388
- end
389
- end
390
- @builder.div(:class => 'backtrace') do
391
- @builder.pre do
392
- backtrace = exception.backtrace
393
- backtrace.delete_if { |x| x =~ /\/gems\/(cucumber|rspec)/ }
394
- @builder << backtrace_line(backtrace.join("\n"))
395
- end
396
- end
397
- extra = extra_failure_content(backtrace)
398
- @builder << extra unless extra == ""
399
- end
400
-
401
- def set_scenario_color(status)
402
- if status == :undefined or status == :pending
403
- set_scenario_color_pending
404
- end
405
- if status == :failed
406
- set_scenario_color_failed
407
- end
408
- end
409
-
410
- def set_scenario_color_failed
411
- @builder.script do
412
- @builder.text!("makeRed('cucumber-header');") unless @header_red
413
- @header_red = true
414
- @builder.text!("makeRed('scenario_#{@scenario_number}');") unless @scenario_red
415
- @scenario_red = true
416
- end
417
- end
418
-
419
- def set_scenario_color_pending
420
- @builder.script do
421
- @builder.text!("makeYellow('cucumber-header');") unless @header_red
422
- @builder.text!("makeYellow('scenario_#{@scenario_number}');") unless @scenario_red
423
- end
424
- end
425
-
426
- def get_step_count(features)
427
- count = 0
428
- features = features.instance_variable_get("@features")
429
- features.each do |feature|
430
- #get background steps
431
- if feature.instance_variable_get("@background")
432
- background = feature.instance_variable_get("@background")
433
- background.init
434
- background_steps = background.instance_variable_get("@steps").instance_variable_get("@steps")
435
- count += background_steps.size
436
- end
437
- #get scenarios
438
- feature.instance_variable_get("@feature_elements").each do |scenario|
439
- scenario.init
440
- #get steps
441
- steps = scenario.instance_variable_get("@steps").instance_variable_get("@steps")
442
- count += steps.size
443
-
444
- #get example table
445
- examples = scenario.instance_variable_get("@examples_array")
446
- unless examples.nil?
447
- examples.each do |example|
448
- example_matrix = example.instance_variable_get("@outline_table").instance_variable_get("@cell_matrix")
449
- count += example_matrix.size
450
- end
451
- end
452
-
453
- #get multiline step tables
454
- steps.each do |step|
455
- multi_arg = step.instance_variable_get("@multiline_arg")
456
- next if multi_arg.nil?
457
- matrix = multi_arg.instance_variable_get("@cell_matrix")
458
- count += matrix.size unless matrix.nil?
459
- end
460
- end
461
- end
462
- return count
463
- end
464
-
465
- def build_step(keyword, step_match, status)
466
- step_name = step_match.format_args(lambda{|param| %{<span class="param">#{param}</span>}})
467
- @builder.div(:class => 'step_name') do |div|
468
- @builder.span(keyword, :class => 'keyword')
469
- @builder.span(:class => 'step val') do |name|
470
- name << h(step_name).gsub(/&lt;span class=&quot;(.*?)&quot;&gt;/, '<span class="\1">').gsub(/&lt;\/span&gt;/, '</span>')
471
- end
472
- end
473
-
474
- step_file = step_match.file_colon_line
475
- step_file.gsub(/^([^:]*\.rb):(\d*)/) do
476
- if ENV['TM_PROJECT_DIRECTORY']
477
- step_file = "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
478
- end
479
- end
480
-
481
- @builder.div(:class => 'step_file') do |div|
482
- @builder.span do
483
- @builder << step_file
484
- end
485
- end
486
- end
487
-
488
- def build_cell(cell_type, value, attributes)
489
- @builder.__send__(cell_type, attributes) do
490
- @builder.div do
491
- @builder.span(value,:class => 'step param')
492
- end
493
- end
494
- end
495
-
496
- def inline_css
497
- @builder.style(:type => 'text/css') do
498
- @builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
499
- end
500
- end
501
-
502
- def inline_js
503
- @builder.script(:type => 'text/javascript') do
504
- @builder << inline_jquery
505
- @builder << inline_js_content
506
- end
507
- end
508
-
509
- def inline_jquery
510
- File.read(File.dirname(__FILE__) + '/jquery-min.js')
511
- end
512
-
513
- def inline_js_content
514
- <<-EOF
515
-
516
- SCENARIOS = "h3[id^='scenario_'],h3[id^=background_]";
517
-
518
- $(document).ready(function() {
519
- $(SCENARIOS).css('cursor', 'pointer');
520
- $(SCENARIOS).click(function() {
521
- $(this).siblings().toggle(250);
522
- });
523
-
524
- $("#collapser").css('cursor', 'pointer');
525
- $("#collapser").click(function() {
526
- $(SCENARIOS).siblings().hide();
527
- });
528
-
529
- $("#expander").css('cursor', 'pointer');
530
- $("#expander").click(function() {
531
- $(SCENARIOS).siblings().show();
532
- });
533
- })
534
-
535
- function moveProgressBar(percentDone) {
536
- $("cucumber-header").css('width', percentDone +"%");
537
- }
538
- function makeRed(element_id) {
539
- $('#'+element_id).css('background', '#C40D0D');
540
- $('#'+element_id).css('color', '#FFFFFF');
541
- }
542
- function makeYellow(element_id) {
543
- $('#'+element_id).css('background', '#FAF834');
544
- $('#'+element_id).css('color', '#000000');
545
- }
546
-
547
- EOF
548
- end
549
-
550
- def move_progress
551
- @builder << " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
552
- end
553
-
554
- def percent_done
555
- result = 100.0
556
- if @step_count != 0
557
- result = ((@step_number).to_f / @step_count.to_f * 1000).to_i / 10.0
558
- end
559
- result
560
- end
561
-
562
- def format_exception(exception)
563
- (["#{exception.message}"] + exception.backtrace).join("\n")
564
- end
565
-
566
- def backtrace_line(line)
567
- line.gsub(/\A([^:]*\.(?:rb|feature|haml)):(\d*).*\z/) do
568
- if ENV['TM_PROJECT_DIRECTORY']
569
- "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
570
- else
571
- line
572
- end
573
- end
574
- end
575
-
576
- def print_stats(features)
577
- @builder << "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{format_duration(features.duration)} seconds</strong>\";</script>"
578
- @builder << "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{print_stat_string(features)}\";</script>"
579
- end
580
-
581
- def print_stat_string(features)
582
- string = String.new
583
- string << dump_count(@step_mother.scenarios.length, "scenario")
584
- scenario_count = print_status_counts{|status| @step_mother.scenarios(status)}
585
- string << scenario_count if scenario_count
586
- string << "<br />"
587
- string << dump_count(@step_mother.steps.length, "step")
588
- step_count = print_status_counts{|status| @step_mother.steps(status)}
589
- string << step_count if step_count
590
- end
591
-
592
- def print_status_counts
593
- counts = [:failed, :skipped, :undefined, :pending, :passed].map do |status|
594
- elements = yield status
595
- elements.any? ? "#{elements.length} #{status.to_s}" : nil
596
- end.compact
597
- return " (#{counts.join(', ')})" if counts.any?
598
- end
599
-
600
- def dump_count(count, what, state=nil)
601
- [count, state, "#{what}#{count == 1 ? '' : 's'}"].compact.join(" ")
602
- end
603
-
604
- def create_builder(io)
605
- OrderedXmlMarkup.new(:target => io, :indent => 0)
606
- end
607
-
608
- class SnippetExtractor #:nodoc:
609
- class NullConverter; def convert(code, pre); code; end; end #:nodoc:
610
- begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
611
-
612
- def snippet(error)
613
- raw_code, line = snippet_for(error[0])
614
- highlighted = @@converter.convert(raw_code, false)
615
- highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
616
- post_process(highlighted, line)
617
- end
618
-
619
- def snippet_for(error_line)
620
- if error_line =~ /(.*):(\d+)/
621
- file = $1
622
- line = $2.to_i
623
- [lines_around(file, line), line]
624
- else
625
- ["# Couldn't get snippet for #{error_line}", 1]
626
- end
627
- end
628
-
629
- def lines_around(file, line)
630
- if File.file?(file)
631
- lines = File.open(file).read.split("\n")
632
- min = [0, line-3].max
633
- max = [line+1, lines.length-1].min
634
- selected_lines = []
635
- selected_lines.join("\n")
636
- lines[min..max].join("\n")
637
- else
638
- "# Couldn't get snippet for #{file}"
639
- end
640
- end
641
-
642
- def post_process(highlighted, offending_line)
643
- new_lines = []
644
- highlighted.split("\n").each_with_index do |line, i|
645
- new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
646
- new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
647
- new_lines << new_line
648
- end
649
- new_lines.join("\n")
650
- end
651
-
652
- end
653
- end
654
- end
655
- end