zhimin-rwebspec 1.4.0.2 → 1.4.1

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