From 7e9ae081b309cf70771e90ff0c839e36800cba1f Mon Sep 17 00:00:00 2001 From: Federico Franco Date: Sun, 1 Feb 2026 17:32:36 +0900 Subject: [PATCH] [rb] fix select being able to select options hidden by css rules --- rb/lib/selenium/webdriver/support/select.rb | 19 +++++++++++++++++++ .../selenium/webdriver/select_spec.rb | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/rb/lib/selenium/webdriver/support/select.rb b/rb/lib/selenium/webdriver/support/select.rb index f5ad753f14914..aa2ec5a0d9d24 100644 --- a/rb/lib/selenium/webdriver/support/select.rb +++ b/rb/lib/selenium/webdriver/support/select.rb @@ -217,10 +217,20 @@ def deselect_by_index(index) def select_option(option) raise Error::UnsupportedOperationError, 'You may not select a disabled option' unless option.enabled? + unless css_property_and_visible?(option) + raise Error::ElementNotInteractableError, + 'You may not select an invisible option' + end + option.click unless option.selected? end def deselect_option(option) + unless css_property_and_visible?(option) + raise Error::ElementNotInteractableError, + 'You may not deselect an invisible option' + end + option.click if option.selected? end @@ -266,6 +276,15 @@ def find_by_index(index) def find_by_value(value) @element.find_elements(xpath: ".//option[@value = #{Escaper.escape value}]") end + + def css_property_and_visible?(element) + css_value_candidates = %w[hidden none 0 0.0].to_set + css_property_candidates = %w[visibility display opacity] + + css_property_candidates.none? do |property| + css_value_candidates.include?(element.css_value(property)) + end + end end # Select end # Support end # WebDriver diff --git a/rb/spec/integration/selenium/webdriver/select_spec.rb b/rb/spec/integration/selenium/webdriver/select_spec.rb index dbea49e93584a..d7e78b800f770 100644 --- a/rb/spec/integration/selenium/webdriver/select_spec.rb +++ b/rb/spec/integration/selenium/webdriver/select_spec.rb @@ -27,6 +27,7 @@ module Support let(:multi_select) { described_class.new(driver.find_element(id: 'multi')) } let(:single_disabled) { described_class.new(driver.find_element(name: 'single_disabled')) } let(:multi_disabled) { described_class.new(driver.find_element(name: 'multi_disabled')) } + let(:multi_invisible) { described_class.new(driver.find_element(id: 'invisible_multi_select')) } before { driver.navigate.to url_for('formPage.html') } after { reset_driver! if GlobalTestEnv.rbe? && GlobalTestEnv.browser == :chrome } @@ -120,6 +121,14 @@ module Support it 'errors when not found' do expect { multi_select.select_by(:text, 'invalid') }.to raise_exception(Error::NoSuchElementError) end + + it 'errors when option is invisible', :aggregate_failures do + %w[Apples Pears Oranges Lemons].each do |text| + expect { + multi_invisible.select_by(:text, text) + }.to raise_exception(Error::ElementNotInteractableError) + end + end end context 'when by index' do @@ -298,6 +307,14 @@ module Support it 'errors when not found' do expect { multi_select.deselect_by(:text, 'invalid') }.to raise_exception(Error::NoSuchElementError) end + + it 'errors when option is invisible', :aggregate_failures do + %w[Apples Pears Oranges Lemons].each do |text| + expect { + multi_invisible.deselect_by(:text, text) + }.to raise_exception(Error::ElementNotInteractableError) + end + end end context 'when by index' do