rwebspec-webdriver 0.1

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