rwebspec-webdriver 0.1.1 → 0.1.2

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