When splitting CSS only split on relevant commas
This commit is contained in:
parent
0a276d84cb
commit
f140cd3374
|
@ -225,14 +225,14 @@ module Capybara
|
||||||
raise ArgumentError, "XPath expressions are not supported for the :class filter with CSS based selectors"
|
raise ArgumentError, "XPath expressions are not supported for the :class filter with CSS based selectors"
|
||||||
end
|
end
|
||||||
|
|
||||||
if process_class || process_id
|
if process_id || process_class
|
||||||
css_selectors = expr.split(',').map(&:rstrip)
|
expr = ::Capybara::Selector::CSS.split(expr).map do |sel|
|
||||||
expr = css_selectors.map do |sel|
|
sel += "##{::Capybara::Selector::CSS.escape(options[:id])}" if process_id
|
||||||
sel += "##{Capybara::Selector::CSS.escape(options[:id])}" if process_id
|
|
||||||
sel += css_from_classes(Array(options[:class])) if process_class
|
sel += css_from_classes(Array(options[:class])) if process_class
|
||||||
sel
|
sel
|
||||||
end.join(", ")
|
end.join(", ")
|
||||||
end
|
end
|
||||||
|
|
||||||
expr
|
expr
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,85 @@ module Capybara
|
||||||
c =~ %r{[ -/:-~]} ? "\\#{c}" : format("\\%06x", c.ord)
|
c =~ %r{[ -/:-~]} ? "\\#{c}" : format("\\%06x", c.ord)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.split(css)
|
||||||
|
Splitter.new.split(css)
|
||||||
|
end
|
||||||
|
|
||||||
S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
|
S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
|
||||||
H = /[0-9a-fA-F]/
|
H = /[0-9a-fA-F]/
|
||||||
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
|
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
|
||||||
NONASCII = /[#{S}]/
|
NONASCII = /[#{S}]/
|
||||||
ESCAPE = /#{UNICODE}|\\[ -~#{S}]/
|
ESCAPE = /#{UNICODE}|\\[ -~#{S}]/
|
||||||
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
|
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
|
||||||
|
|
||||||
|
class Splitter
|
||||||
|
def split(css)
|
||||||
|
selectors = []
|
||||||
|
StringIO.open(css) do |str|
|
||||||
|
selector = ""
|
||||||
|
while (c = str.getc)
|
||||||
|
case c
|
||||||
|
when '['
|
||||||
|
selector += parse_square(str)
|
||||||
|
when '('
|
||||||
|
selector += parse_paren(str)
|
||||||
|
when '"', "'"
|
||||||
|
selector += parse_string(c, str)
|
||||||
|
when '\\'
|
||||||
|
selector += c + str.getc
|
||||||
|
when ','
|
||||||
|
selectors << selector.strip
|
||||||
|
selector = ""
|
||||||
|
else
|
||||||
|
selector += c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
selectors << selector.strip
|
||||||
|
end
|
||||||
|
selectors
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_square(strio)
|
||||||
|
parse_block('[', ']', strio)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_paren(strio)
|
||||||
|
parse_block('(', ')', strio)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_block(start, final, strio)
|
||||||
|
block = start
|
||||||
|
while (c = strio.getc)
|
||||||
|
case c
|
||||||
|
when final
|
||||||
|
return block + c
|
||||||
|
when '\\'
|
||||||
|
block += c + strio.getc
|
||||||
|
when '"', "'"
|
||||||
|
block += parse_string(c, strio)
|
||||||
|
else
|
||||||
|
block += c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise ArgumentError, "Invalid CSS Selector - Block end '#{final}' not found"
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_string(quote, strio)
|
||||||
|
string = quote
|
||||||
|
while (c = strio.getc)
|
||||||
|
string += c
|
||||||
|
case c
|
||||||
|
when quote
|
||||||
|
return string
|
||||||
|
when '\\'
|
||||||
|
string += strio.getc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise ArgumentError, 'Invalid CSS Selector - string end not found'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Capybara::Selector::CSS::Splitter do
|
||||||
|
let :splitter do
|
||||||
|
::Capybara::Selector::CSS::Splitter.new
|
||||||
|
end
|
||||||
|
|
||||||
|
context "split not needed" do
|
||||||
|
it "normal CSS selector" do
|
||||||
|
css = 'div[id="abc"]'
|
||||||
|
expect(splitter.split(css)).to eq [css]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "comma in strings" do
|
||||||
|
css = 'div[id="a,bc"]'
|
||||||
|
expect(splitter.split(css)).to eq [css]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "comma in pseudo-selector" do
|
||||||
|
css = 'div.class1:not(.class1, .class2)'
|
||||||
|
expect(splitter.split(css)).to eq [css]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "split needed" do
|
||||||
|
it "root level comma" do
|
||||||
|
css = 'div.class1, span, p.class2'
|
||||||
|
expect(splitter.split(css)).to eq ['div.class1', 'span', 'p.class2']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "root level comma when quotes and pseudo selectors" do
|
||||||
|
css = 'div.class1[id="abc\\"def,ghi"]:not(.class3, .class4), span[id=\'a"c\\\'de\'], section, #abc\\,def'
|
||||||
|
expect(splitter.split(css)).to eq ['div.class1[id="abc\\"def,ghi"]:not(.class3, .class4)', 'span[id=\'a"c\\\'de\']', 'section', '#abc\\,def']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue