capybara 3.11.1 → 3.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +18 -1
- data/README.md +4 -4
- data/lib/capybara.rb +7 -0
- 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.rb +7 -15
- 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/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/spec/css_builder_spec.rb +99 -0
- data/spec/result_spec.rb +6 -0
- data/spec/selector_spec.rb +26 -1
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b3b4d3a612269d9ee165e7ed94e1e62f91c6fd85bcc5c5ae614c25d0c22bd32
|
4
|
+
data.tar.gz: a2bdfa0c2398789bd15d791e3b2d0d03d9c3371033e4f4e4d21e302db845c93f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4815632f5bc14c932e9b5181d68584ac3088f11241e8ee3c19f3ee0a27190f8ad1544b88a532f901eaf79f9ae3c2f721dfca1d2d6d20d098a40438042b9f64a
|
7
|
+
data.tar.gz: eb2765462cdbdda76a4ca5d100b93688029d1ca727c00579c5e9e5d77f26fc4a0fe64bbe3616bb3f89b34e730c337e242b319b2ea606b5287a780f8219ee5b08
|
data/History.md
CHANGED
@@ -1,7 +1,24 @@
|
|
1
|
+
# Version 3.12.0
|
2
|
+
Release date: 2018-11-28
|
3
|
+
|
4
|
+
### Added
|
5
|
+
|
6
|
+
* Support Ruby 2.6 endless range in Result#[] and query `:between` option
|
7
|
+
* Pre-registered headless firefox driver :selenium_headless [Andrew Havens]
|
8
|
+
* Selenium driver now defaults to clearing `sessionStorage` and `localStorage`. To disable pass `clear_local_storage: false` and/or `clear_session_storage: false` when creating Capybara::Selenium::Driver instance in your driver registration
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
* Raise error if only :x or :y are passed as an offset to click methods
|
13
|
+
|
14
|
+
### Removed
|
15
|
+
|
16
|
+
* Support for RSpec < 3.5
|
17
|
+
|
1
18
|
# Version 3.11.1
|
2
19
|
Release date: 2018-11-16
|
3
20
|
|
4
|
-
###Fixed
|
21
|
+
### Fixed
|
5
22
|
|
6
23
|
* Fixed :link_or_button XPath generation when it has had an expression filter added
|
7
24
|
|
data/README.md
CHANGED
@@ -6,8 +6,7 @@
|
|
6
6
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
7
|
[![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
|
8
8
|
|
9
|
-
**Note** You are viewing the README for the 3.
|
10
|
-
|
9
|
+
**Note** You are viewing the README for the 3.12.x version of Capybara.
|
11
10
|
|
12
11
|
Capybara helps you test web applications by simulating how a real user would
|
13
12
|
interact with your app. It is agnostic about the driver running your tests and
|
@@ -139,7 +138,7 @@ There are also explicit tags for each registered driver set up for you (`@seleni
|
|
139
138
|
|
140
139
|
## <a name="using-capybara-with-rspec"></a>Using Capybara with RSpec
|
141
140
|
|
142
|
-
Load RSpec 3.
|
141
|
+
Load RSpec 3.5+ support by adding the following line (typically to your
|
143
142
|
`spec_helper.rb` file):
|
144
143
|
|
145
144
|
```ruby
|
@@ -394,6 +393,7 @@ and add it to your Gemfile if you're using bundler.
|
|
394
393
|
Capybara pre-registers a number of named drives that use Selenium - they are:
|
395
394
|
|
396
395
|
* :selenium => Selenium driving Firefox
|
396
|
+
* :selenium_headless => Selenium driving Firefox in a headless configuration
|
397
397
|
* :selenium_chrome => Selenium driving Chrome
|
398
398
|
* :selenium_chrome_headless => Selenium driving Chrome in a headless configuration
|
399
399
|
|
@@ -404,7 +404,7 @@ to the browsers. See the section on adding and configuring drivers.
|
|
404
404
|
|
405
405
|
**Note**: drivers which run the server in a different thread may not share the
|
406
406
|
same transaction as your tests, causing data not to be shared between your test
|
407
|
-
and test server, see
|
407
|
+
and test server, see [Transactions and database setup](#transactions-and-database-setup) below.
|
408
408
|
|
409
409
|
### <a name="capybara-webkit"></a>Capybara-webkit
|
410
410
|
|
data/lib/capybara.rb
CHANGED
@@ -532,6 +532,13 @@ Capybara.register_driver :selenium do |app|
|
|
532
532
|
Capybara::Selenium::Driver.new(app)
|
533
533
|
end
|
534
534
|
|
535
|
+
Capybara.register_driver :selenium_headless do |app|
|
536
|
+
Capybara::Selenium::Driver.load_selenium
|
537
|
+
browser_options = ::Selenium::WebDriver::Firefox::Options.new
|
538
|
+
browser_options.args << '-headless'
|
539
|
+
Capybara::Selenium::Driver.new(app, browser: :firefox, options: browser_options)
|
540
|
+
end
|
541
|
+
|
535
542
|
Capybara.register_driver :selenium_chrome do |app|
|
536
543
|
Capybara::Selenium::Driver.new(app, browser: :chrome)
|
537
544
|
end
|
@@ -153,7 +153,9 @@ module Capybara
|
|
153
153
|
# @option offset [Integer] y Y coordinate to offset the click location from the top left corner of the element
|
154
154
|
# @return [Capybara::Node::Element] The element
|
155
155
|
def click(*keys, wait: nil, **offset)
|
156
|
-
|
156
|
+
raise ArgumentError, 'You must specify both x: and y: for a click offset' if nil ^ offset[:x] ^ offset[:y]
|
157
|
+
|
158
|
+
synchronize(wait) { base.click(Array(keys), offset) }
|
157
159
|
self
|
158
160
|
end
|
159
161
|
|
@@ -164,6 +166,8 @@ module Capybara
|
|
164
166
|
# @macro click_modifiers
|
165
167
|
# @return [Capybara::Node::Element] The element
|
166
168
|
def right_click(*keys, wait: nil, **offset)
|
169
|
+
raise ArgumentError, 'You must specify both x: and y: for a click offset' if nil ^ offset[:x] ^ offset[:y]
|
170
|
+
|
167
171
|
synchronize(wait) { base.right_click(keys, offset) }
|
168
172
|
self
|
169
173
|
end
|
@@ -175,6 +179,8 @@ module Capybara
|
|
175
179
|
# @macro click_modifiers
|
176
180
|
# @return [Capybara::Node::Element] The element
|
177
181
|
def double_click(*keys, wait: nil, **offset)
|
182
|
+
raise ArgumentError, 'You must specify both x: and y: for a click offset' if nil ^ offset[:x] ^ offset[:y]
|
183
|
+
|
178
184
|
synchronize(wait) { base.double_click(keys, offset) }
|
179
185
|
self
|
180
186
|
end
|
@@ -546,24 +546,6 @@ module Capybara
|
|
546
546
|
end
|
547
547
|
end
|
548
548
|
|
549
|
-
# Deprecated
|
550
|
-
# TODO: remove
|
551
|
-
def refute_selector(*args, &optional_filter_block)
|
552
|
-
warn '`refute_selector` was never meant to be in this scope unless ' \
|
553
|
-
'using minitest. Either replace with `assert_no_selector` ' \
|
554
|
-
"or require 'capybara/minitest'."
|
555
|
-
assert_no_selector(*args, &optional_filter_block)
|
556
|
-
end
|
557
|
-
|
558
|
-
# Deprecated
|
559
|
-
# TODO: remove
|
560
|
-
def refute_matches_selector(*args, &optional_filter_block)
|
561
|
-
warn '`refute_matches_selector` was never meant to be in this scope unless ' \
|
562
|
-
'using minitest. Either replace with `assert_not_matches_selector` ' \
|
563
|
-
"or require 'capybara/minitest'."
|
564
|
-
assert_not_matches_selector(*args, &optional_filter_block)
|
565
|
-
end
|
566
|
-
|
567
549
|
##
|
568
550
|
#
|
569
551
|
# Checks if the current node matches given selector
|
@@ -79,7 +79,7 @@ module Capybara
|
|
79
79
|
if count
|
80
80
|
message << " #{occurrences count}"
|
81
81
|
elsif between
|
82
|
-
message << " between #{between.first} and #{between.last} times"
|
82
|
+
message << " between #{between.first} and #{between.end ? between.last : 'infinite'} times"
|
83
83
|
elsif maximum
|
84
84
|
message << " at most #{occurrences maximum}"
|
85
85
|
elsif minimum
|
@@ -62,13 +62,11 @@ module Capybara
|
|
62
62
|
|
63
63
|
def matches_filters?(node, node_filter_errors = [])
|
64
64
|
return true if (@resolved_node&.== node) && options[:allow_self]
|
65
|
-
return false unless matches_locator_filter?(node)
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
matches_node_filters?(node, node_filter_errors) && matches_filter_block?(node)
|
66
|
+
matches_locator_filter?(node) &&
|
67
|
+
matches_system_filters?(node) &&
|
68
|
+
matches_node_filters?(node, node_filter_errors) &&
|
69
|
+
matches_filter_block?(node)
|
72
70
|
rescue *(node.respond_to?(:session) ? node.session.driver.invalid_element_errors : [])
|
73
71
|
false
|
74
72
|
end
|
@@ -93,11 +91,11 @@ module Capybara
|
|
93
91
|
exact = exact? if exact.nil?
|
94
92
|
expr = apply_expression_filters(@expression)
|
95
93
|
expr = exact ? expr.to_xpath(:exact) : expr.to_s if expr.respond_to?(:to_xpath)
|
96
|
-
|
94
|
+
filtered_expression(expr)
|
97
95
|
end
|
98
96
|
|
99
97
|
def css
|
100
|
-
|
98
|
+
filtered_expression(apply_expression_filters(@expression))
|
101
99
|
end
|
102
100
|
|
103
101
|
# @api private
|
@@ -136,12 +134,10 @@ module Capybara
|
|
136
134
|
end
|
137
135
|
|
138
136
|
def find_selector(locator)
|
139
|
-
|
140
|
-
|
141
|
-
else
|
142
|
-
|
143
|
-
end
|
144
|
-
selector || Selector.all[session_options.default_selector]
|
137
|
+
case locator
|
138
|
+
when Symbol then Selector[locator]
|
139
|
+
else Selector.for(locator)
|
140
|
+
end || Selector[session_options.default_selector]
|
145
141
|
end
|
146
142
|
|
147
143
|
def find_nodes_by_selector_format(node, exact)
|
@@ -165,6 +161,8 @@ module Capybara
|
|
165
161
|
end
|
166
162
|
|
167
163
|
def matches_node_filters?(node, errors)
|
164
|
+
applied_filters << :node
|
165
|
+
|
168
166
|
unapplied_options = options.keys - valid_keys
|
169
167
|
@selector.with_filter_errors(errors) do
|
170
168
|
node_filters.all? do |filter_name, filter|
|
@@ -196,7 +194,7 @@ module Capybara
|
|
196
194
|
end
|
197
195
|
|
198
196
|
def filter_set(name)
|
199
|
-
::Capybara::Selector::FilterSet
|
197
|
+
::Capybara::Selector::FilterSet[name]
|
200
198
|
end
|
201
199
|
|
202
200
|
def node_filters
|
@@ -235,18 +233,11 @@ module Capybara
|
|
235
233
|
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
|
236
234
|
end
|
237
235
|
|
238
|
-
def
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
def filtered_css(expr)
|
245
|
-
::Capybara::Selector::CSS.split(expr).map do |sel|
|
246
|
-
sel += conditions_from_id if use_default_id_filter?
|
247
|
-
sel += conditions_from_classes if use_default_class_filter?
|
248
|
-
sel
|
249
|
-
end.join(', ')
|
236
|
+
def filtered_expression(expr)
|
237
|
+
conditions = {}
|
238
|
+
conditions[:id] = options[:id] if use_default_id_filter?
|
239
|
+
conditions[:class] = options[:class] if use_default_class_filter?
|
240
|
+
builder(expr).add_attribute_conditions(conditions)
|
250
241
|
end
|
251
242
|
|
252
243
|
def use_default_id_filter?
|
@@ -257,14 +248,6 @@ module Capybara
|
|
257
248
|
options.key?(:class) && !custom_keys.include?(:class)
|
258
249
|
end
|
259
250
|
|
260
|
-
def conditions_from_classes
|
261
|
-
builder.class_conditions(options[:class])
|
262
|
-
end
|
263
|
-
|
264
|
-
def conditions_from_id
|
265
|
-
builder.attribute_conditions(id: options[:id])
|
266
|
-
end
|
267
|
-
|
268
251
|
def apply_expression_filters(expression)
|
269
252
|
unapplied_options = options.keys - valid_keys
|
270
253
|
expression_filters.inject(expression) do |expr, (name, ef)|
|
@@ -307,12 +290,14 @@ module Capybara
|
|
307
290
|
end
|
308
291
|
|
309
292
|
def matches_locator_filter?(node)
|
310
|
-
return true
|
293
|
+
return true unless @selector.locator_filter
|
311
294
|
|
312
|
-
@selector.locator_filter.
|
295
|
+
@selector.locator_filter.matches?(node, @locator, @selector)
|
313
296
|
end
|
314
297
|
|
315
298
|
def matches_system_filters?(node)
|
299
|
+
applied_filters << :system
|
300
|
+
|
316
301
|
matches_id_filter?(node) &&
|
317
302
|
matches_class_filter?(node) &&
|
318
303
|
matches_text_filter?(node) &&
|
@@ -374,8 +359,8 @@ module Capybara
|
|
374
359
|
@selector.default_visibility(session_options.ignore_hidden_elements, options)
|
375
360
|
end
|
376
361
|
|
377
|
-
def builder
|
378
|
-
selector.builder
|
362
|
+
def builder(expr)
|
363
|
+
selector.builder(expr)
|
379
364
|
end
|
380
365
|
end
|
381
366
|
end
|
data/lib/capybara/result.rb
CHANGED
@@ -59,7 +59,7 @@ module Capybara
|
|
59
59
|
nil
|
60
60
|
end
|
61
61
|
when Range
|
62
|
-
idx.max
|
62
|
+
idx.end && idx.max # endless range will have end == nil
|
63
63
|
end
|
64
64
|
|
65
65
|
if max_idx.nil?
|
@@ -92,9 +92,9 @@ module Capybara
|
|
92
92
|
end
|
93
93
|
|
94
94
|
if between
|
95
|
-
min, max = between.
|
96
|
-
size = load_up_to(max + 1)
|
97
|
-
return between.include?(size)
|
95
|
+
min, max = between.min, (between.end && between.max)
|
96
|
+
size = load_up_to(max ? max + 1 : min)
|
97
|
+
return size <=> min unless between.include?(size)
|
98
98
|
end
|
99
99
|
|
100
100
|
0
|
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
|
@@ -489,7 +481,7 @@ Capybara.add_selector(:element) do
|
|
489
481
|
end
|
490
482
|
|
491
483
|
expression_filter(:attributes, matcher: /.+/) do |xpath, name, val|
|
492
|
-
xpath
|
484
|
+
builder(xpath).add_attribute_conditions(name => val)
|
493
485
|
end
|
494
486
|
|
495
487
|
node_filter(:attributes, matcher: /.+/) do |node, name, val|
|
@@ -6,41 +6,73 @@ module Capybara
|
|
6
6
|
class Selector
|
7
7
|
# @api private
|
8
8
|
class CSSBuilder
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
def initialize(expression)
|
10
|
+
@expression = expression || ''
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :expression
|
14
|
+
|
15
|
+
def add_attribute_conditions(**attributes)
|
16
|
+
@expression = attributes.inject(expression) do |css, (name, value)|
|
17
|
+
conditions = if name == :class
|
18
|
+
class_conditions(value)
|
19
|
+
elsif value.is_a? Regexp
|
20
|
+
Selector::RegexpDisassembler.new(value).alternated_substrings.map do |strs|
|
21
|
+
strs.map do |str|
|
22
|
+
"[#{name}*='#{str}'#{' i' if value.casefold?}]"
|
18
23
|
end.join
|
19
|
-
when true
|
20
|
-
"[#{attribute}]"
|
21
|
-
when false
|
22
|
-
':not([attribute])'
|
23
|
-
else
|
24
|
-
if attribute == :id
|
25
|
-
"##{::Capybara::Selector::CSS.escape(value)}"
|
26
|
-
else
|
27
|
-
"[#{attribute}='#{value}']"
|
28
|
-
end
|
29
24
|
end
|
30
|
-
|
25
|
+
else
|
26
|
+
[attribute_conditions(name => value)]
|
27
|
+
end
|
28
|
+
|
29
|
+
::Capybara::Selector::CSS.split(css).map do |sel|
|
30
|
+
next sel if conditions.empty?
|
31
|
+
|
32
|
+
conditions.map { |cond| sel + cond }.join(', ')
|
33
|
+
end.join(', ')
|
31
34
|
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
32
38
|
|
33
|
-
|
34
|
-
|
39
|
+
def attribute_conditions(attributes)
|
40
|
+
attributes.map do |attribute, value|
|
41
|
+
case value
|
35
42
|
when XPath::Expression
|
36
|
-
raise ArgumentError,
|
43
|
+
raise ArgumentError, "XPath expressions are not supported for the :#{attribute} filter with CSS based selectors"
|
37
44
|
when Regexp
|
38
|
-
|
45
|
+
Selector::RegexpDisassembler.new(value).substrings.map do |str|
|
46
|
+
"[#{attribute}*='#{str}'#{' i' if value.casefold?}]"
|
47
|
+
end.join
|
48
|
+
when true
|
49
|
+
"[#{attribute}]"
|
50
|
+
when false
|
51
|
+
':not([attribute])'
|
39
52
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
53
|
+
if attribute == :id
|
54
|
+
"##{::Capybara::Selector::CSS.escape(value)}"
|
55
|
+
else
|
56
|
+
"[#{attribute}='#{value}']"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end.join
|
60
|
+
end
|
61
|
+
|
62
|
+
def class_conditions(classes)
|
63
|
+
case classes
|
64
|
+
when XPath::Expression
|
65
|
+
raise ArgumentError, 'XPath expressions are not supported for the :class filter with CSS based selectors'
|
66
|
+
when Regexp
|
67
|
+
Selector::RegexpDisassembler.new(classes).alternated_substrings.map do |strs|
|
68
|
+
strs.map do |str|
|
69
|
+
"[class*='#{str}'#{' i' if classes.casefold?}]"
|
70
|
+
end.join
|
43
71
|
end
|
72
|
+
else
|
73
|
+
cls = Array(classes).group_by { |cl| cl.start_with?('!') && !cl.start_with?('!!!') }
|
74
|
+
[(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
|
75
|
+
cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..-1))})" }).join]
|
44
76
|
end
|
45
77
|
end
|
46
78
|
end
|