Add support for `focused:` filter
[Follow-up to #2489][], adds support for filtering fields, links, and buttons based on whether or not they're focused. [Follow-up to #2489]: https://github.com/teamcapybara/capybara/pull/2489
This commit is contained in:
parent
ed3c947130
commit
c3b601ebc3
|
@ -10,6 +10,7 @@ Release date: unreleased
|
||||||
* `allow_label_click` accepts click options to be used when clicking an associated label
|
* `allow_label_click` accepts click options to be used when clicking an associated label
|
||||||
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTL5 when available
|
* Deprecated `allow_gumbo=` in favor of `use_html5_parsing=` to enable use of Nokogiri::HTL5 when available
|
||||||
* `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
|
* `Session#active_element` returns the element with focus - Not supported by the `RackTest` driver [Sean Doyle]
|
||||||
|
* Support `focused:` filter for finding interactive elements - Not supported by the `RackTest` driver [Sean Doyle]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ module Capybara
|
||||||
|
|
||||||
SPATIAL_KEYS = %i[above below left_of right_of near].freeze
|
SPATIAL_KEYS = %i[above below left_of right_of near].freeze
|
||||||
VALID_KEYS = SPATIAL_KEYS + COUNT_KEYS +
|
VALID_KEYS = SPATIAL_KEYS + COUNT_KEYS +
|
||||||
%i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set]
|
%i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set focused]
|
||||||
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
VALID_MATCH = %i[first smart prefer_exact one].freeze
|
||||||
|
|
||||||
def initialize(*args,
|
def initialize(*args,
|
||||||
|
@ -73,6 +73,8 @@ module Capybara
|
||||||
|
|
||||||
desc << " with id #{options[:id]}" if options[:id]
|
desc << " with id #{options[:id]}" if options[:id]
|
||||||
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
desc << " with classes [#{Array(options[:class]).join(',')}]" if options[:class]
|
||||||
|
desc << ' that is focused' if options[:focused]
|
||||||
|
desc << ' that is not focused' if options[:focused] == false
|
||||||
|
|
||||||
desc << case options[:style]
|
desc << case options[:style]
|
||||||
when String
|
when String
|
||||||
|
@ -435,6 +437,7 @@ module Capybara
|
||||||
matches_id_filter?(node) &&
|
matches_id_filter?(node) &&
|
||||||
matches_class_filter?(node) &&
|
matches_class_filter?(node) &&
|
||||||
matches_style_filter?(node) &&
|
matches_style_filter?(node) &&
|
||||||
|
matches_focused_filter?(node) &&
|
||||||
matches_text_filter?(node) &&
|
matches_text_filter?(node) &&
|
||||||
matches_exact_text_filter?(node)
|
matches_exact_text_filter?(node)
|
||||||
end
|
end
|
||||||
|
@ -494,6 +497,16 @@ module Capybara
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def matches_focused_filter?(node)
|
||||||
|
if options.key?(:focused)
|
||||||
|
node_is_focused = node == node.session.active_element
|
||||||
|
|
||||||
|
node_is_focused == options[:focused]
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def need_to_process_classes?
|
def need_to_process_classes?
|
||||||
case options[:class]
|
case options[:class]
|
||||||
when Regexp then true
|
when Regexp then true
|
||||||
|
|
|
@ -14,6 +14,7 @@ require 'capybara/selector/definition'
|
||||||
# * :left_of (Element) - Match elements left of the passed element on the page
|
# * :left_of (Element) - Match elements left of the passed element on the page
|
||||||
# * :right_of (Element) - Match elements right of the passed element on the page
|
# * :right_of (Element) - Match elements right of the passed element on the page
|
||||||
# * :near (Element) - Match elements near (within 50px) the passed element on the page
|
# * :near (Element) - Match elements near (within 50px) the passed element on the page
|
||||||
|
# * :focused (Boolean) - Match elements with focus (requires JavaScript)
|
||||||
#
|
#
|
||||||
# ### Built-in Selectors
|
# ### Built-in Selectors
|
||||||
#
|
#
|
||||||
|
|
|
@ -65,6 +65,18 @@ Capybara::SpecHelper.spec '#has_button?' do
|
||||||
it 'should not affect other selectors when enable_aria_role: false' do
|
it 'should not affect other selectors when enable_aria_role: false' do
|
||||||
expect(@session).to have_button('Click me!', enable_aria_role: false)
|
expect(@session).to have_button('Click me!', enable_aria_role: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused:', requires: [:js] do
|
||||||
|
it 'should be true if a field has focus when focused: true' do
|
||||||
|
@session.send_keys(:tab)
|
||||||
|
|
||||||
|
expect(@session).to have_button('A Button', focused: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if a field does not have focus when focused: false' do
|
||||||
|
expect(@session).not_to have_button('A Button', focused: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara::SpecHelper.spec '#has_no_button?' do
|
Capybara::SpecHelper.spec '#has_no_button?' do
|
||||||
|
@ -117,4 +129,16 @@ Capybara::SpecHelper.spec '#has_no_button?' do
|
||||||
it 'should not affect other selectors when enable_aria_role: false' do
|
it 'should not affect other selectors when enable_aria_role: false' do
|
||||||
expect(@session).to have_no_button('Junk button that does not exist', enable_aria_role: false)
|
expect(@session).to have_no_button('Junk button that does not exist', enable_aria_role: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused:', requires: [:js] do
|
||||||
|
it 'should be true if a field has focus when focused: true' do
|
||||||
|
expect(@session).to have_no_button('A Button', focused: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if a field does not have focus when focused: false' do
|
||||||
|
@session.send_keys(:tab)
|
||||||
|
|
||||||
|
expect(@session).to have_no_button('A Button', focused: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,6 +110,18 @@ Capybara::SpecHelper.spec '#has_field' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused', requires: [:js] do
|
||||||
|
it 'should be true if a field has focus' do
|
||||||
|
2.times { @session.send_keys(:tab) }
|
||||||
|
|
||||||
|
expect(@session).to have_field('An Input', focused: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if a field does not have focus' do
|
||||||
|
expect(@session).to have_field('An Input', focused: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with valid', requires: [:js] do
|
context 'with valid', requires: [:js] do
|
||||||
it 'should be true if field is valid' do
|
it 'should be true if field is valid' do
|
||||||
@session.fill_in 'required', with: 'something'
|
@session.fill_in 'required', with: 'something'
|
||||||
|
@ -184,6 +196,18 @@ Capybara::SpecHelper.spec '#has_no_field' do
|
||||||
expect(@session).to have_no_field('Languages', type: 'textarea')
|
expect(@session).to have_no_field('Languages', type: 'textarea')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused', requires: [:js] do
|
||||||
|
it 'should be true if a field does not have focus' do
|
||||||
|
expect(@session).to have_field('An Input', focused: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if a field has focus' do
|
||||||
|
2.times { @session.send_keys(:tab) }
|
||||||
|
|
||||||
|
expect(@session).to have_field('An Input', focused: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara::SpecHelper.spec '#has_checked_field?' do
|
Capybara::SpecHelper.spec '#has_checked_field?' do
|
||||||
|
|
|
@ -18,6 +18,18 @@ Capybara::SpecHelper.spec '#has_link?' do
|
||||||
expect(@session).not_to have_link('A link', href: '/nonexistent-href')
|
expect(@session).not_to have_link('A link', href: '/nonexistent-href')
|
||||||
expect(@session).not_to have_link('A link', href: /nonexistent/)
|
expect(@session).not_to have_link('A link', href: /nonexistent/)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused:', requires: [:js] do
|
||||||
|
it 'should be true if the given link is on the page and has focus' do
|
||||||
|
@session.send_keys(:tab)
|
||||||
|
|
||||||
|
expect(@session).to have_link('labore', focused: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if the given link is on the page and does not have focus' do
|
||||||
|
expect(@session).to have_link('labore', focused: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara::SpecHelper.spec '#has_no_link?' do
|
Capybara::SpecHelper.spec '#has_no_link?' do
|
||||||
|
@ -36,4 +48,16 @@ Capybara::SpecHelper.spec '#has_no_link?' do
|
||||||
expect(@session).to have_no_link('A link', href: '/nonexistent-href')
|
expect(@session).to have_no_link('A link', href: '/nonexistent-href')
|
||||||
expect(@session).to have_no_link('A link', href: %r{/nonexistent-href})
|
expect(@session).to have_no_link('A link', href: %r{/nonexistent-href})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with focused:', requires: [:js] do
|
||||||
|
it 'should be true if the given link is on the page and has focus' do
|
||||||
|
expect(@session).to have_no_link('labore', focused: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should be false if the given link is on the page and does not have focus' do
|
||||||
|
@session.send_keys(:tab)
|
||||||
|
|
||||||
|
expect(@session).to have_no_link('labore', focused: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<h1>Form</h1>
|
<h1>Form</h1>
|
||||||
|
|
||||||
|
<button type="button" tabindex="1">A Button</button>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
An Input
|
||||||
|
<input type="text" tabindex="2">
|
||||||
|
</label>
|
||||||
|
|
||||||
<form action="/form" method="post" novalidate>
|
<form action="/form" method="post" novalidate>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<p class="para" id="first" data-random="abc\def" style="line-height: 25px;">
|
<p class="para" id="first" data-random="abc\def" style="line-height: 25px;">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
||||||
tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple">labore</a>
|
tempor incididunt ut <a href="/with_simple_html" title="awesome title" class="simple" tabindex="1">labore</a>
|
||||||
et dolore magna aliqua. Ut enim ad minim veniam,
|
et dolore magna aliqua. Ut enim ad minim veniam,
|
||||||
quis nostrud exercitation <a href="/foo" id="foo" data-test-id="test-foo">ullamco</a> laboris nisi
|
quis nostrud exercitation <a href="/foo" id="foo" data-test-id="test-foo">ullamco</a> laboris nisi
|
||||||
ut aliquip ex ea commodo consequat.
|
ut aliquip ex ea commodo consequat.
|
||||||
|
|
|
@ -12,7 +12,6 @@ skipped_tests = %i[
|
||||||
screenshot
|
screenshot
|
||||||
frames
|
frames
|
||||||
windows
|
windows
|
||||||
active_element
|
|
||||||
send_keys
|
send_keys
|
||||||
server
|
server
|
||||||
hover
|
hover
|
||||||
|
|
Loading…
Reference in New Issue