Skip to content

Commit 2561228

Browse files
test: test on *.test domains
Instead of testing on `localhost`, provide `--host-resolver-rules` arg to Chrome and test on `*.test` domains.
1 parent a53f6f1 commit 2561228

File tree

6 files changed

+123
-101
lines changed

6 files changed

+123
-101
lines changed

tests/conftest.py

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,27 @@
3030
from tools.local_http_server import LocalHttpServer
3131

3232

33-
@pytest_asyncio.fixture(scope='session')
34-
def local_server_http() -> Generator[LocalHttpServer, None, None]:
35-
"""
36-
Returns an instance of a LocalHttpServer without SSL pointing to localhost.
37-
"""
38-
server = LocalHttpServer()
39-
yield server
33+
@pytest.fixture(scope="session")
34+
def some_domain():
35+
return 'some_domain.test'
4036

41-
server.clear()
42-
if server.is_running():
43-
server.stop()
44-
return
37+
38+
@pytest.fixture(scope="session")
39+
def another_domain():
40+
return 'another_domain.test'
41+
42+
43+
@pytest.fixture(scope="session")
44+
def ssl_domain():
45+
return 'ssl_domain.test'
4546

4647

4748
@pytest_asyncio.fixture(scope='session')
48-
def local_server_http_another_host() -> Generator[LocalHttpServer, None, None]:
49+
def local_http_server(some_domain) -> Generator[LocalHttpServer, None, None]:
4950
"""
50-
Returns an instance of a LocalHttpServer without SSL pointing to `127.0.0.1`
51+
Returns an instance of a LocalHttpServer without SSL pointing to localhost.
5152
"""
52-
server = LocalHttpServer('127.0.0.1')
53+
server = LocalHttpServer(default_host=some_domain)
5354
yield server
5455

5556
server.clear()
@@ -59,9 +60,9 @@ def local_server_http_another_host() -> Generator[LocalHttpServer, None, None]:
5960

6061

6162
@pytest_asyncio.fixture(scope='session')
62-
def local_server_bad_ssl() -> Generator[LocalHttpServer, None, None]:
63+
def local_server_bad_ssl(ssl_domain) -> Generator[LocalHttpServer, None, None]:
6364
""" Returns an instance of a LocalHttpServer with bad SSL certificate. """
64-
server = LocalHttpServer(protocol='https')
65+
server = LocalHttpServer(default_host=ssl_domain, protocol='https')
6566
yield server
6667

6768
server.clear()
@@ -116,22 +117,28 @@ async def capabilities(request):
116117
async def websocket(_websocket_connection, test_headless_mode, capabilities,
117118
request):
118119
"""Return a websocket with an active BiDi session."""
119-
default_capabilities = {"webSocketUrl": True, "goog:chromeOptions": {}}
120+
default_capabilities = {
121+
"webSocketUrl": True,
122+
"goog:chromeOptions": {
123+
"args": [
124+
# Required for testing with `.test` domains.
125+
'--host-resolver-rules=MAP *.test 127.0.0.1',
126+
]
127+
}
128+
}
120129
maybe_browser_bin = os.getenv("BROWSER_BIN")
121130
if maybe_browser_bin:
122131
default_capabilities["goog:chromeOptions"][
123132
"binary"] = maybe_browser_bin
124133

125134
if test_headless_mode != "false":
126135
if test_headless_mode == "old":
127-
default_capabilities["goog:chromeOptions"]["args"] = [
128-
"--headless=old", '--hide-scrollbars', '--mute-audio'
129-
]
136+
default_capabilities["goog:chromeOptions"]["args"].extend(
137+
["--headless=old", "--hide-scrollbars", "--mute-audio"])
130138
else:
131139
# Default to new headless mode.
132-
default_capabilities["goog:chromeOptions"]["args"] = [
133-
"--headless=new"
134-
]
140+
default_capabilities["goog:chromeOptions"]["args"].extend(
141+
["--headless=new"])
135142

