nimboids-capybara 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/History.txt +289 -0
  2. data/README.rdoc +722 -0
  3. data/lib/capybara.rb +252 -0
  4. data/lib/capybara/cucumber.rb +28 -0
  5. data/lib/capybara/driver/base.rb +64 -0
  6. data/lib/capybara/driver/node.rb +74 -0
  7. data/lib/capybara/dsl.rb +168 -0
  8. data/lib/capybara/node/actions.rb +162 -0
  9. data/lib/capybara/node/base.rb +63 -0
  10. data/lib/capybara/node/document.rb +25 -0
  11. data/lib/capybara/node/element.rb +201 -0
  12. data/lib/capybara/node/finders.rb +197 -0
  13. data/lib/capybara/node/matchers.rb +417 -0
  14. data/lib/capybara/node/simple.rb +132 -0
  15. data/lib/capybara/rack_test/browser.rb +121 -0
  16. data/lib/capybara/rack_test/driver.rb +80 -0
  17. data/lib/capybara/rack_test/form.rb +80 -0
  18. data/lib/capybara/rack_test/node.rb +105 -0
  19. data/lib/capybara/rails.rb +17 -0
  20. data/lib/capybara/rspec.rb +26 -0
  21. data/lib/capybara/rspec/features.rb +22 -0
  22. data/lib/capybara/rspec/matchers.rb +154 -0
  23. data/lib/capybara/selector.rb +89 -0
  24. data/lib/capybara/selenium/driver.rb +163 -0
  25. data/lib/capybara/selenium/node.rb +91 -0
  26. data/lib/capybara/server.rb +90 -0
  27. data/lib/capybara/session.rb +321 -0
  28. data/lib/capybara/spec/driver.rb +301 -0
  29. data/lib/capybara/spec/fixtures/capybara.jpg +3 -0
  30. data/lib/capybara/spec/fixtures/test_file.txt +1 -0
  31. data/lib/capybara/spec/public/test.js +43 -0
  32. data/lib/capybara/spec/session.rb +154 -0
  33. data/lib/capybara/spec/session/all_spec.rb +78 -0
  34. data/lib/capybara/spec/session/attach_file_spec.rb +73 -0
  35. data/lib/capybara/spec/session/check_spec.rb +65 -0
  36. data/lib/capybara/spec/session/choose_spec.rb +26 -0
  37. data/lib/capybara/spec/session/click_button_spec.rb +304 -0
  38. data/lib/capybara/spec/session/click_link_or_button_spec.rb +36 -0
  39. data/lib/capybara/spec/session/click_link_spec.rb +119 -0
  40. data/lib/capybara/spec/session/current_host_spec.rb +68 -0
  41. data/lib/capybara/spec/session/current_url_spec.rb +15 -0
  42. data/lib/capybara/spec/session/fill_in_spec.rb +125 -0
  43. data/lib/capybara/spec/session/find_button_spec.rb +18 -0
  44. data/lib/capybara/spec/session/find_by_id_spec.rb +18 -0
  45. data/lib/capybara/spec/session/find_field_spec.rb +26 -0
  46. data/lib/capybara/spec/session/find_link_spec.rb +19 -0
  47. data/lib/capybara/spec/session/find_spec.rb +149 -0
  48. data/lib/capybara/spec/session/first_spec.rb +105 -0
  49. data/lib/capybara/spec/session/has_button_spec.rb +32 -0
  50. data/lib/capybara/spec/session/has_content_spec.rb +106 -0
  51. data/lib/capybara/spec/session/has_css_spec.rb +243 -0
  52. data/lib/capybara/spec/session/has_field_spec.rb +192 -0
  53. data/lib/capybara/spec/session/has_link_spec.rb +37 -0
  54. data/lib/capybara/spec/session/has_select_spec.rb +129 -0
  55. data/lib/capybara/spec/session/has_selector_spec.rb +129 -0
  56. data/lib/capybara/spec/session/has_table_spec.rb +96 -0
  57. data/lib/capybara/spec/session/has_xpath_spec.rb +123 -0
  58. data/lib/capybara/spec/session/headers.rb +19 -0
  59. data/lib/capybara/spec/session/javascript.rb +289 -0
  60. data/lib/capybara/spec/session/response_code.rb +19 -0
  61. data/lib/capybara/spec/session/select_spec.rb +113 -0
  62. data/lib/capybara/spec/session/text_spec.rb +19 -0
  63. data/lib/capybara/spec/session/uncheck_spec.rb +21 -0
  64. data/lib/capybara/spec/session/unselect_spec.rb +61 -0
  65. data/lib/capybara/spec/session/within_spec.rb +178 -0
  66. data/lib/capybara/spec/test_app.rb +142 -0
  67. data/lib/capybara/spec/views/buttons.erb +4 -0
  68. data/lib/capybara/spec/views/fieldsets.erb +29 -0
  69. data/lib/capybara/spec/views/form.erb +365 -0
  70. data/lib/capybara/spec/views/frame_one.erb +8 -0
  71. data/lib/capybara/spec/views/frame_two.erb +8 -0
  72. data/lib/capybara/spec/views/header_links.erb +7 -0
  73. data/lib/capybara/spec/views/host_links.erb +12 -0
  74. data/lib/capybara/spec/views/popup_one.erb +8 -0
  75. data/lib/capybara/spec/views/popup_two.erb +8 -0
  76. data/lib/capybara/spec/views/postback.erb +13 -0
  77. data/lib/capybara/spec/views/tables.erb +122 -0
  78. data/lib/capybara/spec/views/with_html.erb +78 -0
  79. data/lib/capybara/spec/views/with_html_entities.erb +1 -0
  80. data/lib/capybara/spec/views/with_js.erb +48 -0
  81. data/lib/capybara/spec/views/with_scope.erb +36 -0
  82. data/lib/capybara/spec/views/with_simple_html.erb +1 -0
  83. data/lib/capybara/spec/views/within_frames.erb +10 -0
  84. data/lib/capybara/spec/views/within_popups.erb +25 -0
  85. data/lib/capybara/util/save_and_open_page.rb +44 -0
  86. data/lib/capybara/util/timeout.rb +27 -0
  87. data/lib/capybara/version.rb +3 -0
  88. data/spec/basic_node_spec.rb +77 -0
  89. data/spec/capybara_spec.rb +46 -0
  90. data/spec/driver/rack_test_driver_spec.rb +89 -0
  91. data/spec/driver/selenium_driver_spec.rb +50 -0
  92. data/spec/dsl_spec.rb +253 -0
  93. data/spec/fixtures/selenium_driver_rspec_failure.rb +8 -0
  94. data/spec/fixtures/selenium_driver_rspec_success.rb +8 -0
  95. data/spec/rspec/features_spec.rb +45 -0
  96. data/spec/rspec/matchers_spec.rb +495 -0
  97. data/spec/rspec_spec.rb +53 -0
  98. data/spec/save_and_open_page_spec.rb +155 -0
  99. data/spec/server_spec.rb +89 -0
  100. data/spec/session/rack_test_session_spec.rb +55 -0
  101. data/spec/session/selenium_session_spec.rb +26 -0
  102. data/spec/spec_helper.rb +30 -0
  103. data/spec/string_spec.rb +77 -0
  104. data/spec/timeout_spec.rb +28 -0
  105. metadata +346 -0
