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:
Sean Doyle 2021-08-14 19:32:42 -04:00
parent ed3c947130
commit c3b601ebc3
9 changed files with 96 additions and 3 deletions

View File

@ -10,6 +10,7 @@ Release date: unreleased
* `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
* `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

View File

@ -9,7 +9,7 @@ module Capybara
SPATIAL_KEYS = %i[above below left_of right_of near].freeze
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
def initialize(*args,
@ -73,6 +73,8 @@ module Capybara
desc << " with id #{options[:id]}" if options[:id]
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]
when String
@ -435,6 +437,7 @@ module Capybara
matches_id_filter?(node) &&
matches_class_filter?(node) &&
matches_style_filter?(node) &&
matches_focused_filter?(node) &&
matches_text_filter?(node) &&
matches_exact_text_filter?(node)
end
@ -494,6 +497,16 @@ module Capybara
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?
case options[:class]
when Regexp then true

View File

@ -14,6 +14,7 @@ require 'capybara/selector/definition'
# * :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
# * :near (Element) - Match elements near (within 50px) the passed element on the page
# * :focused (Boolean) - Match elements with focus (requires JavaScript)
#
# ### Built-in Selectors
#

View File

@ -65,6 +65,18 @@ Capybara::SpecHelper.spec '#has_button?' do
it 'should not affect other selectors when enable_aria_role: false' do
expect(@session).to have_button('Click me!', enable_aria_role: false)
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
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
expect(@session).to have_no_button('Junk button that does not exist', enable_aria_role: false)
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

View File

@ -110,6 +110,18 @@ Capybara::SpecHelper.spec '#has_field' do
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
it 'should be true if field is valid' do
@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')
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
Capybara::SpecHelper.spec '#has_checked_field?' do

View File

@ -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/)
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
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: %r{/nonexistent-href})
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

View File

@ -1,5 +1,12 @@
<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>
<p>

View File

@ -19,7 +19,7 @@
<p class="para" id="first" data-random="abc\def" style="line-height: 25px;">
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,
quis nostrud exercitation <a href="/foo" id="foo" data-test-id="test-foo">ullamco</a> laboris nisi
ut aliquip ex ea commodo consequat.

View File

@ -12,7 +12,6 @@ skipped_tests = %i[
screenshot
frames
windows
active_element
send_keys
server
hover