Skip to content

Commit 14c19db

Browse files
authored
Merge pull request #283 from plotly/andrew/test_timing
Andrew/test timing
2 parents c4cd320 + 11ab28d commit 14c19db

File tree

4 files changed

+85
-5
lines changed

4 files changed

+85
-5
lines changed

ROADMAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Roadmap
22

3+
- [ ] Extract local download check to chromium implementation class
34
- [x] Fix up browser deps error (eliminate in-package analysis)
45
- [x] Switch to process group and kill that to catch all chromium children
56
- [x] Add helpers for running javascript

src/choreographer/protocol/devtools_async_helpers.py

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,52 @@
55
import asyncio
66
from typing import TYPE_CHECKING
77

8+
import logistro
9+
810
if TYPE_CHECKING:
911
from choreographer import Browser, Tab
12+
from choreographer.protocol.devtools_async import Session
1013

1114
from . import BrowserResponse
1215

16+
_logger = logistro.getLogger(__name__)
17+
18+
# Abit about the mechanics of chrome:
19+
# Whether or not a Page.loadEventFired event fires is a bit
20+
# racey. Optimistically, it's buffered and fired after subscription
21+
# even if the event happened in the past.
22+
# Doesn't seem to always work out that way, so we also use
23+
# javascript to create a "loaded" event, but for the case
24+
# where we need to timeout- loading a page that never resolves,
25+
# the browser might actually load an about:blank instead and then
26+
# fire the event, misleading the user, so we check the url.
27+
28+
29+
async def _check_document_ready(session: Session, url: str) -> BrowserResponse:
30+
return await session.send_command(
31+
"Runtime.evaluate",
32+
params={
33+
"expression": """
34+
new Promise((resolve) => {
35+
if (
36+
(document.readyState === 'complete') &&
37+
(window.location==`""" # CONCATENATE!
38+
f"{url!s}"
39+
"""`)
40+
){
41+
resolve("Was complete");
42+
} else {
43+
window.addEventListener(
44+
'load', () => resolve("Event loaded")
45+
);
46+
}
47+
})
48+
""",
49+
"awaitPromise": True,
50+
"returnByValue": True,
51+
},
52+
)
53+
1354

1455
async def create_and_wait(
1556
browser: Browser,
@@ -29,24 +70,61 @@ async def create_and_wait(
2970
The created Tab
3071
3172
"""
73+
_logger.debug("Creating tab")
3274
tab = await browser.create_tab(url)
75+
_logger.debug("Creating session")
3376
temp_session = await tab.create_session()
3477

3578
try:
79+
_logger.debug("Subscribing to loadEven and enabling events.")
3680
load_future = temp_session.subscribe_once("Page.loadEventFired")
3781
await temp_session.send_command("Page.enable")
3882
await temp_session.send_command("Runtime.enable")
3983

4084
if url:
4185
try:
42-
await asyncio.wait_for(load_future, timeout=timeout)
43-
except (asyncio.TimeoutError, asyncio.CancelledError, TimeoutError):
86+
# JavaScript evaluation to check if document is loaded
87+
js_ready_future = asyncio.create_task(
88+
_check_document_ready(temp_session, url),
89+
)
90+
_logger.debug(f"Starting wait: timeout={timeout}")
91+
# Race between the two methods: first one to complete wins
92+
done, pending = await asyncio.wait(
93+
[
94+
load_future,
95+
js_ready_future,
96+
],
97+
return_when=asyncio.FIRST_COMPLETED,
98+
timeout=timeout,
99+
)
100+
_logger.debug(f"Finish wait, is done? {bool(done)}")
101+
102+
for task in pending:
103+
_logger.debug(f"Cancelling: {task}")
104+
task.cancel()
105+
106+
if not done:
107+
_logger.debug("Timeout waiting for js or event")
108+
raise asyncio.TimeoutError( # noqa: TRY301
109+
"Page load timeout",
110+
)
111+
else:
112+
_logger.debug(f"Task which finished: {done}")
113+
114+
except (
115+
asyncio.TimeoutError,
116+
asyncio.CancelledError,
117+
TimeoutError,
118+
) as e:
44119
# Stop the page load when timeout occurs
120+
_logger.debug("Need to stop page loading, error.", exc_info=e)
45121
await temp_session.send_command("Page.stopLoading")
46122
raise
47123
finally:
124+
_logger.debug("Closing session")
48125
await tab.close_session(temp_session.session_id)
49126

127+
_logger.debug("Returning tab.")
50128
return tab
51129

52130

@@ -71,9 +149,10 @@ async def navigate_and_wait(
71149
temp_session = await tab.create_session()
72150

73151
try:
152+
# Subscribe BEFORE enabling domains to avoid race condition
153+
load_future = temp_session.subscribe_once("Page.loadEventFired")
74154
await temp_session.send_command("Page.enable")
75155
await temp_session.send_command("Runtime.enable")
76-
load_future = temp_session.subscribe_once("Page.loadEventFired")
77156
try:
78157

79158
async def _freezers() -> None:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version": "144.0.7527.0", "revision": "1544685", "downloads": {"chrome": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/linux64/chrome-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-arm64/chrome-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-x64/chrome-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win32/chrome-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win64/chrome-win64.zip"}], "chromedriver": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/linux64/chromedriver-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-arm64/chromedriver-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-x64/chromedriver-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win32/chromedriver-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win64/chromedriver-win64.zip"}], "chrome-headless-shell": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/linux64/chrome-headless-shell-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-arm64/chrome-headless-shell-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/mac-x64/chrome-headless-shell-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win32/chrome-headless-shell-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/144.0.7527.0/win64/chrome-headless-shell-win64.zip"}]}}
1+
{"version": "142.0.7444.175", "revision": "1522585", "downloads": {"chrome": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/linux64/chrome-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-arm64/chrome-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-x64/chrome-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win32/chrome-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win64/chrome-win64.zip"}], "chromedriver": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/linux64/chromedriver-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-arm64/chromedriver-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-x64/chromedriver-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win32/chromedriver-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win64/chromedriver-win64.zip"}], "chrome-headless-shell": [{"platform": "linux64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/linux64/chrome-headless-shell-linux64.zip"}, {"platform": "mac-arm64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-arm64/chrome-headless-shell-mac-arm64.zip"}, {"platform": "mac-x64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/mac-x64/chrome-headless-shell-mac-x64.zip"}, {"platform": "win32", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win32/chrome-headless-shell-win32.zip"}, {"platform": "win64", "url": "https://storage.googleapis.com/chrome-for-testing-public/142.0.7444.175/win64/chrome-headless-shell-win64.zip"}]}}

tests/test_devtools_async_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def test_create_and_wait(browser):
2828
initial_tab_count = len(browser.tabs)
2929

3030
# Create a simple HTML page as a data URL
31-
data_url = "chrome://version"
31+
data_url = "https://www.example.com"
3232

3333
# Test 1: Create tab with data URL - should succeed
3434
tab1 = await create_and_wait(browser, url=data_url, timeout=5.0)

0 commit comments

Comments
 (0)