whirlwind 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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