Skip to content

Commit 722376a

Browse files
authored
[py] add links to documentation for errors (#12156)
* [py] include links to documentation for error messages * [py] make custom exception for driver location
1 parent 370428c commit 722376a

8 files changed

Lines changed: 84 additions & 26 deletions

File tree

py/selenium/common/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
from .exceptions import NoAlertPresentException
3535
from .exceptions import NoSuchAttributeException
3636
from .exceptions import NoSuchCookieException
37+
from .exceptions import NoSuchDriverException
3738
from .exceptions import NoSuchElementException
3839
from .exceptions import NoSuchFrameException
3940
from .exceptions import NoSuchShadowRootException
4041
from .exceptions import NoSuchWindowException
4142
from .exceptions import ScreenshotException
42-
from .exceptions import SeleniumManagerException
4343
from .exceptions import SessionNotCreatedException
4444
from .exceptions import StaleElementReferenceException
4545
from .exceptions import TimeoutException
@@ -56,6 +56,7 @@
5656
"NoSuchWindowException",
5757
"NoSuchElementException",
5858
"NoSuchAttributeException",
59+
"NoSuchDriverException",
5960
"NoSuchShadowRootException",
6061
"StaleElementReferenceException",
6162
"InvalidElementStateException",
@@ -82,5 +83,4 @@
8283
"InvalidSessionIdException",
8384
"SessionNotCreatedException",
8485
"UnknownMethodException",
85-
"SeleniumManagerException",
8686
]

py/selenium/common/exceptions.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
from typing import Optional
2121
from typing import Sequence
2222

23+
SUPPORT_MSG = "For documentation on this error, please visit:"
24+
ERROR_URL = "https://www.selenium.dev/documentation/webdriver/troubleshooting/errors"
25+
2326

