capybara 1.0.1 → 1.1.0.rc1
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.
- data/History.txt +25 -0
- data/README.rdoc +18 -3
- data/lib/capybara.rb +6 -1
- data/lib/capybara/driver/base.rb +4 -0
- data/lib/capybara/dsl.rb +23 -1
- data/lib/capybara/node/base.rb +19 -3
- data/lib/capybara/node/element.rb +39 -16
- data/lib/capybara/node/finders.rb +21 -29
- data/lib/capybara/node/matchers.rb +6 -6
- data/lib/capybara/node/simple.rb +3 -7
- data/lib/capybara/rack_test/browser.rb +21 -13
- data/lib/capybara/rack_test/driver.rb +1 -1
- data/lib/capybara/rack_test/node.rb +2 -1
- data/lib/capybara/selenium/driver.rb +8 -1
- data/lib/capybara/session.rb +16 -3
- data/lib/capybara/spec/driver.rb +5 -5
- data/lib/capybara/spec/public/test.js +5 -0
- data/lib/capybara/spec/session/click_button_spec.rb +1 -1
- data/lib/capybara/spec/session/click_link_spec.rb +6 -0
- data/lib/capybara/spec/session/current_host_spec.rb +6 -0
- data/lib/capybara/spec/session/find_spec.rb +6 -0
- data/lib/capybara/spec/session/javascript.rb +67 -0
- data/lib/capybara/spec/session/within_spec.rb +11 -0
- data/lib/capybara/spec/test_app.rb +13 -1
- data/lib/capybara/spec/views/form.erb +1 -1
- data/lib/capybara/spec/views/with_html.erb +1 -0
- data/lib/capybara/spec/views/with_js.erb +7 -2
- data/lib/capybara/version.rb +1 -1
- data/spec/driver/rack_test_driver_spec.rb +6 -0
- data/spec/driver/selenium_driver_spec.rb +21 -0
- data/spec/dsl_spec.rb +44 -0
- data/spec/fixtures/selenium_driver_rspec_failure.rb +8 -0
- data/spec/fixtures/selenium_driver_rspec_success.rb +8 -0
- data/spec/rspec_spec.rb +0 -1
- data/spec/session/rack_test_session_spec.rb +12 -0
- data/spec/spec_helper.rb +3 -0
- metadata +114 -173
- data/lib/capybara/spec/public/jquery-ui.js +0 -35
- data/lib/capybara/spec/public/jquery.js +0 -19
data/History.txt
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
# Version 1.1.0
|
2
|
+
|
3
|
+
Release date:
|
4
|
+
|
5
|
+
### Fixed
|
6
|
+
|
7
|
+
* Sensible inspect for Capybara::Session [Jo Liss]
|
8
|
+
* Fix headers and host on redirect [Matt Colyer, Jonas Nicklas, Kim Burgestrand]
|
9
|
+
* using_driver now restores the old driver instead of reverting to the default [Carol Nichols]
|
10
|
+
* Errors when following links relative to the root path under rack-test [Jonas Nicklas, Kim Burgestrand]
|
11
|
+
* Make sure exit codes are propagated properly [Edgar Beigarts]
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
* resynchronization is off by default under Selenium
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
* Elements are automatically reloaded (including parents) during wait [Jonas Nicklas]
|
20
|
+
* Rescue driver specific element errors, such as the dreaded ObsoleteElementError and retry [Jonas Nicklas]
|
21
|
+
* Raise an error if something has frozen time [Jonas Nicklas]
|
22
|
+
* Allow within to take a node instead of a selector [Peter Williams]
|
23
|
+
* Using wait_time_time to change wait time for a block of code [Jonas Nicklas, Kim Burgestrand]
|
24
|
+
* Option for rack-test driver to disable data-method hack [Jonas Nicklas, Kim Burgestrand]
|
25
|
+
|
1
26
|
# Version 1.0.1
|
2
27
|
|
3
28
|
Release date: 2011-08-12
|
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ Capybara aims to simplify the process of integration testing Rack applications,
|
|
8
8
|
such as Rails, Sinatra or Merb. Capybara simulates how a real user would
|
9
9
|
interact with a web application. It is agnostic about the driver running your
|
10
10
|
tests and currently comes with Rack::Test and Selenium support built in.
|
11
|
-
HtmlUnit and env.js are supported through external gems.
|
11
|
+
HtmlUnit, WebKit and env.js are supported through external gems.
|
12
12
|
|
13
13
|
A complete reference is available at
|
14
14
|
{at rubydoc.info}[http://rubydoc.info/github/jnicklas/capybara/master].
|
@@ -36,7 +36,7 @@ create a topic branch for every separate change you make.
|
|
36
36
|
Capybara uses bundler in development. To set up a development environment, simply do:
|
37
37
|
|
38
38
|
git submodule update --init
|
39
|
-
gem install bundler
|
39
|
+
gem install bundler
|
40
40
|
bundle install
|
41
41
|
|
42
42
|
== Using Capybara with Cucumber
|
@@ -282,6 +282,21 @@ Ruby 1.8.7 at this time.
|
|
282
282
|
Note: Envjs does not support transactional fixtures; see the section
|
283
283
|
"Transactional Fixtures" below.
|
284
284
|
|
285
|
+
=== Capybara-webkit
|
286
|
+
|
287
|
+
The {capybara-webkit drive}[https://github.com/thoughtbot/capybara-webkit] is for true headless
|
288
|
+
testing. It uses WebKitQt to start a rendering engine process. It can execute JavaScript as well.
|
289
|
+
It is significantly faster than drivers like Selenium since it does not load an entire browser.
|
290
|
+
|
291
|
+
You can install it with:
|
292
|
+
|
293
|
+
gem install capybara-webkit
|
294
|
+
|
295
|
+
And you can use it by:
|
296
|
+
|
297
|
+
Capybara.javascript_driver = :webkit
|
298
|
+
|
299
|
+
|
285
300
|
== The DSL
|
286
301
|
|
287
302
|
Capybara's DSL (domain-specific language) is inspired by Webrat. While
|
@@ -470,7 +485,7 @@ When issuing instructions to the DSL such as:
|
|
470
485
|
|
471
486
|
If clicking on the *foo* link causes triggers an asynchronous process, such as
|
472
487
|
an Ajax request, which, when complete will add the *bar* link to the page,
|
473
|
-
clicking on the *bar* link would be
|
488
|
+
clicking on the *bar* link would be expected to fail, since that link doesn't
|
474
489
|
exist yet. However Capybara is smart enought to retry finding the link for a
|
475
490
|
brief period of time before giving up and throwing an error. The same is true of
|
476
491
|
the next line, which looks for the content *baz* on the page; it will retry
|
data/lib/capybara.rb
CHANGED
@@ -4,7 +4,9 @@ require 'xpath'
|
|
4
4
|
module Capybara
|
5
5
|
class CapybaraError < StandardError; end
|
6
6
|
class DriverNotFoundError < CapybaraError; end
|
7
|
+
class FrozenInTime < CapybaraError; end
|
7
8
|
class ElementNotFound < CapybaraError; end
|
9
|
+
class ExpectationNotMet < ElementNotFound; end
|
8
10
|
class FileNotFound < CapybaraError; end
|
9
11
|
class UnselectNotAllowed < CapybaraError; end
|
10
12
|
class NotSupportedByDriverError < CapybaraError; end
|
@@ -16,7 +18,7 @@ module Capybara
|
|
16
18
|
attr_accessor :asset_root, :app_host, :run_server, :default_host
|
17
19
|
attr_accessor :server_port, :server_boot_timeout
|
18
20
|
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements, :prefer_visible_elements
|
19
|
-
attr_accessor :save_and_open_page_path
|
21
|
+
attr_accessor :save_and_open_page_path, :automatic_reload
|
20
22
|
|
21
23
|
##
|
22
24
|
#
|
@@ -36,6 +38,8 @@ module Capybara
|
|
36
38
|
# [default_wait_time = Integer] The number of seconds to wait for asynchronous processes to finish (Default: 2)
|
37
39
|
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: false)
|
38
40
|
# [prefer_visible_elements = Boolean] Whether to prefer visible elements over hidden elements (Default: true)
|
41
|
+
# [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
|
42
|
+
# [save_and_open_page_path = String] Where to put pages saved through save_and_open_page (Default: Dir.pwd)
|
39
43
|
#
|
40
44
|
# === DSL Options
|
41
45
|
#
|
@@ -236,6 +240,7 @@ Capybara.configure do |config|
|
|
236
240
|
config.ignore_hidden_elements = false
|
237
241
|
config.prefer_visible_elements = true
|
238
242
|
config.default_host = "http://www.example.com"
|
243
|
+
config.automatic_reload = true
|
239
244
|
end
|
240
245
|
|
241
246
|
Capybara.register_driver :rack_test do |app|
|
data/lib/capybara/driver/base.rb
CHANGED
data/lib/capybara/dsl.rb
CHANGED
@@ -49,10 +49,23 @@ module Capybara
|
|
49
49
|
# Yield a block using a specific driver
|
50
50
|
#
|
51
51
|
def using_driver(driver)
|
52
|
+
previous_driver = Capybara.current_driver
|
52
53
|
Capybara.current_driver = driver
|
53
54
|
yield
|
54
55
|
ensure
|
55
|
-
|
56
|
+
@current_driver = previous_driver
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
#
|
61
|
+
# Yield a block using a specific wait time
|
62
|
+
#
|
63
|
+
def using_wait_time(seconds)
|
64
|
+
previous_wait_time = Capybara.default_wait_time
|
65
|
+
Capybara.default_wait_time = seconds
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
Capybara.default_wait_time = previous_wait_time
|
56
69
|
end
|
57
70
|
|
58
71
|
##
|
@@ -114,6 +127,15 @@ module Capybara
|
|
114
127
|
Capybara.using_session(name, &block)
|
115
128
|
end
|
116
129
|
|
130
|
+
##
|
131
|
+
#
|
132
|
+
# Shortcut to working in a different session. This is useful when Capybara is included
|
133
|
+
# in a class or module.
|
134
|
+
#
|
135
|
+
def using_wait_time(seconds, &block)
|
136
|
+
Capybara.using_wait_time(seconds, &block)
|
137
|
+
end
|
138
|
+
|
117
139
|
##
|
118
140
|
#
|
119
141
|
# Shortcut to accessing the current session. This is useful when Capybara is included in a
|
data/lib/capybara/node/base.rb
CHANGED
@@ -22,7 +22,7 @@ module Capybara
|
|
22
22
|
# session.has_css?('#foobar') # from Capybara::Node::Matchers
|
23
23
|
#
|
24
24
|
class Base
|
25
|
-
attr_reader :session, :base
|
25
|
+
attr_reader :session, :base, :parent
|
26
26
|
|
27
27
|
include Capybara::Node::Finders
|
28
28
|
include Capybara::Node::Actions
|
@@ -33,10 +33,26 @@ module Capybara
|
|
33
33
|
@base = base
|
34
34
|
end
|
35
35
|
|
36
|
+
def reload
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
36
40
|
protected
|
37
41
|
|
38
|
-
def
|
39
|
-
|
42
|
+
def wait_until(seconds=Capybara.default_wait_time)
|
43
|
+
start_time = Time.now
|
44
|
+
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
rescue => e
|
48
|
+
raise e unless driver.wait?
|
49
|
+
raise e unless (driver.respond_to?(:invalid_element_errors) and driver.invalid_element_errors.include?(e.class)) or e.is_a?(Capybara::ElementNotFound)
|
50
|
+
raise e if (Time.now - start_time) >= seconds
|
51
|
+
sleep(0.05)
|
52
|
+
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
|
53
|
+
reload if Capybara.automatic_reload
|
54
|
+
retry
|
55
|
+
end
|
40
56
|
end
|
41
57
|
|
42
58
|
def driver
|
@@ -22,12 +22,18 @@ module Capybara
|
|
22
22
|
#
|
23
23
|
class Element < Base
|
24
24
|
|
25
|
+
def initialize(session, base, parent, selector)
|
26
|
+
super(session, base)
|
27
|
+
@parent = parent
|
28
|
+
@selector = selector
|
29
|
+
end
|
30
|
+
|
25
31
|
##
|
26
32
|
#
|
27
33
|
# @return [Object] The native element from the driver, this allows access to driver specific methods
|
28
34
|
#
|
29
35
|
def native
|
30
|
-
base.native
|
36
|
+
wait_until { base.native }
|
31
37
|
end
|
32
38
|
|
33
39
|
##
|
@@ -35,7 +41,7 @@ module Capybara
|
|
35
41
|
# @return [String] The text of the element
|
36
42
|
#
|
37
43
|
def text
|
38
|
-
base.text
|
44
|
+
wait_until { base.text }
|
39
45
|
end
|
40
46
|
|
41
47
|
##
|
@@ -48,7 +54,7 @@ module Capybara
|
|
48
54
|
# @return [String] The value of the attribute
|
49
55
|
#
|
50
56
|
def [](attribute)
|
51
|
-
base[attribute]
|
57
|
+
wait_until { base[attribute] }
|
52
58
|
end
|
53
59
|
|
54
60
|
##
|
@@ -56,7 +62,7 @@ module Capybara
|
|
56
62
|
# @return [String] The value of the form element
|
57
63
|
#
|
58
64
|
def value
|
59
|
-
base.value
|
65
|
+
wait_until { base.value }
|
60
66
|
end
|
61
67
|
|
62
68
|
##
|
@@ -66,7 +72,7 @@ module Capybara
|
|
66
72
|
# @param [String] value The new value
|
67
73
|
#
|
68
74
|
def set(value)
|
69
|
-
base.set(value)
|
75
|
+
wait_until { base.set(value) }
|
70
76
|
end
|
71
77
|
|
72
78
|
##
|
@@ -74,7 +80,7 @@ module Capybara
|
|
74
80
|
# Select this node if is an option element inside a select tag
|
75
81
|
#
|
76
82
|
def select_option
|
77
|
-
base.select_option
|
83
|
+
wait_until { base.select_option }
|
78
84
|
end
|
79
85
|
|
80
86
|
##
|
@@ -82,7 +88,7 @@ module Capybara
|
|
82
88
|
# Unselect this node if is an option element inside a multiple select tag
|
83
89
|
#
|
84
90
|
def unselect_option
|
85
|
-
base.unselect_option
|
91
|
+
wait_until { base.unselect_option }
|
86
92
|
end
|
87
93
|
|
88
94
|
##
|
@@ -90,7 +96,7 @@ module Capybara
|
|
90
96
|
# Click the Element
|
91
97
|
#
|
92
98
|
def click
|
93
|
-
base.click
|
99
|
+
wait_until { base.click }
|
94
100
|
end
|
95
101
|
|
96
102
|
##
|
@@ -98,7 +104,7 @@ module Capybara
|
|
98
104
|
# @return [String] The tag name of the element
|
99
105
|
#
|
100
106
|
def tag_name
|
101
|
-
base.tag_name
|
107
|
+
wait_until { base.tag_name }
|
102
108
|
end
|
103
109
|
|
104
110
|
##
|
@@ -109,7 +115,7 @@ module Capybara
|
|
109
115
|
# @return [Boolean] Whether the element is visible
|
110
116
|
#
|
111
117
|
def visible?
|
112
|
-
base.visible?
|
118
|
+
wait_until { base.visible? }
|
113
119
|
end
|
114
120
|
|
115
121
|
##
|
@@ -119,7 +125,7 @@ module Capybara
|
|
119
125
|
# @return [Boolean] Whether the element is checked
|
120
126
|
#
|
121
127
|
def checked?
|
122
|
-
base.checked?
|
128
|
+
wait_until { base.checked? }
|
123
129
|
end
|
124
130
|
|
125
131
|
##
|
@@ -129,7 +135,7 @@ module Capybara
|
|
129
135
|
# @return [Boolean] Whether the element is selected
|
130
136
|
#
|
131
137
|
def selected?
|
132
|
-
base.selected?
|
138
|
+
wait_until { base.selected? }
|
133
139
|
end
|
134
140
|
|
135
141
|
##
|
@@ -139,7 +145,7 @@ module Capybara
|
|
139
145
|
# @return [String] An XPath expression
|
140
146
|
#
|
141
147
|
def path
|
142
|
-
base.path
|
148
|
+
wait_until { base.path }
|
143
149
|
end
|
144
150
|
|
145
151
|
##
|
@@ -150,7 +156,7 @@ module Capybara
|
|
150
156
|
# @param [String] event The name of the event to trigger
|
151
157
|
#
|
152
158
|
def trigger(event)
|
153
|
-
base.trigger(event)
|
159
|
+
wait_until { base.trigger(event) }
|
154
160
|
end
|
155
161
|
|
156
162
|
##
|
@@ -164,7 +170,25 @@ module Capybara
|
|
164
170
|
# @param [Capybara::Element] node The element to drag to
|
165
171
|
#
|
166
172
|
def drag_to(node)
|
167
|
-
base.drag_to(node.base)
|
173
|
+
wait_until { base.drag_to(node.base) }
|
174
|
+
end
|
175
|
+
|
176
|
+
def find(*args)
|
177
|
+
wait_until { super }
|
178
|
+
end
|
179
|
+
|
180
|
+
def first(*args)
|
181
|
+
wait_until { super }
|
182
|
+
end
|
183
|
+
|
184
|
+
def all(*args)
|
185
|
+
wait_until { super }
|
186
|
+
end
|
187
|
+
|
188
|
+
def reload
|
189
|
+
reloaded = parent.reload.first(@selector.name, @selector.locator, @selector.options)
|
190
|
+
@base = reloaded.base if reloaded
|
191
|
+
self
|
168
192
|
end
|
169
193
|
|
170
194
|
def inspect
|
@@ -172,7 +196,6 @@ module Capybara
|
|
172
196
|
rescue NotSupportedByDriverError
|
173
197
|
%(#<Capybara::Element tag="#{tag_name}">)
|
174
198
|
end
|
175
|
-
|
176
199
|
end
|
177
200
|
end
|
178
201
|
end
|
@@ -15,7 +15,7 @@ module Capybara
|
|
15
15
|
# +find+ takes the same options as +all+.
|
16
16
|
#
|
17
17
|
# page.find('#foo').find('.bar')
|
18
|
-
# page.find(:xpath, '//div[contains("bar")]')
|
18
|
+
# page.find(:xpath, '//div[contains(., "bar")]')
|
19
19
|
# page.find('li', :text => 'Quox').click_link('Delete')
|
20
20
|
#
|
21
21
|
# @param (see Capybara::Node::Finders#all)
|
@@ -24,18 +24,7 @@ module Capybara
|
|
24
24
|
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
25
25
|
#
|
26
26
|
def find(*args)
|
27
|
-
|
28
|
-
node = wait_conditionally_until { first(*args) }
|
29
|
-
rescue TimeoutError
|
30
|
-
end
|
31
|
-
unless node
|
32
|
-
options = extract_normalized_options(args)
|
33
|
-
normalized = Capybara::Selector.normalize(*args)
|
34
|
-
message = options[:message] || "Unable to find #{normalized.name} #{normalized.locator.inspect}"
|
35
|
-
message = normalized.failure_message.call(self, normalized) if normalized.failure_message
|
36
|
-
raise Capybara::ElementNotFound, message
|
37
|
-
end
|
38
|
-
return node
|
27
|
+
wait_until { first(*args) or raise_find_error(*args) }
|
39
28
|
end
|
40
29
|
|
41
30
|
##
|
@@ -120,10 +109,10 @@ module Capybara
|
|
120
109
|
def all(*args)
|
121
110
|
options = extract_normalized_options(args)
|
122
111
|
|
123
|
-
Capybara::Selector.normalize(*args)
|
124
|
-
|
125
|
-
|
126
|
-
|
112
|
+
selector = Capybara::Selector.normalize(*args)
|
113
|
+
selector.xpaths.
|
114
|
+
map { |path| find_in_base(selector, path) }.flatten.
|
115
|
+
select { |node| matches_options(node, options) }
|
127
116
|
end
|
128
117
|
|
129
118
|
##
|
@@ -143,10 +132,11 @@ module Capybara
|
|
143
132
|
options = extract_normalized_options(args)
|
144
133
|
found_elements = []
|
145
134
|
|
146
|
-
Capybara::Selector.normalize(*args)
|
147
|
-
|
135
|
+
selector = Capybara::Selector.normalize(*args)
|
136
|
+
selector.xpaths.each do |path|
|
137
|
+
find_in_base(selector, path).each do |node|
|
148
138
|
if matches_options(node, options)
|
149
|
-
found_elements <<
|
139
|
+
found_elements << node
|
150
140
|
return found_elements.last if not Capybara.prefer_visible_elements or node.visible?
|
151
141
|
end
|
152
142
|
end
|
@@ -156,16 +146,18 @@ module Capybara
|
|
156
146
|
|
157
147
|
protected
|
158
148
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
Capybara::
|
149
|
+
def raise_find_error(*args)
|
150
|
+
options = extract_normalized_options(args)
|
151
|
+
normalized = Capybara::Selector.normalize(*args)
|
152
|
+
message = options[:message] || "Unable to find #{normalized.name} #{normalized.locator.inspect}"
|
153
|
+
message = normalized.failure_message.call(self, normalized) if normalized.failure_message
|
154
|
+
raise Capybara::ElementNotFound, message
|
165
155
|
end
|
166
156
|
|
167
|
-
def
|
168
|
-
|
157
|
+
def find_in_base(selector, xpath)
|
158
|
+
base.find(xpath).map do |node|
|
159
|
+
Capybara::Node::Element.new(session, node, self, selector)
|
160
|
+
end
|
169
161
|
end
|
170
162
|
|
171
163
|
def extract_normalized_options(args)
|
@@ -197,7 +189,7 @@ module Capybara
|
|
197
189
|
end
|
198
190
|
|
199
191
|
def has_selected_options?(node, expected)
|
200
|
-
actual = node.
|
192
|
+
actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
|
201
193
|
(expected - actual).empty?
|
202
194
|
end
|
203
195
|
end
|