webrat 0.3.4 → 0.4.0

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