2427
class WebDriverException(Exception):
2528
"""Base webdriver exception."""
@@ -70,6 +73,13 @@ class NoSuchElementException(WebDriverException):
7073
for how to write a wait wrapper to wait for an element to appear.
7174
"""
7275

76+
def __init__(
77+
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
78+
) -> None:
79+
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#no-such-element-exception"
80+
81+
super().__init__(with_support, screen, stacktrace)
82+
7383

7484
class NoSuchAttributeException(WebDriverException):
7585
"""Thrown when the attribute of element could not be found.
@@ -102,6 +112,13 @@ class StaleElementReferenceException(WebDriverException):
102112
* Element may have been inside an iframe or another context which was refreshed.
103113
"""
104114

115+
def __init__(
116+
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
117+
) -> None:
118+
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#stale-element-reference-exception"
119+
120+
super().__init__(with_support, screen, stacktrace)
121+
105122

106123
class InvalidElementStateException(WebDriverException):
107124
"""Thrown when a command could not be completed because the element is in
@@ -194,6 +211,13 @@ class InvalidSelectorException(WebDriverException):
194211
"count(//input)").
195212
"""
196213

214+
def __init__(
215+
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
216+
) -> None:
217+
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}#invalid-selector-exception"
218+
219+
super().__init__(with_support, screen, stacktrace)
220+
197221

198222
class ImeNotAvailableException(WebDriverException):
199223
"""Thrown when IME support is not available.
@@ -253,5 +277,12 @@ class UnknownMethodException(WebDriverException):
253277
for that URL."""
254278

255279

256-
class SeleniumManagerException(WebDriverException):
257-
"""Raised when an issue interacting with selenium manager occurs."""
280+
class NoSuchDriverException(WebDriverException):
281+
"""Raised when driver is not specified and cannot be located."""
282+
283+
def __init__(
284+
self, msg: Optional[str] = None, screen: Optional[str] = None, stacktrace: Optional[Sequence[str]] = None
285+
) -> None:
286+
with_support = f"{msg}; {SUPPORT_MSG} {ERROR_URL}/driver_location"
287+
288+
super().__init__(with_support, screen, stacktrace)

py/selenium/webdriver/common/driver_finder.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
# under the License.
1717
import logging
1818
import shutil
19+
from pathlib import Path
1920

20-
from selenium.common.exceptions import WebDriverException
21+
from selenium.common.exceptions import NoSuchDriverException
2122
from selenium.webdriver.common.options import BaseOptions
2223
from selenium.webdriver.common.selenium_manager import SeleniumManager
2324
from selenium.webdriver.common.service import Service
@@ -36,10 +37,13 @@ def __init__(self) -> None:
3637

3738
@staticmethod
3839
def get_path(service: Service, options: BaseOptions) -> str:
40+
path = shutil.which(service.path)
3941
try:
40-
path = shutil.which(service.path) or SeleniumManager().driver_location(options)
41-
except WebDriverException as err:
42-
logger.warning("Unable to obtain driver using Selenium Manager: " + err.msg)
43-
raise err
42+
path = SeleniumManager().driver_location(options) if path is None else path
43+
except Exception as err:
44+
raise NoSuchDriverException(f"Unable to obtain {service.path} using Selenium Manager; {err}")
45+
46+
if path is None or not Path(path).is_file():
47+
raise NoSuchDriverException(f"Unable to locate or obtain {service.path}")
4448

4549
return path

py/selenium/webdriver/common/selenium_manager.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pathlib import Path
2222
from typing import List
2323

24-
from selenium.common.exceptions import SeleniumManagerException
24+
from selenium.common import WebDriverException
2525
from selenium.webdriver.common.options import BaseOptions
2626

2727
logger = logging.getLogger(__name__)
@@ -57,8 +57,7 @@ def get_binary() -> Path:
5757
path = Path(__file__).parent.joinpath(directory, file)
5858

5959
if not path.is_file():
60-
tracker = "https://github.com/SeleniumHQ/selenium/issues"
61-
raise SeleniumManagerException(f"{path} is missing. Please open an issue on {tracker}")
60+
raise WebDriverException(f"Unable to obtain working Selenium Manager binary; {path}")
6261

6362
return path
6463

@@ -109,15 +108,18 @@ def run(args: List[str]) -> str:
109108
"""
110109
command = " ".join(args)
111110
logger.debug(f"Executing process: {command}")
112-
completed_proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
113-
stdout = completed_proc.stdout.decode("utf-8").rstrip("\n")
114-
stderr = completed_proc.stderr.decode("utf-8").rstrip("\n")
115-
output = json.loads(stdout)
116-
result = output["result"]["message"]
111+
try:
112+
completed_proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
113+
stdout = completed_proc.stdout.decode("utf-8").rstrip("\n")
114+
stderr = completed_proc.stderr.decode("utf-8").rstrip("\n")
115+
output = json.loads(stdout)
116+
result = output["result"]["message"]
117+
except Exception as err:
118+
raise WebDriverException(f"Unsuccessful command executed: {command}; {err}")
119+
117120
if completed_proc.returncode:
118-
raise SeleniumManagerException(f"Selenium Manager failed for: {command}.\n{result}{stderr}")
121+
raise WebDriverException(f"Unsuccessful command executed: {command}.\n{result}{stderr}")
119122
else:
120-
# Selenium Manager exited successfully, return executable path and print warnings
121123
for item in output["logs"]:
122124
if item["level"] == "WARN":
123125
logger.warning(item["message"])

