From afb3d9693ae43569de93d5ea516b10e0d3b127b4 Mon Sep 17 00:00:00 2001 From: aguspe Date: Sat, 22 Jun 2024 20:56:09 +0200 Subject: [PATCH 1/6] Add support for self generated urls in all error messages --- rb/lib/selenium/webdriver/common/error.rb | 43 ++++++++++--------- .../lib/selenium/webdriver/common/error.rbs | 5 +++ .../selenium/webdriver/error_spec.rb | 11 +++++ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/rb/lib/selenium/webdriver/common/error.rb b/rb/lib/selenium/webdriver/common/error.rb index 69adfe4e2fe35..097f070b20e4d 100644 --- a/rb/lib/selenium/webdriver/common/error.rb +++ b/rb/lib/selenium/webdriver/common/error.rb @@ -37,17 +37,30 @@ def self.for_error(error) SUPPORT_MSG = 'For documentation on this error, please visit:' ERROR_URL = 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors' - class WebDriverError < StandardError; end + class WebDriverError < StandardError + + def initialize(msg = '') + super("#{msg}; #{SUPPORT_MSG} #{url}") + end + + def url + first_word, rest = parsed_class_name.split(/(?=[A-Z])/, 2) + rest_with_hyphens = rest.gsub(/([A-Z])/, '-\1') + "#{ERROR_URL}##{first_word.downcase}#{rest_with_hyphens.downcase.sub('error', 'exception')}" + end + + private + + def parsed_class_name + self.class.to_s.sub('Selenium::WebDriver::Error::', '') + end + end # # An element could not be located on the page using the given search parameters. # - class NoSuchElementError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#no-such-element-exception") - end - end + class NoSuchElementError < WebDriverError; end # # A command to switch to a frame could not be satisfied because the frame could not be found. @@ -65,11 +78,7 @@ class UnknownCommandError < WebDriverError; end # A command failed because the referenced element is no longer attached to the DOM. # - class StaleElementReferenceError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#stale-element-reference-exception") - end - end + class StaleElementReferenceError < WebDriverError; end # # A command failed because the referenced shadow root is no longer attached to the DOM. @@ -143,11 +152,7 @@ class ScriptTimeoutError < WebDriverError; end # Argument was an invalid selector. # - class InvalidSelectorError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#invalid-selector-exception") - end - end + class InvalidSelectorError < WebDriverError; end # # A new session could not be created. @@ -232,11 +237,7 @@ class UnsupportedOperationError < WebDriverError; end # Indicates that driver was not specified and could not be located. # - class NoSuchDriverError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}/driver_location") - end - end + class NoSuchDriverError < WebDriverError; end end # Error end # WebDriver end # Selenium diff --git a/rb/sig/lib/selenium/webdriver/common/error.rbs b/rb/sig/lib/selenium/webdriver/common/error.rbs index cf084eaa6a4c3..52b388374f85b 100644 --- a/rb/sig/lib/selenium/webdriver/common/error.rbs +++ b/rb/sig/lib/selenium/webdriver/common/error.rbs @@ -8,6 +8,11 @@ module Selenium ERROR_URL: String class WebDriverError < StandardError + def url: -> String + + private + + def parsed_class_name: -> String end class NoSuchElementError < WebDriverError diff --git a/rb/spec/integration/selenium/webdriver/error_spec.rb b/rb/spec/integration/selenium/webdriver/error_spec.rb index 577df2c64d2d7..f1fde6bab9471 100644 --- a/rb/spec/integration/selenium/webdriver/error_spec.rb +++ b/rb/spec/integration/selenium/webdriver/error_spec.rb @@ -22,6 +22,8 @@ module Selenium module WebDriver describe Error do + let(:base_url) { 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors' } + it 'raises an appropriate error' do driver.navigate.to url_for('xhtmlTest.html') @@ -29,6 +31,15 @@ module WebDriver driver.find_element(id: 'nonexistent') }.to raise_error(WebDriver::Error::NoSuchElementError) end + + context 'with self generated url' do + it 'provides the right url for NoSuchElementError' do + driver.navigate.to url_for('xhtmlTest.html') + driver.find_element(id: 'nonexistent') + rescue WebDriver::Error::NoSuchElementError => e + expect(e.message).to include("#{base_url}#no-such-element-exception") + end + end end end # WebDriver end # Selenium From 9e40089d038fb96b92052061362e62b574342029 Mon Sep 17 00:00:00 2001 From: aguspe Date: Sun, 23 Jun 2024 20:38:33 +0200 Subject: [PATCH 2/6] Add more error tests for self generated urls --- .../selenium/webdriver/error_spec.rb | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/rb/spec/integration/selenium/webdriver/error_spec.rb b/rb/spec/integration/selenium/webdriver/error_spec.rb index d22feffd459b9..7d7f7279fe3ef 100644 --- a/rb/spec/integration/selenium/webdriver/error_spec.rb +++ b/rb/spec/integration/selenium/webdriver/error_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Error, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do + describe Error, exclusive: { bidi: false, reason: 'Not yet implemented with BiDi' } do let(:base_url) { 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors' } it 'raises an appropriate error' do @@ -35,9 +35,42 @@ module WebDriver context 'with self generated url' do it 'provides the right url for NoSuchElementError' do driver.navigate.to url_for('xhtmlTest.html') - driver.find_element(id: 'nonexistent') - rescue WebDriver::Error::NoSuchElementError => e - expect(e.message).to include("#{base_url}#no-such-element-exception") + + expect { + driver.find_element(id: 'nonexistent') + }.to raise_error(Selenium::WebDriver::Error::NoSuchElementError, /#{base_url}#no-such-element-exception/) + end + + it 'provides the right url for StaleElementReferenceError' do + driver.navigate.to url_for('formPage.html') + button = driver.find_element(id: 'imageButton') + driver.navigate.refresh + + expect { + button.click + }.to raise_error(Selenium::WebDriver::Error::StaleElementReferenceError, + /#{base_url}#stale-element-reference-exception/) + end + + it 'provides the right url for UnknownError' do + driver.network_conditions = { offline: false, latency: 56, download_throughput: 789, upload_throughput: 600 } + driver.delete_network_conditions + + expect { + driver.network_conditions + }.to raise_error(Selenium::WebDriver::Error::UnknownError, /#{base_url}#unknown-exception/) + end + + it 'provides the right url for NoSuchAlertError' do + expect { + driver.switch_to.alert + }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError, /#{base_url}#no-such-alert-exception/) + end + + it 'provides the right url for ScriptTimeoutError' do + expect { + driver.execute_async_script 'return 1 + 2;' + }.to raise_error(Selenium::WebDriver::Error::ScriptTimeoutError, /#{base_url}#script-timeout-exception/) end end end From b0dea33925e310ff2ab019b40d537807e7bcc6b0 Mon Sep 17 00:00:00 2001 From: aguspe Date: Mon, 24 Jun 2024 20:28:37 +0200 Subject: [PATCH 3/6] Added extra url tests to validate right url generated --- .../selenium/webdriver/error_spec.rb | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/rb/spec/integration/selenium/webdriver/error_spec.rb b/rb/spec/integration/selenium/webdriver/error_spec.rb index 7d7f7279fe3ef..9b36528a0c6e4 100644 --- a/rb/spec/integration/selenium/webdriver/error_spec.rb +++ b/rb/spec/integration/selenium/webdriver/error_spec.rb @@ -72,6 +72,51 @@ module WebDriver driver.execute_async_script 'return 1 + 2;' }.to raise_error(Selenium::WebDriver::Error::ScriptTimeoutError, /#{base_url}#script-timeout-exception/) end + + it 'provides the right url for NoSuchWindowError' do + expect { + driver.switch_to.window 'nonexistent' + }.to raise_error(Selenium::WebDriver::Error::NoSuchWindowError, /#{base_url}#no-such-window-exception/) + end + + it 'provides the right url for JavascriptError' do + driver.navigate.to url_for('xhtmlTest.html') + expect { + driver.execute_script('return squiggle();') + }.to raise_error(Selenium::WebDriver::Error::JavascriptError, /#{base_url}#javascript-exception/) + end + + it 'provides the right url for TimeoutError' do + block = -> { raise Error::NoSuchElementError, 'foo' } + + expect { + wait(0.5).until(&block) + }.to raise_error(Error::TimeoutError, /#{base_url}#timeout-exception/) + end + end + + it 'provides the right url for NoSuchShadowRootError' do + driver.navigate.to url_for('simpleTest.html') + div = driver.find_element(css: 'div') + expect { + div.shadow_root + }.to raise_error(Error::NoSuchShadowRootError, /#{base_url}#no-such-shadow-root-exception/) + end + + it 'provides the right url for InvalidCookieDomainError' do + expect { + driver.manage.add_cookie name: 'domain', + value: 'different', + domain: 'selenium.dev' + }.to raise_error(Error::InvalidCookieDomainError, /#{base_url}#invalid-cookie-domain-exception/) + end + + it 'provides the right url for InvalidSelectorError', + exclude: {browser: %i[safari safari_preview], reason: 'Safari TimeoutError'} do + driver.navigate.to url_for('xhtmlTest.html') + expect { + driver.find_element(xpath: '*?//-') + }.to raise_error(Error::InvalidSelectorError, /#{base_url}#invalid-selector-exception/) end end end # WebDriver From b48ddf612d87f435b992679987cdd3e09d68da54 Mon Sep 17 00:00:00 2001 From: aguspe Date: Tue, 25 Jun 2024 18:09:25 +0200 Subject: [PATCH 4/6] Re-implement error tests and fix rubocop --- rb/lib/selenium/webdriver/common/error.rb | 48 +++++----- .../selenium/webdriver/error_spec.rb | 93 +------------------ .../webdriver/common/driver_finder_spec.rb | 3 +- 3 files changed, 30 insertions(+), 114 deletions(-) diff --git a/rb/lib/selenium/webdriver/common/error.rb b/rb/lib/selenium/webdriver/common/error.rb index 097f070b20e4d..850828d84ebcb 100644 --- a/rb/lib/selenium/webdriver/common/error.rb +++ b/rb/lib/selenium/webdriver/common/error.rb @@ -37,30 +37,24 @@ def self.for_error(error) SUPPORT_MSG = 'For documentation on this error, please visit:' ERROR_URL = 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors' - class WebDriverError < StandardError + URLS = { + NoSuchElementError: "#{ERROR_URL}#no-such-element-exception", + StaleElementReferenceError: "#{ERROR_URL}#stale-element-reference-exception", + InvalidSelectorError: "#{ERROR_URL}#invalid-selector-exception", + NoSuchDriverError: "#{ERROR_URL}/driver_location" + }.freeze - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{url}") - end - - def url - first_word, rest = parsed_class_name.split(/(?=[A-Z])/, 2) - rest_with_hyphens = rest.gsub(/([A-Z])/, '-\1') - "#{ERROR_URL}##{first_word.downcase}#{rest_with_hyphens.downcase.sub('error', 'exception')}" - end - - private - - def parsed_class_name - self.class.to_s.sub('Selenium::WebDriver::Error::', '') - end - end + class WebDriverError < StandardError; end # # An element could not be located on the page using the given search parameters. # - class NoSuchElementError < WebDriverError; end + class NoSuchElementError < WebDriverError + def initialize(msg = '') + super("#{msg}; #{SUPPORT_MSG} #{URLS[:NoSuchElementError]}") + end + end # # A command to switch to a frame could not be satisfied because the frame could not be found. @@ -78,7 +72,11 @@ class UnknownCommandError < WebDriverError; end # A command failed because the referenced element is no longer attached to the DOM. # - class StaleElementReferenceError < WebDriverError; end + class StaleElementReferenceError < WebDriverError + def initialize(msg = '') + super("#{msg}; #{SUPPORT_MSG} #{URLS[:StaleElementReferenceError]}") + end + end # # A command failed because the referenced shadow root is no longer attached to the DOM. @@ -152,7 +150,11 @@ class ScriptTimeoutError < WebDriverError; end # Argument was an invalid selector. # - class InvalidSelectorError < WebDriverError; end + class InvalidSelectorError < WebDriverError + def initialize(msg = '') + super("#{msg}; #{SUPPORT_MSG} #{URLS[:InvalidSelectorError]}") + end + end # # A new session could not be created. @@ -237,7 +239,11 @@ class UnsupportedOperationError < WebDriverError; end # Indicates that driver was not specified and could not be located. # - class NoSuchDriverError < WebDriverError; end + class NoSuchDriverError < WebDriverError + def initialize(msg = '') + super("#{msg}; #{SUPPORT_MSG} #{URLS[:NoSuchDriverError]}") + end + end end # Error end # WebDriver end # Selenium diff --git a/rb/spec/integration/selenium/webdriver/error_spec.rb b/rb/spec/integration/selenium/webdriver/error_spec.rb index 9b36528a0c6e4..ad5fd3ae0ff65 100644 --- a/rb/spec/integration/selenium/webdriver/error_spec.rb +++ b/rb/spec/integration/selenium/webdriver/error_spec.rb @@ -21,102 +21,13 @@ module Selenium module WebDriver - describe Error, exclusive: { bidi: false, reason: 'Not yet implemented with BiDi' } do - let(:base_url) { 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors' } - + describe Error, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do it 'raises an appropriate error' do driver.navigate.to url_for('xhtmlTest.html') expect { driver.find_element(id: 'nonexistent') - }.to raise_error(WebDriver::Error::NoSuchElementError) - end - - context 'with self generated url' do - it 'provides the right url for NoSuchElementError' do - driver.navigate.to url_for('xhtmlTest.html') - - expect { - driver.find_element(id: 'nonexistent') - }.to raise_error(Selenium::WebDriver::Error::NoSuchElementError, /#{base_url}#no-such-element-exception/) - end - - it 'provides the right url for StaleElementReferenceError' do - driver.navigate.to url_for('formPage.html') - button = driver.find_element(id: 'imageButton') - driver.navigate.refresh - - expect { - button.click - }.to raise_error(Selenium::WebDriver::Error::StaleElementReferenceError, - /#{base_url}#stale-element-reference-exception/) - end - - it 'provides the right url for UnknownError' do - driver.network_conditions = { offline: false, latency: 56, download_throughput: 789, upload_throughput: 600 } - driver.delete_network_conditions - - expect { - driver.network_conditions - }.to raise_error(Selenium::WebDriver::Error::UnknownError, /#{base_url}#unknown-exception/) - end - - it 'provides the right url for NoSuchAlertError' do - expect { - driver.switch_to.alert - }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError, /#{base_url}#no-such-alert-exception/) - end - - it 'provides the right url for ScriptTimeoutError' do - expect { - driver.execute_async_script 'return 1 + 2;' - }.to raise_error(Selenium::WebDriver::Error::ScriptTimeoutError, /#{base_url}#script-timeout-exception/) - end - - it 'provides the right url for NoSuchWindowError' do - expect { - driver.switch_to.window 'nonexistent' - }.to raise_error(Selenium::WebDriver::Error::NoSuchWindowError, /#{base_url}#no-such-window-exception/) - end - - it 'provides the right url for JavascriptError' do - driver.navigate.to url_for('xhtmlTest.html') - expect { - driver.execute_script('return squiggle();') - }.to raise_error(Selenium::WebDriver::Error::JavascriptError, /#{base_url}#javascript-exception/) - end - - it 'provides the right url for TimeoutError' do - block = -> { raise Error::NoSuchElementError, 'foo' } - - expect { - wait(0.5).until(&block) - }.to raise_error(Error::TimeoutError, /#{base_url}#timeout-exception/) - end - end - - it 'provides the right url for NoSuchShadowRootError' do - driver.navigate.to url_for('simpleTest.html') - div = driver.find_element(css: 'div') - expect { - div.shadow_root - }.to raise_error(Error::NoSuchShadowRootError, /#{base_url}#no-such-shadow-root-exception/) - end - - it 'provides the right url for InvalidCookieDomainError' do - expect { - driver.manage.add_cookie name: 'domain', - value: 'different', - domain: 'selenium.dev' - }.to raise_error(Error::InvalidCookieDomainError, /#{base_url}#invalid-cookie-domain-exception/) - end - - it 'provides the right url for InvalidSelectorError', - exclude: {browser: %i[safari safari_preview], reason: 'Safari TimeoutError'} do - driver.navigate.to url_for('xhtmlTest.html') - expect { - driver.find_element(xpath: '*?//-') - }.to raise_error(Error::InvalidSelectorError, /#{base_url}#invalid-selector-exception/) + }.to raise_error(WebDriver::Error::NoSuchElementError, /#no-such-element-exception/) end end end # WebDriver diff --git a/rb/spec/unit/selenium/webdriver/common/driver_finder_spec.rb b/rb/spec/unit/selenium/webdriver/common/driver_finder_spec.rb index fafa0803f61f1..175c191c09763 100644 --- a/rb/spec/unit/selenium/webdriver/common/driver_finder_spec.rb +++ b/rb/spec/unit/selenium/webdriver/common/driver_finder_spec.rb @@ -63,8 +63,7 @@ module WebDriver expect { described_class.new(Options.chrome, Service.chrome).driver_path }.to output(/Exception occurred: this error/).to_stderr_from_any_process - }.to raise_error(WebDriver::Error::NoSuchDriverError, - /Unable to obtain chromedriver; For documentation on this error/) + }.to raise_error(WebDriver::Error::NoSuchDriverError, /driver_location/) end it 'creates arguments' do From fbca5c2784c1906f9fe769cd1915b13491d80ad2 Mon Sep 17 00:00:00 2001 From: aguspe Date: Tue, 25 Jun 2024 18:11:48 +0200 Subject: [PATCH 5/6] Update types --- rb/sig/lib/selenium/webdriver/common/error.rbs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rb/sig/lib/selenium/webdriver/common/error.rbs b/rb/sig/lib/selenium/webdriver/common/error.rbs index 52b388374f85b..2ef3ba9e669b0 100644 --- a/rb/sig/lib/selenium/webdriver/common/error.rbs +++ b/rb/sig/lib/selenium/webdriver/common/error.rbs @@ -7,12 +7,9 @@ module Selenium ERROR_URL: String - class WebDriverError < StandardError - def url: -> String - - private + URLS: Hash[Symbol, String] - def parsed_class_name: -> String + class WebDriverError < StandardError end class NoSuchElementError < WebDriverError From 040805d54941496a664bf1e5362271d53876784e Mon Sep 17 00:00:00 2001 From: aguspe Date: Tue, 25 Jun 2024 18:41:43 +0200 Subject: [PATCH 6/6] Update implementation to remove constructor duplication --- rb/lib/selenium/webdriver/common/error.rb | 29 +++++++++---------- .../lib/selenium/webdriver/common/error.rbs | 1 + 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/rb/lib/selenium/webdriver/common/error.rb b/rb/lib/selenium/webdriver/common/error.rb index 850828d84ebcb..7e3de39f2d6f4 100644 --- a/rb/lib/selenium/webdriver/common/error.rb +++ b/rb/lib/selenium/webdriver/common/error.rb @@ -44,17 +44,22 @@ def self.for_error(error) NoSuchDriverError: "#{ERROR_URL}/driver_location" }.freeze - class WebDriverError < StandardError; end + class WebDriverError < StandardError + def initialize(msg = '') + # Remove this conditional when all the error pages have been documented + super(URLS[class_name] ? "#{msg}; #{SUPPORT_MSG} #{URLS[class_name]}" : msg) + end + + def class_name + self.class.name&.split('::')&.last&.to_sym + end + end # # An element could not be located on the page using the given search parameters. # - class NoSuchElementError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{URLS[:NoSuchElementError]}") - end - end + class NoSuchElementError < WebDriverError; end # # A command to switch to a frame could not be satisfied because the frame could not be found. @@ -150,11 +155,7 @@ class ScriptTimeoutError < WebDriverError; end # Argument was an invalid selector. # - class InvalidSelectorError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{URLS[:InvalidSelectorError]}") - end - end + class InvalidSelectorError < WebDriverError; end # # A new session could not be created. @@ -239,11 +240,7 @@ class UnsupportedOperationError < WebDriverError; end # Indicates that driver was not specified and could not be located. # - class NoSuchDriverError < WebDriverError - def initialize(msg = '') - super("#{msg}; #{SUPPORT_MSG} #{URLS[:NoSuchDriverError]}") - end - end + class NoSuchDriverError < WebDriverError; end end # Error end # WebDriver end # Selenium diff --git a/rb/sig/lib/selenium/webdriver/common/error.rbs b/rb/sig/lib/selenium/webdriver/common/error.rbs index 2ef3ba9e669b0..16c7caf9d033a 100644 --- a/rb/sig/lib/selenium/webdriver/common/error.rbs +++ b/rb/sig/lib/selenium/webdriver/common/error.rbs @@ -10,6 +10,7 @@ module Selenium URLS: Hash[Symbol, String] class WebDriverError < StandardError + def class_name: -> Symbol? end class NoSuchElementError < WebDriverError