Support Regexp for `exact_text` option

This commit is contained in:
Thomas Walpole 2022-05-07 16:50:27 -07:00
parent 2fece8d516
commit 5c8e975d5c
4 changed files with 37 additions and 8 deletions

View File

@ -10,6 +10,7 @@ Release date: unreleased
* [Beta] CSP nonces inserted into animation disabler additions - Issue #2542
* Support `<base>` element in rack-test driver - ISsue #2544
* [Beta] `Element#shadow_root` support. Requires selenium-webdriver 4.1+. Only currently supported with Chrome when using the selenium driver. Note: only CSS can be used to find elements from the shadow root. Therefore you won't be able to use most Capybara helper methods (`fill_in`, `click_link`, `find_field`, etc) directly from the shadow root since those locators are built using XPath. If you first locate a descendant from the shadow root using CSS then you should be able to use all the Capybara methods from there.
* Regexp now supported for `exact_text` finder option
### Fixed

View File

@ -18,7 +18,7 @@ module Capybara
#
# @!macro system_filters
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
# @option options [String, Boolean] exact_text
# @option options [String, Regexp, String] exact_text
# When String the elements contained text must match exactly, when Boolean controls whether the `text` option must match exactly.
# Defaults to {Capybara.configure exact_text}.
# @option options [Boolean] normalize_ws

View File

@ -27,6 +27,12 @@ module Capybara
@order = order
@filter_cache = Hash.new { |hsh, key| hsh[key] = {} }
if @options[:text].is_a?(Regexp) && [true, false].include?(@options[:exact_text])
Capybara::Helpers.warn(
"Boolean 'exact_text' option is not supported when 'text' option is a Regexp - ignoring"
)
end
super(@options)
self.session_options = session_options
@ -541,16 +547,19 @@ module Capybara
def matches_text_filter?(node)
value = options[:text]
return true unless value
return matches_text_exactly?(node, value) if exact_text == true
return matches_text_exactly?(node, value) if exact_text == true && !value.is_a?(Regexp)
regexp = value.is_a?(Regexp) ? value : Regexp.escape(value.to_s)
matches_text_regexp?(node, regexp)
end
def matches_exact_text_filter?(node)
return true unless exact_text.is_a?(String)
matches_text_exactly?(node, exact_text)
case exact_text
when String, Regexp
matches_text_exactly?(node, exact_text)
else
true
end
end
def matches_visibility_filters?(node)
@ -578,17 +587,21 @@ module Capybara
def matches_text_exactly?(node, value)
regexp = value.is_a?(Regexp) ? value : /\A#{Regexp.escape(value.to_s)}\z/
matches_text_regexp?(node, regexp)
matches_text_regexp(node, regexp).then { |m| m&.pre_match == '' && m&.post_match == '' }
end
def normalize_ws
options.fetch(:normalize_ws, session_options.default_normalize_ws)
end
def matches_text_regexp?(node, regexp)
def matches_text_regexp(node, regexp)
text_visible = visible
text_visible = :all if text_visible == :hidden
node.text(text_visible, normalize_ws: normalize_ws).match?(regexp)
node.text(text_visible, normalize_ws: normalize_ws).match(regexp)
end
def matches_text_regexp?(node, regexp)
!matches_text_regexp(node, regexp).nil?
end
def default_visibility

View File

@ -117,6 +117,21 @@ Capybara::SpecHelper.spec '#has_selector?' do
expect(@session).to have_selector(:id, 'h2one', text: 'Header Class Test One', exact_text: false)
expect(@session).to have_selector(:id, 'h2one', text: 'Header Class Test', exact_text: false)
end
it 'should warn if text option is a regexp that it is ignoring exact_text' do
allow(Capybara::Helpers).to receive(:warn)
expect(@session).to have_selector(:id, 'h2one', text: /Class Test/, exact_text: true)
expect(Capybara::Helpers).to have_received(:warn).with(/'exact_text' option is not supported/)
end
end
context 'regexp' do
it 'should only match when it fully matches' do
expect(@session).to have_selector(:id, 'h2one', exact_text: /Header Class Test One/)
expect(@session).to have_no_selector(:id, 'h2one', exact_text: /Header Class Test/)
expect(@session).to have_no_selector(:id, 'h2one', exact_text: /Class Test One/)
expect(@session).to have_no_selector(:id, 'h2one', exact_text: /Class Test/)
end
end
end