py/test/selenium/webdriver/common/driver_element_finding_tests.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ def test_finding_multiple_elements_by_id_with_space_should_return_empty_list(dri
9393
assert len(elements) == 0
9494

9595

96+
def test_no_such_element_error(driver, pages):
97+
pages.load("formPage.html")
98+
msg = r"\/errors#no-such-element-exception"
99+
with pytest.raises(NoSuchElementException, match=msg):
100+
driver.find_element(By.ID, "non_Existent_Button")
101+
102+
96103
# By.name positive
97104

98105

@@ -268,7 +275,8 @@ def test_should_not_find_element_by_class_when_the_name_queried_is_shorter_than_
268275

269276
def test_finding_asingle_element_by_empty_class_name_should_throw(driver, pages):
270277
pages.load("xhtmlTest.html")
271-
with pytest.raises(InvalidSelectorException):
278+
msg = r"\/errors#invalid-selector-exception"
279+
with pytest.raises(InvalidSelectorException, match=msg):
272280
driver.find_element(By.CLASS_NAME, "")
273281

274282

py/test/selenium/webdriver/common/selenium_manager_tests.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
import pytest
2121

22-
from selenium.common.exceptions import SeleniumManagerException
22+
from selenium.common.exceptions import WebDriverException
2323
from selenium.webdriver.chrome.options import Options
24+
from selenium.webdriver.chrome.service import Service
25+
from selenium.webdriver.common.driver_finder import DriverFinder
2426
from selenium.webdriver.common.proxy import Proxy
2527
from selenium.webdriver.common.selenium_manager import SeleniumManager
2628

@@ -85,8 +87,18 @@ def test_proxy_is_used_for_sm(mocker):
8587

8688

8789
def test_stderr_is_propagated_to_exception_messages():
88-
msg = r"Selenium Manager failed for:.* --browser foo --output json\.\nInvalid browser name: foo\n"
89-
with pytest.raises(SeleniumManagerException, match=msg):
90+
msg = r"Unsuccessful command executed:.* --browser foo --output json\.\nInvalid browser name: foo\n"
91+
with pytest.raises(WebDriverException, match=msg):
9092
manager = SeleniumManager()
9193
binary = manager.get_binary()
9294
_ = manager.run([str(binary), "--browser", "foo", "--output", "json"])
95+
96+
97+
def test_driver_finder_error(mocker):
98+
mocker.patch("selenium.webdriver.common.selenium_manager.SeleniumManager.driver_location", return_value=None)
99+
100+
service = Service()
101+
options = Options()
102+
msg = r"Unable to locate or obtain chromedriver.*errors\/driver_location"
103+
with pytest.raises(WebDriverException, match=msg):
104+
DriverFinder.get_path(service, options)

py/test/selenium/webdriver/common/stale_reference_tests.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def test_old_page(driver, pages):
2525
pages.load("simpleTest.html")
2626
elem = driver.find_element(by=By.ID, value="links")
2727
pages.load("xhtmlTest.html")
28-
with pytest.raises(StaleElementReferenceException):
28+
msg = r"\/errors#stale-element-reference-exception"
29+
with pytest.raises(StaleElementReferenceException, match=msg):
2930
elem.click()
3031

3132

py/test/selenium/webdriver/support/relative_by_tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def test_no_such_element_is_raised_rather_than_index_error(driver, pages):
8686
with pytest.raises(NoSuchElementException) as exc:
8787
anchor = driver.find_element(By.ID, "second")
8888
driver.find_element(locate_with(By.ID, "nonexistentid").above(anchor))
89-
assert exc.value.msg == "Cannot locate relative element with: {'id': 'nonexistentid'}"
89+
assert "Cannot locate relative element with: {'id': 'nonexistentid'}" in exc.value.msg
9090

9191

9292
def test_near_locator_should_find_near_elements(driver, pages):
@@ -105,4 +105,4 @@ def test_near_locator_should_not_find_far_elements(driver, pages):
105105
with pytest.raises(NoSuchElementException) as exc:
106106
driver.find_element(locate_with(By.ID, "rect4").near(rect3))
107107

108-
assert exc.value.msg == "Cannot locate relative element with: {'id': 'rect4'}"
108+
assert "Cannot locate relative element with: {'id': 'rect4'}" in exc.value.msg

0 commit comments

Comments
 (0)