webrat 0.3.4 → 0.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.
Files changed (63) hide show
  1. data/History.txt +140 -30
  2. data/README.rdoc +85 -0
  3. data/Rakefile +72 -22
  4. data/install.rb +1 -1
  5. data/lib/webrat.rb +12 -17
  6. data/lib/webrat/core.rb +9 -7
  7. data/lib/webrat/core/configuration.rb +87 -0
  8. data/lib/webrat/core/{area.rb → elements/area.rb} +7 -20
  9. data/lib/webrat/core/elements/element.rb +33 -0
  10. data/lib/webrat/core/elements/field.rb +394 -0
  11. data/lib/webrat/core/elements/form.rb +103 -0
  12. data/lib/webrat/core/elements/label.rb +31 -0
  13. data/lib/webrat/core/{link.rb → elements/link.rb} +15 -26
  14. data/lib/webrat/core/elements/select_option.rb +35 -0
  15. data/lib/webrat/core/locators.rb +13 -85
  16. data/lib/webrat/core/locators/area_locator.rb +38 -0
  17. data/lib/webrat/core/locators/button_locator.rb +54 -0
  18. data/lib/webrat/core/locators/field_by_id_locator.rb +37 -0
  19. data/lib/webrat/core/locators/field_labeled_locator.rb +56 -0
  20. data/lib/webrat/core/locators/field_locator.rb +25 -0
  21. data/lib/webrat/core/locators/field_named_locator.rb +41 -0
  22. data/lib/webrat/core/locators/form_locator.rb +19 -0
  23. data/lib/webrat/core/locators/label_locator.rb +34 -0
  24. data/lib/webrat/core/locators/link_locator.rb +66 -0
  25. data/lib/webrat/core/locators/locator.rb +20 -0
  26. data/lib/webrat/core/locators/select_option_locator.rb +59 -0
  27. data/lib/webrat/core/logging.rb +5 -9
  28. data/lib/webrat/core/matchers/have_content.rb +19 -44
  29. data/lib/webrat/core/matchers/have_selector.rb +15 -2
  30. data/lib/webrat/core/matchers/have_tag.rb +15 -2
  31. data/lib/webrat/core/matchers/have_xpath.rb +21 -28
  32. data/lib/webrat/core/methods.rb +32 -15
  33. data/lib/webrat/core/mime.rb +3 -3
  34. data/lib/webrat/core/save_and_open_page.rb +50 -0
  35. data/lib/webrat/core/scope.rb +183 -41
  36. data/lib/webrat/core/session.rb +125 -63
  37. data/lib/webrat/core/xml.rb +115 -0
  38. data/lib/webrat/core/xml/hpricot.rb +19 -0
  39. data/lib/webrat/core/xml/nokogiri.rb +76 -0
  40. data/lib/webrat/core/xml/rexml.rb +24 -0
  41. data/lib/webrat/core_extensions/deprecate.rb +1 -1
  42. data/lib/webrat/mechanize.rb +58 -12
  43. data/lib/webrat/merb.rb +7 -73
  44. data/lib/webrat/merb_session.rb +65 -0
  45. data/lib/webrat/rack.rb +1 -1
  46. data/lib/webrat/rails.rb +56 -55
  47. data/lib/webrat/rspec-rails.rb +13 -0
  48. data/lib/webrat/selenium.rb +92 -1
  49. data/lib/webrat/selenium/matchers.rb +146 -0
  50. data/lib/webrat/selenium/selenium_session.rb +179 -80
  51. data/lib/webrat/sinatra.rb +14 -4
  52. data/vendor/selenium-server.jar +0 -0
  53. metadata +36 -17
  54. data/README.txt +0 -90
  55. data/TODO.txt +0 -10
  56. data/init.rb +0 -3
  57. data/lib/webrat/core/field.rb +0 -332
  58. data/lib/webrat/core/flunk.rb +0 -7
  59. data/lib/webrat/core/form.rb +0 -130
  60. data/lib/webrat/core/label.rb +0 -18
  61. data/lib/webrat/core/nokogiri.rb +0 -44
  62. data/lib/webrat/core/select_option.rb +0 -29
  63. data/lib/webrat/rails/redirect_actions.rb +0 -18
