rwebspec-webdriver 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ module RWebSpec
2
+
3
+ module ElementLocator
4
+
5
+ BUTTON_VALID_TYPES = %w[button reset submit image]
6
+ def button_elements
7
+ find_elements(:xpath, ".//button | .//input[#{attribute_expression :type => BUTTON_VALID_TYPES}]")
8
+ end
9
+
10
+ CHECK_BOX_TYPES = %w(checkbox)
11
+ def check_box_elements(how, what, opts = [])
12
+ find_elements(:xpath, ".//input[#{attribute_expression :type => CHECK_BOX_TYPES}]")
13
+ end
14
+
15
+ RADIO_TYPES = %w(radio)
16
+ def radio_elements(how, what, opts = [])
17
+ find_elements(:xpath, ".//input[#{attribute_expression :type => RADIO_TYPES}]")
18
+ end
19
+
20
+ def select_elements(how, what, opts = [])
21
+ find_elements(:xpath, ".//input[#{attribute_expression :type => RADIO_TYPES}]")
22
+ end
23
+
24
+ # TextField, TextArea
25
+ TEXT_FILED_TYPES = %w(text)
26
+ def text_field_elements
27
+ find_elements(:xpath, ".//input[#{attribute_expression :type => TEXT_FILED_TYPES}]")
28
+ end
29
+
30
+ def text_area_elements
31
+ find_elements(:xpath, ".//textarea")
32
+ end
33
+
34
+ FILE_FIELD_TYPES = %w(file)
35
+ def file_field_elements
36
+ find_elements(:xpath, ".//input[#{attribute_expression :type => FILE_FIELD_TYPES}]")
37
+ end
38
+
39
+ HIDDEN_TYPES = %w(hidden)
40
+ def hidden_elements
41
+ find_elements(:xpath, ".//input[#{attribute_expression :type => HIDDEN_TYPES}]")
42
+ end
43
+
44
+ #---
45
+ def find_by_tag(tag)
46
+ find_elements(:tag_name, tag)
47
+ end
48
+
49
+ def should_use_label_element?
50
+ @selector[:tag_name] != "option" rescue false
51
+ end
52
+
53
+ def equal_pair(key, value)
54
+ # we assume :label means a corresponding label element, not the attribute
55
+ if key == :label && should_use_label_element?
56
+ "@id=//label[normalize-space()='#{value}']/@for"
57
+ else
58
+ "#{lhs_for(key)}='#{value}'"
59
+ end
60
+ end
61
+
62
+ def lhs_for(key)
63
+ case key
64
+ when :text, 'text'
65
+ 'normalize-space()'
66
+ when :href
67
+ # TODO: change this behaviour?
68
+ 'normalize-space(@href)'
69
+ else
70
+ "@#{key.to_s.gsub("_", "-")}"
71
+ end
72
+ end
73
+
74
+
75
+ def attribute_expression(selectors)
76
+ selectors.map do |key, val|
77
+ if val.kind_of?(Array)
78
+ "(" + val.map { |v| equal_pair(key, v) }.join(" or ") + ")"
79
+ else
80
+ equal_pair(key, val)
81
+ end
82
+ end.join(" and ")
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,172 @@
1
+
2
+ module RWebSpec
3
+ module LoadTestHelper
4
+
5
+ include RWebSpec::Utils
6
+ include RWebSpec::Assert
7
+
8
+ MAX_VU = 1000
9
+
10
+ # only support firefox or Celerity
11
+ def open_browser(base_url, options = {})
12
+ default_options = {:resynchronize => false, :firefox => false }
13
+ options = default_options.merge(options)
14
+ options[:firefox] ||= (ENV['LOADWISE_PREVIEW'] || $LOADWISE_PREVIEW)
15
+ RWebSpec::WebBrowser.new(base_url, nil, options)
16
+ end
17
+
18
+ # maybe attach_browser
19
+
20
+ # Does not provide real function, other than make enhancing test syntax
21
+ #
22
+ # Example:
23
+ # allow { click_button('Register') }
24
+ def allow(&block)
25
+ yield
26
+ end
27
+ alias shall_allow allow
28
+ alias allowing allow
29
+
30
+ # try operation, ignore if errors occur
31
+ #
32
+ # Example:
33
+ # failsafe { click_link("Logout") } # try logout, but it still OK if not being able to (already logout))
34
+ def failsafe(&block)
35
+ begin
36
+ yield
37
+ rescue =>e
38
+ end
39
+ end
40
+ alias fail_safe failsafe
41
+
42
+ # Try the operation up to specified timeout (in seconds), and sleep given interval (in seconds).
43
+ # Error will be ignored until timeout
44
+ # Example
45
+ # try { click_link('waiting')}
46
+ # try(10, 2) { click_button('Search' } # try to click the 'Search' button upto 10 seconds, try every 2 seconds
47
+ # try { click_button('Search' }
48
+ def try(timeout = @@default_timeout, polling_interval = @@default_polling_interval || 1, &block)
49
+ start_time = Time.now
50
+
51
+ last_error = nil
52
+ until (duration = Time.now - start_time) > timeout
53
+ begin
54
+ return if yield
55
+ last_error = nil
56
+ rescue => e
57
+ last_error = e
58
+ end
59
+ sleep polling_interval
60
+ end
61
+
62
+ raise "Timeout after #{duration.to_i} seconds with error: #{last_error}." if last_error
63
+ raise "Timeout after #{duration.to_i} seconds."
64
+ end
65
+ alias try_upto try
66
+
67
+ ##
68
+ # Convert :first to 1, :second to 2, and so on...
69
+ def symbol_to_sequence(symb)
70
+ value = { :zero => 0,
71
+ :first => 1,
72
+ :second => 2,
73
+ :third => 3,
74
+ :fourth => 4,
75
+ :fifth => 5,
76
+ :sixth => 6,
77
+ :seventh => 7,
78
+ :eighth => 8,
79
+ :ninth => 9,
80
+ :tenth => 10 }[symb]
81
+ return value || symb.to_i
82
+ end
83
+
84
+ # monitor current execution using
85
+ #
86
+ # Usage
87
+ # log_time { browser.click_button('Confirm') }
88
+ def log_time(msg, &block)
89
+ start_time = Time.now
90
+ yield
91
+ end_time = Time.now
92
+
93
+ Thread.current[:log] ||= []
94
+ Thread.current[:log] << {:file => File.basename(__FILE__),
95
+ :message => msg,
96
+ :start_time => Time.now,
97
+ :duration => Time.now - start_time}
98
+
99
+ if $LOADWISE_MONITOR
100
+ begin
101
+ require 'java'
102
+ puts "Calling Java 1"
103
+ java_import com.loadwise.db.MemoryDatabase
104
+ #puts "Calling Java 2: #{MemoryDatabase.count}"
105
+ MemoryDatabase.addEntry(1, "zdfa01", "a_spec.rb", msg, start_time, end_time);
106
+ puts "Calling Java Ok: #{MemoryDatabase.count}"
107
+ rescue NameError => ne
108
+ puts "Name Error: #{ne}"
109
+ # failed to load Java class
110
+ rescue => e
111
+ puts "Failed to calling Java: #{e.class.name}"
112
+ end
113
+ end
114
+ # How to notify LoadWise at real time
115
+ # LoadWise to collect CPU
116
+ end
117
+
118
+ def run_with_virtual_users(virtual_user_count = 2, preview = false, &block)
119
+ raise "too many virtual users" if virtual_user_count > MAX_VU
120
+
121
+ begin
122
+ if defined?(LOADWISE_PREVIEW)
123
+ preview = LOADWISE_PREVIEW
124
+ end
125
+ rescue => e1
126
+ end
127
+
128
+ if preview
129
+ virtual_user_count = 1
130
+ $LOADWISE_PREVIEW = true
131
+ end
132
+
133
+ if (virtual_user_count <= 1)
134
+ yield
135
+ else
136
+ threads = []
137
+ vu_reports = {}
138
+ virtual_user_count.times do |idx|
139
+ threads[idx] = Thread.new do
140
+ start_time = Time.now
141
+ vu_reports[idx] ||= []
142
+ begin
143
+ yield
144
+ vu_reports[idx] = Thread.current[:log]
145
+ rescue => e
146
+ vu_reports[idx] = Thread.current[:log]
147
+ vu_reports[idx] ||= []
148
+ vu_reports[idx] << { :error => e }
149
+ end
150
+ vu_reports[idx] ||= []
151
+ vu_reports[idx] << { :message => "Total Duration", :duration => Time.now - start_time }
152
+ puts "VU[#{idx+1}] #{Time.now - start_time}s"
153
+ end
154
+ end
155
+
156
+ threads.each {|t| t.join; }
157
+ vu_reports.each do |key, value|
158
+ value.each do |entry|
159
+ if entry[:error] then
160
+ puts "Error: #{entry[:error]}"
161
+ else
162
+ puts "[#{key}] #{entry[:message]}, #{entry[:duration]}"
163
+ end
164
+ end
165
+ end
166
+
167
+ return vu_reports
168
+ end
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,37 @@
1
+ class ContainsText
2
+
3
+ # this is what the matcher is called on.
4
+ # In this case:
5
+ # foo.should contains(:bars)
6
+ # foo would be passed to the +initialize+
7
+ def initialize(expected)
8
+ @expected = expected
9
+ end
10
+
11
+ def matches?(actual)
12
+ @actual = actual
13
+ return actual && actual.include?(@expected)
14
+ end
15
+
16
+ def actual_text
17
+ @actual.to_s.length > 256 ? @actual[0, 255] : @actual
18
+ end
19
+
20
+ # error message for should
21
+ def failure_message
22
+ "expected '#{actual_text}' to contain '#{@expected}', but it didn't"
23
+ end
24
+
25
+ # error message for should_not
26
+ def negative_failure_message
27
+ "expected '#{actual_text}' not to contain '#{@expected}', but it did"
28
+ end
29
+
30
+ end
31
+
32
+
33
+ # This method is the one you use with should/should_not
34
+ def contains_text?(expected)
35
+ ContainsText.new(expected)
36
+ end
37
+ alias contains? contains_text?
@@ -0,0 +1,147 @@
1
+ module RWebSpec
2
+ module Popup
3
+
4
+ #= Popup
5
+ #
6
+
7
+ # Start background thread to click popup windows
8
+ # Warning:
9
+ # Make browser window active
10
+ # Don't mouse your mouse to focus other window during test execution
11
+ def check_for_popups
12
+ autoit = WIN32OLE.new('AutoItX3.Control')
13
+ #
14
+ # Do forever - assumes popups could occur anywhere/anytime in your
15
+ # application.
16
+ loop do
17
+ # Look for window with given title. Give up after 1 second.
18
+ ret = autoit.WinWait('Windows Internet Explorer', '', 1)
19
+ #
20
+ # If window found, send appropriate keystroke (e.g. {enter}, {Y}, {N}).
21
+ if (ret==1) then
22
+ autoit.Send('{enter}')
23
+ end
24
+ #
25
+ # Take a rest to avoid chewing up cycles and give another thread a go.
26
+ # Then resume the loop.
27
+ sleep(3)
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Check for "Security Information" and "Security Alert" alert popup, click 'Yes'
33
+ #
34
+ # Usage: For individual test suite
35
+ #
36
+ # before(:all) do
37
+ # $popup = Thread.new { check_for_alerts }
38
+ # open_in_browser
39
+ # ...
40
+ # end
41
+ #
42
+ # after(:all) do
43
+ # close_browser
44
+ # Thread.kill($popup)
45
+ # end
46
+ #
47
+ # or for all tests,
48
+ # $popup = Thread.new { check_for_alerts }
49
+ # at_exit{ Thread.kill($popup) }
50
+ def check_for_security_alerts
51
+ autoit = WIN32OLE.new('AutoItX3.Control')
52
+ loop do
53
+ ["Security Alert", "Security Information"].each do |win_title|
54
+ ret = autoit.WinWait(win_title, '', 1)
55
+ if (ret==1) then
56
+ autoit.Send('{Y}')
57
+ end
58
+ end
59
+ sleep(3)
60
+ end
61
+ end
62
+
63
+ def verify_alert(title = "Microsoft Internet Explorer", button = "OK")
64
+ if is_windows? && !is_firefox?
65
+ WIN32OLE.new('AutoItX3.Control').ControlClick(title, '', button)
66
+ else
67
+ raise "This function only supports IE"
68
+ end
69
+ end
70
+
71
+ def click_button_in_security_information_popup(button = "&Yes")
72
+ verify_alert("Security Information", "", button)
73
+ end
74
+ alias click_security_information_popup click_button_in_security_information_popup
75
+
76
+ def click_button_in_security_alert_popup(button = "&Yes")
77
+ verify_alert("Security Alert", "", button)
78
+ end
79
+ alias click_security_alert_popup click_button_in_security_alert_popup
80
+
81
+ def click_button_in_javascript_popup(button = "OK")
82
+ verify_alert()
83
+ end
84
+ alias click_javascript_popup click_button_in_javascript_popup
85
+
86
+ ##
87
+ # This only works for IEs
88
+ # Cons:
89
+ # - Slow
90
+ # - only works in IE
91
+ # - does not work for security alert ?
92
+ def ie_popup_clicker(button_name = "OK", max_wait = 15)
93
+ require 'watir/contrib/enabled_popup'
94
+ require 'win32ole'
95
+ hwnd = ie.enabled_popup(15)
96
+ if (hwnd) #yeah! a popup
97
+ popup = WinClicker.new
98
+ popup.makeWindowActive(hwnd) #Activate the window.
99
+ popup.clickWindowsButton_hwnd(hwnd, button_name) #Click the button
100
+ #popup.clickWindowsButton(/Internet/,button_name,30)
101
+ popup = nil
102
+ end
103
+ end
104
+
105
+ def click_popup_window(button, wait_time= 9, user_input=nil )
106
+ @web_browser.start_clicker(button, wait_time, user_input)
107
+ sleep 0.5
108
+ end
109
+ # run a separate process waiting for the popup window to click
110
+ #
111
+ #
112
+ def prepare_to_click_button_in_popup(button = "OK", wait_time = 3)
113
+ # !@web_browser.is_firefox?
114
+ # TODO: firefox is OK
115
+ if RUBY_PLATFORM =~ /mswin/ || RUBY_PLATFORM =~ /mingw/ then
116
+ start_checking_js_dialog(button, wait_time)
117
+ else
118
+ raise "this only support on Windows and on IE"
119
+ end
120
+ end
121
+
122
+ # Start a background process to click the button on a javascript popup window
123
+ def start_checking_js_dialog(button = "OK", wait_time = 3)
124
+ w = WinClicker.new
125
+ longName = File.expand_path(File.dirname(__FILE__)).gsub("/", "\\" )
126
+ shortName = w.getShortFileName(longName)
127
+ c = "start ruby #{shortName}\\clickJSDialog.rb #{button} #{wait_time} "
128
+ w.winsystem(c)
129
+ w = nil
130
+ end
131
+
132
+ # Click the button in javascript popup dialog
133
+ # Usage:
134
+ # click_button_in_popup_after { click_link('Cancel')}
135
+ # click_button_in_popup_after("OK") { click_link('Cancel')}
136
+ #
137
+ def click_button_in_popup_after(options = {:button => "OK", :wait_time => 3}, &block)
138
+ if is_windows? then
139
+ start_checking_js_dialog(options[:button], options[:wait_time])
140
+ yield
141
+ else
142
+ raise "this only support on Windows and on IE"
143
+ end
144
+ end
145
+
146
+ end
147
+ end
@@ -0,0 +1,99 @@
1
+ require 'uri'
2
+ require File.dirname(__FILE__) + "/driver"
3
+ require File.dirname(__FILE__) + "/web_page"
4
+ require File.dirname(__FILE__) + "/assert"
5
+
6
+ # ZZ patches to RSpec 1.1.2 - 1.1.4
7
+ # - add to_s method to example_group
8
+ module Spec
9
+ module Example
10
+ class ExampleGroup
11
+ def to_s
12
+ @_defined_description
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ # example
19
+ # should link_by_text(text, options).size > 0
20
+
21
+ module RWebSpec
22
+ module RSpecHelper
23
+ include RWebSpec::Driver
24
+ include RWebSpec::Assert
25
+ include RWebSpec::Utils
26
+
27
+ # --
28
+ # Content
29
+ # --
30
+
31
+ def table_source(table_id)
32
+ table(:id, table_id).innerHTML
33
+ # elem = @web_browser.document.getElementById(table_id)
34
+ # raise "The element '#{table_id}' is not a table or there are multple elements with same id" unless elem.name.uppercase == "TABLE"
35
+ # elem.innerHTML
36
+ end
37
+ alias table_source_by_id table_source
38
+
39
+ def element_text(elem_id)
40
+ @web_browser.element_value(elem_id)
41
+ end
42
+ alias element_text_by_id element_text
43
+
44
+ #TODO: is it working?
45
+ def element_source(elem_id)
46
+ @web_browser.get_html_in_element(elem_id)
47
+ end
48
+
49
+
50
+ def button_by_id(button_id)
51
+ button(:id, button_id)
52
+ end
53
+
54
+ def buttons_by_caption(text)
55
+ button(:text, text)
56
+ end
57
+ alias buttons_by_text buttons_by_caption
58
+
59
+ def link_by_id(link_id)
60
+ link(:id, link_id)
61
+ end
62
+
63
+ # default options: exact => true
64
+ def links_by_text(link_text, options = {})
65
+ options.merge!({:exact=> true})
66
+ matching_links = []
67
+ links.each { |link|
68
+ matching_links << link if (options[:exact] ? link.text == link_text : link.text.include?(link_text))
69
+ }
70
+ return matching_links
71
+ end
72
+ alias links_with_text links_by_text
73
+
74
+ def save_page(file_name = nil)
75
+ @web_browser.save_page(file_name)
76
+ end
77
+
78
+ def save_content_to_file(content, file_name = nil)
79
+ file_name ||= Time.now.strftime("%Y%m%d%H%M%S") + ".html"
80
+ puts "about to save page: #{File.expand_path(file_name)}"
81
+ File.open(file_name, "w").puts content
82
+ end
83
+
84
+ # When running
85
+ def debugging?
86
+ $ITEST2_DEBUGGING && $ITEST2_RUNNING_AS == "test_case"
87
+ end
88
+
89
+ # RSpec Matchers
90
+ #
91
+ # Example,
92
+ # a_number.should be_odd_number
93
+ def be_odd_number
94
+ simple_matcher("must be odd number") { |actual| actual && actual.to_id % 2 == 1}
95
+ end
96
+
97
+ end
98
+
99
+ end