nimboids-capybara 1.1.2

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 (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