@@ -1,3 +1,94 @@
1
- require "selenium"
1
+ require "webrat"
2
+ gem "selenium-client", ">=1.2.9"
3
+ require "selenium/client"
2
4
  require "webrat/selenium/selenium_session"
5
+ require "webrat/selenium/matchers"
3
6
 
7
+ module Webrat
8
+
9
+ def self.with_selenium_server #:nodoc:
10
+ start_selenium_server
11
+ yield
12
+ stop_selenium_server
13
+ end
14
+
15
+ def self.start_selenium_server #:nodoc:
16
+ unless Webrat.configuration.selenium_server_address
17
+ remote_control = ::Selenium::RemoteControl::RemoteControl.new("0.0.0.0", Webrat.configuration.selenium_server_port, 5)
18
+ remote_control.jar_file = File.expand_path(__FILE__ + "../../../../vendor/selenium-server.jar")
19
+ remote_control.start :background => true
20
+ end
21
+ TCPSocket.wait_for_service :host => (Webrat.configuration.selenium_server_address || "0.0.0.0"), :port => Webrat.configuration.selenium_server_port
22
+ end
23
+
24
+ def self.stop_selenium_server #:nodoc:
25
+ ::Selenium::RemoteControl::RemoteControl.new("0.0.0.0", Webrat.configuration.selenium_server_port, 5).stop unless Webrat.configuration.selenium_server_address
26
+ end
27
+
28
+ def self.start_app_server #:nodoc:
29
+ pid_file = File.expand_path(RAILS_ROOT + "/tmp/pids/mongrel_selenium.pid")
30
+ system("mongrel_rails start -d --chdir=#{RAILS_ROOT} --port=#{Webrat.configuration.application_port} --environment=#{Webrat.configuration.application_environment} --pid #{pid_file} &")
31
+ TCPSocket.wait_for_service :host => Webrat.configuration.application_address, :port => Webrat.configuration.application_port.to_i
32
+ end
33
+
34
+ def self.stop_app_server #:nodoc:
35
+ pid_file = File.expand_path(RAILS_ROOT + "/tmp/pids/mongrel_selenium.pid")
36
+ system "mongrel_rails stop -c #{RAILS_ROOT} --pid #{pid_file}"
37
+ end
38
+
39
+ # To use Webrat's Selenium support, you'll need the selenium-client gem installed.
40
+ # Activate it with (for example, in your <tt>env.rb</tt>):
41
+ #
42
+ # require "webrat"
43
+ #
44
+ # Webrat.configure do |config|
45
+ # config.mode = :selenium
46
+ # end
47
+ #
48
+ # == Dropping down to the selenium-client API
49
+ #
50
+ # If you ever need to do something with Selenium not provided in the Webrat API,
51
+ # you can always drop down to the selenium-client API using the <tt>selenium</tt> method.
52
+ # For example:
53
+ #
54
+ # When "I drag the photo to the left" do
55
+ # selenium.dragdrop("id=photo_123", "+350, 0")
56
+ # end
57
+ #
58
+ # == Auto-starting of the mongrel and java server
59
+ #
60
+ # Webrat will automatically start the Selenium Java server process and an instance
61
+ # of Mongrel when a test is run. The Mongrel will run in the "selenium" environment
62
+ # instead of "test", so ensure you've got that defined, and will run on port 3001.
63
+ #
64
+ # == Waiting
65
+ #
66
+ # In order to make writing Selenium tests as easy as possible, Webrat will automatically
67
+ # wait for the correct elements to exist on the page when trying to manipulate them
68
+ # with methods like <tt>fill_in</tt>, etc. In general, this means you should be able to write
69
+ # your Webrat::Selenium tests ignoring the concurrency issues that can plague in-browser
70
+ # testing, so long as you're using the Webrat API.
71
+ module Selenium
72
+ module Methods
73
+ def response
74
+ webrat_session.response
75
+ end
76
+
77
+ def wait_for(*args, &block)
78
+ webrat_session.wait_for(*args, &block)
79
+ end
80
+
81
+ def save_and_open_screengrab
82
+ webrat_session.save_and_open_screengrab
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ module ActionController #:nodoc:
89
+ IntegrationTest.class_eval do
90
+ include Webrat::Methods
91
+ include Webrat::Selenium::Methods
92
+ include Webrat::Selenium::Matchers
93
+ end
94
+ end
@@ -0,0 +1,146 @@
1
+ module Webrat
2
+ module Selenium
3
+ module Matchers
4
+
5
+ class HaveXpath
6
+ def initialize(expected)
7
+ @expected = expected
8
+ end
9
+
10
+ def matches?(response)
11
+ response.session.wait_for do
12
+ response.selenium.is_element_present("xpath=#{@expected}")
13
+ end
14
+ end
15
+
16
+ # ==== Returns
17
+ # String:: The failure message.
18
+ def failure_message
19
+ "expected following text to match xpath #{@expected}:\n#{@document}"
20
+ end
21
+
22
+ # ==== Returns
23
+ # String:: The failure message to be displayed in negative matches.
24
+ def negative_failure_message
25
+ "expected following text to not match xpath #{@expected}:\n#{@document}"
26
+ end
27
+ end
28
+
29
+ def have_xpath(xpath)
30
+ HaveXpath.new(xpath)
31
+ end
32
+
33
+ def assert_have_xpath(expected)
34
+ hs = HaveXpath.new(expected)
35
+ assert hs.matches?(response), hs.failure_message
36
+ end
37
+
38
+ def assert_have_no_xpath(expected)
39
+ hs = HaveXpath.new(expected)
40
+ assert !hs.matches?(response), hs.negative_failure_message
41
+ end
42
+
43
+ class HaveSelector
44
+ def initialize(expected)
45
+ @expected = expected
46
+ end
47
+
48
+ def matches?(response)
49
+ response.session.wait_for do
50
+ response.selenium.is_element_present("css=#{@expected}")
51
+ end
52
+ end
53
+
54
+ # ==== Returns
55
+ # String:: The failure message.
56
+ def failure_message
57
+ "expected following text to match selector #{@expected}:\n#{@document}"
58
+ end
59
+
60
+ # ==== Returns
61
+ # String:: The failure message to be displayed in negative matches.
62
+ def negative_failure_message
63
+ "expected following text to not match selector #{@expected}:\n#{@document}"
64
+ end
65
+ end
66
+
67
+ def have_selector(content)
68
+ HaveSelector.new(content)
69
+ end
70
+
71
+ # Asserts that the body of the response contains
72
+ # the supplied selector
73
+ def assert_have_selector(expected)
74
+ hs = HaveSelector.new(expected)
75
+ assert hs.matches?(response), hs.failure_message
76
+ end
77
+
78
+ # Asserts that the body of the response
79
+ # does not contain the supplied string or regepx
80
+ def assert_have_no_selector(expected)
81
+ hs = HaveSelector.new(expected)
82
+ assert !hs.matches?(response), hs.negative_failure_message
83
+ end
84
+
85
+ class HasContent #:nodoc:
86
+ def initialize(content)
87
+ @content = content
88
+ end
89
+
90
+ def matches?(response)
91
+ if @content.is_a?(Regexp)
92
+ text_finder = "regexp:#{@content.source}"
93
+ else
94
+ text_finder = @content
95
+ end
96
+
97
+ response.session.wait_for do
98
+ response.selenium.is_text_present(text_finder)
99
+ end
100
+ end
101
+
102
+ # ==== Returns
103
+ # String:: The failure message.
104
+ def failure_message
105
+ "expected the following element's content to #{content_message}:\n#{@element}"
106
+ end
107
+
108
+ # ==== Returns
109
+ # String:: The failure message to be displayed in negative matches.
110
+ def negative_failure_message
111
+ "expected the following element's content to not #{content_message}:\n#{@element}"
112
+ end
113
+
114
+ def content_message
115
+ case @content
116
+ when String
117
+ "include \"#{@content}\""
118
+ when Regexp
119
+ "match #{@content.inspect}"
120
+ end
121
+ end
122
+ end
123
+
124
+ # Matches the contents of an HTML document with
125
+ # whatever string is supplied
126
+ def contain(content)
127
+ HasContent.new(content)
128
+ end
129
+
130
+ # Asserts that the body of the response contain
131
+ # the supplied string or regexp
132
+ def assert_contain(content)
133
+ hc = HasContent.new(content)
134
+ assert hc.matches?(response), hc.failure_message
135
+ end
136
+
137
+ # Asserts that the body of the response
138
+ # does not contain the supplied string or regepx
139
+ def assert_not_contain(content)
140
+ hc = HasContent.new(content)
141
+ assert !hc.matches?(response), hc.negative_failure_message
142
+ end
143
+
144
+ end
145
+ end
146
+ end
@@ -1,136 +1,235 @@
1
+ require "webrat/core/save_and_open_page"
2
+
1
3
  module Webrat
