diff --git a/lib/capybara/selector/builders/css_builder.rb b/lib/capybara/selector/builders/css_builder.rb index a116e58f..9d612992 100644 --- a/lib/capybara/selector/builders/css_builder.rb +++ b/lib/capybara/selector/builders/css_builder.rb @@ -38,7 +38,7 @@ module Capybara attribute_conditions(class: classes) else cls = Array(classes).group_by { |cl| cl.start_with?('!') && !cl.start_with?('!!!') } - (cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/,''))}" } + + (cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } + cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..-1))})" }).join end end diff --git a/lib/capybara/selector/builders/xpath_builder.rb b/lib/capybara/selector/builders/xpath_builder.rb index 897065c0..a491110e 100644 --- a/lib/capybara/selector/builders/xpath_builder.rb +++ b/lib/capybara/selector/builders/xpath_builder.rb @@ -33,7 +33,7 @@ module Capybara if klass.start_with?('!') && !klass.start_with?('!!!') !XPath.attr(:class).contains_word(klass.slice(1..-1)) else - XPath.attr(:class).contains_word(klass.sub(/^!!/,'')) + XPath.attr(:class).contains_word(klass.sub(/^!!/, '')) end end.reduce(:&) end diff --git a/lib/capybara/selector/selector.rb b/lib/capybara/selector/selector.rb index 642a9d97..2e8d4b55 100644 --- a/lib/capybara/selector/selector.rb +++ b/lib/capybara/selector/selector.rb @@ -227,12 +227,7 @@ module Capybara # @return [#call] The block that will be called to generate the XPath expression # def xpath(*allowed_filters, &block) - if block - @format, @expression = :xpath, block - allowed_filters = parameter_names(block) if allowed_filters.empty? - allowed_filters.flatten.each { |ef| expression_filters[ef] = Filters::IdentityExpressionFilter.new(ef) } - end - format == :xpath ? @expression : nil + expression(:xpath, allowed_filters, &block) end ## @@ -250,12 +245,7 @@ module Capybara # @return [#call] The block that will be called to generate the CSS selector # def css(*allowed_filters, &block) - if block - @format, @expression = :css, block - allowed_filters = parameter_names(block) if allowed_filters.empty? - allowed_filters.flatten.each { |ef| expression_filters[ef] = Filters::IdentityExpressionFilter.new(ef) } - end - format == :css ? @expression : nil + expression(:css, allowed_filters, &block) end ## @@ -465,6 +455,15 @@ module Capybara def parameter_names(block) block.parameters.select { |(type, _name)| %i[key keyreq].include? type }.map { |(_type, name)| name } end + + def expression(type, allowed_filters, &block) + if block + @format, @expression = type, block + allowed_filters = parameter_names(block) if allowed_filters.empty? + allowed_filters.flatten.each { |ef| expression_filters[ef] = Filters::IdentityExpressionFilter.new(ef) } + end + format == type ? @expression : nil + end end end diff --git a/spec/selector_spec.rb b/spec/selector_spec.rb index 146ac11b..20235f01 100644 --- a/spec/selector_spec.rb +++ b/spec/selector_spec.rb @@ -253,7 +253,7 @@ RSpec.describe Capybara do expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!cc', '!dd', 'bb']).size).to eq 1 end - it 'handles classes starting with ! by requiring negated negated first', :focus_ do + it 'handles classes starting with ! by requiring negated negated first' do expect(string.all(:custom_css_selector, 'div, p', class: ['!!!mine']).size).to eq 1 expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!!!mine']).size).to eq 1 end