Skip to content

Commit 4d59ee8

Browse files
[py] use local webserver for BiDi network auth/header tests
The BiDi network authentication and extra-header tests navigated to postman-echo.com, an external service that makes the tests flaky and dependent on network access. Move them onto the existing local SimpleWebServer so everything runs on the same machine. Adds a /basic-auth endpoint to the test webserver (401 + Basic challenge, 200 once the expected credentials are supplied) and reuses the existing /echo_headers route for the extra-header tests.
1 parent 7ca11b9 commit 4d59ee8

2 files changed

Lines changed: 46 additions & 14 deletions

File tree

py/test/selenium/webdriver/common/bidi_network_tests.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ def callback(request: Request):
9898
# origin, suppressing authRequired challenges in later tests — restart the
9999
# driver afterwards so challenge-dependent tests start clean.
100100
@pytest.mark.needs_fresh_driver
101-
def test_continue_with_auth(driver):
101+
def test_continue_with_auth(driver, pages):
102102
callback_id = driver.network.add_auth_handler("postman", "password")
103103
assert callback_id is not None, "Request handler not added"
104104
driver.browsing_context.navigate(
105-
context=driver.current_window_handle, url="https://postman-echo.com/basic-auth", wait=ReadinessState.COMPLETE
105+
context=driver.current_window_handle, url=pages.url("basic-auth"), wait=ReadinessState.COMPLETE
106106
)
107107
assert "authenticated" in driver.page_source, "Authorization failed"
108108

@@ -410,32 +410,32 @@ def test_request_and_response_handlers_compose(driver, pages):
410410
# needs_fresh_driver: successful authentication warms the browser's HTTP auth
411411
# cache for the origin, which would suppress the challenge in later tests.
412412
@pytest.mark.needs_fresh_driver
413-
def test_provide_credentials_for_matching_url(driver):
413+
def test_provide_credentials_for_matching_url(driver, pages):
414414
def handle_authentication(auth):
415415
auth.provide_credentials("postman", "password")
416416

417-
handler_id = driver.network.add_authentication_handler(["https://postman-echo.com/**"], handle_authentication)
417+
handler_id = driver.network.add_authentication_handler(["**/basic-auth"], handle_authentication)
418418
try:
419-
_navigate(driver, "https://postman-echo.com/basic-auth")
419+
_navigate(driver, pages.url("basic-auth"))
420420
assert "authenticated" in driver.page_source, "Authorization failed"
421421
finally:
422422
driver.network.remove_authentication_handler(handler_id)
423423

424424

425-
def test_cancel_authentication_challenge(driver):
425+
def test_cancel_authentication_challenge(driver, pages):
426426
def handle_authentication(auth):
427427
auth.cancel()
428428

429429
handler_id = driver.network.add_authentication_handler(handle_authentication)
430430
try:
431-
_navigate(driver, "https://postman-echo.com/basic-auth")
431+
_navigate(driver, pages.url("basic-auth"))
432432
assert "authenticated" not in driver.page_source, "Cancelled challenge still authenticated"
433433
finally:
434434
driver.network.remove_authentication_handler(handler_id)
435435

436436

437437
@pytest.mark.needs_fresh_driver
438-
def test_authentication_handler_observes_challenge_details(driver):
438+
def test_authentication_handler_observes_challenge_details(driver, pages):
439439
challenges = []
440440

441441
def handle_authentication(auth):
@@ -444,9 +444,9 @@ def handle_authentication(auth):
444444

445445
handler_id = driver.network.add_authentication_handler(handle_authentication)
446446
try:
447-
_navigate(driver, "https://postman-echo.com/basic-auth")
447+
_navigate(driver, pages.url("basic-auth"))
448448
assert challenges, "Authentication handler did not run"
449-
assert challenges[0][0].startswith("https://postman-echo.com/basic-auth")
449+
assert challenges[0][0].endswith("/basic-auth")
450450
finally:
451451
driver.network.remove_authentication_handler(handler_id)
452452

