Testing XPath Builder Attribute Handling in Capybara
This test suite validates the XPathBuilder functionality in Capybara’s selector system, focusing on attribute condition handling and XPath generation for DOM element selection.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
teamcapybara/capybara
spec/xpath_builder_spec.rb
# frozen_string_literal: true
require 'spec_helper'
# rubocop:disable RSpec/InstanceVariable
RSpec.describe Capybara::Selector::XPathBuilder do
let :builder do
described_class.new(@xpath)
end
context 'add_attribute_conditions' do
it 'adds a single string condition to a single selector' do
@xpath = './/div'
selector = builder.add_attribute_conditions(random: 'abc')
expect(selector).to eq %((.//div)[(./@random = 'abc')])
end
it 'adds multiple string conditions to a single selector' do
@xpath = './/div'
selector = builder.add_attribute_conditions(random: 'abc', other: 'def')
expect(selector).to eq %(((.//div)[(./@random = 'abc')])[(./@other = 'def')])
end
it 'adds a single string condition to a multiple selector' do
@xpath = XPath.descendant(:div, :ul)
selector = builder.add_attribute_conditions(random: 'abc')
expect(selector.to_s).to eq @xpath[XPath.attr(:random) == 'abc'].to_s
end
it 'adds multiple string conditions to a multiple selector' do
@xpath = XPath.descendant(:div, :ul)
selector = builder.add_attribute_conditions(random: 'abc', other: 'def')
expect(selector.to_s).to eq %(.//*[self::div | self::ul][(./@random = 'abc')][(./@other = 'def')])
end
it 'adds simple regexp conditions to a single selector' do
@xpath = XPath.descendant(:div)
selector = builder.add_attribute_conditions(random: /abc/, other: /def/)
expect(selector.to_s).to eq %(.//div[./@random[contains(., 'abc')]][./@other[contains(., 'def')]])
end
it 'adds wildcard regexp conditions to a single selector' do
@xpath = './/div'
selector = builder.add_attribute_conditions(random: /abc.*def/, other: /def.*ghi/)
expect(selector).to eq %(((.//div)[./@random[(contains(., 'abc') and contains(., 'def'))]])[./@other[(contains(., 'def') and contains(., 'ghi'))]])
end
it 'adds alternated regexp conditions to a single selector' do
@xpath = XPath.descendant(:div)
selector = builder.add_attribute_conditions(random: /abc|def/, other: /def|ghi/)
expect(selector.to_s).to eq %(.//div[./@random[(contains(., 'abc') or contains(., 'def'))]][./@other[(contains(., 'def') or contains(., 'ghi'))]])
end
it 'adds alternated regexp conditions to a multiple selector' do
@xpath = XPath.descendant(:div, :ul)
selector = builder.add_attribute_conditions(other: /def.*ghi|jkl/)
expect(selector.to_s).to eq %(.//*[self::div | self::ul][./@other[((contains(., 'def') and contains(., 'ghi')) or contains(., 'jkl'))]])
end
it "returns original selector when regexp can't be substringed" do
@xpath = './/div'
selector = builder.add_attribute_conditions(other: /.+/)
expect(selector).to eq '(.//div)[./@other]'
end
context ':class' do
it 'handles string' do
@xpath = './/a'
selector = builder.add_attribute_conditions(class: 'my_class')
expect(selector).to eq %((.//a)[contains(concat(' ', normalize-space(./@class), ' '), ' my_class ')])
end
it 'handles negated strings' do
@xpath = XPath.descendant(:a)
selector = builder.add_attribute_conditions(class: '!my_class')
expect(selector.to_s).to eq @xpath[!XPath.attr(:class).contains_word('my_class')].to_s
end
it 'handles array of strings' do
@xpath = './/a'
selector = builder.add_attribute_conditions(class: %w[my_class my_other_class])
expect(selector).to eq %((.//a)[(contains(concat(' ', normalize-space(./@class), ' '), ' my_class ') and contains(concat(' ', normalize-space(./@class), ' '), ' my_other_class '))])
end
it 'handles array of string when negated included' do
@xpath = XPath.descendant(:a)
selector = builder.add_attribute_conditions(class: %w[my_class !my_other_class])
expect(selector.to_s).to eq @xpath[XPath.attr(:class).contains_word('my_class') & !XPath.attr(:class).contains_word('my_other_class')].to_s
end
end
end
end
# rubocop:enable RSpec/InstanceVariable