@@ -0,0 +1,91 @@
1
+ class Capybara::Selenium::Node < Capybara::Driver::Node
2
+ def text
3
+ native.text
4
+ end
5
+
6
+ def [](name)
7
+ native.attribute(name.to_s)
8
+ rescue Selenium::WebDriver::Error::WebDriverError
9
+ nil
10
+ end
11
+
12
+ def value
13
+ if tag_name == "select" and self[:multiple] and not self[:multiple] == "false"
14
+ native.find_elements(:xpath, ".//option").select { |n| n.selected? }.map { |n| n[:value] || n.text }
15
+ else
16
+ native[:value]
17
+ end
18
+ end
19
+
20
+ def set(value)
21
+ if tag_name == 'input' and type == 'radio'
22
+ click
23
+ elsif tag_name == 'input' and type == 'checkbox'
24
+ click if value ^ native.attribute('checked').to_s.eql?("true")
25
+ elsif tag_name == 'input' and type == 'file'
26
+ resynchronize do
27
+ native.send_keys(value.to_s)
28
+ end
29
+ elsif tag_name == 'textarea' or tag_name == 'input'
30
+ resynchronize do
31
+ native.clear
32
+ native.send_keys(value.to_s)
33
+ end
34
+ end
35
+ end
36
+
37
+ def select_option
38
+ resynchronize { native.click } unless selected?
39
+ end
40
+
41
+ def unselect_option
42
+ if select_node['multiple'] != 'multiple' and select_node['multiple'] != 'true'
43
+ raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
44
+ end
45
+ resynchronize { native.click } if selected?
46
+ end
47
+
48
+ def click
49
+ resynchronize { native.click }
50
+ end
51
+
52
+ def drag_to(element)
53
+ resynchronize { driver.browser.action.drag_and_drop(native, element.native).perform }
54
+ end
55
+
56
+ def tag_name
57
+ native.tag_name.downcase
58
+ end
59
+
60
+ def visible?
61
+ displayed = native.displayed?
62
+ displayed and displayed != "false"
63
+ end
64
+
65
+ def selected?
66
+ selected = native.selected?
67
+ selected and selected != "false"
68
+ end
69
+
70
+ alias :checked? :selected?
71
+
72
+ def find(locator)
73
+ native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
74
+ end
75
+
76
+ private
77
+
78
+ def resynchronize
79
+ driver.resynchronize { yield }
80
+ end
81
+
82
+ # a reference to the select node if this is an option node
83
+ def select_node
84
+ find('./ancestor::select').first
85
+ end
86
+
87
+ def type
88
+ self[:type]
89
+ end
90
+
91
+ end
@@ -0,0 +1,90 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'rack'
4
+ require 'capybara/util/timeout'
5
+
6
+ module Capybara
7
+ class Server
8
+ class Identify
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ if env["PATH_INFO"] == "/__identify__"
15
+ [200, {}, [@app.object_id.to_s]]
16
+ else
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def ports
24
+ @ports ||= {}
25
+ end
26
+ end
27
+
28
+ attr_reader :app, :port
29
+
30
+ def initialize(app)
31
+ @app = app
32
+ end
33
+
34
+ def host
35
+ "127.0.0.1"
36
+ end
37
+
38
+ def url(path)
39
+ if path =~ /^http/
40
+ path
41
+ else
42
+ (Capybara.app_host || "http://#{host}:#{port}") + path.to_s
43
+ end
44
+ end
45
+
46
+ def responsive?
47
+ res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
48
+
49
+ if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
50
+ return res.body == @app.object_id.to_s
51
+ end
52
+ rescue Errno::ECONNREFUSED, Errno::EBADF
53
+ return false
54
+ end
55
+
56
+ def boot
57
+ if @app
58
+ @port = Capybara::Server.ports[@app.object_id]
59
+
60
+ if not @port or not responsive?
61
+ @port = Capybara.server_port || find_available_port
62
+ Capybara::Server.ports[@app.object_id] = @port
63
+
64
+ Thread.new do
65
+ Capybara.server.call(Identify.new(@app), @port)
66
+ end
67
+
68
+ Capybara.timeout(Capybara.server_boot_timeout) do
69
+ if responsive? then true else sleep(0.5) and false end
70
+ end
71
+ end
72
+ end
73
+ rescue TimeoutError
74
+ puts "Rack application timed out during boot"
75
+ exit
76
+ else
77
+ self
78
+ end
79
+
80
+ private
81
+
82
+ def find_available_port
83
+ server = TCPServer.new('127.0.0.1', 0)
84
+ server.addr[1]
85
+ ensure
86
+ server.close if server
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,321 @@
1
+ require 'capybara/util/timeout'
2
+
3
+ module Capybara
4
+
5
+ ##
6
+ #
7
+ # The Session class represents a single user's interaction with the system. The Session can use
8
+ # any of the underlying drivers. A session can be initialized manually like this:
9
+ #
10
+ # session = Capybara::Session.new(:culerity, MyRackApp)
11
+ #
12
+ # The application given as the second argument is optional. When running Capybara against an external
13
+ # page, you might want to leave it out:
14
+ #
15
+ # session = Capybara::Session.new(:culerity)
16
+ # session.visit('http://www.google.com')
17
+ #
18
+ # Session provides a number of methods for controlling the navigation of the page, such as +visit+,
19
+ # +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
20
+ # the current HTML document. This allows interaction:
21
+ #
22
+ # session.fill_in('q', :with => 'Capybara')
23
+ # session.click_button('Search')
24
+ # session.should have_content('Capybara')
25
+ #
26
+ # When using capybara/dsl, the Session is initialized automatically for you.
27
+ #
28
+ class Session
29
+ NODE_METHODS = [
30
+ :all, :first, :attach_file, :text, :check, :choose,
31
+ :click_link_or_button, :click_button, :click_link, :field_labeled,
32
+ :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link,
33
+ :has_content?, :has_css?, :has_no_content?, :has_no_css?, :has_no_xpath?,
34
+ :has_xpath?, :select, :uncheck, :has_link?, :has_no_link?, :has_button?,
35
+ :has_no_button?, :has_field?, :has_no_field?, :has_checked_field?,
36
+ :has_unchecked_field?, :has_no_table?, :has_table?, :unselect,
37
+ :has_select?, :has_no_select?, :has_selector?, :has_no_selector?,
38
+ :click_on, :has_no_checked_field?, :has_no_unchecked_field?
39
+ ]
40
+ SESSION_METHODS = [
41
+ :body, :html, :current_url, :current_host, :evaluate_script, :source,
42
+ :visit, :wait_until, :within, :within_fieldset, :within_table,
43
+ :within_frame, :within_window, :current_path, :save_page,
44
+ :save_and_open_page, :reset_session!
45
+ ]
46
+ DSL_METHODS = NODE_METHODS + SESSION_METHODS
47
+
48
+ attr_reader :mode, :app
49
+
50
+ def initialize(mode, app=nil)
51
+ @mode = mode
52
+ @app = app
53
+ end
54
+
55
+ def driver
56
+ @driver ||= begin
57
+ unless Capybara.drivers.has_key?(mode)
58
+ other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
59
+ raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
60
+ end
61
+ Capybara.drivers[mode].call(app)
62
+ end
63
+ end
64
+
65
+ ##
66
+ #
67
+ # Reset the session, removing all cookies.
68
+ #
69
+ def reset!
70
+ driver.reset!
71
+ end
72
+ alias_method :cleanup!, :reset!
73
+ alias_method :reset_session!, :reset!
74
+
75
+ ##
76
+ #
77
+ # Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
78
+ #
79
+ # @return [Hash{String => String}] A hash of response headers.
80
+ #
81
+ def response_headers
82
+ driver.response_headers
83
+ end
84
+
85
+ ##
86
+ #
87
+ # Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
88
+ #
89
+ # @return [Integer] Current HTTP status code
90
+ #
91
+ def status_code
92
+ driver.status_code
93
+ end
94
+
95
+ ##
96
+ #
97
+ # @return [String] A snapshot of the HTML of the current document, as it looks right now (potentially modified by JavaScript).
98
+ #
99
+ def body
100
+ driver.body
101
+ end
102
+ alias_method :html, :body
103
+
104
+ ##
105
+ #
106
+ # @return [String] HTML source of the document, before being modified by JavaScript.
107
+ #
108
+ def source
109
+ driver.source
110
+ end
111
+
112
+ ##
113
+ #
114
+ # @return [String] Path of the current page, without any domain information
115
+ #
116
+ def current_path
117
+ path = URI.parse(current_url).path
118
+ path if path and not path.empty?
119
+ end
120
+
121
+ ##
122
+ #
123
+ # @return [String] Host of the current page
124
+ #
125
+ def current_host
126
+ uri = URI.parse(current_url)
127
+ "#{uri.scheme}://#{uri.host}" if uri.host
128
+ end
129
+
130
+ ##
131
+ #
132
+ # @return [String] Fully qualified URL of the current page
133
+ #
134
+ def current_url
135
+ driver.current_url
136
+ end
137
+
138
+ ##
139
+ #
140
+ # Navigate to the given URL. The URL can either be a relative URL or an absolute URL
141
+ # The behaviour of either depends on the driver.
142
+ #
143
+ # session.visit('/foo')
144
+ # session.visit('http://google.com')
145
+ #
146
+ # For drivers which can run against an external application, such as culerity and selenium
147
+ # giving an absolute URL will navigate to that page. This allows testing applications
148
+ # running on remote servers. For these drivers, setting Capybara.app_host will make the
149
+ # remote server the default. For example:
150
+ #
151
+ # Capybara.app_host = 'http://google.com'
152
+ # session.visit('/') # visits the google homepage
153
+ #
154
+ # @param [String] url The URL to navigate to
155
+ #
156
+ def visit(url)
157
+ driver.visit(url)
158
+ end
159
+
160
+ ##
161
+ #
162
+ # Execute the given block for a particular scope on the page. Within will find the first
163
+ # element matching the given selector and execute the block scoped to that element:
164
+ #
165
+ # within(:xpath, '//div[@id="delivery-address"]') do
166
+ # fill_in('Street', :with => '12 Main Street')
167
+ # end
168
+ #
169
+ # It is possible to omit the first parameter, in that case, the selector is assumed to be
170
+ # of the type set in Capybara.default_selector.
171
+ #
172
+ # within('div#delivery-address') do
173
+ # fill_in('Street', :with => '12 Main Street')
174
+ # end
175
+ #
176
+ # @overload within(*find_args)
177
+ # @param (see Capybara::Node::Finders#all)
178
+ #
179
+ # @overload within(a_node)
180
+ # @param [Capybara::Node::Base] a_node The node in whose scope the block should be evaluated
181
+ #
182
+ # @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
183
+ #
184
+ def within(*args)
185
+ new_scope = if args.size == 1 && Capybara::Node::Base === args.first
186
+ args.first
187
+ else
188
+ find(*args)
189
+ end
190
+ begin
191
+ scopes.push(new_scope)
192
+ yield
193
+ ensure
194
+ scopes.pop
195
+ end
196
+ end
197
+
198
+ ##
199
+ #
200
+ # Execute the given block within the a specific fieldset given the id or legend of that fieldset.
201
+ #
202
+ # @param [String] locator Id or legend of the fieldset
203
+ #
204
+ def within_fieldset(locator)
205
+ within :xpath, XPath::HTML.fieldset(locator) do
206
+ yield
207
+ end
208
+ end
209
+
210
+ ##
211
+ #
212
+ # Execute the given block within the a specific table given the id or caption of that table.
213
+ #
214
+ # @param [String] locator Id or caption of the table
215
+ #
216
+ def within_table(locator)
217
+ within :xpath, XPath::HTML.table(locator) do
218
+ yield
219
+ end
220
+ end
221
+
222
+ ##
223
+ #
224
+ # Execute the given block within the given iframe given the id of that iframe. Only works on
225
+ # some drivers (e.g. Selenium)
226
+ #
227
+ # @param [String] locator Id of the frame
228
+ #
229
+ def within_frame(frame_id)
230
+ driver.within_frame(frame_id) do
231
+ yield
232
+ end
233
+ end
234
+
235
+ ##
236
+ #
237
+ # Execute the given block within the given window. Only works on
238
+ # some drivers (e.g. Selenium)
239
+ #
240
+ # @param [String] locator of the window
241
+ #
242
+ def within_window(handle, &blk)
243
+ driver.within_window(handle, &blk)
244
+ end
245
+
246
+ ##
247
+ #
248
+ # Retry executing the block until a truthy result is returned or the timeout time is exceeded
249
+ #
250
+ # @param [Integer] timeout The amount of seconds to retry executing the given block
251
+ #
252
+ def wait_until(timeout = Capybara.default_wait_time)
253
+ Capybara.timeout(timeout,driver) { yield }
254
+ end
255
+
256
+ ##
257
+ #
258
+ # Execute the given script, not returning a result. This is useful for scripts that return
259
+ # complex objects, such as jQuery statements. +execute_script+ should always be used over
260
+ # +evaluate_script+ whenever possible.
261
+ #
262
+ # @param [String] script A string of JavaScript to execute
263
+ #
264
+ def execute_script(script)
265
+ driver.execute_script(script)
266
+ end
267
+
268
+ ##
269
+ #
270
+ # Evaluate the given JavaScript and return the result. Be careful when using this with
271
+ # scripts that return complex objects, such as jQuery statements. +execute_script+ might
272
+ # be a better alternative.
273
+ #
274
+ # @param [String] script A string of JavaScript to evaluate
275
+ # @return [Object] The result of the evaluated JavaScript (may be driver specific)
276
+ #
277
+ def evaluate_script(script)
278
+ driver.evaluate_script(script)
279
+ end
280
+
281
+ ##
282
+ #
283
+ # Save a snapshot of the page and open it in a browser for inspection
284
+ #
285
+ def save_page
286
+ require 'capybara/util/save_and_open_page'
287
+ Capybara.save_page(body)
288
+ end
289
+
290
+ def save_and_open_page
291
+ require 'capybara/util/save_and_open_page'
292
+ Capybara.save_and_open_page(body)
293
+ end
294
+
295
+ def document
296
+ @document ||= Capybara::Node::Document.new(self, driver)
297
+ end
298
+
299
+ NODE_METHODS.each do |method|
300
+ class_eval <<-RUBY
301
+ def #{method}(*args, &block)
302
+ current_node.send(:#{method}, *args, &block)
303
+ end
304
+ RUBY
305
+ end
306
+
307
+ def inspect
308
+ %(#<Capybara::Session>)
309
+ end
310
+
311
+ private
312
+
313
+ def current_node
314
+ scopes.last
315
+ end
316
+
317
+ def scopes
318
+ @scopes ||= [document]
319
+ end
320
+ end
321
+ end