4
+ class TimeoutError < WebratError
5
+ end
6
+
7
+ class SeleniumResponse
8
+ attr_reader :body
9
+ attr_reader :session
10
+
11
+ def initialize(session, body)
12
+ @session = session
13
+ @body = body
14
+ end
15
+
16
+ def selenium
17
+ session.selenium
18
+ end
19
+ end
20
+
2
21
  class SeleniumSession
3
-
4
- def initialize(selenium_driver) #:nodoc:
5
- @selenium = selenium_driver
6
- extend_selenium
7
- define_location_strategies
22
+ include Webrat::SaveAndOpenPage
23
+
24
+ def initialize(*args) # :nodoc:
25
+ end
26
+
27
+ def simulate
8
28
  end
9
-
29
+
30
+ def automate
31
+ yield
32
+ end
33
+
10
34
  def visit(url)
11
- @selenium.open(url)
35
+ selenium.open(url)
12
36
  end
13
-
14
- alias_method :visits, :visit
15
-
37
+
38
+ webrat_deprecate :visits, :visit
39
+
16
40
  def fill_in(field_identifier, options)
17
41
  locator = "webrat=#{Regexp.escape(field_identifier)}"
18
- @selenium.type(locator, "#{options[:with]}")
42
+ selenium.wait_for_element locator, 5
43
+ selenium.type(locator, "#{options[:with]}")
44
+ end
45
+
46
+ webrat_deprecate :fills_in, :fill_in
47
+
48
+ def response
49
+ SeleniumResponse.new(self, response_body)
19
50
  end
