rwebspec-webdriver 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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