zhimin-rwebspec 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ #***********************************************************
2
+ #* Copyright (c) 2006 - 2009, Zhimin Zhan.
3
+ #* Distributed open-source, see full license in MIT-LICENSE
4
+ #***********************************************************
5
+
6
+ # Load active_support, so that we can use 1.days.ago
7
+ begin
8
+ require 'active_support/basic_object'
9
+ require 'active_support/duration'
10
+ rescue LoadError => no_as1_err
11
+ # active_support 2.0 loaded error
12
+ end
13
+ require 'active_support/core_ext'
14
+ require 'spec'
15
+
16
+ RWEBUNIT_VERSION = "1.4.0"
17
+
18
+ # Extra full path to load libraries
19
+ require File.dirname(__FILE__) + "/rwebspec/using_pages"
20
+ require File.dirname(__FILE__) + "/rwebspec/test_utils"
21
+ require File.dirname(__FILE__) + "/rwebspec/web_page"
22
+ require File.dirname(__FILE__) + "/rwebspec/assert"
23
+ require File.dirname(__FILE__) + "/rwebspec/itest_plugin"
24
+ require File.dirname(__FILE__) + "/rwebspec/web_browser"
25
+ require File.dirname(__FILE__) + "/rwebspec/driver"
26
+ require File.dirname(__FILE__) + "/rwebspec/test_script"
27
+ require File.dirname(__FILE__) + "/rwebspec/context"
28
+ require File.dirname(__FILE__) + "/rwebspec/rspec_helper"
29
+ require File.dirname(__FILE__) + "/rspec_extensions"
30
+ require File.dirname(__FILE__) + "/watir_extensions"
31
+ require File.dirname(__FILE__) + "/rwebspec/matchers/contains_text"
@@ -0,0 +1,361 @@
1
+ require 'test/unit/assertions'
2
+
3
+ module RWebSpec
4
+ module Assert
5
+
6
+ include Test::Unit::Assertions
7
+
8
+ def assert_not(condition, msg = "")
9
+ assert(!condition, msg)
10
+ end
11
+
12
+ def assert_nil(actual, msg="")
13
+ assert(actual.nil?, msg)
14
+ end
15
+
16
+ def assert_not_nil(actual, msg="")
17
+ assert(!actual.nil?, msg)
18
+ end
19
+
20
+ def fail(message)
21
+ assert(false, message)
22
+ end
23
+
24
+ # assertions
25
+ def assert_title_equals(title)
26
+ assert_equals(title, @web_browser.page_title)
27
+ end
28
+ alias assert_title assert_title_equals
29
+
30
+ # Assert text present in page source (html)
31
+ # assert_text_present("<h1>iTest2</h1>")
32
+ def assert_text_present(text)
33
+ assert((@web_browser.page_source.include? text), 'expected text: ' + text + ' not found')
34
+ end
35
+
36
+ # Assert text not present in page source (html)
37
+ # assert_text_not_present("<h1>iTest2</h1>")
38
+ def assert_text_not_present(text)
39
+ assert(!(@web_browser.page_source.include? text), 'expected text: ' + text + ' found')
40
+ end
41
+
42
+
43
+ ##
44
+ # Link
45
+
46
+ # Assert a link with specified text (exact match) in the page
47
+ #
48
+ # <a href="">Click Me</a>
49
+ # assert_link_present_with_exact("Click Me") => true
50
+ # assert_link_present_with_exact("Click") => false
51
+ #
52
+ def assert_link_present_with_exact(link_text)
53
+ @web_browser.links.each { |link|
54
+ return if link_text == link.text
55
+ }
56
+ fail( "can't find the link with text: #{link_text}")
57
+ end
58
+
59
+ def assert_link_not_present_with_exact(link_text)
60
+ @web_browser.links.each { |link|
61
+ assert(link_text != link.text, "unexpected link (exact): #{link_text} found")
62
+ }
63
+ end
64
+
65
+ # Assert a link containing specified text in the page
66
+ #
67
+ # <a href="">Click Me</a>
68
+ # assert_link_present_with_text("Click ") # =>
69
+ #
70
+ def assert_link_present_with_text(link_text)
71
+ @web_browser.links.each { |link|
72
+ return if link.text.include?(link_text)
73
+ }
74
+ fail( "can't find the link containing text: #{link_text}")
75
+ end
76
+
77
+ def assert_link_not_present_with_text(link_text)
78
+ @web_browser.links.each { |link|
79
+ assert(!link.text.include?(link_text), "unexpected link containing: #{link_text} found")
80
+ }
81
+ end
82
+
83
+
84
+ ##
85
+ # Checkbox
86
+ def assert_checkbox_not_selected(checkbox_name)
87
+ @web_browser.checkboxes.each { |checkbox|
88
+ if (checkbox.name == checkbox_name) then
89
+ assert(!checkbox.isSet?, "Checkbox #{checkbox_name} is checked unexpectly")
90
+ end
91
+ }
92
+ end
93
+ alias assert_checkbox_not_checked assert_checkbox_not_selected
94
+
95
+ def assert_checkbox_selected(checkbox_name)
96
+ @web_browser.checkboxes.each { |checkbox|
97
+ if (checkbox.name == checkbox_name) then
98
+ assert(checkbox.isSet?, "Checkbox #{checkbox_name} not checked")
99
+ end
100
+ }
101
+ end
102
+ alias assert_checkbox_checked assert_checkbox_selected
103
+
104
+ ##
105
+ # select
106
+ def assert_option_value_not_present(select_name, option_value)
107
+ @web_browser.select_lists.each { |select|
108
+ continue unless select.name == select_name
109
+ select.o.each do |option| # items in the list
110
+ assert(!(option.value == option_value), "unexpected select option: #{option_value} for #{select_name} found")
111
+ end
112
+ }
113
+ end
114
+ alias assert_select_value_not_present assert_option_value_not_present
115
+
116
+ def assert_option_not_present(select_name, option_label)
117
+ @web_browser.select_lists.each { |select|
118
+ next unless select.name == select_name
119
+ select.o.each do |option| # items in the list
120
+ assert(!(option.text == option_label), "unexpected select option: #{option_label} for #{select_name} found")
121
+ end
122
+ }
123
+ end
124
+ alias assert_select_label_not_present assert_option_not_present
125
+
126
+ def assert_option_value_present(select_name, option_value)
127
+ @web_browser.select_lists.each { |select|
128
+ next unless select.name == select_name
129
+ select.o.each do |option| # items in the list
130
+ return if option.value == option_value
131
+ end
132
+ }
133
+ assert(false, "can't find the combob box with value: #{option_value}")
134
+ end
135
+ alias assert_select_value_present assert_option_value_present
136
+
137
+ def assert_option_present(select_name, option_label)
138
+ @web_browser.select_lists.each { |select|
139
+ next unless select.name == select_name
140
+ select.o.each do |option| # items in the list
141
+ return if option.text == option_label
142
+ end
143
+ }
144
+ assert(false, "can't find the combob box: #{select_name} with value: #{option_label}")
145
+ end
146
+ alias assert_select_label_present assert_option_present
147
+
148
+ def assert_option_equals(select_name, option_label)
149
+ @web_browser.select_lists.each { |select|
150
+ next unless select.name == select_name
151
+ select.o.each do |option| # items in the list
152
+ if (option.text == option_label) then
153
+ assert_equal(select.value, option.value, "Select #{select_name}'s value is not equal to expected option label: '#{option_label}'")
154
+ end
155
+ end
156
+ }
157
+ end
158
+ alias assert_select_label assert_option_equals
159
+
160
+ def assert_option_value_equals(select_name, option_value)
161
+ @web_browser.select_lists.each { |select|
162
+ next unless select.name == select_name
163
+ assert_equal(select.value, option_value, "Select #{select_name}'s value is not equal to expected: '#{option_value}'")
164
+ }
165
+ end
166
+ alias assert_select_value assert_option_value_equals
167
+
168
+ ##
169
+ # radio
170
+
171
+ # radio_group is the name field, radio options 'value' field
172
+ def assert_radio_option_not_present(radio_group, radio_option)
173
+ @web_browser.radios.each { |radio|
174
+ if (radio.name == radio_group) then
175
+ assert(!(radio_option == radio.value), "unexpected radio option: " + radio_option + " found")
176
+ end
177
+ }
178
+ end
179
+
180
+ def assert_radio_option_present(radio_group, radio_option)
181
+ @web_browser.radios.each { |radio|
182
+ return if (radio.name == radio_group) and (radio_option == radio.value)
183
+ }
184
+ fail("can't find the radio option : '#{radio_option}'")
185
+ end
186
+
187
+ def assert_radio_option_selected(radio_group, radio_option)
188
+ @web_browser.radios.each { |radio|
189
+ if (radio.name == radio_group and radio_option == radio.value) then
190
+ assert(radio.isSet?, "Radio button #{radio_group}-[#{radio_option}] not checked")
191
+ end
192
+ }
193
+ end
194
+ alias assert_radio_button_checked assert_radio_option_selected
195
+ alias assert_radio_option_checked assert_radio_option_selected
196
+
197
+ def assert_radio_option_not_selected(radio_group, radio_option)
198
+ @web_browser.radios.each { |radio|
199
+ if (radio.name == radio_group and radio_option == radio.value) then
200
+ assert(!radio.isSet?, "Radio button #{radio_group}-[#{radio_option}] checked unexpected")
201
+ end
202
+ }
203
+ end
204
+ alias assert_radio_button_not_checked assert_radio_option_not_selected
205
+ alias assert_radio_option_not_checked assert_radio_option_not_selected
206
+
207
+ ##
208
+ # Button
209
+ def assert_button_not_present(button_id)
210
+ @web_browser.buttons.each { |button|
211
+ assert(button.id != button_id, "unexpected button id: #{button_id} found")
212
+ }
213
+ end
214
+
215
+ def assert_button_not_present_with_text(text)
216
+ @web_browser.buttons.each { |button|
217
+ assert(button.value != text, "unexpected button id: #{text} found")
218
+ }
219
+ end
220
+
221
+ def assert_button_present(button_id)
222
+ @web_browser.buttons.each { |button|
223
+ return if button_id == button.id
224
+ }
225
+ assert(false, "can't find the button with id: #{button_id}")
226
+ end
227
+
228
+ def assert_button_present_with_text(button_text)
229
+ @web_browser.buttons.each { |button|
230
+ return if button_text == button.value
231
+ }
232
+ assert(false, "can't find the button with text: #{button_text}")
233
+ end
234
+
235
+
236
+ def assert_equals(expected, actual, msg=nil)
237
+ assert(expected == actual, (msg.nil?) ? "Expected: #{expected} diff from actual: #{actual}" : msg)
238
+ end
239
+
240
+
241
+
242
+ # Check a HTML element exists or not
243
+ # Example:
244
+ # assert_exists("label", "receipt_date")
245
+ # assert_exists(:span, :receipt_date)
246
+ def assert_exists(tag, element_id) {}
247
+ begin
248
+ assert eval("#{tag}(:id, '#{element_id.to_s}').exists?")
249
+ rescue => e
250
+ raise "Element '#{tag}' with id: '#{element_id}' not found, #{e}"
251
+ end
252
+ end
253
+ alias assert_exists? assert_exists
254
+ alias assert_element_exists assert_exists
255
+
256
+ def assert_not_exists(tag, element_id) {}
257
+ begin
258
+ assert_not eval("#{tag}(:id, '#{element_id.to_s}').exists?")
259
+ raise "Unexpected element'#{tag}' + with id: '#{element_id}' found"
260
+ rescue => e
261
+ end
262
+ end
263
+ alias assert_not_exists? assert_not_exists
264
+ alias assert_element_not_exists? assert_not_exists
265
+
266
+
267
+ # Assert tag with element id is visible?, eg.
268
+ # assert_visible(:div, "public_notice")
269
+ # assert_visible(:span, "public_span")
270
+ def assert_visible(tag, element_id)
271
+ begin
272
+ assert(eval("#{tag}(:id, '#{element_id.to_s}').visible?"))
273
+ rescue => e
274
+ raise "Element '#{tag}' with id: '#{element_id}' not visible, #{e}"
275
+ end
276
+ end
277
+
278
+ # Assert tag with element id is hidden?, example
279
+ # assert_hidden(:div, "secret")
280
+ # assert_hidden(:span, "secret_span")
281
+ def assert_hidden(tag, element_id)
282
+ begin
283
+ assert(!eval("#{tag}(:id, '#{element_id.to_s}').visible?"))
284
+ rescue => e
285
+ raise "Element '#{tag}' with id: '#{element_id}' is visible, #{e}"
286
+ end
287
+ end
288
+ alias assert_not_visible assert_hidden
289
+
290
+
291
+ # Assert given text appear inside a table (inside <table> tag like below)
292
+ #
293
+ # <table id="t1">
294
+ #
295
+ # <tbody>
296
+ # <tr id="row_1">
297
+ # <td id="cell_1_1">A</td>
298
+ # <td id="cell_1_2">B</td>
299
+ # </tr>
300
+ # <tr id="row_2">
301
+ # <td id="cell_2_1">a</td>
302
+ # <td id="cell_2_2">b</td>
303
+ # </tr>
304
+ # </tbody>
305
+ #
306
+ # </table>
307
+ #
308
+ # The plain text view of above table
309
+ # A B a b
310
+ #
311
+ # Examples
312
+ # assert_text_present_in_table("t1", ">A<") # => true
313
+ # assert_text_present_in_table("t1", ">A<", :just_plain_text => true) # => false
314
+ def assert_text_present_in_table(table_id, text, options = { :just_plain_text => false })
315
+ assert(table_source(table_id, options).include?(text), "the text #{text} not found in table #{table_id}")
316
+ end
317
+ alias assert_text_in_table assert_text_present_in_table
318
+
319
+ def assert_text_not_present_in_table(table_id, text, options = { :just_plain_text => false })
320
+ assert_not(table_source(table_id, options).include?(text), "the text #{text} not found in table #{table_id}")
321
+ end
322
+ alias assert_text_not_in_table assert_text_not_present_in_table
323
+
324
+ # Assert a text field (with given name) has the value
325
+ #
326
+ # <input id="tid" name="text1" value="text already there" type="text">
327
+ #
328
+ # assert_text_field_value("text1", "text already there") => true
329
+ #
330
+ def assert_text_field_value(textfield_name, text)
331
+ assert_equal(text, text_field(:name, textfield_name).value)
332
+ end
333
+
334
+
335
+ #-- Not tested
336
+ # -----
337
+
338
+ def assert_text_in_element(element_id, text)
339
+ elem = element_by_id(element_id)
340
+ assert_not_nil(elem.innerText, "element #{element_id} has no text")
341
+ assert(elem.innerText.include?(text), "the text #{text} not found in element #{element_id}")
342
+ end
343
+
344
+ # Use
345
+ #
346
+
347
+ #TODO for drag-n-drop, check the postion in list
348
+ # def assert_position_in_list(list_element_id)
349
+ # raise "not implemented"
350
+ # end
351
+
352
+ private
353
+ def table_source(table_id, options)
354
+ elem_table = table(:id, table_id.to_s)
355
+ elem_table_text = elem_table.text
356
+ elem_table_html = is_firefox? ? elem_table.innerHTML : elem_table.html
357
+ table_source = options[:just_plain_text] ? elem_table_text : elem_table_html
358
+ end
359
+
360
+ end
361
+ end
@@ -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,732 @@
1
+ # convenient methods to drive the browser.
2
+ #
3
+ # Instead of
4
+ # browser.click_button("submit")
5
+ # You can just use
6
+ # click_button("submit")
7
+ #
8
+ require File.join(File.dirname(__FILE__), 'itest_plugin')
9
+ require File.join(File.dirname(__FILE__), 'popup')
10
+ require 'timeout'
11
+ require 'uri'
12
+
13
+ module RWebSpec
14
+ module Driver
15
+ include RWebSpec::ITestPlugin
16
+ include RWebSpec::Popup
17
+
18
+ @@default_polling_interval = 1 # second
19
+ @@default_timeout = 30 # seconds
20
+
21
+ # open a browser, and set base_url via hash, but does not acually
22
+ #
23
+ # example:
24
+ # open_browser :base_url => http://localhost:8080
25
+ #
26
+ # There are 3 ways to set base url
27
+ # 1. pass as first argument
28
+ # 2. If running using iTest2, used as confiured
29
+ # 3. Use default value set
30
+ def open_browser(base_url = nil, options = {})
31
+ base_url ||= $ITEST2_PROJECT_BASE_URL
32
+ base_url ||= $BASE_URL
33
+ raise "base_url must be set" if base_url.nil?
34
+
35
+ default_options = {:speed => "fast",
36
+ :visible => true,
37
+ :highlight_colour => 'yellow',
38
+ :close_others => true,
39
+ :start_new => false, # start a new browser always
40
+ :go => true}
41
+
42
+ options = default_options.merge options
43
+ options[:firefox] = true if "Firefox" == $ITEST2_BROWSER || "Firefox" == $BROWSER
44
+ ($ITEST2_HIDE_BROWSER) ? $HIDE_IE = true : $HIDE_IE = false
45
+
46
+ if base_url =~ /^file:/
47
+ uri_base = base_url
48
+ else
49
+ uri = URI.parse(base_url)
50
+ uri_base = "#{uri.scheme}://#{uri.host}:#{uri.port}"
51
+ end
52
+
53
+ if options[:start_new] || $celerity_loaded
54
+ @web_browser = WebBrowser.new(uri_base, nil, options)
55
+ else
56
+ @web_browser = WebBrowser.reuse(uri_base, options) # Reuse existing browser
57
+ end
58
+
59
+ if base_url =~ /^file:/
60
+ goto_url(base_url) # for files, no base url
61
+ else
62
+ (uri.path.length == 0) ? begin_at("/") : begin_at(uri.path) if options[:go]
63
+ end
64
+
65
+ return @web_browser
66
+ end
67
+ alias open_browser_with open_browser
68
+
69
+ # return the underlying RWebSpec::Browser object
70
+ def browser
71
+ @web_browser
72
+ end
73
+
74
+
75
+ # Close the current browser window (started by the script). If no browser started, then close
76
+ # all browser windows.
77
+ #
78
+ def close_browser
79
+ if @web_browser
80
+ # Old iTest2 version
81
+ # @web_browser.close_browser unless $ITEST2_LEAVE_BROWSER_OPEN_AFTER_RUN
82
+ @web_browser.close_browser
83
+ else
84
+ WebBrowser.close_all_browsers
85
+ end
86
+ end
87
+ alias close_ie close_browser
88
+
89
+
90
+ # Close all opening browser windows
91
+ #
92
+ def close_all_browsers
93
+ if is_firefox?
94
+ FireWatir::Firefox.close_all
95
+ else
96
+ Watir::IE.close_all
97
+ end
98
+ end
99
+
100
+ # Verify the next page following an operation.
101
+ #
102
+ # Typical usage:
103
+ # login_page.click_login
104
+ # expect_page HomePage
105
+ def expect_page(page_clazz, argument = nil)
106
+ if argument
107
+ page_clazz.new(@web_browser, argument)
108
+ else
109
+ page_clazz.new(@web_browser)
110
+ end
111
+ end
112
+
113
+ def context
114
+ @web_browser.context
115
+ end
116
+
117
+ # Starting browser with a URL
118
+ #
119
+ # Example:
120
+ # begin_at("http://www.itest2.com")
121
+ def begin_at(url)
122
+ dump_caller_stack
123
+ @web_browser.begin_at(url)
124
+ end
125
+
126
+ # Return the Watir::IE instance
127
+ #
128
+ def ie
129
+ @web_browser.ie
130
+ end
131
+
132
+ # Return the FireWatir::Firefox instance
133
+ #
134
+ def firefox
135
+ @web_browser.firefox
136
+ end
137
+
138
+ def is_firefox?
139
+ @web_browser.is_firefox? if @web_browser
140
+ end
141
+
142
+
143
+ # Go to another page on the testing site.
144
+ #
145
+ # open_browser("http://www.itest2.com")
146
+ # goto_page("/demo") # visit page http://www.itest2.com/demo
147
+ #
148
+ def goto_page(page)
149
+ operation_delay
150
+ dump_caller_stack
151
+ @web_browser.goto_page(page) if @web_browser
152
+ end
153
+ alias visit goto_page
154
+
155
+ # Go to another web site, normally different site being tested on
156
+ #
157
+ # open_browser("http://www.itest2.com")
158
+ # goto_url("http://myorganized.info")
159
+ def goto_url(url)
160
+ @web_browser.goto_url url
161
+ end
162
+
163
+ # Attach to existinb browser window
164
+ #
165
+ # attach_browser(:title, )
166
+ def attach_browser(how, what, options = {})
167
+ options.merge!(:browser => is_firefox? ? "Firefox" : "IE")
168
+ begin
169
+ options.merge!(:base_url => browser.context.base_url)
170
+ rescue => e
171
+ puts "error to attach to browser: #{e}"
172
+ end
173
+ WebBrowser.attach_browser(how, what, options)
174
+ end
175
+
176
+ # Reuse current an opened browser window instead of opening a new one
177
+ # example:
178
+ # use_current_browser(:title, /.*/) # use what ever browser window
179
+ # use_current_browser(:title, "iTest2") # use browser window with title "iTest2"
180
+ def use_current_browser(how = :title, what = /.*/)
181
+ @web_browser = WebBrowser.attach_browser(how, what)
182
+ end
183
+
184
+ ##
185
+ # Delegate to WebTester
186
+ #
187
+ # Note:
188
+ # label(:id, "abc") # OK
189
+ # label(:id, :abc) # Error
190
+ #
191
+ # Depends on which object type, you can use following attribute
192
+ # More details: http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
193
+ #
194
+ # :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. *
195
+ # :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. *
196
+ # :value Used to find a text field with a given default value, or a button with a given caption, or a text field
197
+ # :text Used for links, spans, divs and other element that contain text.
198
+ # :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.
199
+ # :class Used for an element that has a "class=" attribute.
200
+ # :title Used for an element that has a "title=" attribute.
201
+ # :xpath Finds the item using xpath query.
202
+ # :method Used only for forms, the method attribute of a form is either GET or POST.
203
+ # :action Used only for form elements, specifies the URL where the form is to be submitted.
204
+ # :href Used to identify a link by its "href=" attribute.
205
+ # :src Used to identify an image by its URL.
206
+ #
207
+
208
+ # area <area> tags
209
+ # button <input> tags with type=button, submit, image or reset
210
+ # check_box <input> tags with type=checkbox
211
+ # div <div> tags
212
+ # form <form> tags
213
+ # frame frames, including both the <frame> elements and the corresponding pages
214
+ # h1 - h6 <h1>, <h2>, <h3>, <h4>, <h5>, <h6> tags
215
+ # hidden <input> tags with type=hidden
216
+ # image <img> tags
217
+ # label <label> tags (including "for" attribute)
218
+ # li <li> tags
219
+ # link <a> (anchor) tags
220
+ # map <map> tags
221
+ # radio <input> tags with the type=radio; known as radio buttons
222
+ # select_list <select> tags, known as drop-downs or drop-down lists
223
+ # span <span> tags
224
+ # table <table> tags, including row and cell methods for accessing nested elements
225
+ # text_field <input> tags with the type=text (single-line), type=textarea (multi-line), and type=password
226
+ # p <p> (paragraph) tags, because
227
+ [: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|
228
+ define_method method do |*args|
229
+ dump_caller_stack
230
+ # add check for @web_browser, in case the moudule included without init browser
231
+ @web_browser.send(method, *args) if @web_browser
232
+ end
233
+ end
234
+ alias td cell
235
+ alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
236
+ alias tr row
237
+
238
+ [:back, :forward, :refresh].each do |method|
239
+ define_method(method) do
240
+ dump_caller_stack
241
+ operation_delay
242
+ @web_browser.send(method) if @web_browser
243
+ end
244
+ end
245
+ alias refresh_page refresh
246
+ alias go_back back
247
+ alias go_forward forward
248
+
249
+ [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields].each do |method|
250
+ define_method method do
251
+ dump_caller_stack
252
+ @web_browser.send(method) if @web_browser
253
+ end
254
+ end
255
+
256
+ # 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.
257
+ #
258
+ # page.check_checkbox('bad_ones', 'Chicken Little')
259
+ # page.check_checkbox('good_ones', ['Cars', 'Toy Story'])
260
+ #
261
+ [: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, :select_file_for_upload, :check_checkbox, :uncheck_checkbox, :select_option].each do |method|
262
+ define_method method do |*args|
263
+ dump_caller_stack
264
+ operation_delay
265
+ @web_browser.send(method, *args) if @web_browser
266
+ end
267
+ end
268
+
269
+ alias enter_text set_form_element
270
+ alias set_hidden_field set_form_element
271
+ alias click_link click_link_with_text
272
+ alias click_button_with_text click_button_with_caption
273
+ alias click_button click_button_with_caption
274
+ alias click_radio_button click_radio_option
275
+ alias clear_radio_button clear_radio_option
276
+
277
+ # for text field can be easier to be identified by attribute "id" instead of "name", not recommended though
278
+ def enter_text_with_id(textfield_id, value)
279
+ dump_caller_stack
280
+ operation_delay
281
+ text_field(:id, textfield_id).set(value)
282
+ end
283
+
284
+ def contains_text(text)
285
+ @web_browser.contains_text(text)
286
+ end
287
+
288
+ # Click image buttion with image source name
289
+ #
290
+ # For an image submit button <input name="submit" type="image" src="/images/search_button.gif">
291
+ # click_button_with_image("search_button.gif")
292
+ def click_button_with_image_src_contains(image_filename)
293
+ dump_caller_stack
294
+ operation_delay
295
+ found = nil
296
+ raise "no buttons in this page" if buttons.length <= 0
297
+ buttons.each { |btn|
298
+ if btn && btn.src && btn.src.include?(image_filename) then
299
+ found = btn
300
+ break
301
+ end
302
+ }
303
+ raise "not image button with src: #{image_filename} found" if found.nil?
304
+ found.click
305
+ end
306
+ alias click_button_with_image click_button_with_image_src_contains
307
+
308
+ def new_popup_window(options)
309
+ @web_browser.new_popup_window(options)
310
+ end
311
+
312
+
313
+ # Warning: this does not work well with Firefox yet.
314
+ def element_text(elem_id)
315
+ @web_browser.element_value(elem_id)
316
+ end
317
+
318
+ # Identify DOM element by ID
319
+ # Warning: it is only supported on IE
320
+ def element_by_id(elem_id)
321
+ @web_browser.element_by_id(elem_id)
322
+ end
323
+
324
+ # ---
325
+ # For debugging
326
+ # ---
327
+ def dump_response(stream = nil)
328
+ @web_browser.dump_response(stream)
329
+ end
330
+
331
+ # For current page souce to a file in specified folder for inspection
332
+ #
333
+ # save_current_page(:dir => "C:\\mysite", filename => "abc", :replacement => true)
334
+ def save_current_page(options = {})
335
+ default_options = {:replacement => true}
336
+ options = default_options.merge(options)
337
+ if options[:dir]
338
+ # already defined the dir
339
+ to_dir = options[:dir]
340
+ elsif $ITEST2_RUNNING_SPEC_ID && $ITEST2_WORKING_DIR
341
+
342
+ $ITEST2_DUMP_DIR = File.join($ITEST2_WORKING_DIR, "dump")
343
+ FileUtils.mkdir($ITEST2_DUMP_DIR) unless File.exists?($ITEST2_DUMP_DIR)
344
+
345
+ spec_run_id = $ITEST2_RUNNING_SPEC_ID
346
+ spec_run_dir_name = spec_run_id.to_s.rjust(4, "0") unless spec_run_id == "unknown"
347
+ to_dir = File.join($ITEST2_DUMP_DIR, spec_run_dir_name)
348
+ else
349
+ to_dir = ENV['TEMP_DIR'] || "C:\\temp"
350
+ end
351
+
352
+ if options[:filename]
353
+ file_name = options[:filename]
354
+ else
355
+ file_name = Time.now.strftime("%m%d%H%M%S") + ".html"
356
+ end
357
+
358
+ Dir.mkdir(to_dir) unless File.exists?(to_dir)
359
+ file = File.join(to_dir, file_name)
360
+
361
+ content = page_source
362
+ base_url = @web_browser.context.base_url
363
+ current_url = @web_browser.url
364
+ current_url =~ /(.*\/).*$/
365
+ current_url_parent = $1
366
+ if options[:replacement] && base_url =~ /^http:/
367
+ File.new(file, "w").puts absolutize_page_hpricot(content, base_url, current_url_parent)
368
+ else
369
+ File.new(file, "w").puts content
370
+ end
371
+
372
+ end
373
+
374
+
375
+ # <link rel="stylesheet" type="text/css" href="/stylesheets/default.css" />
376
+ # '<script type="text/javascript" src="http://www.jeroenwijering.com/embed/swfobject.js"></script>'
377
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
378
+ # <script type="text/javascript" src="/javascripts/scriptaculous.js?load=effects,builder"></script>
379
+ # <script type="text/javascript" src="/javascripts/extensions/gallery/lightbox.js"></script>
380
+ # <link href="/stylesheets/extensions/gallery/lightbox.css" rel="stylesheet" type="text/css" />
381
+ # <img src="images/mission_48.png" />
382
+ def absolutize_page(content, base_url, current_url_parent)
383
+ modified_content = ""
384
+ content.each_line do |line|
385
+ if line =~ /<script\s+.*src=["'']?(.*)["'].*/i then
386
+ script_src = $1
387
+ substitute_relative_path_in_src_line(line, script_src, base_url, current_url_parent)
388
+ elsif line =~ /<link\s+.*href=["'']?(.*)["'].*/i then
389
+ link_href = $1
390
+ substitute_relative_path_in_src_line(line, link_href, base_url, current_url_parent)
391
+ elsif line =~ /<img\s+.*src=["'']?(.*)["'].*/i then
392
+ img_src = $1
393
+ substitute_relative_path_in_src_line(line, img_src, base_url, current_url_parent)
394
+ end
395
+
396
+ modified_content += line
397
+ end
398
+ return modified_content
399
+ end
400
+
401
+ # absolutize_page referencs using hpricot
402
+ #
403
+ def absolutize_page_hpricot(content, base_url, parent_url)
404
+ begin
405
+ require 'hpricot'
406
+ doc = Hpricot(content)
407
+ base_url.slice!(-1) if ends_with?(base_url, "/")
408
+ (doc/'link').each { |e| e['href'] = absolutify_url(e['href'], base_url, parent_url) || ""}
409
+ (doc/'img').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || ""}
410
+ (doc/'script').each { |e| e['src'] = absolutify_url(e['src'], base_url, parent_url) || ""}
411
+ return doc.to_html
412
+ rescue => e
413
+ absolutize_page(content, base_url, parent_url)
414
+ end
415
+ end
416
+
417
+ ##
418
+ # change
419
+ # <script type="text/javascript" src="/javascripts/prototype.js"></script>
420
+ # to
421
+ # <script type="text/javascript" src="http://itest2.com/javascripts/prototype.js"></script>
422
+ def absolutify_url(src, base_url, parent_url)
423
+ if src.nil? || src.empty? || src == "//:" || src =~ /\s*http:\/\//i
424
+ return src
425
+ end
426
+
427
+ return "#{base_url}#{src}" if src =~ /^\s*\//
428
+ return "#{parent_url}#{src}" if parent_url
429
+ return src
430
+ end
431
+
432
+ # substut
433
+ def substitute_relative_path_in_src_line(line, script_src, host_url, page_parent_url)
434
+ unless script_src =~ /^["']?http:/
435
+ host_url.slice!(-1) if ends_with?(host_url, "/")
436
+ if script_src =~ /^\s*\// # absolute_path
437
+ line.gsub!(script_src, "#{host_url}#{script_src}")
438
+ else #relative_path
439
+ line.gsub!(script_src, "#{page_parent_url}#{script_src}")
440
+ end
441
+ end
442
+ end
443
+
444
+ def ends_with?(str, suffix)
445
+ suffix = suffix.to_s
446
+ str[-suffix.length, suffix.length] == suffix
447
+ end
448
+
449
+ # current web page title
450
+ def page_title
451
+ @web_browser.page_title
452
+ end
453
+
454
+ # current page source (in HTML)
455
+ def page_source
456
+ @web_browser.page_source
457
+ end
458
+
459
+ # return plain text view of page
460
+ def page_text
461
+ @web_browser.text
462
+ end
463
+
464
+ # return the text of specific (identified by attribute "id") label tag
465
+ # For page containing
466
+ # <label id="preferred_ide">iTest2</label>
467
+ # label_with_id("preferred_ids") # => iTest2
468
+ def label_with_id(label_id)
469
+ label(:id, label_id.to_s).text
470
+ end
471
+
472
+ # return the text of specific (identified by attribute "id") span tag
473
+ # For page containing
474
+ # <span id="preferred_recorder">iTest2/Watir Recorder</span>
475
+ # span_with_id("preferred_recorder") # => iTest2/Watir Recorder
476
+ def span_with_id(span_id)
477
+ span(:id, span_id).text
478
+ end
479
+
480
+ # return the text of specific (identified by attribute "id") ta tag
481
+ # For page containing
482
+ # <td id="preferred_recorder">iTest2/Watir Recorder</span>
483
+ # td_with_id("preferred_recorder") # => iTest2/Watir Recorder
484
+ def cell_with_id(cell_id)
485
+ cell(:id, cell_id).text
486
+ end
487
+ alias table_data_with_id cell_with_id
488
+
489
+
490
+ def is_mac?
491
+ RUBY_PLATFORM.downcase.include?("darwin")
492
+ end
493
+
494
+ def is_windows?
495
+ RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw32")
496
+ end
497
+
498
+ def is_linux?
499
+ RUBY_PLATFORM.downcase.include?("linux")
500
+ end
501
+
502
+ # Support browser (IE) operations using unicode
503
+ # Example:
504
+ # click_button("Google 搜索")
505
+ def support_utf8
506
+ if is_windows?
507
+ require 'win32ole'
508
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
509
+ end
510
+ end
511
+ alias support_unicode support_utf8
512
+
513
+ #= Convenient functions
514
+ #
515
+
516
+ # Using Ruby block syntax to create interesting domain specific language,
517
+ # may be appeal to someone.
518
+
519
+ # Example:
520
+ # on @page do |i|
521
+ # i.enter_text('btn1')
522
+ # i.click_button('btn1')
523
+ # end
524
+ def on(page, &block)
525
+ yield page
526
+ end
527
+
528
+ # fail the test if user can perform the operation
529
+ #
530
+ # Example:
531
+ # shall_not_allow { 1/0 }
532
+ def shall_not_allow(&block)
533
+ operation_performed_ok = false
534
+ begin
535
+ yield
536
+ operation_performed_ok = true
537
+ rescue
538
+ end
539
+ raise "Operation shall not be allowed" if operation_performed_ok
540
+ end
541
+ alias do_not_allow shall_not_allow
542
+
543
+ # Does not provide real function, other than make enhancing test syntax
544
+ #
545
+ # Example:
546
+ # allow { click_button('Register') }
547
+ def allow(&block)
548
+ yield
549
+ end
550
+ alias shall_allow allow
551
+ alias allowing allow
552
+
553
+ # try operation, ignore if errors occur
554
+ #
555
+ # Example:
556
+ # failsafe { click_link("Logout") } # try logout, but it still OK if not being able to (already logout))
557
+ def failsafe(&block)
558
+ begin
559
+ yield
560
+ rescue =>e
561
+ end
562
+ end
563
+ alias fail_safe failsafe
564
+
565
+
566
+ # Execute the provided block until either (1) it returns true, or
567
+ # (2) the timeout (in seconds) has been reached. If the timeout is reached,
568
+ # a TimeOutException will be raised. The block will always
569
+ # execute at least once.
570
+ #
571
+ # This does not handle error, if the given block raise error, the statement finish with error
572
+ # Examples:
573
+ # wait_until {puts 'hello'}
574
+ # wait_until { div(:id, :receipt_date).exists? }
575
+ #
576
+ def wait_until(timeout = @@default_timeout || 30, polling_interval = @@default_polling_interval || 1, &block)
577
+ waiter = Watir::Waiter.new(timeout, polling_interval)
578
+ waiter.wait_until { yield }
579
+ end
580
+
581
+ # Wait for specific seconds for an Ajax update finish.
582
+ # Trick: In your Rails application,
583
+ # :loading => "Element.show('search_indicator');
584
+ # :complete => "Element.hide('search_indicator');
585
+ #
586
+ # <%= image_tag("indicator.gif", :id => 'search_indicator', :style => 'display:none') %>
587
+ #
588
+ # Typical usage:
589
+ # ajax_wait_for_element("search_indicator", 30)
590
+ # ajax_wait_for_element("search_indicator", 30, "show")
591
+ # ajax_wait_for_element("search_indicator", 30, "hide")
592
+ # ajax_wait_for_element("search_indicator", 30, "show", 5) # check every 5 seconds
593
+ #
594
+ # Warning: this method has not been fully tested, if you are not using Rails, change your parameter accordingly.
595
+ #
596
+ def ajax_wait_for_element(element_id, seconds, status='show', check_interval = @@default_polling_interval)
597
+ count = 0
598
+ check_interval = 1 if check_interval < 1 or check_interval > seconds
599
+ while count < (seconds / check_interval) do
600
+ search_indicator = @web_browser.element_by_id(element_id)
601
+ search_indicator_outer_html = search_indicator.outerHtml if search_indicator
602
+ if status == 'hide'
603
+ return true if search_indicator_outer_html and !search_indicator_outer_html.include?('style="DISPLAY: none"')
604
+ else
605
+ return true if search_indicator_outer_html and search_indicator_outer_html.include?('style="DISPLAY: none"')
606
+ end
607
+ sleep check_interval if check_interval > 0 and check_interval < 5 * 60 # wait max 5 minutes
608
+ count += 1
609
+ end
610
+ return false
611
+ end
612
+
613
+ #Wait the element with given id to be present in web page
614
+ #
615
+ # Warning: this not working in Firefox, try use wait_util or try instead
616
+ def wait_for_element(element_id, timeout = @@default_timeout, interval = @@default_polling_interval)
617
+ start_time = Time.now
618
+ #TODO might not work with Firefox
619
+ until @web_browser.element_by_id(element_id) do
620
+ sleep(interval)
621
+ if (Time.now - start_time) > timeout
622
+ raise RuntimeError, "failed to find element: #{element_id} for max #{timeout}"
623
+ end
624
+ end
625
+ end
626
+ =begin
627
+
628
+ # TODO: Firewatir does not suport retrieving style or outerHtml
629
+ # http://jira.openqa.org/browse/WTR-260
630
+ # http://code.google.com/p/firewatir/issues/detail?id=76
631
+ #
632
+ # Max timeout value is 10 minutes
633
+ #
634
+ def ajax_call_complete_after_element_hidden(elem_id, check_start = 0.5, timeout = 5, interval = 0.5, &block)
635
+ yield
636
+ sleep check_start # the time allowed to perform the coomplete
637
+ timeout = 10 * 60 if timeout > 10 * 600 or timeout <= 0
638
+ begin
639
+ Timeout::timeout(timeout) {
640
+ begin
641
+ elem = element_by_id(elem_id)
642
+ while elem do
643
+ puts "outer=>#{elem.outerHtml}|"
644
+ puts "style =>#{elem.attribute_value('style')}|"
645
+ sleep interval
646
+ elem = element_by_id(elem_id)
647
+ end
648
+ rescue => e
649
+ puts e
650
+ end
651
+ }
652
+ rescue Timeout::Error
653
+ # Too slow!!
654
+ raise "Too slow, wait max #{timeout} seconds, the element #{elem_id} still there"
655
+ end
656
+ end
657
+
658
+ =end
659
+
660
+ # Try the operation up to specified times, and sleep given interval (in seconds)
661
+ # Error will be ignored until timeout
662
+ # Example
663
+ # repeat_try(3, 2) { click_button('Search' } # 3 times, 6 seconds in total
664
+ # repeat_try { click_button('Search' } # using default 5 tries, 2 second interval
665
+ def repeat_try(num_tries = @@default_timeout || 30, interval = @@default_polling_interval || 1, &block)
666
+ num_tries ||= 1
667
+ (num_tries - 1).times do |num|
668
+ begin
669
+ yield
670
+ return
671
+ rescue => e
672
+ # puts "debug: #{num} failed: #{e}"
673
+ sleep interval
674
+ end
675
+ end
676
+
677
+ # last try, throw error if still fails
678
+ begin
679
+ yield
680
+ rescue => e
681
+ raise e.to_s + " after trying #{num_tries} times every #{interval} seconds"
682
+ end
683
+ yield
684
+ end
685
+
686
+ # TODO: syntax
687
+
688
+ # Try the operation up to specified timeout (in seconds), and sleep given interval (in seconds).
689
+ # Error will be ignored until timeout
690
+ # Example
691
+ # try { click_link('waiting')}
692
+ # try(10, 2) { click_button('Search' } # try to click the 'Search' button upto 10 seconds, try every 2 seconds
693
+ # try { click_button('Search' }
694
+ def try(timeout = @@default_timeout, polling_interval = @@default_polling_interval || 1, &block)
695
+ start_time = Time.now
696
+
697
+ last_error = nil
698
+ until (duration = Time.now - start_time) > timeout
699
+ begin
700
+ return if yield
701
+ last_error = nil
702
+ rescue => e
703
+ last_error = e
704
+ end
705
+ sleep polling_interval
706
+ end
707
+
708
+ raise "Timeout after #{duration.to_i} seconds with error: #{last_error}." if last_error
709
+ raise "Timeout after #{duration.to_i} seconds."
710
+ end
711
+ alias try_upto try
712
+
713
+ ##
714
+ # Convert :first to 1, :second to 2, and so on...
715
+ def symbol_to_sequence(symb)
716
+ value = { :zero => 0,
717
+ :first => 1,
718
+ :second => 2,
719
+ :third => 3,
720
+ :fourth => 4,
721
+ :fifth => 5,
722
+ :sixth => 6,
723
+ :seventh => 7,
724
+ :eighth => 8,
725
+ :ninth => 9,
726
+ :tenth => 10 }[symb]
727
+ return value || symb.to_i
728
+ end
729
+
730
+ end
731
+
732
+ end