zhimin-rwebspec 1.4.0

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