From c3b601ebc3c1d4a1ed716055d934b54d6c710dda Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Sat, 14 Aug 2021 19:32:42 -0400 Subject: [PATCH] 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 --- History.md | 1 + lib/capybara/queries/selector_query.rb | 15 +++++++++++- lib/capybara/selector.rb | 1 + lib/capybara/spec/session/has_button_spec.rb | 24 ++++++++++++++++++++ lib/capybara/spec/session/has_field_spec.rb | 24 ++++++++++++++++++++ lib/capybara/spec/session/has_link_spec.rb | 24 ++++++++++++++++++++ lib/capybara/spec/views/form.erb | 7 ++++++ lib/capybara/spec/views/with_html.erb | 2 +- spec/rack_test_spec.rb | 1 - 9 files changed, 96 insertions(+), 3 deletions(-) diff --git a/History.md b/History.md index b663f636..03fa3dfd 100644 --- a/History.md +++ b/History.md @@ -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 diff --git a/lib/capybara/queries/selector_query.rb b/lib/capybara/queries/selector_query.rb index 502432b6..e362fc3c 100644 --- a/lib/capybara/queries/selector_query.rb +++ b/lib/capybara/queries/selector_query.rb @@ -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 diff --git a/lib/capybara/selector.rb b/lib/capybara/selector.rb index 52fe8c75..593c779c 100644 --- a/lib/capybara/selector.rb +++ b/lib/capybara/selector.rb @@ -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 # diff --git a/lib/capybara/spec/session/has_button_spec.rb b/lib/capybara/spec/session/has_button_spec.rb index 550bc9f2..04359fe1 100644 --- a/lib/capybara/spec/session/has_button_spec.rb +++ b/lib/capybara/spec/session/has_button_spec.rb @@ -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 diff --git a/lib/capybara/spec/session/has_field_spec.rb b/lib/capybara/spec/session/has_field_spec.rb index eeabd21b..6b75688e 100644 --- a/lib/capybara/spec/session/has_field_spec.rb +++ b/lib/capybara/spec/session/has_field_spec.rb @@ -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 diff --git a/lib/capybara/spec/session/has_link_spec.rb b/lib/capybara/spec/session/has_link_spec.rb index 05627eca..dc8980df 100644 --- a/lib/capybara/spec/session/has_link_spec.rb +++ b/lib/capybara/spec/session/has_link_spec.rb @@ -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 diff --git a/lib/capybara/spec/views/form.erb b/lib/capybara/spec/views/form.erb index 5c542b07..7c49abce 100644 --- a/lib/capybara/spec/views/form.erb +++ b/lib/capybara/spec/views/form.erb @@ -1,5 +1,12 @@

Form

+ + + +

diff --git a/lib/capybara/spec/views/with_html.erb b/lib/capybara/spec/views/with_html.erb index 5420b708..3bf3ebba 100644 --- a/lib/capybara/spec/views/with_html.erb +++ b/lib/capybara/spec/views/with_html.erb @@ -19,7 +19,7 @@

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod - tempor incididunt ut labore + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. diff --git a/spec/rack_test_spec.rb b/spec/rack_test_spec.rb index 0451eed4..5ee93691 100644 --- a/spec/rack_test_spec.rb +++ b/spec/rack_test_spec.rb @@ -12,7 +12,6 @@ skipped_tests = %i[ screenshot frames windows - active_element send_keys server hover