@@ -472,21 +472,21 @@ def test_clear_authentication_handlers_removes_all_intercepts(driver):
472472
# ---------------------------------------------------------------------------
473473

474474

475-
def test_extra_header_is_sent_with_requests(driver):
475+
def test_extra_header_is_sent_with_requests(driver, pages):
476476
driver.network.add_extra_header("x-selenium-extra", "extra-header-value")
477477
try:
478-
_navigate(driver, "https://postman-echo.com/headers")
478+
_navigate(driver, pages.url("echo_headers"))
479479
assert "x-selenium-extra" in driver.page_source, "Extra header not sent"
480480
assert "extra-header-value" in driver.page_source, "Extra header value not sent"
481481
finally:
482482
driver.network.clear_extra_headers()
483483

484484

485-
def test_removed_extra_header_is_not_sent(driver):
485+
def test_removed_extra_header_is_not_sent(driver, pages):
486486
driver.network.add_extra_header("x-selenium-extra", "extra-header-value")
487487
driver.network.remove_extra_header("x-selenium-extra")
488488

489-
_navigate(driver, "https://postman-echo.com/headers")
489+
_navigate(driver, pages.url("echo_headers"))
490490
assert "x-selenium-extra" not in driver.page_source, "Removed extra header still sent"
491491
assert driver.network.intercepts == [], "Intercept not removed"
492492

py/test/selenium/webdriver/common/webserver.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
It serves the testing html pages that are needed by the webdriver unit tests.
2121
"""
2222

23+
import base64
2324
import contextlib
2425
import logging
2526
import os
@@ -45,6 +46,11 @@ def updir():
4546
DEFAULT_PORT = 8000
4647
HTML_ROOT = os.path.join(WEBDRIVER, "../../../../common/src/web")
4748

49+
# Credentials accepted by the /basic-auth endpoint. Tests that exercise
50+
# authentication handlers must provide these to be considered authenticated.
51+
AUTH_USERNAME = "postman"
52+
AUTH_PASSWORD = "password"
53+
4854
if not os.path.isdir(HTML_ROOT):
4955
raise Exception(
5056
"Can't find 'common_web' directory, try setting WEBDRIVER environment variable.\n"
@@ -80,6 +86,18 @@ def _serve_file(self, file_path):
8086
else:
8187
return content, "text/html"
8288

89+
def _is_authenticated(self):
90+
"""Return True when the request carries the expected Basic credentials."""
91+
auth_header = self.headers.get("Authorization", "")
92+
if not auth_header.startswith("Basic "):
93+
return False
94+
try:
95+
decoded = base64.b64decode(auth_header.removeprefix("Basic ")).decode("utf-8")
96+
except (ValueError, UnicodeDecodeError):
97+
return False
98+
username, _, password = decoded.partition(":")
99+
return username == AUTH_USERNAME and password == AUTH_PASSWORD
100+
83101
def _send_response(self, content_type="text/html"):
84102
"""Send a response."""
85103
self.send_response(200)
@@ -116,6 +134,20 @@ def do_GET(self):
116134
self.wfile.write(b"cookie set")
117135
return
118136

137+
if path == "basic-auth":
138+
if self._is_authenticated():
139+
self._send_response("application/json")
140+
self.wfile.write(b'{"authenticated": true}')
141+
else:
142+
self.send_response(401)
143+
self.send_header("WWW-Authenticate", 'Basic realm="Fake Realm"')
144+
self.send_header("Content-type", "text/plain")
145+
self.end_headers()
146+
# Body must not contain the substring "authenticated" — tests
147+
# assert its absence when an auth challenge is cancelled.
148+
self.wfile.write(b"Access denied")
149+
return
150+
119151
file_path = os.path.join(HTML_ROOT, path)
120152
if path.startswith("page/"):
121153
html = self._serve_page(path[5:])

0 commit comments

Comments
 (0)