136143
session_capabilities = merge_dicts_recursively(default_capabilities,
137144
capabilities)
@@ -267,60 +274,60 @@ def url_all_origins(request, url_example, url_example_another_origin, html):
267274

268275

269276
@pytest.fixture
270-
def url_base(local_server_http):
277+
def url_base(local_http_server):
271278
"""Return a generic example URL with status code 200."""
272-
return local_server_http.url_base()
279+
return local_http_server.url_base()
273280

274281

275282
@pytest.fixture
276-
def url_example(local_server_http):
283+
def url_example(local_http_server):
277284
"""Return a generic example URL with status code 200."""
278-
return local_server_http.url_200()
285+
return local_http_server.url_200()
279286

280287

281288
@pytest.fixture
282-
def url_example_another_origin(local_server_http_another_host):
289+
def url_example_another_origin(local_http_server, another_domain):
283290
"""Return a generic example URL with status code 200, in a domain other than
284291
the example_url fixture."""
285-
return local_server_http_another_host.url_200()
292+
return local_http_server.url_200(host=another_domain)
286293

287294

288295
@pytest.fixture
289-
def url_auth_required(local_server_http):
296+
def url_auth_required(local_http_server):
290297
"""Return a URL that requires authentication (status code 401).
291298
Alternatively, any of the following URLs could also be used:
292299
- "https://authenticationtest.com/HTTPAuth/"
293300
- "http://the-internet.herokuapp.com/basic_auth"
294301
- "http://httpstat.us/401"
295302
"""
296-
return local_server_http.url_basic_auth()
303+
return local_http_server.url_basic_auth()
297304

298305

299306
@pytest.fixture
300-
def url_hang_forever(local_server_http):
307+
def url_hang_forever(local_http_server):
301308
"""Return a URL that hangs forever."""
302309
try:
303-
yield local_server_http.url_hang_forever()
310+
yield local_http_server.url_hang_forever()
304311
finally:
305-
local_server_http.hang_forever_stop()
312+
local_http_server.hang_forever_stop()
306313

307314

308315
@pytest.fixture(scope="session")
309-
def url_bad_ssl(local_server_bad_ssl):
316+
def url_bad_ssl(local_server_bad_ssl, ssl_domain):
310317
"""
311318
Return a URL with an invalid certificate authority from a SSL certificate.
312319
In Chromium, this generates the following error:
313320
314321
> Your connection is not private
315322
> NET::ERR_CERT_AUTHORITY_INVALID
316323
"""
317-
return local_server_bad_ssl.url_200()
324+
return local_server_bad_ssl.url_200(host=ssl_domain)
318325

319326

320327
@pytest.fixture
321-
def url_cacheable(local_server_http):
328+
def url_cacheable(local_http_server):
322329
"""Return a generic example URL that can be cached."""
323-
return local_server_http.url_cacheable()
330+
return local_http_server.url_cacheable()
324331

325332

326333
@pytest.fixture
@@ -509,11 +516,11 @@ async def activate_main_tab():
509516

510517

