capybara 3.11.0 → 3.12.0
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.
- checksums.yaml +4 -4
- data/History.md +24 -0
- data/README.md +4 -4
- data/lib/capybara/node/element.rb +7 -1
- data/lib/capybara/node/matchers.rb +0 -18
- data/lib/capybara/queries/base_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +24 -39
- data/lib/capybara/result.rb +4 -4
- data/lib/capybara/selector/builders/css_builder.rb +59 -27
- data/lib/capybara/selector/builders/xpath_builder.rb +50 -35
- data/lib/capybara/selector/css.rb +7 -7
- data/lib/capybara/selector/filter.rb +1 -0
- data/lib/capybara/selector/filter_set.rb +17 -15
- data/lib/capybara/selector/filters/locator_filter.rb +19 -0
- data/lib/capybara/selector/regexp_disassembler.rb +104 -61
- data/lib/capybara/selector/selector.rb +14 -5
- data/lib/capybara/selector.rb +10 -20
- data/lib/capybara/selenium/driver.rb +14 -9
- data/lib/capybara/selenium/driver_specializations/{marionette_driver.rb → firefox_driver.rb} +3 -3
- data/lib/capybara/selenium/nodes/{marionette_node.rb → firefox_node.rb} +1 -1
- data/lib/capybara/spec/session/all_spec.rb +8 -1
- data/lib/capybara/spec/session/assert_selector_spec.rb +0 -10
- data/lib/capybara/spec/session/click_button_spec.rb +5 -3
- data/lib/capybara/spec/session/click_link_spec.rb +5 -5
- data/lib/capybara/spec/session/find_spec.rb +1 -1
- data/lib/capybara/spec/session/first_spec.rb +1 -1
- data/lib/capybara/spec/session/has_css_spec.rb +7 -0
- data/lib/capybara/spec/session/has_xpath_spec.rb +17 -0
- data/lib/capybara/spec/session/node_spec.rb +10 -3
- data/lib/capybara/spec/session/window/window_spec.rb +2 -2
- data/lib/capybara/spec/spec_helper.rb +1 -2
- data/lib/capybara/spec/views/obscured.erb +3 -0
- data/lib/capybara/version.rb +1 -1
- data/lib/capybara.rb +7 -0
- data/spec/css_builder_spec.rb +99 -0
- data/spec/result_spec.rb +6 -0
- data/spec/selector_spec.rb +41 -0
- data/spec/selenium_spec_chrome.rb +18 -16
- data/spec/selenium_spec_chrome_remote.rb +0 -2
- data/spec/{selenium_spec_marionette.rb → selenium_spec_firefox.rb} +31 -25
- data/spec/selenium_spec_firefox_remote.rb +4 -6
- data/spec/shared_selenium_session.rb +2 -2
- data/spec/spec_helper.rb +5 -5
- data/spec/xpath_builder_spec.rb +91 -0
- metadata +10 -7
|
@@ -65,22 +65,6 @@ module Capybara
|
|
|
65
65
|
strs
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
def min_repeat(exp)
|
|
69
|
-
exp.quantifier&.min || 1
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def max_repeat(exp)
|
|
73
|
-
exp.quantifier&.max || 1
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def fixed_repeat?(exp)
|
|
77
|
-
min_repeat(exp) == max_repeat(exp)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def optional?(exp)
|
|
81
|
-
min_repeat(exp).zero?
|
|
82
|
-
end
|
|
83
|
-
|
|
84
68
|
def combine(strs)
|
|
85
69
|
suffixes = [[]]
|
|
86
70
|
strs.reverse_each do |str|
|
|
@@ -91,9 +75,7 @@ module Capybara
|
|
|
91
75
|
prefixes.product(suffixes) { |pair| result << pair.flatten(1) }
|
|
92
76
|
suffixes = result
|
|
93
77
|
else
|
|
94
|
-
suffixes.each
|
|
95
|
-
arr.unshift str
|
|
96
|
-
end
|
|
78
|
+
suffixes.each { |arr| arr.unshift str }
|
|
97
79
|
end
|
|
98
80
|
end
|
|
99
81
|
suffixes
|
|
@@ -106,59 +88,120 @@ module Capybara
|
|
|
106
88
|
end
|
|
107
89
|
|
|
108
90
|
def extract_strings(expression, alternation: false)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
91
|
+
Expression.new(expression).extract_strings(alternation)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @api private
|
|
95
|
+
class Expression
|
|
96
|
+
def initialize(exp)
|
|
97
|
+
@exp = exp
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def extract_strings(process_alternatives)
|
|
101
|
+
strings = []
|
|
102
|
+
each do |exp|
|
|
103
|
+
next strings.push(nil) if exp.optional? && !process_alternatives
|
|
115
104
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
105
|
+
next strings.push(exp.alternative_strings) if exp.alternation? && process_alternatives
|
|
106
|
+
|
|
107
|
+
strings.concat(exp.strings(process_alternatives))
|
|
119
108
|
end
|
|
109
|
+
strings
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
protected
|
|
113
|
+
|
|
114
|
+
def alternation?
|
|
115
|
+
(type == :meta) && !terminal?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def optional?
|
|
119
|
+
min_repeat.zero?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def terminal?
|
|
123
|
+
@exp.terminal?
|
|
124
|
+
end
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
def strings(process_alternatives)
|
|
127
|
+
if indeterminate?
|
|
128
|
+
[nil]
|
|
129
|
+
elsif terminal?
|
|
130
|
+
terminal_strings
|
|
131
|
+
elsif optional?
|
|
132
|
+
optional_strings
|
|
133
|
+
else
|
|
134
|
+
repeated_strings(process_alternatives)
|
|
124
135
|
end
|
|
136
|
+
end
|
|
125
137
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
else
|
|
131
|
-
strings.push(nil)
|
|
132
|
-
next
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
if optional?(exp)
|
|
136
|
-
strings.push(Set.new([[''], [text]]))
|
|
137
|
-
strings.push(nil) unless max_repeat(exp) == 1
|
|
138
|
-
next
|
|
139
|
-
else
|
|
140
|
-
strings.push(text * min_repeat(exp))
|
|
141
|
-
end
|
|
142
|
-
elsif optional?(exp)
|
|
143
|
-
strings.push(Set.new([[''], extract_strings(exp, alternation: true)]))
|
|
144
|
-
strings.push(nil) unless max_repeat(exp) == 1
|
|
145
|
-
next
|
|
138
|
+
def terminal_strings
|
|
139
|
+
text = case @exp.type
|
|
140
|
+
when :literal then @exp.text
|
|
141
|
+
when :escape then @exp.char
|
|
146
142
|
else
|
|
147
|
-
|
|
143
|
+
return [nil]
|
|
148
144
|
end
|
|
149
|
-
|
|
145
|
+
|
|
146
|
+
optional? ? options_set(text) : repeat_set(text)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def optional_strings
|
|
150
|
+
options_set(extract_strings(true))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def repeated_strings(process_alternatives)
|
|
154
|
+
repeat_set extract_strings(process_alternatives)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def alternative_strings
|
|
158
|
+
alts = alternatives.map { |sub_exp| sub_exp.extract_strings(alternation: true) }
|
|
159
|
+
alts.all?(&:any?) ? Set.new(alts) : nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def indeterminate?
|
|
165
|
+
%i[meta set].include?(type)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def min_repeat
|
|
169
|
+
@exp.quantifier&.min || 1
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def max_repeat
|
|
173
|
+
@exp.quantifier&.max || 1
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def fixed_repeat?
|
|
177
|
+
min_repeat == max_repeat
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def type
|
|
181
|
+
@exp.type
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def repeat_set(str)
|
|
185
|
+
strs = Array(str * min_repeat)
|
|
186
|
+
strs.push(nil) unless fixed_repeat?
|
|
187
|
+
strs
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def options_set(strs)
|
|
191
|
+
strs = [Set.new([[''], Array(strs)])]
|
|
192
|
+
strs.push(nil) unless max_repeat == 1
|
|
193
|
+
strs
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def alternatives
|
|
197
|
+
@exp.alternatives.map { |exp| Expression.new(exp) }
|
|
150
198
|
end
|
|
151
|
-
strings
|
|
152
|
-
end
|
|
153
199
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if alternatives.all?(&:any?)
|
|
157
|
-
Set.new(alternatives)
|
|
158
|
-
else
|
|
159
|
-
nil
|
|
200
|
+
def each
|
|
201
|
+
@exp.each { |exp| yield Expression.new(exp) }
|
|
160
202
|
end
|
|
161
203
|
end
|
|
204
|
+
private_constant :Expression
|
|
162
205
|
end
|
|
163
206
|
end
|
|
164
207
|
end
|
|
@@ -169,17 +169,25 @@ module Capybara
|
|
|
169
169
|
@selectors ||= {} # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
170
170
|
end
|
|
171
171
|
|
|
172
|
+
def [](name)
|
|
173
|
+
all.fetch(name.to_sym) { |sel_type| raise ArgumentError, "Unknown selector type (:#{sel_type})" }
|
|
174
|
+
end
|
|
175
|
+
|
|
172
176
|
def add(name, &block)
|
|
173
177
|
all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block)
|
|
174
178
|
end
|
|
175
179
|
|
|
176
180
|
def update(name, &block)
|
|
177
|
-
|
|
181
|
+
self[name].instance_eval(&block)
|
|
178
182
|
end
|
|
179
183
|
|
|
180
184
|
def remove(name)
|
|
181
185
|
all.delete(name.to_sym)
|
|
182
186
|
end
|
|
187
|
+
|
|
188
|
+
def for(locator)
|
|
189
|
+
all.values.find { |sel| sel.match?(locator) }
|
|
190
|
+
end
|
|
183
191
|
end
|
|
184
192
|
|
|
185
193
|
def initialize(name, &block)
|
|
@@ -348,8 +356,9 @@ module Capybara
|
|
|
348
356
|
|
|
349
357
|
def_delegators :@filter_set, :node_filter, :expression_filter, :filter
|
|
350
358
|
|
|
351
|
-
def locator_filter(&block)
|
|
352
|
-
|
|
359
|
+
def locator_filter(*types, **options, &block)
|
|
360
|
+
types.each { |type| options[type] = true }
|
|
361
|
+
@locator_filter = Filters::LocatorFilter.new(block, options) if block
|
|
353
362
|
@locator_filter
|
|
354
363
|
end
|
|
355
364
|
|
|
@@ -400,7 +409,7 @@ module Capybara
|
|
|
400
409
|
end
|
|
401
410
|
|
|
402
411
|
# @api private
|
|
403
|
-
def builder
|
|
412
|
+
def builder(expr = nil)
|
|
404
413
|
case format
|
|
405
414
|
when :css
|
|
406
415
|
Capybara::Selector::CSSBuilder
|
|
@@ -408,7 +417,7 @@ module Capybara
|
|
|
408
417
|
Capybara::Selector::XPathBuilder
|
|
409
418
|
else
|
|
410
419
|
raise NotImplementedError, "No builder exists for selector of type #{format}"
|
|
411
|
-
end
|
|
420
|
+
end.new(expr)
|
|
412
421
|
end
|
|
413
422
|
|
|
414
423
|
# @api private
|
data/lib/capybara/selector.rb
CHANGED
|
@@ -34,7 +34,7 @@ Capybara.add_selector(:css) do
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
Capybara.add_selector(:id) do
|
|
37
|
-
xpath { |id| XPath.descendant
|
|
37
|
+
xpath { |id| builder(XPath.descendant).add_attribute_conditions(id: id) }
|
|
38
38
|
locator_filter { |node, id| id.is_a?(Regexp) ? node[:id] =~ id : true }
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -92,8 +92,7 @@ end
|
|
|
92
92
|
|
|
93
93
|
Capybara.add_selector(:link) do
|
|
94
94
|
xpath do |locator, href: true, alt: nil, title: nil, **|
|
|
95
|
-
xpath = XPath.descendant(:a)
|
|
96
|
-
xpath = xpath[@href_conditions = builder.attribute_conditions(href: href)]
|
|
95
|
+
xpath = builder(XPath.descendant(:a)).add_attribute_conditions(href: href)
|
|
97
96
|
|
|
98
97
|
unless locator.nil?
|
|
99
98
|
locator = locator.to_s
|
|
@@ -119,25 +118,18 @@ Capybara.add_selector(:link) do
|
|
|
119
118
|
end
|
|
120
119
|
|
|
121
120
|
expression_filter(:download, valid_values: [true, false, String]) do |expr, download|
|
|
122
|
-
expr
|
|
121
|
+
builder(expr).add_attribute_conditions(download: download)
|
|
123
122
|
end
|
|
124
123
|
|
|
125
124
|
describe_expression_filters do |**options|
|
|
126
125
|
desc = +''
|
|
127
126
|
if (href = options[:href])
|
|
128
|
-
if
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
desc << " with href matching #{href.inspect}"
|
|
132
|
-
end
|
|
127
|
+
desc << " with href #{'matching ' if href.is_a? Regexp}#{href.inspect}"
|
|
128
|
+
elsif options.key?(:href) # is nil/false specified?
|
|
129
|
+
desc << ' with no href attribute'
|
|
133
130
|
end
|
|
134
|
-
desc << ' with no href attribute' if options.fetch(:href, true).nil?
|
|
135
131
|
desc
|
|
136
132
|
end
|
|
137
|
-
|
|
138
|
-
describe_node_filters do |href: nil, **|
|
|
139
|
-
" with href matching #{href.inspect}" if href.is_a?(Regexp) && @href_conditions.nil?
|
|
140
|
-
end
|
|
141
133
|
end
|
|
142
134
|
|
|
143
135
|
Capybara.add_selector(:button) do
|
|
@@ -161,11 +153,9 @@ Capybara.add_selector(:button) do
|
|
|
161
153
|
image_btn_xpath = image_btn_xpath[alt_matches]
|
|
162
154
|
end
|
|
163
155
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
res_xpath
|
|
156
|
+
%i[value title type].inject(input_btn_xpath.union(btn_xpath).union(image_btn_xpath)) do |memo, ef|
|
|
157
|
+
memo[find_by_attr(ef, options[ef])]
|
|
158
|
+
end
|
|
169
159
|
end
|
|
170
160
|
|
|
171
161
|
node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
|
|
@@ -491,7 +481,7 @@ Capybara.add_selector(:element) do
|
|
|
491
481
|
end
|
|
492
482
|
|
|
493
483
|
expression_filter(:attributes, matcher: /.+/) do |xpath, name, val|
|
|
494
|
-
xpath
|
|
484
|
+
builder(xpath).add_attribute_conditions(name => val)
|
|
495
485
|
end
|
|
496
486
|
|
|
497
487
|
node_filter(:attributes, matcher: /.+/) do |node, name, val|
|
|
@@ -6,10 +6,10 @@ require 'English'
|
|
|
6
6
|
class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
7
7
|
DEFAULT_OPTIONS = {
|
|
8
8
|
browser: :firefox,
|
|
9
|
-
clear_local_storage:
|
|
10
|
-
clear_session_storage:
|
|
9
|
+
clear_local_storage: nil,
|
|
10
|
+
clear_session_storage: nil
|
|
11
11
|
}.freeze
|
|
12
|
-
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage].freeze
|
|
12
|
+
SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout].freeze
|
|
13
13
|
attr_reader :app, :options
|
|
14
14
|
|
|
15
15
|
def self.load_selenium
|
|
@@ -23,6 +23,9 @@ class Capybara::Selenium::Driver < Capybara::Driver::Base
|
|
|
23
23
|
|
|
24
24
|
def browser
|
|
25
25
|
@browser ||= begin
|
|
26
|
+
if options[:timeout]
|
|
27
|
+
options[:http_client] ||= Selenium::WebDriver::Remote::Http::Default.new(read_timeout: options[:timeout])
|
|
28
|
+
end
|
|
26
29
|
processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) }
|
|
27
30
|
Selenium::WebDriver.for(options[:browser], processed_options).tap do |driver|
|
|
28
31
|
specialize_driver(driver)
|
|
@@ -260,15 +263,17 @@ private
|
|
|
260
263
|
end
|
|
261
264
|
|
|
262
265
|
def clear_storage
|
|
263
|
-
clear_session_storage
|
|
264
|
-
clear_local_storage
|
|
266
|
+
clear_session_storage unless options[:clear_session_storage] == false
|
|
267
|
+
clear_local_storage unless options[:clear_local_storage] == false
|
|
268
|
+
rescue Selenium::WebDriver::Error::JavascriptError # rubocop:disable Lint/HandleExceptions
|
|
269
|
+
# session/local storage may not be available if on non-http pages (e.g. about:blank)
|
|
265
270
|
end
|
|
266
271
|
|
|
267
272
|
def clear_session_storage
|
|
268
273
|
if @browser.respond_to? :session_storage
|
|
269
274
|
@browser.session_storage.clear
|
|
270
275
|
else
|
|
271
|
-
warn 'sessionStorage clear requested but is not
|
|
276
|
+
warn 'sessionStorage clear requested but is not supported by this driver' unless options[:clear_session_storage].nil?
|
|
272
277
|
end
|
|
273
278
|
end
|
|
274
279
|
|
|
@@ -276,7 +281,7 @@ private
|
|
|
276
281
|
if @browser.respond_to? :local_storage
|
|
277
282
|
@browser.local_storage.clear
|
|
278
283
|
else
|
|
279
|
-
warn 'localStorage clear requested but is not
|
|
284
|
+
warn 'localStorage clear requested but is not supported by this driver' unless options[:clear_local_storage].nil?
|
|
280
285
|
end
|
|
281
286
|
end
|
|
282
287
|
|
|
@@ -353,7 +358,7 @@ private
|
|
|
353
358
|
extend ChromeDriver
|
|
354
359
|
when :firefox
|
|
355
360
|
require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(sel_driver)
|
|
356
|
-
extend
|
|
361
|
+
extend FirefoxDriver if sel_driver.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities)
|
|
357
362
|
end
|
|
358
363
|
end
|
|
359
364
|
|
|
@@ -396,4 +401,4 @@ private
|
|
|
396
401
|
end
|
|
397
402
|
|
|
398
403
|
require 'capybara/selenium/driver_specializations/chrome_driver'
|
|
399
|
-
require 'capybara/selenium/driver_specializations/
|
|
404
|
+
require 'capybara/selenium/driver_specializations/firefox_driver'
|
data/lib/capybara/selenium/driver_specializations/{marionette_driver.rb → firefox_driver.rb}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'capybara/selenium/nodes/
|
|
3
|
+
require 'capybara/selenium/nodes/firefox_node'
|
|
4
4
|
|
|
5
|
-
module Capybara::Selenium::Driver::
|
|
5
|
+
module Capybara::Selenium::Driver::FirefoxDriver
|
|
6
6
|
def resize_window_to(handle, width, height)
|
|
7
7
|
within_given_window(handle) do
|
|
8
8
|
# Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643
|
|
@@ -45,6 +45,6 @@ module Capybara::Selenium::Driver::MarionetteDriver
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def build_node(native_node)
|
|
48
|
-
::Capybara::Selenium::
|
|
48
|
+
::Capybara::Selenium::FirefoxNode.new(self, native_node)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
@@ -30,7 +30,7 @@ Capybara::SpecHelper.spec '#all' do
|
|
|
30
30
|
|
|
31
31
|
it 'should accept an XPath instance', :exact_false do
|
|
32
32
|
@session.visit('/form')
|
|
33
|
-
@xpath = Capybara::Selector
|
|
33
|
+
@xpath = Capybara::Selector[:fillable_field].call('Name')
|
|
34
34
|
expect(@xpath).to be_a(::XPath::Union)
|
|
35
35
|
@result = @session.all(@xpath).map(&:value)
|
|
36
36
|
expect(@result).to include('Smith', 'John', 'John Smith')
|
|
@@ -135,6 +135,13 @@ Capybara::SpecHelper.spec '#all' do
|
|
|
135
135
|
it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
|
|
136
136
|
expect { @session.all(:css, 'h1, p', between: 0..3) }.to raise_error(Capybara::ExpectationNotMet)
|
|
137
137
|
end
|
|
138
|
+
|
|
139
|
+
eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.5
|
|
140
|
+
it'treats an endless range as minimum' do
|
|
141
|
+
expect { @session.all(:css, 'h1, p', between: 2..) }.not_to raise_error
|
|
142
|
+
expect { @session.all(:css, 'h1, p', between: 5..) }.to raise_error(Capybara::ExpectationNotMet)
|
|
143
|
+
end
|
|
144
|
+
TEST
|
|
138
145
|
end
|
|
139
146
|
|
|
140
147
|
context 'with multiple count filters' do
|
|
@@ -72,16 +72,6 @@ Capybara::SpecHelper.spec '#assert_selector' do
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
Capybara::SpecHelper.spec '#refute_selector', requires: [:driver] do
|
|
76
|
-
it 'should warn not to use' do
|
|
77
|
-
@session.visit('/with_html')
|
|
78
|
-
doc = @session.document
|
|
79
|
-
allow(doc).to receive(:warn)
|
|
80
|
-
doc.refute_selector(:xpath, '//abbr')
|
|
81
|
-
expect(doc).to have_received(:warn)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
75
|
Capybara::SpecHelper.spec '#assert_no_selector' do
|
|
86
76
|
before do
|
|
87
77
|
@session.visit('/with_html')
|
|
@@ -5,10 +5,12 @@ Capybara::SpecHelper.spec '#click_button' do
|
|
|
5
5
|
@session.visit('/form')
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
it 'should wait for asynchronous load', requires: [:js] do
|
|
8
|
+
it 'should wait for asynchronous load', :focus_, requires: [:js] do
|
|
9
9
|
@session.visit('/with_js')
|
|
10
|
-
@session.
|
|
11
|
-
|
|
10
|
+
@session.using_wait_time(1.5) do
|
|
11
|
+
@session.click_link('Click me')
|
|
12
|
+
@session.click_button('New Here')
|
|
13
|
+
end
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
it 'casts to string' do
|
|
@@ -83,7 +83,7 @@ Capybara::SpecHelper.spec '#click_link' do
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
it "should raise error if link wasn't found" do
|
|
86
|
-
expect { @session.click_link('labore', href: 'invalid_href') }.to raise_error(Capybara::ElementNotFound)
|
|
86
|
+
expect { @session.click_link('labore', href: 'invalid_href') }.to raise_error(Capybara::ElementNotFound, /with href "invalid_href/)
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
@@ -104,8 +104,8 @@ Capybara::SpecHelper.spec '#click_link' do
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
it "should raise an error if no link's href matched the pattern" do
|
|
107
|
-
expect { @session.click_link('labore', href: /invalid_pattern/) }.to raise_error(Capybara::ElementNotFound)
|
|
108
|
-
expect { @session.click_link('labore', href: /.+d+/) }.to raise_error(Capybara::ElementNotFound)
|
|
107
|
+
expect { @session.click_link('labore', href: /invalid_pattern/) }.to raise_error(Capybara::ElementNotFound, %r{with href matching /invalid_pattern/})
|
|
108
|
+
expect { @session.click_link('labore', href: /.+d+/) }.to raise_error(Capybara::ElementNotFound, /#{Regexp.quote "with href matching /.+d+/"}/)
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
context 'href: nil' do
|
|
@@ -114,8 +114,8 @@ Capybara::SpecHelper.spec '#click_link' do
|
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
it 'should raise an error if href attribute exists' do
|
|
117
|
-
expect { @session.click_link('Blank Href', href: nil) }.to raise_error(Capybara::ElementNotFound)
|
|
118
|
-
expect { @session.click_link('Normal Anchor', href: nil) }.to raise_error(Capybara::ElementNotFound)
|
|
117
|
+
expect { @session.click_link('Blank Href', href: nil) }.to raise_error(Capybara::ElementNotFound, /with no href attribute/)
|
|
118
|
+
expect { @session.click_link('Normal Anchor', href: nil) }.to raise_error(Capybara::ElementNotFound, /with no href attribute/)
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
end
|
|
@@ -224,7 +224,7 @@ Capybara::SpecHelper.spec '#find' do
|
|
|
224
224
|
|
|
225
225
|
it 'should accept an XPath instance' do
|
|
226
226
|
@session.visit('/form')
|
|
227
|
-
@xpath = Capybara::Selector
|
|
227
|
+
@xpath = Capybara::Selector[:fillable_field].call('First Name')
|
|
228
228
|
expect(@xpath).to be_a(::XPath::Union)
|
|
229
229
|
expect(@session.find(@xpath).value).to eq('John')
|
|
230
230
|
end
|
|
@@ -24,7 +24,7 @@ Capybara::SpecHelper.spec '#first' do
|
|
|
24
24
|
|
|
25
25
|
it 'should accept an XPath instance' do
|
|
26
26
|
@session.visit('/form')
|
|
27
|
-
@xpath = Capybara::Selector
|
|
27
|
+
@xpath = Capybara::Selector[:fillable_field].call('First Name')
|
|
28
28
|
expect(@xpath).to be_a(::XPath::Union)
|
|
29
29
|
expect(@session.first(@xpath).value).to eq('John')
|
|
30
30
|
end
|
|
@@ -27,11 +27,15 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
|
27
27
|
expect(@session).to have_css('h2', id: 'h2one')
|
|
28
28
|
expect(@session).to have_css('h2')
|
|
29
29
|
expect(@session).to have_css('h2', id: /h2o/)
|
|
30
|
+
expect(@session).to have_css('li', id: /john|paul/)
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
it 'should support :class option' do
|
|
33
34
|
expect(@session).to have_css('li', class: 'guitarist')
|
|
34
35
|
expect(@session).to have_css('li', class: /guitar/)
|
|
36
|
+
expect(@session).to have_css('li', class: /guitar|drummer/)
|
|
37
|
+
expect(@session).to have_css('li', class: %w[beatle guitarist])
|
|
38
|
+
expect(@session).to have_css('li', class: /.*/)
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
it 'should support case insensitive :class and :id options' do
|
|
@@ -111,6 +115,9 @@ Capybara::SpecHelper.spec '#has_css?' do
|
|
|
111
115
|
expect(@session).to have_css('p', count: 3)
|
|
112
116
|
expect(@session).to have_css('p a#foo', count: 1)
|
|
113
117
|
expect(@session).to have_css('p a.doesnotexist', count: 0)
|
|
118
|
+
expect(@session).to have_css('li', class: /guitar|drummer/, count: 4)
|
|
119
|
+
expect(@session).to have_css('li', id: /john|paul/, class: /guitar|drummer/, count: 2)
|
|
120
|
+
expect(@session).to have_css('li', class: %w[beatle guitarist], count: 2)
|
|
114
121
|
end
|
|
115
122
|
|
|
116
123
|
it 'should be false if the content occurs a different number of times than the given' do
|
|
@@ -11,6 +11,20 @@ Capybara::SpecHelper.spec '#has_xpath?' do
|
|
|
11
11
|
expect(@session).to have_xpath("//p[contains(.,'est')]")
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
it 'should support :id option' do
|
|
15
|
+
expect(@session).to have_xpath('//h2', id: 'h2one')
|
|
16
|
+
expect(@session).to have_xpath('//h2')
|
|
17
|
+
expect(@session).to have_xpath('//h2', id: /h2o/)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should support :class option' do
|
|
21
|
+
expect(@session).to have_xpath('//li', class: 'guitarist')
|
|
22
|
+
expect(@session).to have_xpath('//li', class: /guitar/)
|
|
23
|
+
expect(@session).to have_xpath('//li', class: /guitar|drummer/)
|
|
24
|
+
expect(@session).to have_xpath('//li', class: %w[beatle guitarist])
|
|
25
|
+
expect(@session).to have_xpath('//li', class: /.*/)
|
|
26
|
+
end
|
|
27
|
+
|
|
14
28
|
it 'should be false if the given selector is not on the page' do
|
|
15
29
|
expect(@session).not_to have_xpath('//abbr')
|
|
16
30
|
expect(@session).not_to have_xpath("//p//a[@id='doesnotexist']")
|
|
@@ -43,6 +57,9 @@ Capybara::SpecHelper.spec '#has_xpath?' do
|
|
|
43
57
|
expect(@session).to have_xpath("//p//a[@id='foo']", count: 1)
|
|
44
58
|
expect(@session).to have_xpath("//p[contains(.,'est')]", count: 1)
|
|
45
59
|
expect(@session).to have_xpath("//p//a[@id='doesnotexist']", count: 0)
|
|
60
|
+
expect(@session).to have_xpath('//li', class: /guitar|drummer/, count: 4)
|
|
61
|
+
expect(@session).to have_xpath('//li', id: /john|paul/, class: /guitar|drummer/, count: 2)
|
|
62
|
+
expect(@session).to have_xpath('//li', class: %w[beatle guitarist], count: 2)
|
|
46
63
|
end
|
|
47
64
|
|
|
48
65
|
it 'should be false if the content occurs a different number of times than the given' do
|
|
@@ -406,6 +406,13 @@ Capybara::SpecHelper.spec 'node' do
|
|
|
406
406
|
expect(locations[:y].to_f).to be_within(1).of(5)
|
|
407
407
|
end
|
|
408
408
|
|
|
409
|
+
it 'should raise error if both x and y values are not passed' do
|
|
410
|
+
@session.visit('with_js')
|
|
411
|
+
el = @session.find(:css, '#click-test')
|
|
412
|
+
expect { el.click(x: 5) }.to raise_error ArgumentError
|
|
413
|
+
expect { el.click(x: nil, y: 3) }.to raise_error ArgumentError
|
|
414
|
+
end
|
|
415
|
+
|
|
409
416
|
it 'should be able to click a table row', requires: [:js] do
|
|
410
417
|
@session.visit('/tables')
|
|
411
418
|
tr = @session.find(:css, '#agent_table tr:first-child').click
|
|
@@ -416,7 +423,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
|
416
423
|
@session.visit('/obscured')
|
|
417
424
|
obscured = @session.find(:css, '#obscured')
|
|
418
425
|
@session.execute_script <<~JS
|
|
419
|
-
setTimeout(function(){ $('#cover').hide(); },
|
|
426
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
420
427
|
JS
|
|
421
428
|
expect { obscured.click }.not_to raise_error
|
|
422
429
|
end
|
|
@@ -468,7 +475,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
|
468
475
|
@session.visit('/obscured')
|
|
469
476
|
obscured = @session.find(:css, '#obscured')
|
|
470
477
|
@session.execute_script <<~JS
|
|
471
|
-
setTimeout(function(){ $('#cover').hide(); },
|
|
478
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
472
479
|
JS
|
|
473
480
|
expect { obscured.double_click }.not_to raise_error
|
|
474
481
|
end
|
|
@@ -502,7 +509,7 @@ Capybara::SpecHelper.spec 'node' do
|
|
|
502
509
|
@session.visit('/obscured')
|
|
503
510
|
obscured = @session.find(:css, '#obscured')
|
|
504
511
|
@session.execute_script <<~JS
|
|
505
|
-
setTimeout(function(){ $('#cover').hide(); },
|
|
512
|
+
setTimeout(function(){ $('#cover').hide(); }, 700)
|
|
506
513
|
JS
|
|
507
514
|
expect { obscured.right_click }.not_to raise_error
|
|
508
515
|
end
|
|
@@ -120,7 +120,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
|
|
|
120
120
|
|
|
121
121
|
after do
|
|
122
122
|
@session.current_window.resize_to(*@initial_size)
|
|
123
|
-
sleep
|
|
123
|
+
sleep 1
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
it 'should be able to resize window', requires: %i[windows js] do
|
|
@@ -194,7 +194,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
|
|
|
194
194
|
|
|
195
195
|
after do
|
|
196
196
|
@session.current_window.resize_to(*@initial_size)
|
|
197
|
-
sleep
|
|
197
|
+
sleep 1
|
|
198
198
|
end
|
|
199
199
|
|
|
200
200
|
it 'should be able to fullscreen the window' do
|
|
@@ -16,8 +16,7 @@ module Capybara
|
|
|
16
16
|
config.filter_run_excluding requires: method(:filter).to_proc
|
|
17
17
|
config.before { Capybara::SpecHelper.reset! }
|
|
18
18
|
config.after { Capybara::SpecHelper.reset! }
|
|
19
|
-
|
|
20
|
-
config.shared_context_metadata_behavior = :apply_to_host_groups if config.respond_to?(:shared_context_metadata_behavior=)
|
|
19
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def reset!
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
|
4
4
|
<title>with_animation</title>
|
|
5
5
|
<script src="/jquery.js" type="text/javascript" charset="utf-8"></script>
|
|
6
|
+
<script>
|
|
7
|
+
$(document).on('contextmenu', function(e){ e.preventDefault(); })
|
|
8
|
+
</script>
|
|
6
9
|
<style>
|
|
7
10
|
div {
|
|
8
11
|
width: 400px;
|