20
-
21
- alias_method :fills_in, :fill_in
22
-
51
+
23
52
  def response_body #:nodoc:
24
- @selenium.get_html_source
53
+ selenium.get_html_source
25
54
  end
26
-
55
+
27
56
  def click_button(button_text_or_regexp = nil, options = {})
28
57
  if button_text_or_regexp.is_a?(Hash) && options == {}
29
58
  pattern, options = nil, button_text_or_regexp
30
- else
59
+ elsif button_text_or_regexp
31
60
  pattern = adjust_if_regexp(button_text_or_regexp)
32
61
  end
33
62
  pattern ||= '*'
34
- @selenium.click("button=#{pattern}")
35
- wait_for_result(options[:wait])
63
+ locator = "button=#{pattern}"
64
+
65
+ selenium.wait_for_element locator, 5
66
+ selenium.click locator
36
67
  end
37
-
38
- alias_method :clicks_button, :click_button
68
+
69
+ webrat_deprecate :clicks_button, :click_button
39
70
 
40
71
  def click_link(link_text_or_regexp, options = {})
41
72
  pattern = adjust_if_regexp(link_text_or_regexp)
42
- @selenium.click("webratlink=#{pattern}")
43
- wait_for_result(options[:wait])
44
- end
45
-
46
- alias_method :clicks_link, :click_link
47
-
48
- def click_link_within(selector, link_text, options = {})
49
- @selenium.click("webratlinkwithin=#{selector}|#{link_text}")
50
- wait_for_result(options[:wait])
51
- end
52
-
53
- alias_method :clicks_link_within, :click_link_within
54
-
55
- def wait_for_result(wait_type)
56
- if wait_type == :ajax
57
- wait_for_ajax
58
- elsif wait_type == :effects
59
- wait_for_effects
60
- else
61
- wait_for_page_to_load
62
- end
73
+ locator = "webratlink=#{pattern}"
74
+ selenium.wait_for_element locator, 5
75
+ selenium.click locator
63
76
  end
