capybara 2.0.3 → 2.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.md +73 -0
- data/README.md +52 -5
- data/lib/capybara.rb +44 -62
- data/lib/capybara/cucumber.rb +4 -1
- data/lib/capybara/driver/base.rb +13 -9
- data/lib/capybara/driver/node.rb +18 -6
- data/lib/capybara/helpers.rb +111 -22
- data/lib/capybara/node/actions.rb +24 -19
- data/lib/capybara/node/base.rb +25 -32
- data/lib/capybara/node/document.rb +6 -2
- data/lib/capybara/node/element.rb +45 -7
- data/lib/capybara/node/finders.rb +48 -21
- data/lib/capybara/node/matchers.rb +38 -21
- data/lib/capybara/node/simple.rb +29 -7
- data/lib/capybara/query.rb +72 -26
- data/lib/capybara/rack_test/browser.rb +11 -3
- data/lib/capybara/rack_test/css_handlers.rb +8 -0
- data/lib/capybara/rack_test/driver.rb +11 -3
- data/lib/capybara/rack_test/form.rb +8 -1
- data/lib/capybara/rack_test/node.rb +92 -36
- data/lib/capybara/rails.rb +1 -2
- data/lib/capybara/result.rb +21 -37
- data/lib/capybara/rspec/matchers.rb +60 -31
- data/lib/capybara/selector.rb +46 -13
- data/lib/capybara/selenium/driver.rb +42 -6
- data/lib/capybara/selenium/node.rb +23 -5
- data/lib/capybara/session.rb +46 -11
- data/lib/capybara/spec/public/test.js +12 -0
- data/lib/capybara/spec/session/all_spec.rb +9 -8
- data/lib/capybara/spec/session/attach_file_spec.rb +14 -0
- data/lib/capybara/spec/session/check_spec.rb +14 -0
- data/lib/capybara/spec/session/choose_spec.rb +14 -0
- data/lib/capybara/spec/session/click_button_spec.rb +77 -1
- data/lib/capybara/spec/session/click_link_or_button_spec.rb +65 -0
- data/lib/capybara/spec/session/click_link_spec.rb +24 -0
- data/lib/capybara/spec/session/current_scope_spec.rb +29 -0
- data/lib/capybara/spec/session/fill_in_spec.rb +14 -0
- data/lib/capybara/spec/session/find_button_spec.rb +12 -0
- data/lib/capybara/spec/session/find_by_id_spec.rb +12 -1
- data/lib/capybara/spec/session/find_field_spec.rb +30 -0
- data/lib/capybara/spec/session/find_link_spec.rb +12 -0
- data/lib/capybara/spec/session/find_spec.rb +258 -16
- data/lib/capybara/spec/session/first_spec.rb +25 -8
- data/lib/capybara/spec/session/has_css_spec.rb +2 -2
- data/lib/capybara/spec/session/has_field_spec.rb +10 -2
- data/lib/capybara/spec/session/has_selector_spec.rb +9 -0
- data/lib/capybara/spec/session/has_text_spec.rb +96 -0
- data/lib/capybara/spec/session/has_title_spec.rb +47 -0
- data/lib/capybara/spec/session/node_spec.rb +43 -0
- data/lib/capybara/spec/session/reset_session_spec.rb +10 -2
- data/lib/capybara/spec/session/save_page_spec.rb +30 -0
- data/lib/capybara/spec/session/select_spec.rb +65 -0
- data/lib/capybara/spec/session/text_spec.rb +31 -0
- data/lib/capybara/spec/session/title_spec.rb +16 -0
- data/lib/capybara/spec/session/uncheck_spec.rb +14 -0
- data/lib/capybara/spec/session/unselect_spec.rb +42 -0
- data/lib/capybara/spec/session/visit_spec.rb +3 -3
- data/lib/capybara/spec/session/within_frame_spec.rb +14 -0
- data/lib/capybara/spec/session/within_spec.rb +3 -3
- data/lib/capybara/spec/spec_helper.rb +6 -1
- data/lib/capybara/spec/views/form.erb +39 -2
- data/lib/capybara/spec/views/frame_child.erb +9 -0
- data/lib/capybara/spec/views/frame_parent.erb +8 -0
- data/lib/capybara/spec/views/with_base_tag.erb +10 -0
- data/lib/capybara/spec/views/with_count.erb +7 -0
- data/lib/capybara/spec/views/with_hover.erb +17 -0
- data/lib/capybara/spec/views/with_html.erb +21 -2
- data/lib/capybara/spec/views/with_js.erb +5 -0
- data/lib/capybara/spec/views/with_scope.erb +6 -1
- data/lib/capybara/spec/views/with_title.erb +1 -0
- data/lib/capybara/spec/views/within_frames.erb +1 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/basic_node_spec.rb +75 -24
- data/spec/dsl_spec.rb +2 -1
- data/spec/rack_test_spec.rb +4 -3
- data/spec/rspec/matchers_spec.rb +105 -17
- data/spec/server_spec.rb +8 -8
- data/spec/spec_helper.rb +2 -1
- metadata +83 -23
- metadata.gz.sig +0 -0
data/lib/capybara/helpers.rb
CHANGED
@@ -1,31 +1,120 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
module Capybara
|
4
|
+
|
5
|
+
# @api private
|
4
6
|
module Helpers
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
extend self
|
8
|
+
|
9
|
+
##
|
10
|
+
#
|
11
|
+
# Normalizes whitespace space by stripping leading and trailing
|
12
|
+
# whitespace and replacing sequences of whitespace characters
|
13
|
+
# with a single space.
|
14
|
+
#
|
15
|
+
# @param [String] text Text to normalize
|
16
|
+
# @return [String] Normalized text
|
17
|
+
#
|
18
|
+
def normalize_whitespace(text)
|
19
|
+
text.to_s.gsub(/[[:space:]]+/, ' ').strip
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
#
|
24
|
+
# Escapes any characters that would have special meaning in a regexp
|
25
|
+
# if text is not a regexp
|
26
|
+
#
|
27
|
+
# @param [String] text Text to escape
|
28
|
+
# @return [String] Escaped text
|
29
|
+
#
|
30
|
+
def to_regexp(text)
|
31
|
+
text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)))
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
#
|
36
|
+
# Injects a `<base>` tag into the given HTML code, pointing to
|
37
|
+
# `Capybara.asset_host`.
|
38
|
+
#
|
39
|
+
# @param [String] html HTML code to inject into
|
40
|
+
# @param [String] The modified HTML code
|
41
|
+
#
|
42
|
+
def inject_asset_host(html)
|
43
|
+
if Capybara.asset_host
|
44
|
+
if Nokogiri::HTML(html).css("base").empty? and match = html.match(/<head[^<]*?>/)
|
45
|
+
html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
|
46
|
+
end
|
47
|
+
else
|
48
|
+
html
|
17
49
|
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
#
|
54
|
+
# Checks if the given count matches the given count options. By default,
|
55
|
+
# when no options are given, count should be larger than zero.
|
56
|
+
#
|
57
|
+
# @param [Integer] count The actual number. Should be coercible via Integer()
|
58
|
+
# @option [Range] between Count must be within the given range
|
59
|
+
# @option [Integer] count Count must be exactly this
|
60
|
+
# @option [Integer] maximum Count must be smaller than or equal to this value
|
61
|
+
# @option [Integer] minimum Count must be larger than or equal to this value
|
62
|
+
#
|
63
|
+
def matches_count?(count, options={})
|
64
|
+
case
|
65
|
+
when options[:between]
|
66
|
+
options[:between] === count
|
67
|
+
when options[:count]
|
68
|
+
Integer(options[:count]) == count
|
69
|
+
when options[:maximum]
|
70
|
+
Integer(options[:maximum]) >= count
|
71
|
+
when options[:minimum]
|
72
|
+
Integer(options[:minimum]) <= count
|
73
|
+
else
|
74
|
+
count > 0
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
#
|
80
|
+
# Generates a failure message given a description of the query and count
|
81
|
+
# options.
|
82
|
+
#
|
83
|
+
# @param [String] description Description of a query
|
84
|
+
# @option [Range] between Count should have been within the given range
|
85
|
+
# @option [Integer] count Count should have been exactly this
|
86
|
+
# @option [Integer] maximum Count should have been smaller than or equal to this value
|
87
|
+
# @option [Integer] minimum Count should have been larger than or equal to this value
|
88
|
+
#
|
89
|
+
def failure_message(description, options={})
|
90
|
+
message = "expected to find #{description}"
|
91
|
+
if options[:count]
|
92
|
+
message << " #{options[:count]} #{declension('time', 'times', options[:count])}"
|
93
|
+
elsif options[:between]
|
94
|
+
message << " between #{options[:between].first} and #{options[:between].last} times"
|
95
|
+
elsif options[:maximum]
|
96
|
+
message << " at most #{options[:maximum]} #{declension('time', 'times', options[:maximum])}"
|
97
|
+
elsif options[:minimum]
|
98
|
+
message << " at least #{options[:minimum]} #{declension('time', 'times', options[:minimum])}"
|
99
|
+
end
|
100
|
+
message
|
101
|
+
end
|
18
102
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
103
|
+
##
|
104
|
+
#
|
105
|
+
# A poor man's `pluralize`. Given two declensions, one singular and one
|
106
|
+
# plural, as well as a count, this will pick the correct declension. This
|
107
|
+
# way we can generate gramatically correct error message.
|
108
|
+
#
|
109
|
+
# @param [String] singular The singular form of the word
|
110
|
+
# @param [String] plural The plural form of the word
|
111
|
+
# @param [Integer] count The number of items
|
112
|
+
#
|
113
|
+
def declension(singular, plural, count)
|
114
|
+
if count == 1
|
115
|
+
singular
|
116
|
+
else
|
117
|
+
plural
|
29
118
|
end
|
30
119
|
end
|
31
120
|
end
|
@@ -9,8 +9,8 @@ module Capybara
|
|
9
9
|
#
|
10
10
|
# @param [String] locator Text, id or value of link or button
|
11
11
|
#
|
12
|
-
def click_link_or_button(locator)
|
13
|
-
find(:link_or_button, locator).click
|
12
|
+
def click_link_or_button(locator, options={})
|
13
|
+
find(:link_or_button, locator, options).click
|
14
14
|
end
|
15
15
|
alias_method :click_on, :click_link_or_button
|
16
16
|
|
@@ -20,9 +20,11 @@ module Capybara
|
|
20
20
|
# alt text inside the link.
|
21
21
|
#
|
22
22
|
# @param [String] locator Text, id or text of link
|
23
|
+
# @param options
|
24
|
+
# @option options [String] :href The value the href attribute must be
|
23
25
|
#
|
24
|
-
def click_link(locator)
|
25
|
-
find(:link, locator).click
|
26
|
+
def click_link(locator, options={})
|
27
|
+
find(:link, locator, options).click
|
26
28
|
end
|
27
29
|
|
28
30
|
##
|
@@ -31,8 +33,8 @@ module Capybara
|
|
31
33
|
#
|
32
34
|
# @param [String] locator Text, id or value of button
|
33
35
|
#
|
34
|
-
def click_button(locator)
|
35
|
-
find(:button, locator).click
|
36
|
+
def click_button(locator, options={})
|
37
|
+
find(:button, locator, options).click
|
36
38
|
end
|
37
39
|
|
38
40
|
##
|
@@ -47,7 +49,8 @@ module Capybara
|
|
47
49
|
#
|
48
50
|
def fill_in(locator, options={})
|
49
51
|
raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
|
50
|
-
|
52
|
+
with = options.delete(:with)
|
53
|
+
find(:fillable_field, locator, options).set(with)
|
51
54
|
end
|
52
55
|
|
53
56
|
##
|
@@ -59,8 +62,8 @@ module Capybara
|
|
59
62
|
#
|
60
63
|
# @param [String] locator Which radio button to choose
|
61
64
|
#
|
62
|
-
def choose(locator)
|
63
|
-
find(:radio_button, locator).set(true)
|
65
|
+
def choose(locator, options={})
|
66
|
+
find(:radio_button, locator, options).set(true)
|
64
67
|
end
|
65
68
|
|
66
69
|
##
|
@@ -72,8 +75,8 @@ module Capybara
|
|
72
75
|
#
|
73
76
|
# @param [String] locator Which check box to check
|
74
77
|
#
|
75
|
-
def check(locator)
|
76
|
-
find(:checkbox, locator).set(true)
|
78
|
+
def check(locator, options={})
|
79
|
+
find(:checkbox, locator, options).set(true)
|
77
80
|
end
|
78
81
|
|
79
82
|
##
|
@@ -85,8 +88,8 @@ module Capybara
|
|
85
88
|
#
|
86
89
|
# @param [String] locator Which check box to uncheck
|
87
90
|
#
|
88
|
-
def uncheck(locator)
|
89
|
-
find(:checkbox, locator).set(false)
|
91
|
+
def uncheck(locator, options={})
|
92
|
+
find(:checkbox, locator, options).set(false)
|
90
93
|
end
|
91
94
|
|
92
95
|
##
|
@@ -102,9 +105,10 @@ module Capybara
|
|
102
105
|
#
|
103
106
|
def select(value, options={})
|
104
107
|
if options.has_key?(:from)
|
105
|
-
|
108
|
+
from = options.delete(:from)
|
109
|
+
find(:select, from, options).find(:option, value, options).select_option
|
106
110
|
else
|
107
|
-
find(:option, value).select_option
|
111
|
+
find(:option, value, options).select_option
|
108
112
|
end
|
109
113
|
end
|
110
114
|
|
@@ -121,9 +125,10 @@ module Capybara
|
|
121
125
|
#
|
122
126
|
def unselect(value, options={})
|
123
127
|
if options.has_key?(:from)
|
124
|
-
|
128
|
+
from = options.delete(:from)
|
129
|
+
find(:select, from, options).find(:option, value, options).unselect_option
|
125
130
|
else
|
126
|
-
find(:option, value).unselect_option
|
131
|
+
find(:option, value, options).unselect_option
|
127
132
|
end
|
128
133
|
end
|
129
134
|
|
@@ -137,11 +142,11 @@ module Capybara
|
|
137
142
|
# @param [String] locator Which field to attach the file to
|
138
143
|
# @param [String] path The path of the file that will be attached, or an array of paths
|
139
144
|
#
|
140
|
-
def attach_file(locator, path)
|
145
|
+
def attach_file(locator, path, options={})
|
141
146
|
Array(path).each do |p|
|
142
147
|
raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
|
143
148
|
end
|
144
|
-
find(:file_field, locator).set(path)
|
149
|
+
find(:file_field, locator, options).set(path)
|
145
150
|
end
|
146
151
|
end
|
147
152
|
end
|
data/lib/capybara/node/base.rb
CHANGED
@@ -31,7 +31,6 @@ module Capybara
|
|
31
31
|
def initialize(session, base)
|
32
32
|
@session = session
|
33
33
|
@base = base
|
34
|
-
@unsynchronized = false
|
35
34
|
end
|
36
35
|
|
37
36
|
# overridden in subclasses, e.g. Capybara::Node::Element
|
@@ -64,50 +63,44 @@ module Capybara
|
|
64
63
|
# until a certain amount of time passes. The amount of time defaults to
|
65
64
|
# {Capybara.default_wait_time} and can be overriden through the `seconds`
|
66
65
|
# argument. This time is compared with the system time to see how much
|
67
|
-
# time has passed. If the return value of
|
66
|
+
# time has passed. If the return value of `Time.now` is stubbed out,
|
68
67
|
# Capybara will raise `Capybara::FrozenInTime`.
|
69
68
|
#
|
70
|
-
# @param
|
69
|
+
# @param [Integer] seconds Number of seconds to retry this block
|
71
70
|
# @return [Object] The result of the given block
|
72
|
-
# @raise
|
71
|
+
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
|
73
72
|
#
|
74
73
|
def synchronize(seconds=Capybara.default_wait_time)
|
75
74
|
start_time = Time.now
|
76
75
|
|
77
|
-
|
76
|
+
if session.synchronized
|
78
77
|
yield
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
78
|
+
else
|
79
|
+
session.synchronized = true
|
80
|
+
begin
|
81
|
+
yield
|
82
|
+
rescue => e
|
83
|
+
raise e unless driver.wait?
|
84
|
+
raise e unless catch_error?(e)
|
85
|
+
raise e if (Time.now - start_time) >= seconds
|
86
|
+
sleep(0.05)
|
87
|
+
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
|
88
|
+
reload if Capybara.automatic_reload
|
89
|
+
retry
|
90
|
+
ensure
|
91
|
+
session.synchronized = false
|
92
|
+
end
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
91
|
-
##
|
92
|
-
#
|
93
|
-
# Within the given block, prevent synchronize from having any effect.
|
94
|
-
#
|
95
|
-
# This is an internal method which should not be called unless you are
|
96
|
-
# absolutely sure of what you're doing.
|
97
|
-
#
|
98
|
-
# @api private
|
99
|
-
# @return [Object] The result of the given block
|
100
|
-
#
|
101
|
-
def unsynchronized
|
102
|
-
orig = @unsynchronized
|
103
|
-
@unsynchronized = true
|
104
|
-
yield
|
105
|
-
ensure
|
106
|
-
@unsynchronized = orig
|
107
|
-
end
|
108
|
-
|
109
96
|
protected
|
110
97
|
|
98
|
+
def catch_error?(error)
|
99
|
+
(driver.invalid_element_errors + [Capybara::ElementNotFound]).any? do |type|
|
100
|
+
error.is_a?(type)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
111
104
|
def driver
|
112
105
|
session.driver
|
113
106
|
end
|
@@ -42,10 +42,26 @@ module Capybara
|
|
42
42
|
|
43
43
|
##
|
44
44
|
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
48
|
-
|
45
|
+
# Retrieve the text of the element. If `Capybara.ignore_hidden_elements`
|
46
|
+
# is `true`, which it is by default, then this will return only text
|
47
|
+
# which is visible. The exact semantics of this may differ between
|
48
|
+
# drivers, but generally any text within elements with `display:none` is
|
49
|
+
# ignored. This behaviour can be overridden by passing `:all` to this
|
50
|
+
# method.
|
51
|
+
#
|
52
|
+
# @param [:all, :visible] Whether to return only visible or all text
|
53
|
+
#
|
54
|
+
# @return [String] The text of the element
|
55
|
+
#
|
56
|
+
def text(type=nil)
|
57
|
+
type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
|
58
|
+
synchronize do
|
59
|
+
if type == :all
|
60
|
+
base.all_text
|
61
|
+
else
|
62
|
+
base.visible_text
|
63
|
+
end
|
64
|
+
end
|
49
65
|
end
|
50
66
|
|
51
67
|
##
|
@@ -103,6 +119,14 @@ module Capybara
|
|
103
119
|
synchronize { base.click }
|
104
120
|
end
|
105
121
|
|
122
|
+
##
|
123
|
+
#
|
124
|
+
# Hover on the Element
|
125
|
+
#
|
126
|
+
def hover
|
127
|
+
synchronize { base.hover }
|
128
|
+
end
|
129
|
+
|
106
130
|
##
|
107
131
|
#
|
108
132
|
# @return [String] The tag name of the element
|
@@ -142,6 +166,16 @@ module Capybara
|
|
142
166
|
synchronize { base.selected? }
|
143
167
|
end
|
144
168
|
|
169
|
+
##
|
170
|
+
#
|
171
|
+
# Whether or not the element is disabled.
|
172
|
+
#
|
173
|
+
# @return [Boolean] Whether the element is disabled
|
174
|
+
#
|
175
|
+
def disabled?
|
176
|
+
synchronize { base.disabled? }
|
177
|
+
end
|
178
|
+
|
145
179
|
##
|
146
180
|
#
|
147
181
|
# An XPath expression describing where on the page the element can be found
|
@@ -179,15 +213,19 @@ module Capybara
|
|
179
213
|
|
180
214
|
def reload
|
181
215
|
if @allow_reload
|
182
|
-
|
183
|
-
|
216
|
+
begin
|
217
|
+
reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
|
218
|
+
@base = reloaded.base if reloaded
|
219
|
+
rescue => e
|
220
|
+
raise e unless catch_error?(e)
|
221
|
+
end
|
184
222
|
end
|
185
223
|
self
|
186
224
|
end
|
187
225
|
|
188
226
|
def inspect
|
189
227
|
%(#<Capybara::Element tag="#{tag_name}" path="#{path}">)
|
190
|
-
rescue NotSupportedByDriverError
|
228
|
+
rescue NotSupportedByDriverError, 'Capybara::Node::Element#inspect'
|
191
229
|
%(#<Capybara::Element tag="#{tag_name}">)
|
192
230
|
end
|
193
231
|
end
|
@@ -19,11 +19,29 @@ module Capybara
|
|
19
19
|
# page.find('li', :text => 'Quox').click_link('Delete')
|
20
20
|
#
|
21
21
|
# @param (see Capybara::Node::Finders#all)
|
22
|
-
# @
|
23
|
-
# @
|
22
|
+
# @option options [Boolean] match The matching strategy to use.
|
23
|
+
# @option options [false, Numeric] wait How long to wait for the element to appear.
|
24
|
+
#
|
25
|
+
# @return [Capybara::Element] The found element
|
26
|
+
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
|
24
27
|
#
|
25
28
|
def find(*args)
|
26
|
-
|
29
|
+
query = Capybara::Query.new(*args)
|
30
|
+
synchronize(query.wait) do
|
31
|
+
if query.match == :smart or query.match == :prefer_exact
|
32
|
+
result = resolve_query(query, true)
|
33
|
+
result = resolve_query(query, false) if result.size == 0 and not query.exact?
|
34
|
+
else
|
35
|
+
result = resolve_query(query)
|
36
|
+
end
|
37
|
+
if query.match == :one or query.match == :smart and result.size > 1
|
38
|
+
raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
|
39
|
+
end
|
40
|
+
if result.size == 0
|
41
|
+
raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
|
42
|
+
end
|
43
|
+
result.first
|
44
|
+
end.tap(&:allow_reload!)
|
27
45
|
end
|
28
46
|
|
29
47
|
##
|
@@ -33,8 +51,8 @@ module Capybara
|
|
33
51
|
# @param [String] locator Which field to find
|
34
52
|
# @return [Capybara::Element] The found element
|
35
53
|
#
|
36
|
-
def find_field(locator)
|
37
|
-
find(:field, locator)
|
54
|
+
def find_field(locator, options={})
|
55
|
+
find(:field, locator, options)
|
38
56
|
end
|
39
57
|
alias_method :field_labeled, :find_field
|
40
58
|
|
@@ -45,8 +63,8 @@ module Capybara
|
|
45
63
|
# @param [String] locator Which link to find
|
46
64
|
# @return [Capybara::Element] The found element
|
47
65
|
#
|
48
|
-
def find_link(locator)
|
49
|
-
find(:link, locator)
|
66
|
+
def find_link(locator, options={})
|
67
|
+
find(:link, locator, options)
|
50
68
|
end
|
51
69
|
|
52
70
|
##
|
@@ -56,8 +74,8 @@ module Capybara
|
|
56
74
|
# @param [String] locator Which button to find
|
57
75
|
# @return [Capybara::Element] The found element
|
58
76
|
#
|
59
|
-
def find_button(locator)
|
60
|
-
find(:button, locator)
|
77
|
+
def find_button(locator, options={})
|
78
|
+
find(:button, locator, options)
|
61
79
|
end
|
62
80
|
|
63
81
|
##
|
@@ -67,8 +85,8 @@ module Capybara
|
|
67
85
|
# @param [String] id Which element to find
|
68
86
|
# @return [Capybara::Element] The found element
|
69
87
|
#
|
70
|
-
def find_by_id(id)
|
71
|
-
find(:id, id)
|
88
|
+
def find_by_id(id, options={})
|
89
|
+
find(:id, id, options)
|
72
90
|
end
|
73
91
|
|
74
92
|
##
|
@@ -103,18 +121,12 @@ module Capybara
|
|
103
121
|
# @param [String] locator The selector
|
104
122
|
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
|
105
123
|
# @option options [Boolean] visible Only find elements that are visible on the page. Setting this to false
|
106
|
-
#
|
107
|
-
#
|
108
|
-
# @return [
|
124
|
+
# finds invisible _and_ visible elements.
|
125
|
+
# @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
|
126
|
+
# @return [Capybara::Result] A collection of found elements
|
109
127
|
#
|
110
128
|
def all(*args)
|
111
|
-
|
112
|
-
elements = synchronize do
|
113
|
-
base.find(query.xpath).map do |node|
|
114
|
-
Capybara::Node::Element.new(session, node, self, query)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
Capybara::Result.new(elements, query)
|
129
|
+
resolve_query(Capybara::Query.new(*args))
|
118
130
|
end
|
119
131
|
|
120
132
|
##
|
@@ -131,6 +143,21 @@ module Capybara
|
|
131
143
|
def first(*args)
|
132
144
|
all(*args).first
|
133
145
|
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def resolve_query(query, exact=nil)
|
150
|
+
elements = synchronize do
|
151
|
+
if query.selector.format==:css
|
152
|
+
base.find_css(query.css)
|
153
|
+
else
|
154
|
+
base.find_xpath(query.xpath(exact))
|
155
|
+
end.map do |node|
|
156
|
+
Capybara::Node::Element.new(session, node, self, query)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
Capybara::Result.new(elements, query)
|
160
|
+
end
|
134
161
|
end
|
135
162
|
end
|
136
163
|
end
|