rwebspec 1.4.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.
@@ -0,0 +1,734 @@
1
+ # convenient methods to drive the browser.
2
+ #
3
+ # Instead of
4
+ # browser.click_button("submit")
5
+ # You can just use
6
+ # click_button("submit")
7
+ #
8
+ require File.join(File.dirname(__FILE__), 'itest_plugin')
9
+ require File.join(File.dirname(__FILE__), 'popup')
10
+ require 'timeout'
11
+ require 'uri'
12
+
13
+ module RWebSpec
14
+ module Driver
15
+ include RWebSpec::ITestPlugin
16
+ include RWebSpec::Popup
17
+
18
+ @@default_polling_interval = 1 # second
19
+ @@default_timeout = 30 # seconds
20
+
21
+ # open a browser, and set base_url via hash, but does not acually
22
+ #
23
+ # example:
24
+ # open_browser :base_url => http://localhost:8080
25
+ #
26
+ # There are 3 ways to set base url
27
+ # 1. pass as first argument
28
+ # 2. If running using iTest2, used as confiured
29
+ # 3. Use default value set
30
+ def open_browser(base_url = nil, options = {})
31
+ base_url ||= $ITEST2_PROJECT_BASE_URL
32
+ base_url ||= $BASE_URL
33
+ raise "base_url must be set" if base_url.nil?
34
+
35
+ default_options = {:speed => "fast",
36
+ :visible => true,
37
+ :highlight_colour => 'yellow',
38
+ :close_others => true,
39
+ :start_new => false, # start a new browser always
40
+ :go => true}
41
+
42
+ options = default_options.merge options
43
+ options[:firefox] = true if "Firefox" == $ITEST2_BROWSER || "Firefox" == $BROWSER
44
+ ($ITEST2_HIDE_BROWSER) ? $HIDE_IE = true : $HIDE_IE = false
45
+
46
+ if base_url =~ /^file:/
47
+ uri_base = base_url
48
+ else
49
+ uri = URI.parse(base_url)
50
+ uri_base = "#{uri.scheme}://#{uri.host}:#{uri.port}"
51
+ end
52
+
53
+ if options[:start_new] || $celerity_loaded
54
+ @web_browser = WebBrowser.new(uri_base, nil, options)
55
+ else
56
+ @web_browser = WebBrowser.reuse(uri_base, options) # Reuse existing browser
57
+ end
58
+
59
+ if base_url =~ /^file:/
60
+ goto_url(base_url) # for files, no base url
61
+ else
62
+ (uri.path.length == 0) ? begin_at("/") : begin_at(uri.path) if options[:go]
63
+ end
64
+
65
+ return @web_browser
66
+ end
67
+ alias open_browser_with open_browser
68
+
69
+ # return the underlying RWebSpec::Browser object
70
+ def browser
71
+ @web_browser
72
+ end
73
+
74
+
75
+ # Close the current browser window (started by the script). If no browser started, then close
76
+ # all browser windows.
77
+ #
78
+ def close_browser
79
+ if @web_browser
80
+ # Old iTest2 version
81
+ # @web_browser.close_browser unless $ITEST2_LEAVE_BROWSER_OPEN_AFTER_RUN
82
+ @web_browser.close_browser
83
+ else
84
+ WebBrowser.close_all_browsers
85
+ end
86
+ end
87
+ alias close_ie close_browser
88
+
89
+
90
+ # Close all opening browser windows
91
+ #
92
+ def close_all_browsers
93
+ if is_firefox?
94
+ FireWatir::Firefox.close_all
95
+ else
96
+ Watir::IE.close_all
97
+ end
98
+ end
99
+
100
+ # Verify the next page following an operation.
101
+ #
102
+ # Typical usage:
103
+ # login_page.click_login
104
+ # expect_page HomePage
105
+ def expect_page(page_clazz, argument = nil)
106
+ if argument
107
+ page_clazz.new(@web_browser, argument)
108
+ else
109
+ page_clazz.new(@web_browser)
110
+ end
111
+ end
112
+
113
+ def context
114
+ @web_browser.context
115
+ end
116
+
117
+ # Starting browser with a URL
118
+ #
119
+ # Example:
120
+ # begin_at("http://www.itest2.com")
121
+ def begin_at(url)
122
+ dump_caller_stack
123
+ @web_browser.begin_at(url)
124
+ end
125
+
126
+ # Return the Watir::IE instance
127
+ #
128
+ def ie
129
+ @web_browser.ie
130
+ end
131
+
132
+ # Return the FireWatir::Firefox instance
133
+ #
134
+ def firefox
135
+ @web_browser.firefox
136
+ end
137
+
138
+ def is_firefox?
139
+ @web_browser.is_firefox? if @web_browser
140
+ end
141
+
142
+
143
+ # Go to another page on the testing site.
144
+ #
145
+ # open_browser("http://www.itest2.com")
146
+ # goto_page("/demo") # visit page http://www.itest2.com/demo
147
+ #
148
+ def goto_page(page)
149
+ operation_delay
150
+ dump_caller_stack
151
+ @web_browser.goto_page(page) if @web_browser
152
+ end
153
+ alias visit goto_page
154
+
155
+ # Go to another web site, normally different site being tested on
156
+ #
157
+ # open_browser("http://www.itest2.com")
158
+ # goto_url("http://myorganized.info")
159
+ def goto_url(url)
160
+ @web_browser.goto_url url
161
+ end
162
+
163
+ # Attach to existinb browser window
164
+ #
165
+ # attach_browser(:title, )
166
+ def attach_browser(how, what, options = {})
167
+ options.merge!(:browser => is_firefox? ? "Firefox" : "IE")
168
+ begin
169
+ options.merge!(:base_url => browser.context.base_url)
170
+ rescue => e
171
+ puts "error to attach to browser: #{e}"
172
+ end
173
+ WebBrowser.attach_browser(how, what, options)
174
+ end
175
+
176
+ # Reuse current an opened browser window instead of opening a new one
177
+ # example:
178
+ # use_current_browser(:title, /.*/) # use what ever browser window
179
+ # use_current_browser(:title, "iTest2") # use browser window with title "iTest2"
180
+ def use_current_browser(how = :title, what = /.*/)
181
+ @web_browser = WebBrowser.attach_browser(how, what)
182
+ end
183
+
184
+ ##
185
+ # Delegate to WebTester
186
+ #
187
+ # Note:
188
+ # label(:id, "abc") # OK
189
+ # label(:id, :abc) # Error
190
+ #
191
+ # Depends on which object type, you can use following attribute
192
+ # More details: http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
193
+ #
194
+ # :id Used to find an element that has an "id=" attribute. Since each id should be unique, according to the XHTML specification, this is recommended as the most reliable method to find an object. *
195
+ # :name Used to find an element that has a "name=" attribute. This is useful for older versions of HTML, but "name" is deprecated in XHTML. *
196
+ # :value Used to find a text field with a given default value, or a button with a given caption, or a text field
197
+ # :text Used for links, spans, divs and other element that contain text.
198
+ # :index Used to find the nth element of the specified type on a page. For example, button(:index, 2) finds the second button. Current versions of WATIR use 1-based indexing, but future versions will use 0-based indexing.
199
+ # :class Used for an element that has a "class=" attribute.
200
+ # :title Used for an element that has a "title=" attribute.
201
+ # :xpath Finds the item using xpath query.
202
+ # :method Used only for forms, the method attribute of a form is either GET or POST.
203
+ # :action Used only for form elements, specifies the URL where the form is to be submitted.
204
+ # :href Used to identify a link by its "href=" attribute.
205
+ # :src Used to identify an image by its URL.
206
+ #
207
+
208
+ # area <area> tags
209
+ # button <input> tags with type=button, submit, image or reset
210
+ # check_box <input> tags with type=checkbox
211
+ # div <div> tags
212
+ # form <form> tags
213
+ # frame frames, including both the <frame> elements and the corresponding pages
214
+ # h1 - h6 <h1>, <h2>, <h3>, <h4>, <h5>, <h6> tags
215
+ # hidden <input> tags with type=hidden
216
+ # image <img> tags
217
+ # label <label> tags (including "for" attribute)
218
+ # li <li> tags
219
+ # link <a> (anchor) tags
220
+ # map <map> tags
221
+ # radio <input> tags with the type=radio; known as radio buttons
222
+ # select_list <select> tags, known as drop-downs or drop-down lists
223
+ # span <span> tags
224
+ # table <table> tags, including row and cell methods for accessing nested elements
225
+ # text_field <input> tags with the type=text (single-line), type=textarea (multi-line), and type=password
226
+ # p <p> (paragraph) tags, because
227
+ [:area, :button, :cell, :checkbox, :div, :form, :frame, :h1, :h2, :h3, :h4, :h5, :h6, :hidden, :image, :li, :link, :map, :pre, :row, :radio, :select_list, :span, :table, :text_field, :paragraph, :file_field, :label].each do |method|
228
+ define_method method do |*args|
229
+ dump_caller_stack
230
+ # add check for @web_browser, in case the moudule included without init browser
231
+ @web_browser.send(method, *args) if @web_browser
232
+ end
233
+ end
234
+ alias td cell
235
+ alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
236
+ alias tr row
237
+
238
+ [:back, :forward, :refresh].each do |method|
239
+ define_method(method) do
240
+ dump_caller_stack
241
+ operation_delay
242
+ @web_browser.send(method) if @web_browser
243
+ end
244
+ end
245
+ alias refresh_page refresh
246
+ alias go_back back
247
+ alias go_forward forward
248
+
249
+ [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields].each do |method|
250
+ define_method method do
251
+ dump_caller_stack
252
+ @web_browser.send(method) if @web_browser
253
+ end
254
+ end
255
+
256
+ # Check one or more checkboxes with same name, can accept a string or an array of string as values checkbox, pass array as values will try to set mulitple checkboxes.
257
+ #
258
+ # page.check_checkbox('bad_ones', 'Chicken Little')
259
+ # page.check_checkbox('good_ones', ['Cars', 'Toy Story'])
260
+ #
261
+ [:set_form_element, :click_link_with_text, :click_link_with_id, :submit, :click_button_with_id, :click_button_with_name, :click_button_with_caption, :click_button_with_value, :click_radio_option, :clear_radio_option, :select_file_for_upload, :check_checkbox, :uncheck_checkbox, :select_option].each do |method|
262
+ define_method method do |*args|
263
+ dump_caller_stack
264
+ operation_delay
265
+ @web_browser.send(method, *args) if @web_browser
266
+ end
267
+ end
268
+
269
+ alias enter_text set_form_element
270
+ alias set_hidden_field set_form_element
271
+ alias click_link click_link_with_text
272
+ alias click_button_with_text click_button_with_caption
273
+ alias click_button click_button_with_caption
274
+ alias click_radio_button click_radio_option
275
+ alias clear_radio_button clear_radio_option
276
+
277
+ # for text field can be easier to be identified by attribute "id" instead of "name", not recommended though
278
+ def enter_text_with_id(textfield_id, value)
279
+ dump_caller_stack
280
+ operation_delay
281
+ text_field(:id, textfield_id).set(value)
282
+ end
283
+
284
+ def contains_text(text)
285
+ @web_browser.contains_text(text)
286
+ end
287
+
288
+ # Click image buttion with image source name
289
+ #
290
+ # For an image submit button <input name="submit" type="image" src="/images/search_button.gif">
291
+ # click_button_with_image("search_button.gif")
292
+ def click_button_with_image_src_contains(image_filename)
293
+ dump_caller_stack
294
+ operation_delay
295
+ found = nil
296
+ raise "no buttons in this page" if buttons.length <= 0
297
+ buttons.each { |btn|
298
+ if btn && btn.src && btn.src.include?(image_filename) then
299
+ found = btn
300
+ break
301
+ end
302
+ }
303
+ raise "not image button with src: #{image_filename} found" if found.nil?
304
+ found.click
305
+ end
306
+ alias click_button_with_image click_button_with_image_src_contains
307
+
308
+ def new_popup_window(options)
309
+ @web_browser.new_popup_window(options)
310
+ end
311
+
312
+
313
+ # Warning: this does not work well with Firefox yet.
314
+ def element_text(elem_id)
315
+ @web_browser.element_value(elem_id)
316
+ end
317
+
318
+ # Identify DOM element by ID
319
+ # Warning: it is only supported on IE
320
+ def element_by_id(elem_id)
321
+ @web_browser.element_by_id(elem_id)
322
+ end
323
+
324
+ # ---
325
+ # For debugging
326
+ # ---
327
+ def dump_response(stream = nil)
328
+ @web_browser.dump_response(stream)
329
+ end
330
+
331
+ # For current page souce to a file in specified folder for inspection
332
+ #
333
+ # save_current_page(:dir => "C:\\mysite", filename => "abc", :replacement => true)
334
+ def save_current_page(options = {})
335
+ default_options = {:replacement => true}
336
+ options = default_options.merge(options)
337
+ if options[:dir]
338
+ # already defined the dir
339
+ to_dir = options[:dir]
340
+ elsif $ITEST2_RUNNING_SPEC_ID && $ITEST2_WORKING_DIR
341
+
342
+ $ITEST2_DUMP_DIR = File.join($ITEST2_WORKING_DIR, "dump")
343
+ FileUtils.mkdir($ITEST2_DUMP_DIR) unless File.exists?($ITEST2_DUMP_DIR)
344
+
345
+ spec_run_id = $ITEST2_RUNNING_SPEC_ID
346
+ spec_run_dir_name = spec_run_id.to_s.rjust(4, "0") unless spec_run_id == "unknown"
347
+ to_dir = File.join($ITEST2_DUMP_DIR, spec_run_dir_name)
348
+ else
349
+ to_dir = ENV['TEMP_DIR'] || (is_windows? ? "C:\\temp" : "/tmp")
350
+ end
351
+
352
+ if options[:filename]
353
+ file_name = options[:filename]
354
+ else
355
+ file_name = Time.now.strftime("%m%d%H%M%S") + ".html"
356
+ end
357
+
358
+ Dir.mkdir(to_dir) unless File.exists?(to_dir)
359
+ file = File.join(to_dir, file_name)
360
+
361
+ content = page_source
362
+ base_url = @web_browser.context.base_url
363
+ current_url = @web_browser.url
364
+ current_url =~ /(.*\/).*$/
365
+ current_url_parent = $1
366
+ if options[:replacement] && base_url =~ /^http:/
367
+ File.new(file, "w").puts absolutize_page_hpricot(content, base_url, current_url_parent)
368
+ else
369
+ File.new(file, "w").puts content
370
+ end
371
+
372
+ end
373
+
374
+
375
+ # <link rel="stylesheet" type="text/css" href="/stylesheets/default.css" />
376
+ # '<script type="text/javascript" src="http://www.jeroenwijering.com/embed/swfobject.js"></script>'
377
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
378
+ # <script type="text/javascript" src="/javascripts/scriptaculous.js?load=effects,builder"></script>
379
+ # <script type="text/javascript" src="/javascripts/extensions/gallery/lightbox.js"></script>
380
+ # <link href="/stylesheets/extensions/gallery/lightbox.css" rel="stylesheet" type="text/css" />
381
+ # <img src="images/mission_48.png" />
382
+ def absolutize_page(content, base_url, current_url_parent)
383
+ modified_content = ""
384
+ content.each_line do |line|
385
+ if line =~ /<script\s+.*src=["'']?(.*)["'].*/i then
386
+ script_src = $1
387
+ substitute_relative_path_in_src_line(line, script_src, base_url, current_url_parent)
388
+ elsif line =~ /<link\s+.*href=["'']?(.*)["'].*/i then
389
+ link_href = $1
390
+ substitute_relative_path_in_src_line(line, link_href, base_url, current_url_parent)
391
+ elsif line =~ /<img\s+.*src=["'']?(.*)["'].*/i then
392
+ img_src = $1
393
+ substitute_relative_path_in_src_line(line, img_src, base_url, current_url_parent)
394
+ end
395
+
396
+ modified_content += line
397
+ end
398
+ return modified_content
399
+ end
400
+
401
+ # absolutize_page referencs using hpricot
402
+ #
403
+ def absolutize_page_hpricot(content, base_url, parent_url)
404
+ return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
405
+ begin
406
+ require 'hpricot'
407
+ doc = Hpricot(content)
408
+ base_url.slice!(-1) if ends_with?(base_url, "/")
409
+ (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || ""}
410
+ (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || ""}
411
+ (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || ""}
412
+ return doc.to_html
413
+ rescue => e
414
+ absolutize_page(content, base_url, parent_url)
415
+ end
416
+ end
417
+
418
+ ##
419
+ # change
420
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
421
+ # to
422
+ # <script type="text/javascript" src="http://itest2.com/javascripts/prototype.js"></script>
423
+ def absolutify_url(src, base_url, parent_url)
424
+ if src.nil? || src.empty? || src == "//:" || src =~ /\s*http:\/\//i
425
+ return src
426
+ end
427
+
428
+ return "#{base_url}#{src}" if src =~ /^\s*\//
429
+ return "#{parent_url}#{src}" if parent_url
430
+ return src
431
+ end
432
+
433
+ # substut
434
+ def substitute_relative_path_in_src_line(line, script_src, host_url, page_parent_url)
435
+ unless script_src =~ /^["']?http:/
436
+ host_url.slice!(-1) if ends_with?(host_url, "/")
437
+ if script_src =~ /^\s*\// # absolute_path
438
+ line.gsub!(script_src, "#{host_url}#{script_src}")
439
+ else #relative_path
440
+ line.gsub!(script_src, "#{page_parent_url}#{script_src}")
441
+ end
442
+ end
443
+ end
444
+
445
+ def ends_with?(str, suffix)
446
+ suffix = suffix.to_s
447
+ str[-suffix.length, suffix.length] == suffix
448
+ end
449
+
450
+ # current web page title
451
+ def page_title
452
+ @web_browser.page_title
453
+ end
454
+
455
+ # current page source (in HTML)
456
+ def page_source
457
+ @web_browser.page_source
458
+ end
459
+
460
+ # return plain text view of page
461
+ def page_text
462
+ @web_browser.text
463
+ end
464
+
465
+ # return the text of specific (identified by attribute "id") label tag
466
+ # For page containing
467
+ # <label id="preferred_ide">iTest2</label>
468
+ # label_with_id("preferred_ids") # => iTest2
469
+ def label_with_id(label_id)
470
+ label(:id, label_id.to_s).text
471
+ end
472
+
473
+ # return the text of specific (identified by attribute "id") span tag
474
+ # For page containing
475
+ # <span id="preferred_recorder">iTest2/Watir Recorder</span>
476
+ # span_with_id("preferred_recorder") # => iTest2/Watir Recorder
477
+ def span_with_id(span_id)
478
+ span(:id, span_id).text
479
+ end
480
+
481
+ # return the text of specific (identified by attribute "id") ta tag
482
+ # For page containing
483
+ # <td id="preferred_recorder">iTest2/Watir Recorder</span>
484
+ # td_with_id("preferred_recorder") # => iTest2/Watir Recorder
485
+ def cell_with_id(cell_id)
486
+ cell(:id, cell_id).text
487
+ end
488
+ alias table_data_with_id cell_with_id
489
+
490
+
491
+ def is_mac?
492
+ RUBY_PLATFORM.downcase.include?("darwin")
493
+ end
494
+
495
+ def is_windows?
496
+ RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw32")
497
+ end
498
+
499
+ def is_linux?
500
+ RUBY_PLATFORM.downcase.include?("linux")
501
+ end
502
+
503
+ # Support browser (IE) operations using unicode
504
+ # Example:
505
+ # click_button("Google 搜索")
506
+ # Reference: http://jira.openqa.org/browse/WTR-219
507
+ def support_utf8
508
+ if is_windows?
509
+ require 'win32ole'
510
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
511
+ end
512
+ end
513
+ alias support_unicode support_utf8
514
+
515
+ #= Convenient functions
516
+ #
517
+
518
+ # Using Ruby block syntax to create interesting domain specific language,
519
+ # may be appeal to someone.
520
+
521
+ # Example:
522
+ # on @page do |i|
523
+ # i.enter_text('btn1')
524
+ # i.click_button('btn1')
525
+ # end
526
+ def on(page, &block)
527
+ yield page
528
+ end
529
+
530
+ # fail the test if user can perform the operation
531
+ #
532
+ # Example:
533
+ # shall_not_allow { 1/0 }
534
+ def shall_not_allow(&block)
535
+ operation_performed_ok = false
536
+ begin
537
+ yield
538
+ operation_performed_ok = true
539
+ rescue
540
+ end
541
+ raise "Operation shall not be allowed" if operation_performed_ok
542
+ end
543
+ alias do_not_allow shall_not_allow
544
+
545
+ # Does not provide real function, other than make enhancing test syntax
546
+ #
547
+ # Example:
548
+ # allow { click_button('Register') }
549
+ def allow(&block)
550
+ yield
551
+ end
552
+ alias shall_allow allow
553
+ alias allowing allow
554
+
555
+ # try operation, ignore if errors occur
556
+ #
557
+ # Example:
558
+ # failsafe { click_link("Logout") } # try logout, but it still OK if not being able to (already logout))
559
+ def failsafe(&block)
560
+ begin
561
+ yield
562
+ rescue =>e
563
+ end
564
+ end
565
+ alias fail_safe failsafe
566
+
567
+
568
+ # Execute the provided block until either (1) it returns true, or
569
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
570
+ # a TimeOutException will be raised. The block will always
571
+ # execute at least once.
572
+ #
573
+ # This does not handle error, if the given block raise error, the statement finish with error
574
+ # Examples:
575
+ # wait_until {puts 'hello'}
576
+ # wait_until { div(:id, :receipt_date).exists? }
577
+ #
578
+ def wait_until(timeout = @@default_timeout || 30, polling_interval = @@default_polling_interval || 1, &block)
579
+ waiter = Watir::Waiter.new(timeout, polling_interval)
580
+ waiter.wait_until { yield }
581
+ end
582
+
583
+ # Wait for specific seconds for an Ajax update finish.
584
+ # Trick: In your Rails application,
585
+ # :loading => "Element.show('search_indicator');
586
+ # :complete => "Element.hide('search_indicator');
587
+ #
588
+ # <%= image_tag("indicator.gif", :id => 'search_indicator', :style => 'display:none') %>
589
+ #
590
+ # Typical usage:
591
+ # ajax_wait_for_element("search_indicator", 30)
592
+ # ajax_wait_for_element("search_indicator", 30, "show")
593
+ # ajax_wait_for_element("search_indicator", 30, "hide")
594
+ # ajax_wait_for_element("search_indicator", 30, "show", 5) # check every 5 seconds
595
+ #
596
+ # Warning: this method has not been fully tested, if you are not using Rails, change your parameter accordingly.
597
+ #
598
+ def ajax_wait_for_element(element_id, seconds, status='show', check_interval = @@default_polling_interval)
599
+ count = 0
600
+ check_interval = 1 if check_interval < 1 or check_interval > seconds
601
+ while count < (seconds / check_interval) do
602
+ search_indicator = @web_browser.element_by_id(element_id)
603
+ search_indicator_outer_html = search_indicator.outerHtml if search_indicator
604
+ if status == 'hide'
605
+ return true if search_indicator_outer_html and !search_indicator_outer_html.include?('style="DISPLAY: none"')
606
+ else
607
+ return true if search_indicator_outer_html and search_indicator_outer_html.include?('style="DISPLAY: none"')
608
+ end
609
+ sleep check_interval if check_interval > 0 and check_interval < 5 * 60 # wait max 5 minutes
610
+ count += 1
611
+ end
612
+ return false
613
+ end
614
+
615
+ #Wait the element with given id to be present in web page
616
+ #
617
+ # Warning: this not working in Firefox, try use wait_util or try instead
618
+ def wait_for_element(element_id, timeout = @@default_timeout, interval = @@default_polling_interval)
619
+ start_time = Time.now
620
+ #TODO might not work with Firefox
621
+ until @web_browser.element_by_id(element_id) do
622
+ sleep(interval)
623
+ if (Time.now - start_time) > timeout
624
+ raise RuntimeError, "failed to find element: #{element_id} for max #{timeout}"
625
+ end
626
+ end
627
+ end
628
+ =begin
629
+
630
+ # TODO: Firewatir does not suport retrieving style or outerHtml
631
+ # http://jira.openqa.org/browse/WTR-260
632
+ # http://code.google.com/p/firewatir/issues/detail?id=76
633
+ #
634
+ # Max timeout value is 10 minutes
635
+ #
636
+ def ajax_call_complete_after_element_hidden(elem_id, check_start = 0.5, timeout = 5, interval = 0.5, &block)
637
+ yield
638
+ sleep check_start # the time allowed to perform the coomplete
639
+ timeout = 10 * 60 if timeout > 10 * 600 or timeout <= 0
640
+ begin
641
+ Timeout::timeout(timeout) {
642
+ begin
643
+ elem = element_by_id(elem_id)
644
+ while elem do
645
+ puts "outer=>#{elem.outerHtml}|"
646
+ puts "style =>#{elem.attribute_value('style')}|"
647
+ sleep interval
648
+ elem = element_by_id(elem_id)
649
+ end
650
+ rescue => e
651
+ puts e
652
+ end
653
+ }
654
+ rescue Timeout::Error
655
+ # Too slow!!
656
+ raise "Too slow, wait max #{timeout} seconds, the element #{elem_id} still there"
657
+ end
658
+ end
659
+
660
+ =end
661
+
662
+ # Try the operation up to specified times, and sleep given interval (in seconds)
663
+ # Error will be ignored until timeout
664
+ # Example
665
+ # repeat_try(3, 2) { click_button('Search' } # 3 times, 6 seconds in total
666
+ # repeat_try { click_button('Search' } # using default 5 tries, 2 second interval
667
+ def repeat_try(num_tries = @@default_timeout || 30, interval = @@default_polling_interval || 1, &block)
668
+ num_tries ||= 1
669
+ (num_tries - 1).times do |num|
670
+ begin
671
+ yield
672
+ return
673
+ rescue => e
674
+ # puts "debug: #{num} failed: #{e}"
675
+ sleep interval
676
+ end
677
+ end
678
+
679
+ # last try, throw error if still fails
680
+ begin
681
+ yield
682
+ rescue => e
683
+ raise e.to_s + " after trying #{num_tries} times every #{interval} seconds"
684
+ end
685
+ yield
686
+ end
687
+
688
+ # TODO: syntax
689
+
690
+ # Try the operation up to specified timeout (in seconds), and sleep given interval (in seconds).
691
+ # Error will be ignored until timeout
692
+ # Example
693
+ # try { click_link('waiting')}
694
+ # try(10, 2) { click_button('Search' } # try to click the 'Search' button upto 10 seconds, try every 2 seconds
695
+ # try { click_button('Search' }
696
+ def try(timeout = @@default_timeout, polling_interval = @@default_polling_interval || 1, &block)
697
+ start_time = Time.now
698
+
699
+ last_error = nil
700
+ until (duration = Time.now - start_time) > timeout
701
+ begin
702
+ return if yield
703
+ last_error = nil
704
+ rescue => e
705
+ last_error = e
706
+ end
707
+ sleep polling_interval
708
+ end
709
+
710
+ raise "Timeout after #{duration.to_i} seconds with error: #{last_error}." if last_error
711
+ raise "Timeout after #{duration.to_i} seconds."
712
+ end
713
+ alias try_upto try
714
+
715
+ ##
716
+ # Convert :first to 1, :second to 2, and so on...
717
+ def symbol_to_sequence(symb)
718
+ value = { :zero => 0,
719
+ :first => 1,
720
+ :second => 2,
721
+ :third => 3,
722
+ :fourth => 4,
723
+ :fifth => 5,
724
+ :sixth => 6,
725
+ :seventh => 7,
726
+ :eighth => 8,
727
+ :ninth => 9,
728
+ :tenth => 10 }[symb]
729
+ return value || symb.to_i
730
+ end
731
+
732
+ end
733
+
734
+ end