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