whirlwind 0.1.0

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