64
77
 
65
- def wait_for_page_to_load(timeout = 15000)
66
- @selenium.wait_for_page_to_load(timeout)
67
- end
78
+ webrat_deprecate :clicks_link, :click_link
68
79
 
69
- def wait_for_ajax(timeout = 15000)
70
- @selenium.wait_for_condition "Ajax.activeRequestCount == 0", timeout
80
+ def click_link_within(selector, link_text, options = {})
81
+ locator = "webratlinkwithin=#{selector}|#{link_text}"
82
+ selenium.wait_for_element locator, 5
83
+ selenium.click locator
71
84
  end
72
85
 
73
- def wait_for_effects(timeout = 15000)
74
- @selenium.wait_for_condition "window.Effect.Queue.size() == 0", timeout
75
- end
86
+ webrat_deprecate :clicks_link_within, :click_link_within
76
87
 
77
- def wait_for_ajax_and_effects
78
- wait_for_ajax
79
- wait_for_effects
80
- end
81
-
82
- def selects(option_text, options = {})
88
+ def select(option_text, options = {})
83
89
  id_or_name_or_label = options[:from]
84
-
90
+
85
91
  if id_or_name_or_label
86
92
  select_locator = "webrat=#{id_or_name_or_label}"
87
93
  else
88
94
  select_locator = "webratselectwithoption=#{option_text}"
89
95
  end
90
- @selenium.select(select_locator, option_text)
96
+
97
+ selenium.wait_for_element select_locator, 5
98
+ selenium.select(select_locator, option_text)
91
99
  end
92
-
100
+
101
+ webrat_deprecate :selects, :select
102
+
93
103
  def choose(label_text)
94
- @selenium.click("webrat=#{label_text}")
104
+ locator = "webrat=#{label_text}"
105
+ selenium.wait_for_element locator, 5
106
+ selenium.click locator
95
107
  end
96
-
97
- alias_method :chooses, :choose
98
-
108
+
109
+ webrat_deprecate :chooses, :choose
110
+
99
111
  def check(label_text)
100
- @selenium.check("webrat=#{label_text}")
112
+ locator = "webrat=#{label_text}"
113
+ selenium.wait_for_element locator, 5
114
+ selenium.check locator
101
115
  end
102
-
103
- alias_method :checks, :check
104
-
105
- def is_ordered(*args) #:nodoc:
106
- @selenium.is_ordered(*args)
116
+
117
+ webrat_deprecate :checks, :check
118
+
119
+ def fire_event(field_identifier, event)
120
+ locator = "webrat=#{Regexp.escape(field_identifier)}"
121
+ selenium.fire_event(locator, "#{event}")
122
+ end
123
+
124
+ def key_down(field_identifier, key_code)
125
+ locator = "webrat=#{Regexp.escape(field_identifier)}"
126
+ selenium.key_down(locator, key_code)
127
+ end
128
+
129
+ def key_up(field_identifier, key_code)
130
+ locator = "webrat=#{Regexp.escape(field_identifier)}"
131
+ selenium.key_up(locator, key_code)
132
+ end
133
+
134
+ def wait_for(params={})
135
+ timeout = params[:timeout] || 5
136
+ message = params[:message] || "Timeout exceeded"
137
+
138
+ begin_time = Time.now
139
+
140
+ while (Time.now - begin_time) < timeout
141
+ value = nil
142
+
143
+ begin
144
+ value = yield
145
+ rescue ::Spec::Expectations::ExpectationNotMetError, ::Selenium::CommandError, Webrat::WebratError
146
+ value = nil
147
+ end
148
+
149
+ return value if value
150
+
151
+ sleep 0.25
152
+ end
153
+
154
+ raise Webrat::TimeoutError.new(message + " (after #{timeout} sec)")
155
+ true
107
156
  end
