rwebspec 3.1.4 → 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.
@@ -1,772 +1,759 @@
1
- # convenient methods to drive the browser.
2
- # convenient methods to drive the browser.
3
- #
4
- # Instead of
5
- # browser.click_button("submit")
6
- # You can just use
7
- # click_button("submit")
8
- #
9
- require File.join(File.dirname(__FILE__), '../plugins/testwise_plugin')
10
- require File.join(File.dirname(__FILE__), 'popup')
11
- require File.join(File.dirname(__FILE__), 'matchers', "contains_text.rb")
12
-
13
- require 'timeout'
14
- require 'uri'
15
-
16
- # require 'watir/screen_capture' if RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
17
-
18
- module RWebSpec
19
- module Driver
20
- include RWebSpec::TestWisePlugin
21
- include RWebSpec::Popup
22
-
23
- # open a browser, and set base_url via hash, but does not acually
24
- #
25
- # example:
26
- # open_browser :base_url => http://localhost:8080
27
- #
28
- # There are 3 ways to set base url
29
- # 1. pass as first argument
30
- # 2. If running using TestWise, used as confiured
31
- # 3. Use default value set
32
- def open_browser(base_url = nil, options = {})
33
-
34
- begin
35
- support_unicode
36
- rescue => e
37
- puts "Unicode may not work in IE, #{e}"
38
- end
39
-
40
- base_url ||= $TESTWISE_PROJECT_BASE_URL
41
- base_url ||= $TESTWISE_PROJECT_BASE_URL
42
- base_url ||= $BASE_URL
43
- raise "base_url must be set" if base_url.nil?
44
-
45
- default_options = {:speed => "fast",
46
- :visible => true,
47
- :highlight_colour => 'yellow',
48
- :close_others => true,
49
- :start_new => false, # start a new browser always
50
- :go => true}
51
-
52
- options = default_options.merge options
53
- options[:firefox] = true if "Firefox" == $TESTWISE_BROWSER || "Firefox" == $TESTWISE_BROWSER || "Firefox" == $BROWSER
54
- ($TESTWISE_HIDE_BROWSER || $TESTWISE_HIDE_BROWSER) ? $HIDE_IE = true : $HIDE_IE = false
55
-
56
- if base_url =~ /^file:/
57
- uri_base = base_url
58
- else
59
- uri = URI.parse(base_url)
60
- uri_base = "#{uri.scheme}://#{uri.host}:#{uri.port}"
61
- end
62
-
63
- if options[:start_new]
64
- @web_browser = WebBrowser.new(uri_base, nil, options)
65
- else
66
- @web_browser = WebBrowser.reuse(uri_base, options) # Reuse existing browser
67
- end
68
-
69
- if base_url =~ /^file:/
70
- goto_url(base_url) # for files, no base url
71
- else
72
- (uri.path.length == 0) ? begin_at("/") : begin_at(uri.path) if options[:go]
73
- end
74
-
75
- return @web_browser
76
- end
77
-
78
- alias open_browser_with open_browser
79
-
80
- # return the underlying RWebSpec::Browser object
81
- def browser
82
- @web_browser
83
- end
84
-
85
-
86
- # Close the current browser window (started by the script). If no browser started, then close
87
- # all browser windows.
88
- #
89
- def close_browser
90
- if @web_browser
91
- # Old TestWise version
92
- # @web_browser.close_browser unless $TESTWISE_LEAVE_BROWSER_OPEN_AFTER_RUN
93
- @web_browser.close_browser
94
- else
95
- close_all_browsers
96
- end
97
- end
98
- alias close_ie close_browser
99
-
100
-
101
- # Close all opening browser windows
102
- #
103
- def close_all_browsers
104
- if @web_browser
105
- Watir::IE.close_all
106
- else
107
- browser_type = $TESTWISE_BROWSER ? $TESTWISE_BROWSER.downcase.to_sym : :ie
108
- WebBrowser.close_all_browsers(browser_type)
109
- end
110
- end
111
-
112
- # Verify the next page following an operation.
113
- #
114
- # Typical usage:
115
- # login_page.click_login
116
- # expect_page HomePage
117
- def expect_page(page_clazz, argument = nil)
118
- if argument
119
- @web_browser.expect_page(page_clazz, argument)
120
- else
121
- @web_browser.expect_page(page_clazz)
122
- end
123
- end
124
-
125
- def context
126
- @web_browser.context
127
- end
128
-
129
- # Starting browser with a URL
130
- #
131
- # Example:
132
- # begin_at("http://www.itest2.com")
133
- def begin_at(url)
134
- dump_caller_stack
135
- @web_browser.begin_at(url)
136
- end
137
-
138
- # Return the Watir::IE instance
139
- #
140
- def ie
141
- @web_browser.ie
142
- end
143
-
144
- # Return the FireWatir::Firefox instance
145
- #
146
- def firefox
147
- @web_browser.firefox
148
- end
149
-
150
- def is_firefox?
151
- @web_browser.is_firefox? if @web_browser
152
- end
153
-
154
- # Go to another page on the testing site.
155
- #
156
- # open_browser("http://www.itest2.com")
157
- # goto_page("/demo") # visit page http://www.itest2.com/demo
158
- #
159
- def goto_page(page)
160
- perform_operation {
161
- @web_browser.goto_page(page) if @web_browser
162
- }
163
- end
164
- alias visit goto_page
165
-
166
-
167
- # Go to another web site, normally different site being tested on
168
- #
169
- # open_browser("http://www.itest2.com")
170
- # goto_url("http://myorganized.info")
171
- def goto_url(url)
172
- @web_browser.goto_url url
173
- end
174
-
175
- # Go to specific url in background (i.e not via browwser, different from goto_url)
176
- # This won't share the session with what's currenlty in browser, proxy setting
177
- #
178
- # One use example: resetting database
179
- # background_visit("/reset")
180
- #
181
- def background_visit(url, opts = {})
182
- require 'httpclient'
183
- begin
184
- client = HTTPClient.new
185
- if url && url =~ /^http/
186
- http_response = client.get(url).body
187
- else
188
- base_url = $TESTWISE_PROJECT_BASE_URL || $TESTWISE_PROJECT_BASE_URL || $BASE_URL
189
- http_response = client.get("#{base_url}#{url}").body
190
- end
191
-
192
- http_response = http_response.content if http_response.respond_to?("content")
193
- rescue => e
194
- raise e
195
- end
196
- end
197
-
198
- # Attach to existing browser window
199
- #
200
- # attach_browser(:title, "Page" )
201
- # attach_browser(:url, "http://wwww..." )
202
- def attach_browser(how, what, options = {})
203
- options.merge!(:browser => is_firefox? ? "Firefox" : "IE") unless options[:browser]
204
- begin
205
- options.merge!(:base_url => browser.context.base_url)
206
- rescue => e
207
- puts "failed to set base_url, ignore : #{e}"
208
- end
209
- WebBrowser.attach_browser(how, what, options)
210
- end
211
-
212
- # Reuse current an opened browser window instead of opening a new one
213
- # example:
214
- # use_current_browser(:title, /.*/) # use what ever browser window
215
- # use_current_browser(:title, "TestWise") # use browser window with title "TestWise"
216
- def use_current_browser(how = :title, what = /.*/)
217
- @web_browser = WebBrowser.attach_browser(how, what)
218
- end
219
-
220
- ##
221
- # Delegate to WebTester
222
- #
223
- # Note:
224
- # label(:id, "abc") # OK
225
- # label(:id, :abc) # Error
226
- #
227
- # Depends on which object type, you can use following attribute
228
- # More details: http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
229
- #
230
- # :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. *
231
- # :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. *
232
- # :value Used to find a text field with a given default value, or a button with a given caption, or a text field
233
- # :text Used for links, spans, divs and other element that contain text.
234
- # :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.
235
- # :class Used for an element that has a "class=" attribute.
236
- # :title Used for an element that has a "title=" attribute.
237
- # :xpath Finds the item using xpath query.
238
- # :method Used only for forms, the method attribute of a form is either GET or POST.
239
- # :action Used only for form elements, specifies the URL where the form is to be submitted.
240
- # :href Used to identify a link by its "href=" attribute.
241
- # :src Used to identify an image by its URL.
242
- #
243
-
244
- # area <area> tags
245
- # button <input> tags with type=button, submit, image or reset
246
- # check_box <input> tags with type=checkbox
247
- # div <div> tags
248
- # form <form> tags
249
- # frame frames, including both the <frame> elements and the corresponding pages
250
- # h1 - h6 <h1>, <h2>, <h3>, <h4>, <h5>, <h6> tags
251
- # hidden <input> tags with type=hidden
252
- # image <img> tags
253
- # label <label> tags (including "for" attribute)
254
- # li <li> tags
255
- # link <a> (anchor) tags
256
- # map <map> tags
257
- # radio <input> tags with the type=radio; known as radio buttons
258
- # select_list <select> tags, known as drop-downs or drop-down lists
259
- # span <span> tags
260
- # table <table> tags, including row and cell methods for accessing nested elements
261
- # text_field <input> tags with the type=text (single-line), type=textarea (multi-line), and type=password
262
- # p <p> (paragraph) tags, because
263
- [:area, :button, :td, :checkbox, :div, :form, :frame, :h1, :h2, :h3, :h4, :h5, :h6, :hidden, :image, :li, :link, :map, :pre, :tr, :radio, :select_list, :span, :table, :text_field, :paragraph, :file_field, :label].each do |method|
264
- define_method method do |* args|
265
- perform_operation { @web_browser.send(method, * args) if @web_browser }
266
- end
267
- end
268
- alias cell td
269
- alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
270
- alias row tr
271
- alias a link
272
- alias img image
273
-
274
-
275
- [:back, :forward, :refresh].each do |method|
276
- define_method(method) do
277
- perform_operation { @web_browser.send(method) if @web_browser }
278
- end
279
- end
280
- alias refresh_page refresh
281
- alias go_back back
282
- alias go_forward forward
283
-
284
- [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields, :divs, :dls, :dds, :dts, :ems, :lis, :maps, :spans, :strongs, :ps, :pres, :labels, :tds, :trs].each do |method|
285
- define_method method do
286
- perform_operation { @web_browser.send(method) if @web_browser }
287
- end
288
- end
289
- alias as links
290
- alias rows trs
291
- alias cells tds
292
- alias imgs images
293
-
294
-
295
- # 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.
296
- #
297
- # page.check_checkbox('bad_ones', 'Chicken Little')
298
- # page.check_checkbox('good_ones', ['Cars', 'Toy Story'])
299
- #
300
- [: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, :check_checkbox, :uncheck_checkbox, :select_option].each do |method|
301
- define_method method do |* args|
302
- perform_operation { @web_browser.send(method, * args) if @web_browser }
303
- end
304
- end
305
-
306
- alias enter_text set_form_element
307
- alias set_hidden_field set_form_element
308
- alias click_link click_link_with_text
309
- alias click_button_with_text click_button_with_caption
310
- alias click_button click_button_with_caption
311
- alias click_radio_button click_radio_option
312
- alias clear_radio_button clear_radio_option
313
-
314
- # for text field can be easier to be identified by attribute "id" instead of "name", not recommended though
315
- def enter_text_with_id(textfield_id, value)
316
- perform_operation { text_field(:id, textfield_id).set(value) }
317
- end
318
-
319
- def perform_operation(& block)
320
- begin
321
- dump_caller_stack
322
- operation_delay
323
- yield
324
- rescue RuntimeError => re
325
- raise re
326
- # ensure
327
- # puts "[DEBUG] ensure #{perform_ok}" unless perform_ok
328
- end
329
- end
330
-
331
- def contains_text(text)
332
- @web_browser.contains_text(text)
333
- end
334
-
335
- # In pages, can't use include, text.should include("abc") won't work
336
- # Instead,
337
- # text.should contains("abc"
338
- def contains(str)
339
- ContainsText.new(str)
340
- end
341
-
342
- alias contain contains
343
-
344
- # Click image buttion with image source name
345
- #
346
- # For an image submit button <input name="submit" type="image" src="/images/search_button.gif">
347
- # click_button_with_image("search_button.gif")
348
- def click_button_with_image_src_contains(image_filename)
349
- perform_operation {
350
- found = nil
351
- raise "no buttons in this page" if buttons.length <= 0
352
- buttons.each { |btn|
353
- if btn && btn.src && btn.src.include?(image_filename) then
354
- found = btn
355
- break
356
- end
357
- }
358
- raise "not image button with src: #{image_filename} found" if found.nil?
359
- found.click
360
- }
361
- end
362
-
363
- alias click_button_with_image click_button_with_image_src_contains
364
-
365
- def new_popup_window(options)
366
- @web_browser.new_popup_window(options)
367
- end
368
-
369
-
370
- # Warning: this does not work well with Firefox yet.
371
- def element_text(elem_id)
372
- @web_browser.element_value(elem_id)
373
- end
374
-
375
- # Identify DOM element by ID
376
- # Warning: it is only supported on IE
377
- def element_by_id(elem_id)
378
- @web_browser.element_by_id(elem_id)
379
- end
380
-
381
- # ---
382
- # For debugging
383
- # ---
384
- def dump_response(stream = nil)
385
- @web_browser.dump_response(stream)
386
- end
387
-
388
- def default_dump_dir
389
- if ($TESTWISE_RUNNING_SPEC_ID && $TESTWISE_WORKING_DIR) || ($TESTWISE_RUNNING_SPEC_ID && $TESTWISE_WORKING_DIR)
390
-
391
- $TESTWISE_DUMP_DIR = $TESTWISE_DUMP_DIR = File.join($TESTWISE_WORKING_DIR, "dump")
392
- FileUtils.mkdir($TESTWISE_DUMP_DIR) unless File.exists?($TESTWISE_DUMP_DIR)
393
-
394
- spec_run_id = $TESTWISE_RUNNING_SPEC_ID || $TESTWISE_RUNNING_SPEC_ID
395
- spec_run_dir_name = spec_run_id.to_s.rjust(4, "0") unless spec_run_id == "unknown"
396
- to_dir = File.join($TESTWISE_DUMP_DIR, spec_run_dir_name)
397
- else
398
- to_dir = ENV['TEMP_DIR'] || (is_windows? ? "C:\\temp" : "/tmp")
399
- end
400
- end
401
-
402
- # For current page souce to a file in specified folder for inspection
403
- #
404
- # save_current_page(:dir => "C:\\mysite", filename => "abc", :replacement => true)
405
- def save_current_page(options = {})
406
- default_options = {:replacement => true}
407
- options = default_options.merge(options)
408
- to_dir = options[:dir] || default_dump_dir
409
-
410
- if options[:filename]
411
- file_name = options[:filename]
412
- else
413
- file_name = Time.now.strftime("%m%d%H%M%S") + ".html"
414
- end
415
-
416
- Dir.mkdir(to_dir) unless File.exists?(to_dir)
417
- file = File.join(to_dir, file_name)
418
-
419
- content = page_source
420
- base_url = @web_browser.context.base_url
421
- current_url = @web_browser.url
422
- current_url =~ /(.*\/).*$/
423
- current_url_parent = $1
424
- if options[:replacement] && base_url =~ /^http:/
425
- File.new(file, "w").puts absolutize_page_nokogiri(content, base_url, current_url_parent)
426
- else
427
- File.new(file, "w").puts content
428
- end
429
-
430
- end
431
-
432
-
433
- # <link rel="stylesheet" type="text/css" href="/stylesheets/default.css" />
434
- # '<script type="text/javascript" src="http://www.jeroenwijering.com/embed/swfobject.js"></script>'
435
- # <script type="text/javascript" src="/javascripts/prototype.js"></script>
436
- # <script type="text/javascript" src="/javascripts/scriptaculous.js?load=effects,builder"></script>
437
- # <script type="text/javascript" src="/javascripts/extensions/gallery/lightbox.js"></script>
438
- # <link href="/stylesheets/extensions/gallery/lightbox.css" rel="stylesheet" type="text/css" />
439
- # <img src="images/mission_48.png" />
440
- def absolutize_page(content, base_url, current_url_parent)
441
- modified_content = ""
442
- content.each_line do |line|
443
- if line =~ /<script\s+.*src=["'']?(.*)["'].*/i then
444
- script_src = $1
445
- substitute_relative_path_in_src_line(line, script_src, base_url, current_url_parent)
446
- elsif line =~ /<link\s+.*href=["'']?(.*)["'].*/i then
447
- link_href = $1
448
- substitute_relative_path_in_src_line(line, link_href, base_url, current_url_parent)
449
- elsif line =~ /<img\s+.*src=["'']?(.*)["'].*/i then
450
- img_src = $1
451
- substitute_relative_path_in_src_line(line, img_src, base_url, current_url_parent)
452
- end
453
-
454
- modified_content += line
455
- end
456
- return modified_content
457
- end
458
-
459
- # absolutize_page referencs using hpricot
460
- #
461
- def absolutize_page_hpricot(content, base_url, parent_url)
462
- return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
463
- begin
464
- require 'hpricot'
465
- doc = Hpricot(content)
466
- base_url.slice!(-1) if ends_with?(base_url, "/")
467
- (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || "" }
468
- (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
469
- (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
470
- return doc.to_html
471
- rescue => e
472
- absolutize_page(content, base_url, parent_url)
473
- end
474
- end
475
-
476
- def absolutize_page_nokogiri(content, base_url, parent_url)
477
- return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
478
- begin
479
- require 'nokogiri'
480
- doc = Nokogiri::HTML(content)
481
- base_url.slice!(-1) if ends_with?(base_url, "/")
482
- (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || "" }
483
- (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
484
- (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
485
- return doc.to_html
486
- rescue => e
487
- absolutize_page(content, base_url, parent_url)
488
- end
489
- end
490
-
491
- ##
492
- # change
493
- # <script type="text/javascript" src="/javascripts/prototype.js"></script>
494
- # to
495
- # <script type="text/javascript" src="http://itest2.com/javascripts/prototype.js"></script>
496
- def absolutify_url(src, base_url, parent_url)
497
- if src.nil? || src.empty? || src == "//:" || src =~ /\s*http:\/\//i
498
- return src
499
- end
500
-
501
- return "#{base_url}#{src}" if src =~ /^\s*\//
502
- return "#{parent_url}#{src}" if parent_url
503
- return src
504
- end
505
-
506
- # substut
507
- def substitute_relative_path_in_src_line(line, script_src, host_url, page_parent_url)
508
- unless script_src =~ /^["']?http:/
509
- host_url.slice!(-1) if ends_with?(host_url, "/")
510
- if script_src =~ /^\s*\// # absolute_path
511
- line.gsub!(script_src, "#{host_url}#{script_src}")
512
- else #relative_path
513
- line.gsub!(script_src, "#{page_parent_url}#{script_src}")
514
- end
515
- end
516
- end
517
-
518
- def ends_with?(str, suffix)
519
- suffix = suffix.to_s
520
- str[-suffix.length, suffix.length] == suffix
521
- end
522
-
523
- # current web page title
524
- def page_title
525
- @web_browser.page_title
526
- end
527
-
528
- # current page source (in HTML)
529
- def page_source
530
- @web_browser.page_source
531
- end
532
-
533
- # return plain text view of page
534
- def page_text
535
- @web_browser.text
536
- end
537
-
538
- # return the text of specific (identified by attribute "id") label tag
539
- # For page containing
540
- # <label id="preferred_ide">TestWise</label>
541
- # label_with_id("preferred_ids") # => TestWise
542
- # label_with_id("preferred_ids", :index => 2) # => TestWise
543
- def label_with_id(label_id, options = {})
544
- if options && options[:index] then
545
- label(:id => label_id.to_s, :index => options[:index]).text
546
- else
547
- label(:id, label_id.to_s).text
548
- end
549
- end
550
-
551
- # return the text of specific (identified by attribute "id") span tag
552
- # For page containing
553
- # <span id="preferred_recorder">iTest2/Watir Recorder</span>
554
- # span_with_id("preferred_recorder") # => iTest2/Watir Recorder
555
- def span_with_id(span_id, options = {})
556
- if options && options[:index] then
557
- span(:id => span_id.to_s, :index => options[:index]).text
558
- else
559
- span(:id, span_id).text
560
- end
561
- end
562
-
563
- # return the text of specific (identified by attribute "id") ta tag
564
- # For page containing
565
- # <td id="preferred_recorder">iTest2/Watir Recorder</span>
566
- # td_with_id("preferred_recorder") # => iTest2/Watir Recorder
567
- def cell_with_id(cell_id, options = {})
568
- if options && options[:index] then
569
- cell(:id => cell_id.to_s, :index => options[:index]).text
570
- else
571
- cell(:id, cell_id).text
572
- end
573
- end
574
-
575
- alias table_data_with_id cell_with_id
576
-
577
-
578
- def is_mac?
579
- RUBY_PLATFORM.downcase.include?("darwin")
580
- end
581
-
582
- def is_windows?
583
- RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
584
- end
585
-
586
- def is_linux?
587
- RUBY_PLATFORM.downcase.include?("linux")
588
- end
589
-
590
- # Support browser (IE) operations using unicode
591
- # Example:
592
- # click_button("Google 搜索")
593
- # Reference: http://jira.openqa.org/browse/WTR-219
594
- def support_utf8
595
- if is_windows?
596
- require 'win32ole'
597
- WIN32OLE.codepage = WIN32OLE::CP_UTF8
598
- end
599
- end
600
-
601
- alias support_unicode support_utf8
602
-
603
-
604
-
605
- # Execute the provided block until either (1) it returns true, or
606
- # (2) the timeout (in seconds) has been reached. If the timeout is reached,
607
- # a TimeOutException will be raised. The block will always
608
- # execute at least once.
609
- #
610
- # This does not handle error, if the given block raise error, the statement finish with error
611
- # Examples:
612
- # wait_until {puts 'hello'}
613
- # wait_until { div(:id, :receipt_date).exists? }
614
- #
615
- def wait_until(timeout = $testwise_polling_timeout || 30, polling_interval = $testwise_polling_interval || 1, & block)
616
- waiter = Watir::Waiter.new(timeout, polling_interval)
617
- waiter.wait_until { yield }
618
- end
619
-
620
- # Wait for specific seconds for an Ajax update finish.
621
- # Trick: In your Rails application,
622
- # :loading => "Element.show('search_indicator');
623
- # :complete => "Element.hide('search_indicator');
624
- #
625
- # <%= image_tag("indicator.gif", :id => 'search_indicator', :style => 'display:none') %>
626
- #
627
- # Typical usage:
628
- # ajax_wait_for_element("search_indicator", 30)
629
- # ajax_wait_for_element("search_indicator", 30, "show")
630
- # ajax_wait_for_element("search_indicator", 30, "hide")
631
- # ajax_wait_for_element("search_indicator", 30, "show", 5) # check every 5 seconds
632
- #
633
- # Warning: this method has not been fully tested, if you are not using Rails, change your parameter accordingly.
634
- #
635
- def ajax_wait_for_element(element_id, seconds, status='show', check_interval = $testwise_polling_interval)
636
- count = 0
637
- check_interval = 1 if check_interval < 1 or check_interval > seconds
638
- while count < (seconds / check_interval) do
639
- search_indicator = @web_browser.element_by_id(element_id)
640
- search_indicator_outer_html = search_indicator.outerHtml if search_indicator
641
- if status == 'hide'
642
- return true if search_indicator_outer_html and !search_indicator_outer_html.include?('style="DISPLAY: none"')
643
- else
644
- return true if search_indicator_outer_html and search_indicator_outer_html.include?('style="DISPLAY: none"')
645
- end
646
- sleep check_interval if check_interval > 0 and check_interval < 5 * 60 # wait max 5 minutes
647
- count += 1
648
- end
649
- return false
650
- end
651
-
652
- #Wait the element with given id to be present in web page
653
- #
654
- # Warning: this not working in Firefox, try use wait_util or try instead
655
- def wait_for_element(element_id, timeout = $testwise_polling_timeout, interval = $testwise_polling_interval)
656
- start_time = Time.now
657
- #TODO might not work with Firefox
658
- until @web_browser.element_by_id(element_id) do
659
- sleep(interval)
660
- if (Time.now - start_time) > timeout
661
- raise RuntimeError, "failed to find element: #{element_id} for max #{timeout}"
662
- end
663
- end
664
- end
665
-
666
- # Clear popup windows such as 'Security Alert' or 'Security Information' popup window,
667
- #
668
- # Screenshot see http://kb2.adobe.com/cps/165/tn_16588.html
669
- #
670
- # You can also by pass security alerts by change IE setting, http://kb.iu.edu/data/amuj.html
671
- #
672
- # Example
673
- # clear_popup("Security Information", 5, true) # check for Security Information for 5 seconds, click Yes
674
- def clear_popup(popup_win_title, seconds = 10, yes = true)
675
- # commonly "Security Alert", "Security Information"
676
- if is_windows?
677
- sleep 1
678
- autoit = WIN32OLE.new('AutoItX3.Control')
679
- # Look for window with given title. Give up after 1 second.
680
- ret = autoit.WinWait(popup_win_title, '', seconds)
681
- #
682
- # If window found, send appropriate keystroke (e.g. {enter}, {Y}, {N}).
683
- if ret == 1 then
684
- puts "about to send click Yes" if debugging?
685
- button_id = yes ? "Button1" : "Button2" # Yes or No
686
- autoit.ControlClick(popup_win_title, '', button_id)
687
- end
688
- sleep(0.5)
689
- else
690
- raise "Currently supported only on Windows"
691
- end
692
- end
693
-
694
- # Watir 1.9 way of handling javascript dialog
695
- def javascript_dialog
696
- @web_browser.javascript_dialog
697
- end
698
-
699
- def select_file_for_upload(file_field_name, file_path)
700
- if is_windows?
701
- normalized_file_path = file_path.gsub("/", "\\")
702
- if $support_ie8 && check_ie_version && @ie_version >= 8
703
- # puts "IE8"
704
- file_field(:name, file_field_name).set(normalized_file_path)
705
- # choose_file_dialog(normalized_file_path)
706
- else
707
- file_field(:name, file_field_name).set(normalized_file_path)
708
- end
709
- else
710
- # for firefox, just call file_field, it may fail
711
- file_field(:name, file_field_name).set(normalized_file_path)
712
- end
713
- end
714
-
715
- def choose_file_dialog(file_path)
716
- Watir.autoit.WinWaitActive("Choose File to Upload", '', 10)
717
- Watir.autoit.ControlSetText("Choose File to Upload", "", 1148, file_path)
718
- Watir.autoit.ControlClick("Choose File to Upload", "", "&Open")
719
- end
720
-
721
- def check_ie_version
722
- if is_windows? && @ie_version.nil?
723
- begin
724
- cmd = 'reg query "HKLM\SOFTWARE\Microsoft\Internet Explorer" /v Version';
725
- result = `#{cmd}`
726
- version_line = nil
727
- result.each do |line|
728
- if (line =~ /Version\s+REG_SZ\s+([\d\.]+)/)
729
- version_line = $1
730
- end
731
- end
732
-
733
- if version_line =~ /^\s*(\d+)\./
734
- @ie_version = $1.to_i
735
- end
736
- rescue => e
737
- end
738
- end
739
- end
740
-
741
- # Use AutoIT3 to send password
742
- # title starts with "Connect to ..."
743
- def basic_authentication_ie(title, username, password, options = {})
744
- default_options = {:textctrl_username => "Edit2",
745
- :textctrl_password => "Edit3",
746
- :button_ok => 'Button1'
747
- }
748
-
749
- options = default_options.merge(options)
750
-
751
- title ||= ""
752
- if title =~ /^Connect\sto/
753
- full_title = title
754
- else
755
- full_title = "Connect to #{title}"
756
- end
757
- require 'rformspec'
758
- login_win = RFormSpec::Window.new(full_title)
759
- login_win.send_control_text(options[:textctrl_username], username)
760
- login_win.send_control_text(options[:textctrl_password], password)
761
- login_win.click_button("OK")
762
- end
763
-
764
- def basic_authentication(username, password, options = {})
765
- basic_authentication_ie(options[:title], username, password, options)
766
- end
767
-
768
- # end of methods
769
-
770
- end
771
-
772
- end
1
+ # convenient methods to drive the browser.
2
+ # convenient methods to drive the browser.
3
+ #
4
+ # Instead of
5
+ # browser.click_button("submit")
6
+ # You can just use
7
+ # click_button("submit")
8
+ #
9
+ require File.join(File.dirname(__FILE__), '../plugins/testwise_plugin')
10
+ require File.join(File.dirname(__FILE__), '../rwebspec-common/popup')
11
+ require File.join(File.dirname(__FILE__), '../rwebspec-common/matchers', "contains_text.rb")
12
+
13
+ require 'timeout'
14
+ require 'uri'
15
+
16
+ # require 'watir/screen_capture' if RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
17
+
18
+ module RWebSpec
19
+ module Driver
20
+ include RWebSpec::TestWisePlugin
21
+ include RWebSpec::Popup
22
+
23
+ # open a browser, and set base_url via hash, but does not acually
24
+ #
25
+ # example:
26
+ # open_browser :base_url => http://localhost:8080
27
+ #
28
+ # There are 3 ways to set base url
29
+ # 1. pass as first argument
30
+ # 2. If running using TestWise, used as confiured
31
+ # 3. Use default value set
32
+ def open_browser(base_url = nil, options = {})
33
+
34
+ begin
35
+ support_unicode
36
+ rescue => e
37
+ puts "Unicode may not work in IE, #{e}"
38
+ end
39
+
40
+ base_url ||= $TESTWISE_PROJECT_BASE_URL
41
+ base_url ||= $TESTWISE_PROJECT_BASE_URL
42
+ base_url ||= $BASE_URL
43
+ raise "base_url must be set" if base_url.nil?
44
+
45
+ default_options = {:speed => "fast",
46
+ :visible => true,
47
+ :highlight_colour => 'yellow',
48
+ :close_others => true,
49
+ :start_new => false, # start a new browser always
50
+ :go => true}
51
+
52
+ options = default_options.merge options
53
+ ($TESTWISE_HIDE_BROWSER || $TESTWISE_HIDE_BROWSER) ? $HIDE_IE = true : $HIDE_IE = false
54
+
55
+ if base_url =~ /^file:/
56
+ uri_base = base_url
57
+ else
58
+ uri = URI.parse(base_url)
59
+ uri_base = "#{uri.scheme}://#{uri.host}:#{uri.port}"
60
+ end
61
+
62
+ if options[:start_new]
63
+ @web_browser = WebBrowser.new(uri_base, nil, options)
64
+ else
65
+ @web_browser = WebBrowser.reuse(uri_base, options) # Reuse existing browser
66
+ end
67
+
68
+ if base_url =~ /^file:/
69
+ goto_url(base_url) # for files, no base url
70
+ else
71
+ (uri.path.length == 0) ? begin_at("/") : begin_at(uri.path) if options[:go]
72
+ end
73
+
74
+ return @web_browser
75
+ end
76
+
77
+ alias open_browser_with open_browser
78
+
79
+ # return the underlying RWebSpec::Browser object
80
+ def browser
81
+ @web_browser
82
+ end
83
+
84
+
85
+ # Close the current browser window (started by the script). If no browser started, then close
86
+ # all browser windows.
87
+ #
88
+ def close_browser
89
+ if @web_browser
90
+ # Old TestWise version
91
+ # @web_browser.close_browser unless $TESTWISE_LEAVE_BROWSER_OPEN_AFTER_RUN
92
+ @web_browser.close_browser
93
+ else
94
+ close_all_browsers
95
+ end
96
+ end
97
+ alias close_ie close_browser
98
+
99
+
100
+ # Close all opening browser windows
101
+ #
102
+ def close_all_browsers
103
+ if @web_browser
104
+ Watir::IE.close_all
105
+ else
106
+ browser_type = $TESTWISE_BROWSER ? $TESTWISE_BROWSER.downcase.to_sym : :ie
107
+ WebBrowser.close_all_browsers(browser_type)
108
+ end
109
+ end
110
+
111
+ # Verify the next page following an operation.
112
+ #
113
+ # Typical usage:
114
+ # login_page.click_login
115
+ # expect_page HomePage
116
+ def expect_page(page_clazz, argument = nil)
117
+ if argument
118
+ @web_browser.expect_page(page_clazz, argument)
119
+ else
120
+ @web_browser.expect_page(page_clazz)
121
+ end
122
+ end
123
+
124
+ def context
125
+ @web_browser.context
126
+ end
127
+
128
+ # Starting browser with a URL
129
+ #
130
+ # Example:
131
+ # begin_at("http://www.itest2.com")
132
+ def begin_at(url)
133
+ dump_caller_stack
134
+ @web_browser.begin_at(url)
135
+ end
136
+
137
+ # Return the Watir::IE instance
138
+ #
139
+ def ie
140
+ @web_browser.ie
141
+ end
142
+
143
+ # Return the FireWatir::Firefox instance
144
+ #
145
+ def firefox
146
+ @web_browser.firefox
147
+ end
148
+
149
+ def is_firefox?
150
+ @web_browser.is_firefox? if @web_browser
151
+ end
152
+
153
+ # Go to another page on the testing site.
154
+ #
155
+ # open_browser("http://www.itest2.com")
156
+ # goto_page("/demo") # visit page http://www.itest2.com/demo
157
+ #
158
+ def goto_page(page)
159
+ perform_operation {
160
+ @web_browser.goto_page(page) if @web_browser
161
+ }
162
+ end
163
+ alias visit goto_page
164
+
165
+
166
+ # Go to another web site, normally different site being tested on
167
+ #
168
+ # open_browser("http://www.itest2.com")
169
+ # goto_url("http://myorganized.info")
170
+ def goto_url(url)
171
+ @web_browser.goto_url url
172
+ end
173
+
174
+ # Go to specific url in background (i.e not via browwser, different from goto_url)
175
+ # This won't share the session with what's currenlty in browser, proxy setting
176
+ #
177
+ # One use example: resetting database
178
+ # background_visit("/reset")
179
+ #
180
+ def background_visit(url, opts = {})
181
+ require 'httpclient'
182
+ begin
183
+ client = HTTPClient.new
184
+ if url && url =~ /^http/
185
+ http_response = client.get(url).body
186
+ else
187
+ base_url = $TESTWISE_PROJECT_BASE_URL || $TESTWISE_PROJECT_BASE_URL || $BASE_URL
188
+ http_response = client.get("#{base_url}#{url}").body
189
+ end
190
+
191
+ http_response = http_response.content if http_response.respond_to?("content")
192
+ rescue => e
193
+ raise e
194
+ end
195
+ end
196
+
197
+ # Attach to existing browser window
198
+ #
199
+ # attach_browser(:title, "Page" )
200
+ # attach_browser(:url, "http://wwww..." )
201
+ def attach_browser(how, what, options = {})
202
+ options.merge!(:browser => is_firefox? ? "Firefox" : "IE") unless options[:browser]
203
+ begin
204
+ options.merge!(:base_url => browser.context.base_url)
205
+ rescue => e
206
+ puts "failed to set base_url, ignore : #{e}"
207
+ end
208
+ WebBrowser.attach_browser(how, what, options)
209
+ end
210
+
211
+ # Reuse current an opened browser window instead of opening a new one
212
+ # example:
213
+ # use_current_browser(:title, /.*/) # use what ever browser window
214
+ # use_current_browser(:title, "TestWise") # use browser window with title "TestWise"
215
+ def use_current_browser(how = :title, what = /.*/)
216
+ @web_browser = WebBrowser.attach_browser(how, what)
217
+ end
218
+
219
+ ##
220
+ # Delegate to WebTester
221
+ #
222
+ # Note:
223
+ # label(:id, "abc") # OK
224
+ # label(:id, :abc) # Error
225
+ #
226
+ # Depends on which object type, you can use following attribute
227
+ # More details: http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
228
+ #
229
+ # :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. *
230
+ # :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. *
231
+ # :value Used to find a text field with a given default value, or a button with a given caption, or a text field
232
+ # :text Used for links, spans, divs and other element that contain text.
233
+ # :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.
234
+ # :class Used for an element that has a "class=" attribute.
235
+ # :title Used for an element that has a "title=" attribute.
236
+ # :xpath Finds the item using xpath query.
237
+ # :method Used only for forms, the method attribute of a form is either GET or POST.
238
+ # :action Used only for form elements, specifies the URL where the form is to be submitted.
239
+ # :href Used to identify a link by its "href=" attribute.
240
+ # :src Used to identify an image by its URL.
241
+ #
242
+
243
+ # area <area> tags
244
+ # button <input> tags with type=button, submit, image or reset
245
+ # check_box <input> tags with type=checkbox
246
+ # div <div> tags
247
+ # form <form> tags
248
+ # frame frames, including both the <frame> elements and the corresponding pages
249
+ # h1 - h6 <h1>, <h2>, <h3>, <h4>, <h5>, <h6> tags
250
+ # hidden <input> tags with type=hidden
251
+ # image <img> tags
252
+ # label <label> tags (including "for" attribute)
253
+ # li <li> tags
254
+ # link <a> (anchor) tags
255
+ # map <map> tags
256
+ # radio <input> tags with the type=radio; known as radio buttons
257
+ # select_list <select> tags, known as drop-downs or drop-down lists
258
+ # span <span> tags
259
+ # table <table> tags, including row and cell methods for accessing nested elements
260
+ # text_field <input> tags with the type=text (single-line), type=textarea (multi-line), and type=password
261
+ # p <p> (paragraph) tags, because
262
+ [:area, :button, :td, :checkbox, :div, :form, :frame, :h1, :h2, :h3, :h4, :h5, :h6, :hidden, :image, :li, :link, :map, :pre, :tr, :radio, :select_list, :span, :table, :text_field, :paragraph, :file_field, :label].each do |method|
263
+ define_method method do |* args|
264
+ perform_operation { @web_browser.send(method, * args) if @web_browser }
265
+ end
266
+ end
267
+ alias cell td
268
+ alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
269
+ alias row tr
270
+ alias a link
271
+ alias img image
272
+
273
+
274
+ [:back, :forward, :refresh].each do |method|
275
+ define_method(method) do
276
+ perform_operation { @web_browser.send(method) if @web_browser }
277
+ end
278
+ end
279
+ alias refresh_page refresh
280
+ alias go_back back
281
+ alias go_forward forward
282
+
283
+ [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields, :divs, :dls, :dds, :dts, :ems, :lis, :maps, :spans, :strongs, :ps, :pres, :labels, :tds, :trs].each do |method|
284
+ define_method method do
285
+ perform_operation { @web_browser.send(method) if @web_browser }
286
+ end
287
+ end
288
+ alias as links
289
+ alias rows trs
290
+ alias cells tds
291
+ alias imgs images
292
+
293
+
294
+ # 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.
295
+ #
296
+ # page.check_checkbox('bad_ones', 'Chicken Little')
297
+ # page.check_checkbox('good_ones', ['Cars', 'Toy Story'])
298
+ #
299
+ [: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, :check_checkbox, :uncheck_checkbox, :select_option].each do |method|
300
+ define_method method do |* args|
301
+ perform_operation { @web_browser.send(method, * args) if @web_browser }
302
+ end
303
+ end
304
+
305
+ alias enter_text set_form_element
306
+ alias set_hidden_field set_form_element
307
+ alias click_link click_link_with_text
308
+ alias click_button_with_text click_button_with_caption
309
+ alias click_button click_button_with_caption
310
+ alias click_radio_button click_radio_option
311
+ alias clear_radio_button clear_radio_option
312
+
313
+ # for text field can be easier to be identified by attribute "id" instead of "name", not recommended though
314
+ def enter_text_with_id(textfield_id, value)
315
+ perform_operation { text_field(:id, textfield_id).set(value) }
316
+ end
317
+
318
+ def perform_operation(& block)
319
+ begin
320
+ dump_caller_stack
321
+ operation_delay
322
+ yield
323
+ rescue RuntimeError => re
324
+ raise re
325
+ # ensure
326
+ # puts "[DEBUG] ensure #{perform_ok}" unless perform_ok
327
+ end
328
+ end
329
+
330
+ def contains_text(text)
331
+ @web_browser.contains_text(text)
332
+ end
333
+
334
+ # In pages, can't use include, text.should include("abc") won't work
335
+ # Instead,
336
+ # text.should contains("abc"
337
+ def contains(str)
338
+ ContainsText.new(str)
339
+ end
340
+
341
+ alias contain contains
342
+
343
+ # Click image buttion with image source name
344
+ #
345
+ # For an image submit button <input name="submit" type="image" src="/images/search_button.gif">
346
+ # click_button_with_image("search_button.gif")
347
+ def click_button_with_image_src_contains(image_filename)
348
+ perform_operation {
349
+ found = nil
350
+ raise "no buttons in this page" if buttons.length <= 0
351
+ buttons.each { |btn|
352
+ if btn && btn.src && btn.src.include?(image_filename) then
353
+ found = btn
354
+ break
355
+ end
356
+ }
357
+ raise "not image button with src: #{image_filename} found" if found.nil?
358
+ found.click
359
+ }
360
+ end
361
+
362
+ alias click_button_with_image click_button_with_image_src_contains
363
+
364
+ def new_popup_window(options)
365
+ @web_browser.new_popup_window(options)
366
+ end
367
+
368
+
369
+ # Warning: this does not work well with Firefox yet.
370
+ def element_text(elem_id)
371
+ @web_browser.element_value(elem_id)
372
+ end
373
+
374
+ # Identify DOM element by ID
375
+ # Warning: it is only supported on IE
376
+ def element_by_id(elem_id)
377
+ @web_browser.element_by_id(elem_id)
378
+ end
379
+
380
+ # ---
381
+ # For debugging
382
+ # ---
383
+ def dump_response(stream = nil)
384
+ @web_browser.dump_response(stream)
385
+ end
386
+
387
+ def default_dump_dir
388
+ if ($TESTWISE_RUNNING_SPEC_ID && $TESTWISE_WORKING_DIR) || ($TESTWISE_RUNNING_SPEC_ID && $TESTWISE_WORKING_DIR)
389
+
390
+ $TESTWISE_DUMP_DIR = $TESTWISE_DUMP_DIR = File.join($TESTWISE_WORKING_DIR, "dump")
391
+ FileUtils.mkdir($TESTWISE_DUMP_DIR) unless File.exists?($TESTWISE_DUMP_DIR)
392
+
393
+ spec_run_id = $TESTWISE_RUNNING_SPEC_ID || $TESTWISE_RUNNING_SPEC_ID
394
+ spec_run_dir_name = spec_run_id.to_s.rjust(4, "0") unless spec_run_id == "unknown"
395
+ to_dir = File.join($TESTWISE_DUMP_DIR, spec_run_dir_name)
396
+ else
397
+ to_dir = ENV['TEMP_DIR'] || (is_windows? ? "C:\\temp" : "/tmp")
398
+ end
399
+ end
400
+
401
+ # For current page souce to a file in specified folder for inspection
402
+ #
403
+ # save_current_page(:dir => "C:\\mysite", filename => "abc", :replacement => true)
404
+ def save_current_page(options = {})
405
+ default_options = {:replacement => true}
406
+ options = default_options.merge(options)
407
+ to_dir = options[:dir] || default_dump_dir
408
+
409
+ if options[:filename]
410
+ file_name = options[:filename]
411
+ else
412
+ file_name = Time.now.strftime("%m%d%H%M%S") + ".html"
413
+ end
414
+
415
+ Dir.mkdir(to_dir) unless File.exists?(to_dir)
416
+ file = File.join(to_dir, file_name)
417
+
418
+ content = page_source
419
+ base_url = @web_browser.context.base_url
420
+ current_url = @web_browser.url
421
+ current_url =~ /(.*\/).*$/
422
+ current_url_parent = $1
423
+ if options[:replacement] && base_url =~ /^http:/
424
+ File.new(file, "w").puts absolutize_page_nokogiri(content, base_url, current_url_parent)
425
+ else
426
+ File.new(file, "w").puts content
427
+ end
428
+
429
+ end
430
+
431
+
432
+ # <link rel="stylesheet" type="text/css" href="/stylesheets/default.css" />
433
+ # '<script type="text/javascript" src="http://www.jeroenwijering.com/embed/swfobject.js"></script>'
434
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
435
+ # <script type="text/javascript" src="/javascripts/scriptaculous.js?load=effects,builder"></script>
436
+ # <script type="text/javascript" src="/javascripts/extensions/gallery/lightbox.js"></script>
437
+ # <link href="/stylesheets/extensions/gallery/lightbox.css" rel="stylesheet" type="text/css" />
438
+ # <img src="images/mission_48.png" />
439
+ def absolutize_page(content, base_url, current_url_parent)
440
+ modified_content = ""
441
+ content.each_line do |line|
442
+ if line =~ /<script\s+.*src=["'']?(.*)["'].*/i then
443
+ script_src = $1
444
+ substitute_relative_path_in_src_line(line, script_src, base_url, current_url_parent)
445
+ elsif line =~ /<link\s+.*href=["'']?(.*)["'].*/i then
446
+ link_href = $1
447
+ substitute_relative_path_in_src_line(line, link_href, base_url, current_url_parent)
448
+ elsif line =~ /<img\s+.*src=["'']?(.*)["'].*/i then
449
+ img_src = $1
450
+ substitute_relative_path_in_src_line(line, img_src, base_url, current_url_parent)
451
+ end
452
+
453
+ modified_content += line
454
+ end
455
+ return modified_content
456
+ end
457
+
458
+ # absolutize_page referencs using hpricot
459
+ #
460
+ def absolutize_page_hpricot(content, base_url, parent_url)
461
+ return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
462
+ begin
463
+ require 'hpricot'
464
+ doc = Hpricot(content)
465
+ base_url.slice!(-1) if ends_with?(base_url, "/")
466
+ (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || "" }
467
+ (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
468
+ (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
469
+ return doc.to_html
470
+ rescue => e
471
+ absolutize_page(content, base_url, parent_url)
472
+ end
473
+ end
474
+
475
+ def absolutize_page_nokogiri(content, base_url, parent_url)
476
+ return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
477
+ begin
478
+ require 'nokogiri'
479
+ doc = Nokogiri::HTML(content)
480
+ base_url.slice!(-1) if ends_with?(base_url, "/")
481
+ (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || "" }
482
+ (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
483
+ (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
484
+ return doc.to_html
485
+ rescue => e
486
+ absolutize_page(content, base_url, parent_url)
487
+ end
488
+ end
489
+
490
+ ##
491
+ # change
492
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
493
+ # to
494
+ # <script type="text/javascript" src="http://itest2.com/javascripts/prototype.js"></script>
495
+ def absolutify_url(src, base_url, parent_url)
496
+ if src.nil? || src.empty? || src == "//:" || src =~ /\s*http:\/\//i
497
+ return src
498
+ end
499
+
500
+ return "#{base_url}#{src}" if src =~ /^\s*\//
501
+ return "#{parent_url}#{src}" if parent_url
502
+ return src
503
+ end
504
+
505
+ # substut
506
+ def substitute_relative_path_in_src_line(line, script_src, host_url, page_parent_url)
507
+ unless script_src =~ /^["']?http:/
508
+ host_url.slice!(-1) if ends_with?(host_url, "/")
509
+ if script_src =~ /^\s*\// # absolute_path
510
+ line.gsub!(script_src, "#{host_url}#{script_src}")
511
+ else #relative_path
512
+ line.gsub!(script_src, "#{page_parent_url}#{script_src}")
513
+ end
514
+ end
515
+ end
516
+
517
+ def ends_with?(str, suffix)
518
+ suffix = suffix.to_s
519
+ str[-suffix.length, suffix.length] == suffix
520
+ end
521
+
522
+ # current web page title
523
+ def page_title
524
+ @web_browser.page_title
525
+ end
526
+
527
+ # current page source (in HTML)
528
+ def page_source
529
+ @web_browser.page_source
530
+ end
531
+
532
+ # return plain text view of page
533
+ def page_text
534
+ @web_browser.text
535
+ end
536
+
537
+ # return the text of specific (identified by attribute "id") label tag
538
+ # For page containing
539
+ # <label id="preferred_ide">TestWise</label>
540
+ # label_with_id("preferred_ids") # => TestWise
541
+ # label_with_id("preferred_ids", :index => 2) # => TestWise
542
+ def label_with_id(label_id, options = {})
543
+ if options && options[:index] then
544
+ label(:id => label_id.to_s, :index => options[:index]).text
545
+ else
546
+ label(:id, label_id.to_s).text
547
+ end
548
+ end
549
+
550
+ # return the text of specific (identified by attribute "id") span tag
551
+ # For page containing
552
+ # <span id="preferred_recorder">iTest2/Watir Recorder</span>
553
+ # span_with_id("preferred_recorder") # => iTest2/Watir Recorder
554
+ def span_with_id(span_id, options = {})
555
+ if options && options[:index] then
556
+ span(:id => span_id.to_s, :index => options[:index]).text
557
+ else
558
+ span(:id, span_id).text
559
+ end
560
+ end
561
+
562
+ # return the text of specific (identified by attribute "id") ta tag
563
+ # For page containing
564
+ # <td id="preferred_recorder">iTest2/Watir Recorder</span>
565
+ # td_with_id("preferred_recorder") # => iTest2/Watir Recorder
566
+ def cell_with_id(cell_id, options = {})
567
+ if options && options[:index] then
568
+ cell(:id => cell_id.to_s, :index => options[:index]).text
569
+ else
570
+ cell(:id, cell_id).text
571
+ end
572
+ end
573
+
574
+ alias table_data_with_id cell_with_id
575
+
576
+
577
+ def is_mac?
578
+ RUBY_PLATFORM.downcase.include?("darwin")
579
+ end
580
+
581
+ def is_windows?
582
+ RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
583
+ end
584
+
585
+ def is_linux?
586
+ RUBY_PLATFORM.downcase.include?("linux")
587
+ end
588
+
589
+ # Support browser (IE) operations using unicode
590
+ # Example:
591
+ # click_button("Google 搜索")
592
+ # Reference: http://jira.openqa.org/browse/WTR-219
593
+ def support_utf8
594
+ if is_windows?
595
+ require 'win32ole'
596
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
597
+ end
598
+ end
599
+
600
+ alias support_unicode support_utf8
601
+
602
+
603
+
604
+ # Execute the provided block until either (1) it returns true, or
605
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
606
+ # a TimeOutException will be raised. The block will always
607
+ # execute at least once.
608
+ #
609
+ # This does not handle error, if the given block raise error, the statement finish with error
610
+ # Examples:
611
+ # wait_until {puts 'hello'}
612
+ # wait_until { div(:id, :receipt_date).exists? }
613
+ #
614
+ def wait_until(timeout = $testwise_polling_timeout || 30, polling_interval = $testwise_polling_interval || 1, & block)
615
+ waiter = Watir::Waiter.new(timeout, polling_interval)
616
+ waiter.wait_until { yield }
617
+ end
618
+
619
+ # Wait for specific seconds for an Ajax update finish.
620
+ # Trick: In your Rails application,
621
+ # :loading => "Element.show('search_indicator');
622
+ # :complete => "Element.hide('search_indicator');
623
+ #
624
+ # <%= image_tag("indicator.gif", :id => 'search_indicator', :style => 'display:none') %>
625
+ #
626
+ # Typical usage:
627
+ # ajax_wait_for_element("search_indicator", 30)
628
+ # ajax_wait_for_element("search_indicator", 30, "show")
629
+ # ajax_wait_for_element("search_indicator", 30, "hide")
630
+ # ajax_wait_for_element("search_indicator", 30, "show", 5) # check every 5 seconds
631
+ #
632
+ # Warning: this method has not been fully tested, if you are not using Rails, change your parameter accordingly.
633
+ #
634
+ def ajax_wait_for_element(element_id, seconds, status='show', check_interval = $testwise_polling_interval)
635
+ count = 0
636
+ check_interval = 1 if check_interval < 1 or check_interval > seconds
637
+ while count < (seconds / check_interval) do
638
+ search_indicator = @web_browser.element_by_id(element_id)
639
+ search_indicator_outer_html = search_indicator.outerHtml if search_indicator
640
+ if status == 'hide'
641
+ return true if search_indicator_outer_html and !search_indicator_outer_html.include?('style="DISPLAY: none"')
642
+ else
643
+ return true if search_indicator_outer_html and search_indicator_outer_html.include?('style="DISPLAY: none"')
644
+ end
645
+ sleep check_interval if check_interval > 0 and check_interval < 5 * 60 # wait max 5 minutes
646
+ count += 1
647
+ end
648
+ return false
649
+ end
650
+
651
+ #Wait the element with given id to be present in web page
652
+ #
653
+ # Warning: this not working in Firefox, try use wait_util or try instead
654
+ def wait_for_element(element_id, timeout = $testwise_polling_timeout, interval = $testwise_polling_interval)
655
+ start_time = Time.now
656
+ #TODO might not work with Firefox
657
+ until @web_browser.element_by_id(element_id) do
658
+ sleep(interval)
659
+ if (Time.now - start_time) > timeout
660
+ raise RuntimeError, "failed to find element: #{element_id} for max #{timeout}"
661
+ end
662
+ end
663
+ end
664
+
665
+ # Clear popup windows such as 'Security Alert' or 'Security Information' popup window,
666
+ #
667
+ # Screenshot see http://kb2.adobe.com/cps/165/tn_16588.html
668
+ #
669
+ # You can also by pass security alerts by change IE setting, http://kb.iu.edu/data/amuj.html
670
+ #
671
+ # Example
672
+ # clear_popup("Security Information", 5, true) # check for Security Information for 5 seconds, click Yes
673
+ def clear_popup(popup_win_title, seconds = 10, yes = true)
674
+ # commonly "Security Alert", "Security Information"
675
+ if is_windows?
676
+ sleep 1
677
+ autoit = WIN32OLE.new('AutoItX3.Control')
678
+ # Look for window with given title. Give up after 1 second.
679
+ ret = autoit.WinWait(popup_win_title, '', seconds)
680
+ #
681
+ # If window found, send appropriate keystroke (e.g. {enter}, {Y}, {N}).
682
+ if ret == 1 then
683
+ puts "about to send click Yes" if debugging?
684
+ button_id = yes ? "Button1" : "Button2" # Yes or No
685
+ autoit.ControlClick(popup_win_title, '', button_id)
686
+ end
687
+ sleep(0.5)
688
+ else
689
+ raise "Currently supported only on Windows"
690
+ end
691
+ end
692
+
693
+ # Watir 1.9 way of handling javascript dialog
694
+ def javascript_dialog
695
+ @web_browser.javascript_dialog
696
+ end
697
+
698
+ def select_file_for_upload(file_field_name, file_path)
699
+ if is_windows?
700
+ normalized_file_path = file_path.gsub("/", "\\")
701
+ file_field(:name, file_field_name).set(normalized_file_path)
702
+ else
703
+ # for firefox, just call file_field, it may fail
704
+ file_field(:name, file_field_name).set(normalized_file_path)
705
+ end
706
+ end
707
+
708
+ def check_ie_version
709
+ if is_windows? && @ie_version.nil?
710
+ begin
711
+ cmd = 'reg query "HKLM\SOFTWARE\Microsoft\Internet Explorer" /v Version';
712
+ result = `#{cmd}`
713
+ version_line = nil
714
+ result.each do |line|
715
+ if (line =~ /Version\s+REG_SZ\s+([\d\.]+)/)
716
+ version_line = $1
717
+ end
718
+ end
719
+
720
+ if version_line =~ /^\s*(\d+)\./
721
+ @ie_version = $1.to_i
722
+ end
723
+ rescue => e
724
+ end
725
+ end
726
+ end
727
+
728
+ # Use AutoIT3 to send password
729
+ # title starts with "Connect to ..."
730
+ def basic_authentication_ie(title, username, password, options = {})
731
+ default_options = {:textctrl_username => "Edit2",
732
+ :textctrl_password => "Edit3",
733
+ :button_ok => 'Button1'
734
+ }
735
+
736
+ options = default_options.merge(options)
737
+
738
+ title ||= ""
739
+ if title =~ /^Connect\sto/
740
+ full_title = title
741
+ else
742
+ full_title = "Connect to #{title}"
743
+ end
744
+ require 'rformspec'
745
+ login_win = RFormSpec::Window.new(full_title)
746
+ login_win.send_control_text(options[:textctrl_username], username)
747
+ login_win.send_control_text(options[:textctrl_password], password)
748
+ login_win.click_button("OK")
749
+ end
750
+
751
+ def basic_authentication(username, password, options = {})
752
+ basic_authentication_ie(options[:title], username, password, options)
753
+ end
754
+
755
+ # end of methods
756
+
757
+ end
758
+
759
+ end