rwebspec-webdriver 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ #
2
+ # clickJSDialog.rb
3
+ #
4
+ #
5
+ # This file contains the JS clicker when it runs as a separate process
6
+ require 'watir/winClicker'
7
+
8
+ button = "OK"
9
+ button = ARGV[0] unless ARGV[0] == nil
10
+ sleepTime = 0
11
+ sleepTime = ARGV[1] unless ARGV[1] == nil
12
+
13
+ clicker= WinClicker.new
14
+ result = clicker.clickJavaScriptDialog( button )
15
+ clicker = nil
@@ -0,0 +1,24 @@
1
+ #***********************************************************
2
+ #* Copyright (c) 2006 - 2009 Zhimin Zhan.
3
+ #* Distributed open-source, see full license in MIT-LICENSE
4
+ #***********************************************************
5
+
6
+ module RWebSpec
7
+
8
+ ##
9
+ # Store test optionns
10
+ #
11
+ class Context
12
+ attr_accessor :base_url
13
+
14
+ def initialize(base_url)
15
+ set_base_url(base_url)
16
+ end
17
+
18
+ def set_base_url(baseUrl)
19
+ @base_url = baseUrl
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,74 @@
1
+ require 'active_record' # change rwebspec/database
2
+
3
+ module RWebSpec
4
+ module DatabaseChecker
5
+
6
+ # Example
7
+ # connect_to_database mysql_db(:host => "localhost", :database => "lavabuild_local", :user => "root", :password => ""), true
8
+ def mysql_db(settings)
9
+ options = {:adapter => "mysql"}
10
+ options.merge!(settings)
11
+ end
12
+
13
+ # connect_to_database sqlite3_db(:database => File.join(File.dirname(__FILE__), "testdata", "sample.sqlite3")), true
14
+ def sqlite3_db(settings)
15
+ options = {:adapter => "sqlite3"}
16
+ options.merge!(settings)
17
+ end
18
+
19
+ def sqlserver_db(settings)
20
+ options = {:adapter => "sqlserver"}
21
+ options[:username] ||= settings[:user]
22
+ options.merge!(settings)
23
+ end
24
+
25
+ def sqlserver_db_dbi(options)
26
+ options[:user] ||= options[:username]
27
+ options[:username] ||= options[:user]
28
+ conn_str = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{options[:host]};Initial Catalog=#{options[:database]};User ID=\"#{options[:user]}\";password=\"#{options[:password]}\" "
29
+ dbh = DBI.connect(conn_str)
30
+ end
31
+
32
+ def clear_database_connection
33
+ begin
34
+ ActiveRecord::Base.remove_connection
35
+ rescue => e
36
+ puts "failed o clear database connection: #{e}"
37
+ end
38
+ end
39
+
40
+ # Connect to databse, example
41
+ # mysql_db(:host => "localhost", :database => "lavabuild_local", :user => "root", :password => "")
42
+ def connect_to_database(db_settings, force = false)
43
+ # only setup database connection once
44
+ if force
45
+ ActiveRecord::Base.establish_connection(db_settings)
46
+ else
47
+ begin
48
+ ActiveRecord::Base.connection
49
+ rescue => e
50
+ require 'pp'
51
+ pp db_settings
52
+ puts "failed to connect: #{e}"
53
+ ActiveRecord::Base.establish_connection(db_settings)
54
+ end
55
+ end
56
+ end
57
+
58
+ def load_table(table_name)
59
+ begin
60
+ ActiveRecord::Base.connection
61
+ rescue =>e
62
+ raise "No database connection setup yet, use connect_to_database() method"
63
+ end
64
+ class_name = table_name.classify
65
+ # define the class, so can use ActiveRecord in
66
+ # such as
67
+ # Perosn.count.should == 2
68
+ def_class = "class ::#{class_name} < ActiveRecord::Base; end"
69
+ eval def_class
70
+ return def_class
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,1009 @@
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__), '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
+ module RWebSpec
17
+ module Driver
18
+ include RWebSpec::TestWisePlugin
19
+ include RWebSpec::Popup
20
+
21
+ @@default_polling_interval = 1 # second
22
+ @@default_timeout = 30 # seconds
23
+
24
+ # open a browser, and set base_url via hash, but does not acually
25
+ #
26
+ # example:
27
+ # open_browser :base_url => http://localhost:8080
28
+ #
29
+ # There are 3 ways to set base url
30
+ # 1. pass as first argument
31
+ # 2. If running using TestWise, used as confiured
32
+ # 3. Use default value set
33
+ #
34
+ #
35
+ # New Options:
36
+ # :browser => :ie | :firefox | :chrome
37
+ def open_browser(base_url = nil, options = {})
38
+ puts "[DEBUG] [SeleniumDriver] Callling open_browser #{base_url}"
39
+ begin
40
+ support_unicode
41
+ rescue => e
42
+ puts "Unicode may not work in IE, #{e}"
43
+ end
44
+
45
+ base_url ||= $ITEST2_PROJECT_BASE_URL
46
+ base_url ||= $BASE_URL
47
+ raise "base_url must be set" if base_url.nil?
48
+
49
+ default_options = {:speed => "fast",
50
+ :visible => true,
51
+ :highlight_colour => 'yellow',
52
+ :close_others => true,
53
+ :start_new => true, # start a new browser always
54
+ :go => true}
55
+
56
+ options = default_options.merge options
57
+
58
+ if $ITEST2_BROWSER
59
+ options[:browser] = $ITEST2_BROWSER.downcase
60
+ end
61
+ if options[:firefox] && options[:browser].nil? then
62
+ options[:browser] = "firefox" # legacy
63
+ end
64
+
65
+ if base_url =~ /^file:/
66
+ uri_base = base_url
67
+ else
68
+ uri = URI.parse(base_url)
69
+ uri_base = "#{uri.scheme}://#{uri.host}:#{uri.port}"
70
+ end
71
+
72
+ if options[:start_new] || $celerity_loaded
73
+ puts "[DEBUG] [SeleniumBrowser] creating a new browser"
74
+ @web_browser = WebBrowser.new(uri_base, nil, options)
75
+ puts "[DEBUG] [SeleniumBrowser] browser: #{@web_browser.inspect}"
76
+ else
77
+ @web_browser = WebBrowser.reuse(uri_base, options) # Reuse existing browser
78
+ end
79
+
80
+ if base_url =~ /^file:/
81
+ goto_url(base_url) # for files, no base url
82
+ else
83
+ (uri.path.length == 0) ? begin_at(base_url) : begin_at(uri.path) if options[:go]
84
+ end
85
+
86
+ return @web_browser
87
+ end
88
+
89
+ alias open_browser_with open_browser
90
+
91
+ # return the underlying RWebSpec::Browser object
92
+ def browser
93
+ @web_browser
94
+ end
95
+
96
+
97
+ def quit
98
+ @web_browser.quit
99
+ end
100
+
101
+ def find_element(how, what)
102
+ @web_browser.find_element(how, what)
103
+ end
104
+
105
+ def find_elements(how, what)
106
+ @web_browser.find_elements(how, what)
107
+ end
108
+
109
+ # Close the current browser window (started by the script). If no browser started, then close
110
+ # all browser windows.
111
+ #
112
+ def close_browser
113
+ if @web_browser
114
+ # Old TestWise version
115
+ # @web_browser.close_browser unless $ITEST2_LEAVE_BROWSER_OPEN_AFTER_RUN
116
+ @web_browser.close_browser
117
+ else
118
+ WebBrowser.close_all_browsers
119
+ end
120
+ end
121
+ alias close_ie close_browser
122
+
123
+
124
+ # Close all opening browser windows
125
+ #
126
+ def close_all_browsers
127
+ #TODO
128
+ end
129
+
130
+ # Verify the next page following an operation.
131
+ #
132
+ # Typical usage:
133
+ # login_page.click_login
134
+ # expect_page HomePage
135
+ def expect_page(page_clazz, argument = nil)
136
+ if argument
137
+ @web_browser.expect_page(page_clazz, argument)
138
+ else
139
+ @web_browser.expect_page(page_clazz)
140
+ end
141
+ end
142
+
143
+ def context
144
+ @web_browser.context
145
+ end
146
+
147
+ # Starting browser with a URL
148
+ #
149
+ # Example:
150
+ # begin_at("http://www.itest2.com")
151
+ def begin_at(url)
152
+ puts "[DEBUG] [SeleniumBrowser] begin_at #{url}"
153
+ dump_caller_stack
154
+ @web_browser.begin_at(url)
155
+ end
156
+
157
+ # Return the Watir::IE instance
158
+ #
159
+ def ie
160
+ @web_browser.ie
161
+ end
162
+
163
+ # Return the FireWatir::Firefox instance
164
+ #
165
+ def firefox
166
+ @web_browser.firefox
167
+ end
168
+
169
+ def is_firefox?
170
+ @web_browser.is_firefox? if @web_browser
171
+ end
172
+
173
+ def is_ie?
174
+ @web_browser.is_ie? if @web_browser
175
+ end
176
+
177
+ def is_htmlunit?
178
+ RUBY_PLATFORM =~ /java/ && @web_browser
179
+ end
180
+
181
+ # Go to another page on the testing site.
182
+ #
183
+ # open_browser("http://www.itest2.com")
184
+ # goto_page("/demo") # visit page http://www.itest2.com/demo
185
+ #
186
+ def goto_page(page)
187
+ perform_operation {
188
+ @web_browser.goto_page(page) if @web_browser
189
+ }
190
+ end
191
+ alias visit goto_page
192
+
193
+ # Go to another web site, normally different site being tested on
194
+ #
195
+ # open_browser("http://www.itest2.com")
196
+ # goto_url("http://myorganized.info")
197
+ def goto_url(url)
198
+ puts "Calling web_browser goto: #{@web_browser.inspect}"
199
+ @web_browser.goto_url url
200
+ end
201
+
202
+ # Attach to existinb browser window
203
+ #
204
+ # attach_browser(:title, )
205
+ def attach_browser(how, what, options = {})
206
+ #TODO
207
+ options.merge!(:browser => is_firefox? ? "Firefox" : "IE") unless options[:browser]
208
+ begin
209
+ options.merge!(:base_url => browser.context.base_url)
210
+ rescue => e
211
+ puts "failed to set base_url, ignore : #{e}"
212
+ end
213
+ WebBrowser.attach_browser(how, what, options)
214
+ end
215
+
216
+ # Reuse current an opened browser window instead of opening a new one
217
+ # example:
218
+ # use_current_browser(:title, /.*/) # use what ever browser window
219
+ # use_current_browser(:title, "TestWise") # use browser window with title "TestWise"
220
+ def use_current_browser(how = :title, what = /.*/)
221
+ @web_browser = WebBrowser.attach_browser(how, what)
222
+ end
223
+
224
+ [:back, :forward, :refresh].each do |method|
225
+ define_method(method) do
226
+ perform_operation { @web_browser.send(method) if @web_browser }
227
+ end
228
+ end
229
+ alias refresh_page refresh
230
+ alias go_back back
231
+ alias go_forward forward
232
+
233
+ ##
234
+ # Delegate to WebTester
235
+ #
236
+ # Note:
237
+ # label(:id, "abc") # OK
238
+ # label(:id, :abc) # Error
239
+ #
240
+ # Depends on which object type, you can use following attribute
241
+ # More details: http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
242
+ #
243
+ # :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. *
244
+ # :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. *
245
+ # :value Used to find a text field with a given default value, or a button with a given caption, or a text field
246
+ # :text Used for links, spans, divs and other element that contain text.
247
+ # :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.
248
+ # :class Used for an element that has a "class=" attribute.
249
+ # :title Used for an element that has a "title=" attribute.
250
+ # :xpath Finds the item using xpath query.
251
+ # :method Used only for forms, the method attribute of a form is either GET or POST.
252
+ # :action Used only for form elements, specifies the URL where the form is to be submitted.
253
+ # :href Used to identify a link by its "href=" attribute.
254
+ # :src Used to identify an image by its URL.
255
+ #
256
+
257
+ # area <area> tags
258
+ # button <input> tags with type=button, submit, image or reset
259
+ # check_box <input> tags with type=checkbox
260
+ # div <div> tags
261
+ # form <form> tags
262
+ # frame frames, including both the <frame> elements and the corresponding pages
263
+ # h1 - h6 <h1>, <h2>, <h3>, <h4>, <h5>, <h6> tags
264
+ # hidden <input> tags with type=hidden
265
+ # image <img> tags
266
+ # label <label> tags (including "for" attribute)
267
+ # li <li> tags
268
+ # link <a> (anchor) tags
269
+ # map <map> tags
270
+ # radio <input> tags with the type=radio; known as radio buttons
271
+ # select_list <select> tags, known as drop-downs or drop-down lists
272
+ # span <span> tags
273
+ # table <table> tags, including row and cell methods for accessing nested elements
274
+ # text_field <input> tags with the type=text (single-line), type=textarea (multi-line), and type=password
275
+ # p <p> (paragraph) tags, because
276
+ [: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|
277
+ define_method method do |* args|
278
+ perform_operation { @web_browser.send(method, * args) if @web_browser }
279
+ end
280
+ end
281
+ alias td cell
282
+ alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
283
+ alias tr row
284
+
285
+
286
+ [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields].each do |method|
287
+ define_method method do
288
+ perform_operation { @web_browser.send(method) if @web_browser }
289
+ end
290
+ end
291
+
292
+ # 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.
293
+ #
294
+ # page.check_checkbox('bad_ones', 'Chicken Little')
295
+ # page.check_checkbox('good_ones', ['Cars', 'Toy Story'])
296
+ #
297
+ [: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|
298
+ define_method method do |* args|
299
+ perform_operation { @web_browser.send(method, * args) if @web_browser }
300
+ end
301
+ end
302
+
303
+ alias enter_text set_form_element
304
+ alias set_hidden_field set_form_element
305
+ alias click_link click_link_with_text
306
+ alias click_button_with_text click_button_with_caption
307
+ alias click_button click_button_with_caption
308
+ alias click_radio_button click_radio_option
309
+ alias clear_radio_button clear_radio_option
310
+
311
+ # for text field can be easier to be identified by attribute "id" instead of "name", not recommended though
312
+ def enter_text_with_id(textfield_id, value)
313
+ perform_operation {
314
+ elements = find_elements(:id, textfield_id)
315
+ if elements.size == 1 then
316
+ elements[0].send_keys(value)
317
+ else
318
+ smaller_set = elements.select {|x| x.tag_name == "textarea" || (x.tag_name == "input" && x.attribute("text")) }
319
+ smaller_set[0].send_keys(value)
320
+ end
321
+ }
322
+ end
323
+
324
+ def perform_operation(& block)
325
+ begin
326
+ dump_caller_stack
327
+ operation_delay
328
+ yield
329
+ rescue RuntimeError => re
330
+ puts "[DEBUG] operation error: #{re}"
331
+ raise re
332
+ # ensure
333
+ # puts "[DEBUG] ensure #{perform_ok}" unless perform_ok
334
+ end
335
+ end
336
+
337
+ def contains_text(text)
338
+ @web_browser.contains_text(text)
339
+ end
340
+
341
+ # In pages, can't use include, text.should include("abc") won't work
342
+ # Instead,
343
+ # text.should contains("abc"
344
+ def contains(str)
345
+ ContainsText.new(str)
346
+ end
347
+
348
+ alias contain contains
349
+
350
+ # Click image buttion with image source name
351
+ #
352
+ # For an image submit button <input name="submit" type="image" src="/images/search_button.gif">
353
+ # click_button_with_image("search_button.gif")
354
+ def click_button_with_image_src_contains(image_filename)
355
+ perform_operation {
356
+ @web_browser.click_button_with_image_src_contains(image_filename)
357
+ }
358
+ end
359
+
360
+ alias click_button_with_image click_button_with_image_src_contains
361
+
362
+ def new_popup_window(options)
363
+ @web_browser.new_popup_window(options)
364
+ end
365
+
366
+
367
+ # Warning: this does not work well with Firefox yet.
368
+ def element_text(elem_id)
369
+ @web_browser.element_value(elem_id)
370
+ end
371
+
372
+ # Identify DOM element by ID
373
+ # Warning: it is only supported on IE
374
+ def element_by_id(elem_id)
375
+ @web_browser.element_by_id(elem_id)
376
+ end
377
+
378
+ # ---
379
+ # For debugging
380
+ # ---
381
+ def dump_response(stream = nil)
382
+ @web_browser.dump_response(stream)
383
+ end
384
+
385
+ def default_dump_dir
386
+ if $ITEST2_RUNNING_SPEC_ID && $ITEST2_WORKING_DIR
387
+
388
+ $ITEST2_DUMP_DIR = File.join($ITEST2_WORKING_DIR, "dump")
389
+ FileUtils.mkdir($ITEST2_DUMP_DIR) unless File.exists?($ITEST2_DUMP_DIR)
390
+
391
+ spec_run_id = $ITEST2_RUNNING_SPEC_ID
392
+ spec_run_dir_name = spec_run_id.to_s.rjust(4, "0") unless spec_run_id == "unknown"
393
+ to_dir = File.join($ITEST2_DUMP_DIR, spec_run_dir_name)
394
+ else
395
+ to_dir = ENV['TEMP_DIR'] || (is_windows? ? "C:\\temp" : "/tmp")
396
+ end
397
+ end
398
+
399
+ # For current page souce to a file in specified folder for inspection
400
+ #
401
+ # save_current_page(:dir => "C:\\mysite", filename => "abc", :replacement => true)
402
+ def save_current_page(options = {})
403
+ default_options = {:replacement => true}
404
+ options = default_options.merge(options)
405
+ to_dir = options[:dir] || default_dump_dir
406
+
407
+ if options[:filename]
408
+ file_name = options[:filename]
409
+ else
410
+ file_name = Time.now.strftime("%m%d%H%M%S") + ".html"
411
+ end
412
+
413
+ Dir.mkdir(to_dir) unless File.exists?(to_dir)
414
+ file = File.join(to_dir, file_name)
415
+
416
+ content = page_source
417
+ base_url = @web_browser.context.base_url
418
+ current_url = @web_browser.url
419
+ current_url =~ /(.*\/).*$/
420
+ current_url_parent = $1
421
+ if options[:replacement] && base_url =~ /^http:/
422
+ File.new(file, "w").puts absolutize_page_hpricot(content, base_url, current_url_parent)
423
+ else
424
+ File.new(file, "w").puts content
425
+ end
426
+
427
+ end
428
+
429
+
430
+ # <link rel="stylesheet" type="text/css" href="/stylesheets/default.css" />
431
+ # '<script type="text/javascript" src="http://www.jeroenwijering.com/embed/swfobject.js"></script>'
432
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
433
+ # <script type="text/javascript" src="/javascripts/scriptaculous.js?load=effects,builder"></script>
434
+ # <script type="text/javascript" src="/javascripts/extensions/gallery/lightbox.js"></script>
435
+ # <link href="/stylesheets/extensions/gallery/lightbox.css" rel="stylesheet" type="text/css" />
436
+ # <img src="images/mission_48.png" />
437
+ def absolutize_page(content, base_url, current_url_parent)
438
+ modified_content = ""
439
+ content.each_line do |line|
440
+ if line =~ /<script\s+.*src=["'']?(.*)["'].*/i then
441
+ script_src = $1
442
+ substitute_relative_path_in_src_line(line, script_src, base_url, current_url_parent)
443
+ elsif line =~ /<link\s+.*href=["'']?(.*)["'].*/i then
444
+ link_href = $1
445
+ substitute_relative_path_in_src_line(line, link_href, base_url, current_url_parent)
446
+ elsif line =~ /<img\s+.*src=["'']?(.*)["'].*/i then
447
+ img_src = $1
448
+ substitute_relative_path_in_src_line(line, img_src, base_url, current_url_parent)
449
+ end
450
+
451
+ modified_content += line
452
+ end
453
+ return modified_content
454
+ end
455
+
456
+ # absolutize_page referencs using hpricot
457
+ #
458
+ def absolutize_page_hpricot(content, base_url, parent_url)
459
+ return absolutize_page(content, base_url, parent_url) if RUBY_PLATFORM == 'java'
460
+ begin
461
+ require 'nokogiri'
462
+ doc = Nokogiri::HTML(content)
463
+ base_url.slice!(-1) if ends_with?(base_url, "/")
464
+ (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || "" }
465
+ (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
466
+ (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || "" }
467
+ return doc.to_html
468
+ rescue => e
469
+ absolutize_page(content, base_url, parent_url)
470
+ end
471
+ end
472
+
473
+ ##
474
+ # change
475
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
476
+ # to
477
+ # <script type="text/javascript" src="http://itest2.com/javascripts/prototype.js"></script>
478
+ def absolutify_url(src, base_url, parent_url)
479
+ if src.nil? || src.empty? || src == "//:" || src =~ /\s*http:\/\//i
480
+ return src
481
+ end
482
+
483
+ return "#{base_url}#{src}" if src =~ /^\s*\//
484
+ return "#{parent_url}#{src}" if parent_url
485
+ return src
486
+ end
487
+
488
+ # substut
489
+ def substitute_relative_path_in_src_line(line, script_src, host_url, page_parent_url)
490
+ unless script_src =~ /^["']?http:/
491
+ host_url.slice!(-1) if ends_with?(host_url, "/")
492
+ if script_src =~ /^\s*\// # absolute_path
493
+ line.gsub!(script_src, "#{host_url}#{script_src}")
494
+ else #relative_path
495
+ line.gsub!(script_src, "#{page_parent_url}#{script_src}")
496
+ end
497
+ end
498
+ end
499
+
500
+ def ends_with?(str, suffix)
501
+ suffix = suffix.to_s
502
+ str[-suffix.length, suffix.length] == suffix
503
+ end
504
+
505
+ # current web page title
506
+ def page_title
507
+ @web_browser.page_title
508
+ end
509
+
510
+ # current page source (in HTML)
511
+ def page_source
512
+ @web_browser.page_source
513
+ end
514
+
515
+ # return plain text view of page
516
+ def page_text
517
+ @web_browser.text
518
+ end
519
+
520
+ # return the text of specific (identified by attribute "id") label tag
521
+ # For page containing
522
+ # <label id="preferred_ide">TestWise</label>
523
+ # label_with_id("preferred_ids") # => TestWise
524
+ # label_with_id("preferred_ids", :index => 2) # => TestWise
525
+ def label_with_id(label_id, options = {})
526
+ if options && options[:index] then
527
+ elements = find_elements(:id, label_id.to_s)
528
+ elements[options[:index].to_i - 1].text
529
+ # label(:id => label_id.to_s, :index => options[:index]).text
530
+ else
531
+ find_element(:id, label_id.to_s).text
532
+ end
533
+ end
534
+
535
+ # return the text of specific (identified by attribute "id") span tag
536
+ # For page containing
537
+ # <span id="preferred_recorder">iTest2/Watir Recorder</span>
538
+ # span_with_id("preferred_recorder") # => iTest2/Watir Recorder
539
+ def span_with_id(span_id, options = {})
540
+ if options && options[:index] then
541
+ elements = find_elements(:id, span_id.to_s)
542
+ elements[options[:index].to_i - 1].text
543
+ else
544
+ find_element(:id, span_id.to_s).text
545
+ end
546
+ end
547
+
548
+ # return the text of specific (identified by attribute "id") ta tag
549
+ # For page containing
550
+ # <td id="preferred_recorder">iTest2/Watir Recorder</span>
551
+ # td_with_id("preferred_recorder") # => iTest2/Watir Recorder
552
+ def cell_with_id(cell_id, options = {})
553
+ if options && options[:index] then
554
+ elements = find_elements(:id, cell_id.to_s)
555
+ elements[options[:index].to_i - 1].text
556
+ else
557
+ find_element(:id, cell_id.to_s).text
558
+ end
559
+ end
560
+ alias table_data_with_id cell_with_id
561
+
562
+
563
+ def is_mac?
564
+ RUBY_PLATFORM.downcase.include?("darwin")
565
+ end
566
+
567
+ def is_windows?
568
+ RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
569
+ end
570
+
571
+ def is_linux?
572
+ RUBY_PLATFORM.downcase.include?("linux")
573
+ end
574
+
575
+ # Support browser (IE) operations using unicode
576
+ # Example:
577
+ # click_button("Google 搜索")
578
+ # Reference: http://jira.openqa.org/browse/WTR-219
579
+ def support_utf8
580
+ if is_windows?
581
+ require 'win32ole'
582
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
583
+ end
584
+ end
585
+
586
+ alias support_unicode support_utf8
587
+
588
+ #= Convenient functions
589
+ #
590
+
591
+ # Using Ruby block syntax to create interesting domain specific language,
592
+ # may be appeal to someone.
593
+
594
+ # Example:
595
+ # on @page do |i|
596
+ # i.enter_text('btn1')
597
+ # i.click_button('btn1')
598
+ # end
599
+ def on(page, & block)
600
+ yield page
601
+ end
602
+
603
+ # fail the test if user can perform the operation
604
+ #
605
+ # Example:
606
+ # shall_not_allow { 1/0 }
607
+ def shall_not_allow(& block)
608
+ operation_performed_ok = false
609
+ begin
610
+ yield
611
+ operation_performed_ok = true
612
+ rescue
613
+ end
614
+ raise "Operation shall not be allowed" if operation_performed_ok
615
+ end
616
+
617
+ alias do_not_allow shall_not_allow
618
+
619
+ # Does not provide real function, other than make enhancing test syntax
620
+ #
621
+ # Example:
622
+ # allow { click_button('Register') }
623
+ def allow(& block)
624
+ yield
625
+ end
626
+
627
+ alias shall_allow allow
628
+ alias allowing allow
629
+
630
+ # try operation, ignore if errors occur
631
+ #
632
+ # Example:
633
+ # failsafe { click_link("Logout") } # try logout, but it still OK if not being able to (already logout))
634
+ def failsafe(& block)
635
+ begin
636
+ yield
637
+ rescue =>e
638
+ end
639
+ end
640
+
641
+ alias fail_safe failsafe
642
+
643
+
644
+ # Execute the provided block until either (1) it returns true, or
645
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
646
+ # a TimeOutException will be raised. The block will always
647
+ # execute at least once.
648
+ #
649
+ # This does not handle error, if the given block raise error, the statement finish with error
650
+ # Examples:
651
+ # wait_until {puts 'hello'}
652
+ # wait_until { div(:id, :receipt_date).exists? }
653
+ #
654
+ def wait_until(timeout = @@default_timeout || 30, polling_interval = @@default_polling_interval || 1, & block)
655
+ end_time = ::Time.now + timeout
656
+
657
+ until ::Time.now > end_time
658
+ result = nil
659
+ begin
660
+ result = yield(self)
661
+ return result if result
662
+ rescue => e
663
+ end
664
+ sleep polling_interval
665
+ end
666
+
667
+ raise TimeoutError, "timed out after #{timeout} seconds"
668
+ end
669
+
670
+ # Wait for specific seconds for an Ajax update finish.
671
+ # Trick: In your Rails application,
672
+ # :loading => "Element.show('search_indicator');
673
+ # :complete => "Element.hide('search_indicator');
674
+ #
675
+ # <%= image_tag("indicator.gif", :id => 'search_indicator', :style => 'display:none') %>
676
+ #
677
+ # Typical usage:
678
+ # ajax_wait_for_element("search_indicator", 30)
679
+ # ajax_wait_for_element("search_indicator", 30, "show")
680
+ # ajax_wait_for_element("search_indicator", 30, "hide")
681
+ # ajax_wait_for_element("search_indicator", 30, "show", 5) # check every 5 seconds
682
+ #
683
+ # Warning: this method has not been fully tested, if you are not using Rails, change your parameter accordingly.
684
+ #
685
+ def ajax_wait_for_element(element_id, seconds, status='show', check_interval = @@default_polling_interval)
686
+ count = 0
687
+ check_interval = 1 if check_interval < 1 or check_interval > seconds
688
+ while count < (seconds / check_interval) do
689
+ search_indicator = @web_browser.element_by_id(element_id)
690
+ search_indicator_outer_html = search_indicator.outerHtml if search_indicator
691
+ if status == 'hide'
692
+ return true if search_indicator_outer_html and !search_indicator_outer_html.include?('style="DISPLAY: none"')
693
+ else
694
+ return true if search_indicator_outer_html and search_indicator_outer_html.include?('style="DISPLAY: none"')
695
+ end
696
+ sleep check_interval if check_interval > 0 and check_interval < 5 * 60 # wait max 5 minutes
697
+ count += 1
698
+ end
699
+ return false
700
+ end
701
+
702
+ #Wait the element with given id to be present in web page
703
+ #
704
+ # Warning: this not working in Firefox, try use wait_util or try instead
705
+ def wait_for_element(element_id, timeout = @@default_timeout, interval = @@default_polling_interval)
706
+ start_time = Time.now
707
+ #TODO might not work with Firefox
708
+ until @web_browser.element_by_id(element_id) do
709
+ sleep(interval)
710
+ if (Time.now - start_time) > timeout
711
+ raise RuntimeError, "failed to find element: #{element_id} for max #{timeout}"
712
+ end
713
+ end
714
+ end
715
+
716
+ =begin
717
+
718
+ # TODO: Firewatir does not suport retrieving style or outerHtml
719
+ # http://jira.openqa.org/browse/WTR-260
720
+ # http://code.google.com/p/firewatir/issues/detail?id=76
721
+ #
722
+ # Max timeout value is 10 minutes
723
+ #
724
+ def ajax_call_complete_after_element_hidden(elem_id, check_start = 0.5, timeout = 5, interval = 0.5, &block)
725
+ yield
726
+ sleep check_start # the time allowed to perform the coomplete
727
+ timeout = 10 * 60 if timeout > 10 * 600 or timeout <= 0
728
+ begin
729
+ Timeout::timeout(timeout) {
730
+ begin
731
+ elem = element_by_id(elem_id)
732
+ while elem do
733
+ puts "outer=>#{elem.outerHtml}|"
734
+ puts "style =>#{elem.attribute_value('style')}|"
735
+ sleep interval
736
+ elem = element_by_id(elem_id)
737
+ end
738
+ rescue => e
739
+ puts e
740
+ end
741
+ }
742
+ rescue Timeout::Error
743
+ # Too slow!!
744
+ raise "Too slow, wait max #{timeout} seconds, the element #{elem_id} still there"
745
+ end
746
+ end
747
+
748
+ =end
749
+
750
+ # Try the operation up to specified times, and sleep given interval (in seconds)
751
+ # Error will be ignored until timeout
752
+ # Example
753
+ # repeat_try(3, 2) { click_button('Search' } # 3 times, 6 seconds in total
754
+ # repeat_try { click_button('Search' } # using default 5 tries, 2 second interval
755
+ def repeat_try(num_tries = @@default_timeout || 30, interval = @@default_polling_interval || 1, & block)
756
+ num_tries ||= 1
757
+ (num_tries - 1).times do |num|
758
+ begin
759
+ yield
760
+ return
761
+ rescue => e
762
+ # puts "debug: #{num} failed: #{e}"
763
+ sleep interval
764
+ end
765
+ end
766
+
767
+ # last try, throw error if still fails
768
+ begin
769
+ yield
770
+ rescue => e
771
+ raise e.to_s + " after trying #{num_tries} times every #{interval} seconds"
772
+ end
773
+ yield
774
+ end
775
+
776
+ # TODO: syntax
777
+
778
+ # Try the operation up to specified timeout (in seconds), and sleep given interval (in seconds).
779
+ # Error will be ignored until timeout
780
+ # Example
781
+ # try { click_link('waiting')}
782
+ # try(10, 2) { click_button('Search' } # try to click the 'Search' button upto 10 seconds, try every 2 seconds
783
+ # try { click_button('Search' }
784
+ def try(timeout = @@default_timeout, polling_interval = @@default_polling_interval || 1, & block)
785
+ start_time = Time.now
786
+
787
+ last_error = nil
788
+ until (duration = Time.now - start_time) > timeout
789
+ begin
790
+ return if yield
791
+ last_error = nil
792
+ rescue => e
793
+ last_error = e
794
+ end
795
+ sleep polling_interval
796
+ end
797
+
798
+ raise "Timeout after #{duration.to_i} seconds with error: #{last_error}." if last_error
799
+ raise "Timeout after #{duration.to_i} seconds."
800
+ end
801
+
802
+ alias try_upto try
803
+
804
+ ##
805
+ # Convert :first to 1, :second to 2, and so on...
806
+ def symbol_to_sequence(symb)
807
+ value = {:zero => 0,
808
+ :first => 1,
809
+ :second => 2,
810
+ :third => 3,
811
+ :fourth => 4,
812
+ :fifth => 5,
813
+ :sixth => 6,
814
+ :seventh => 7,
815
+ :eighth => 8,
816
+ :ninth => 9,
817
+ :tenth => 10}[symb]
818
+ return value || symb.to_i
819
+ end
820
+
821
+ # Clear popup windows such as 'Security Alert' or 'Security Information' popup window,
822
+ #
823
+ # Screenshot see http://kb2.adobe.com/cps/165/tn_16588.html
824
+ #
825
+ # You can also by pass security alerts by change IE setting, http://kb.iu.edu/data/amuj.html
826
+ #
827
+ # Example
828
+ # clear_popup("Security Information", 5, true) # check for Security Information for 5 seconds, click Yes
829
+ def clear_popup(popup_win_title, seconds = 10, yes = true)
830
+ # commonly "Security Alert", "Security Information"
831
+ if is_windows?
832
+ sleep 1
833
+ autoit = WIN32OLE.new('AutoItX3.Control')
834
+ # Look for window with given title. Give up after 1 second.
835
+ ret = autoit.WinWait(popup_win_title, '', seconds)
836
+ #
837
+ # If window found, send appropriate keystroke (e.g. {enter}, {Y}, {N}).
838
+ if ret == 1 then
839
+ puts "about to send click Yes" if debugging?
840
+ button_id = yes ? "Button1" : "Button2" # Yes or No
841
+ autoit.ControlClick(popup_win_title, '', button_id)
842
+ end
843
+ sleep(0.5)
844
+ else
845
+ raise "Currently supported only on Windows"
846
+ end
847
+ end
848
+
849
+ def select_file_for_upload(file_field_name, file_path)
850
+ =begin
851
+ if is_windows?
852
+ normalized_file_path = file_path.gsub("/", "\\")
853
+ if $support_ie8 && check_ie_version && @ie_version >= 8
854
+ # puts "IE8"
855
+ file_field(:name, file_field).set(normalized_file_path)
856
+ # choose_file_dialog(normalized_file_path)
857
+ else
858
+ file_field(:name, file_field).set(normalized_file_path)
859
+ end
860
+ else
861
+ # for firefox, just call file_field, it may fail
862
+ file_field(:name, file_field).set(normalized_file_path)
863
+ end
864
+ =end
865
+ elem = find_element(:name, file_field_name)
866
+ elem.send_keys(file_path)
867
+ end
868
+
869
+ def choose_file_dialog(file_path)
870
+ Watir.autoit.WinWaitActive("Choose File to Upload", '', 10)
871
+ Watir.autoit.ControlSetText("Choose File to Upload", "", 1148, file_path)
872
+ Watir.autoit.ControlClick("Choose File to Upload", "", "&Open")
873
+ end
874
+
875
+ def check_ie_version
876
+ if is_windows? && @ie_version.nil?
877
+ begin
878
+ cmd = 'reg query "HKLM\SOFTWARE\Microsoft\Internet Explorer" /v Version';
879
+ result = `#{cmd}`
880
+ version_line = nil
881
+ result.each do |line|
882
+ if (line =~ /Version\s+REG_SZ\s+([\d\.]+)/)
883
+ version_line = $1
884
+ end
885
+ end
886
+
887
+ if version_line =~ /^\s*(\d+)\./
888
+ @ie_version = $1.to_i
889
+ end
890
+ rescue => e
891
+ end
892
+ end
893
+ end
894
+
895
+ # Use AutoIT3 to send password
896
+ # title starts with "Connect to ..."
897
+ def basic_authentication_ie(title, username, password, options = {})
898
+ default_options = {:textctrl_username => "Edit2",
899
+ :textctrl_password => "Edit3",
900
+ :button_ok => 'Button1'
901
+ }
902
+
903
+ options = default_options.merge(options)
904
+
905
+ title ||= ""
906
+ if title =~ /^Connect\sto/
907
+ full_title = title
908
+ else
909
+ full_title = "Connect to #{title}"
910
+ end
911
+ require 'rformspec'
912
+ login_win = RFormSpec::Window.new(full_title)
913
+ login_win.send_control_text(options[:textctrl_username], username)
914
+ login_win.send_control_text(options[:textctrl_password], password)
915
+ login_win.click_button("OK")
916
+ end
917
+
918
+ # Use JSSH to pass authentication
919
+ # Window title "Authentication required"
920
+ def basic_authentication_firefox(username, password, wait = 3)
921
+ jssh_command = "
922
+ var length = getWindows().length;
923
+ var win;
924
+ var found = false;
925
+ for (var i = 0; i < length; i++) {
926
+ win = getWindows()[i];
927
+ if(win.document.title == \"Authentication Required\") {
928
+ found = true;
929
+ break;
930
+ }
931
+ }
932
+ if (found) {
933
+ var jsdocument = win.document;
934
+ var dialog = jsdocument.getElementsByTagName(\"dialog\")[0];
935
+ jsdocument.getElementsByTagName(\"textbox\")[0].value = \"#{username}\";
936
+ jsdocument.getElementsByTagName(\"textbox\")[1].value = \"#{password}\";
937
+ dialog.getButton(\"accept\").click();
938
+ }
939
+ \n"
940
+ sleep(wait)
941
+ $jssh_socket.send(jssh_command, 0)
942
+ # read_socket()
943
+ end
944
+
945
+ def basic_authentication_celerity(username, password)
946
+ @web_browser.celerity.credentials = "#{username}:#{password}"
947
+ end
948
+
949
+ def basic_authentication(username, password, options = {})
950
+ if is_celerity?
951
+ basic_authentication_celerity(username, password)
952
+ elsif is_firefox?
953
+ basic_authentication_firefox(username, password)
954
+ else
955
+ basic_authentication_ie(options[:title], username, password, options)
956
+ end
957
+ end
958
+
959
+ # take_screenshot to save the current active window
960
+ # TODO can't move mouse
961
+ def take_screenshot_old
962
+ if is_windows? && $ITEST2_DUMP_PAGE
963
+ begin
964
+ puts "[DEBUG] Capturing screenshots..."
965
+ screenshot_image_filename = "rwebspec_" + Time.now.strftime("%m%d%H%M%S") + ".jpg"
966
+ the_dump_dir = default_dump_dir
967
+ FileUtils.mkdir_p(the_dump_dir) unless File.exists?(the_dump_dir)
968
+ screenshot_image_filepath = File.join(the_dump_dir, screenshot_image_filename)
969
+
970
+ screenshot_image_filepath.gsub!("/", "\\") if is_windows?
971
+ screen_capture(screenshot_image_filepath, true)
972
+
973
+ notify_screenshot_location(screenshot_image_filepath)
974
+ rescue
975
+ puts "error: #{Failed to capture screen}"
976
+ end
977
+ end
978
+ end
979
+
980
+
981
+ # use win32screenshot library to save curernt active window, which shall be IE
982
+ #
983
+ def take_screenshot
984
+ if $testwise_screenshot_supported && is_windows?
985
+ begin
986
+ screenshot_image_filename = "screenshot_" + Time.now.strftime("%m%d%H%M%S") + ".jpg"
987
+ the_dump_dir = default_dump_dir
988
+ FileUtils.mkdir_p(the_dump_dir) unless File.exists?(the_dump_dir)
989
+ screenshot_image_filepath = File.join(the_dump_dir, screenshot_image_filename)
990
+ screenshot_image_filepath.gsub!("/", "\\") if is_windows?
991
+
992
+ FileUtils.rm_f(screenshot_image_filepath) if File.exist?(screenshot_image_filepath)
993
+
994
+ case @web_browser.underlying_browser.browser
995
+ when :internet_explorer
996
+ Win32::Screenshot::Take.of(:window, :title => /internet\sexplorer/i).write(screenshot_image_filepath)
997
+ else
998
+ Win32::Screenshot::Take.of(:foreground).write(screenshot_image_filepath)
999
+ end
1000
+ notify_screenshot_location(screenshot_image_filepath)
1001
+ rescue => e
1002
+ puts "error on taking screenshot: #{e}"
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ end
1008
+
1009
+ end