108
-
109
- def dragdrop(*args) #:nodoc:
110
- @selenium.dragdrop(*args)
157
+
158
+ def selenium
159
+ return $browser if $browser
160
+ setup
161
+ $browser
111
162
  end
112
-
163
+
164
+ webrat_deprecate :browser, :selenium
165
+
166
+
167
+ def save_and_open_screengrab
168
+ return unless File.exist?(saved_page_dir)
169
+
170
+ filename = "#{saved_page_dir}/webrat-#{Time.now.to_i}.png"
171
+
172
+ if $browser.chrome_backend?
173
+ $browser.capture_entire_page_screenshot(filename, '')
174
+ else
175
+ $browser.capture_screenshot(filename)
176
+ end
177
+ open_in_browser(filename)
178
+ end
179
+
113
180
  protected
114
-
181
+
182
+ def setup #:nodoc:
183
+ silence_stream(STDOUT) do
184
+ Webrat.start_selenium_server
185
+ Webrat.start_app_server
186
+ end
187
+
188
+ create_browser
189
+ $browser.start
190
+ teardown_at_exit
191
+
192
+ extend_selenium
193
+ define_location_strategies
194
+ $browser.window_maximize
195
+ end
196
+
197
+
198
+ def create_browser
199
+ $browser = ::Selenium::Client::Driver.new(Webrat.configuration.selenium_server_address || "localhost",
200
+ Webrat.configuration.selenium_server_port, Webrat.configuration.selenium_browser_key, "http://#{Webrat.configuration.application_address}:#{Webrat.configuration.application_port}")
201
+ $browser.set_speed(0) unless Webrat.configuration.selenium_server_address
202
+ end
203
+
204
+ def teardown_at_exit #:nodoc:
205
+ at_exit do
206
+ silence_stream(STDOUT) do
207
+ $browser.stop
208
+ Webrat.stop_app_server
209
+ Webrat.stop_selenium_server
210
+ end
211
+ end
212
+ end
213
+
115
214
  def adjust_if_regexp(text_or_regexp) #:nodoc:
116
215
  if text_or_regexp.is_a?(Regexp)
117
216
  "evalregex:#{text_or_regexp.inspect}"
118
217
  else
119
- text_or_regexp
120
- end
218
+ "evalregex:/#{text_or_regexp}/"
219
+ end
121
220
  end
122
-
221
+
123
222
  def extend_selenium #:nodoc:
124
223
  extensions_file = File.join(File.dirname(__FILE__), "selenium_extensions.js")
125
224
  extenions_js = File.read(extensions_file)
126
- @selenium.get_eval(extenions_js)
225
+ selenium.get_eval(extenions_js)
127
226
  end
128
-
227
+
129
228
  def define_location_strategies #:nodoc:
130
229
  Dir[File.join(File.dirname(__FILE__), "location_strategy_javascript", "*.js")].sort.each do |file|
131
230
  strategy_js = File.read(file)
132
231
  strategy_name = File.basename(file, '.js')
133
- @selenium.add_location_strategy(strategy_name, strategy_js)
232
+ selenium.add_location_strategy(strategy_name, strategy_js)
134
233
  end
135
234
  end
136
235
  end