511518
@pytest.fixture
512-
def url_download(local_server_http):
519+
def url_download(local_http_server):
513520
"""Return a URL that triggers a download."""
514521
def url_download(file_name="file-name.txt", content="download content"):
515-
return local_server_http.url_200(
516-
content,
522+
return local_http_server.url_200(
523+
content=content,
517524
content_type="text/html",
518525
headers={
519526
"Content-Disposition": f"attachment; filename=\"{file_name}\""
@@ -523,13 +530,14 @@ def url_download(file_name="file-name.txt", content="download content"):
523530

524531

525532
@pytest.fixture
526-
def html(local_server_http, local_server_http_another_host):
533+
def html(local_http_server, some_domain, another_domain):
527534
"""Return a factory for URL with the given content."""
528535
def html(content="", same_origin=True):
529536
if same_origin:
530-
return local_server_http.url_200(content=content)
537+
return local_http_server.url_200(host=some_domain, content=content)
531538
else:
532-
return local_server_http_another_host.url_200(content=content)
539+
return local_http_server.url_200(host=another_domain,
540+
content=content)
533541

534542
return html
535543

tests/network/test_network.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ async def test_network_global_subscription_enabled_in_new_context(
148148

149149
@pytest.mark.asyncio
150150
async def test_network_before_request_sent_event_with_cookies_emitted(
151-
websocket, context_id, url_base, url_example):
151+
websocket, context_id, url_base, url_example, some_domain):
152152
pytest.xfail(
153153
"TODO: Fix flaky test https://github.com/GoogleChromeLabs/chromium-bidi/issues/2263"
154154
)
@@ -195,7 +195,7 @@ async def test_network_before_request_sent_event_with_cookies_emitted(
195195
"headers": ANY_LIST,
196196
"cookies": AnyOr([
197197
AnyExtending({
198-
"domain": "localhost",
198+
"domain": some_domain,
199199
"httpOnly": False,
200200
"name": "foo",
201201
"path": "/",

tests/script/test_realm.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
# limitations under the License.
1515

1616
import pytest
17-
from anys import ANY_NUMBER, ANY_STR
17+
from anys import ANY_NUMBER, ANY_STR, AnySubstr
1818
from test_helpers import (execute_command, read_JSON_message,
1919
send_JSON_command, subscribe, wait_for_event,
2020
wait_for_filtered_event)
2121

2222

2323
@pytest.mark.asyncio
2424
async def test_realm_realmCreated(websocket, context_id, html,
25-
local_server_http):
25+
local_http_server):
2626
url = html()
2727

2828
await subscribe(websocket, ["script.realmCreated"])
@@ -44,7 +44,7 @@ async def test_realm_realmCreated(websocket, context_id, html,
4444
"method": "script.realmCreated",
4545
"params": {
4646
"type": "window",
47-
"origin": local_server_http.origin(),
47+
"origin": AnySubstr(url),
4848
"realm": ANY_STR,
4949
"context": context_id,
5050
}

tests/tools/local_http_server.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class LocalHttpServer:
5757
content_200: str = 'default 200 page'
5858

5959
__app: Flask
60-
__host: str
60+
__default_host: str
6161
__port: int
6262
__protocol: Literal['http', 'https']
6363

@@ -95,13 +95,15 @@ def stop(self) -> None:
9595
def __html_doc(self, content: str) -> str:
9696
return f"<!DOCTYPE html><html><head><link rel='shortcut icon' href='data:image/x-icon;,' type='image/x-icon'></head><body>{content}</body></html>"
9797

98-
def __init__(self,
99-
host: str = 'localhost',
100-
protocol: Literal['http', 'https'] = 'http') -> None:
98+
def __init__(
99+
self,
100+
# Default host is used to resolve the URL, if a custom host is not provided.
101+
default_host: str = 'localhost',
102+
protocol: Literal['http', 'https'] = 'http') -> None:
101103
self.__app = Flask(__name__)
102104
# Important for some Flask behaviors in a test context
103105
self.__app.testing = True
104-
self.__host = host
106+
self.__default_host = default_host
105107
self.__protocol = protocol
106108
self.__port = find_free_port()
107109

@@ -218,12 +220,12 @@ def _check_server_readiness(self, timeout_s: float = 0.1) -> bool:
218220
context.check_hostname = False
219221
# For self-signed certs
220222
context.verify_mode = ssl.CERT_NONE
221-
conn = http.client.HTTPSConnection(self.__host,
223+
conn = http.client.HTTPSConnection("localhost",
222224
self.__port,
223225
timeout=timeout_s,
224226
context=context)
225227
else:
226-
conn = http.client.HTTPConnection(self.__host,
228+
conn = http.client.HTTPConnection("localhost",
227229
self.__port,
228230
timeout=timeout_s)
229231

@@ -249,9 +251,7 @@ def _wait_for_server_startup(self, max_wait_s: int = 5) -> None:
249251
return
250252
# Short sleep before retrying
251253
time.sleep(0.05)
252-
raise RuntimeError(
253-
f"Flask server failed to start on {self.__protocol}://{self.__host}:{self.__port} within {max_wait_s}s."
254-
)
254+
raise RuntimeError("Flask server failed to start.")
255255

256256
def _start_server(self,
257257
ssl_context_config: tuple[str, str] | None) -> None:
@@ -260,7 +260,9 @@ def _start_server(self,
260260
return
261261

262262
kwargs = {
263-
'host': self.__host,
263+
# Run server on local host.
264+
'host': "localhost",
265+
# Run server on the found free port.
264266
'port': self.__port,
265267
# Should be False for stability and threaded mode
266268
'debug': False,
@@ -286,19 +288,21 @@ def hang_forever_stop(self):
286288
# Release any hanging requests
287289
self.hang_forever_stop_flag.set()
288290

289-
def _build_url(self, path: str) -> str:
290-
"""Constructs a full URL for a given path on this server."""
291-
return f"{self.__protocol}://{self.__host}:{self.__port}{path}"
291+
def _build_url(self, host: str | None, path: str) -> str:
292+
"""Constructs a full URL for the given host and path on this server."""
293+
return f"{self.__protocol}://{host if host is not None else self.__default_host}:{self.__port}{path}"
292294

293-
def origin(self) -> str:
294-
"""Returns the origin (scheme://host:port) of the server."""
295-
return f"{self.__protocol}://{self.__host}:{self.__port}"
296-
297-
def url_base(self) -> str:
295+
def url_base(
296+
self,
297+
host: str | None = None,
298+
) -> str:
298299
"""Returns the URL for the base page (used to prevent CORS issues)."""
299-
return self._build_url(self.__path_base)
300+
return self._build_url(
301+
host if host is not None else self.__default_host,
302+
self.__path_base)
300303

301304
def url_200(self,
305+
host: str | None = None,
302306
content: str | None = None,
303307
content_type: str = "text/html",
304308
headers: dict[str, str] | None = None) -> str:
@@ -325,22 +329,22 @@ def url_200(self,
325329
"headers": headers
326330
}
327331
path = f"{self.__path_200}/{response_id}"
328-
return self._build_url(path)
332+
return self._build_url(host, path)
329333

330-
return self._build_url(self.__path_200)
334+
return self._build_url(host, self.__path_200)
331335

332-
def url_permanent_redirect(self) -> str:
336+
def url_permanent_redirect(self, host: str | None = None) -> str:
333337
"""Returns the URL for a page that permanently redirects to the default 200 page."""
334-
return self._build_url(self.__path_permanent_redirect)
338+
return self._build_url(host, self.__path_permanent_redirect)
335339

336-
def url_basic_auth(self) -> str:
340+
def url_basic_auth(self, host: str | None = None) -> str:
337341
"""Returns the URL for a page protected by Basic authentication."""
338-
return self._build_url(self.__path_basic_auth)
342+
return self._build_url(host, self.__path_basic_auth)
339343

340-
def url_hang_forever(self) -> str:
344+
def url_hang_forever(self, host: str | None = None) -> str:
341345
"""Returns the URL for a page that will hang until `hang_forever_stop()` is called."""
342-
return self._build_url(self.__path_hang_forever)
346+
return self._build_url(host, self.__path_hang_forever)
343347

344-
def url_cacheable(self) -> str:
348+
def url_cacheable(self, host: str | None = None) -> str:
345349
"""Returns the URL for a cacheable page (using Last-Modified and If-Modified-Since)."""
346-
return self._build_url(self.__path_cacheable)
350+
return self._build_url(host, self.__path_cacheable)

0 commit comments

Comments
 (0)