8000 Improve self.wait_for_console() (#1363) · ocdex/pyscript@af981fc · GitHub
[go: up one dir, main page]

Skip to content

Commit af981fc

Browse files
authored
Improve self.wait_for_console() (pyscript#1363)
- Previously, if the message appeared on the console immediately before the call to self.wait_for_console(), the call would hang forever until the timeout. Now, it returns immediately. - `wait_for_pyscript` now logs the time actually taken for waiting - `wait_for_pyscript` takes a `timeout` argument so that we can tweak it on a test-by-test basis
1 parent 088a264 commit af981fc

File tree

3 files changed

+77
-25
lines changed

3 files changed

+77
-25
lines changed

pyscriptjs/tests/integration/support.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -221,25 +221,36 @@ def goto(self, path):
221221

222222
def wait_for_console(self, text, *, timeout=None, check_js_errors=True):
223223
"""
224-
Wait until the given message appear in the console.
224+
Wait until the given message appear in the console. If the message was
225+
already printed in the console, return immediately.
225226
226227
Note: it must be the *exact* string as printed by e.g. console.log.
227-
If you need more control on the predicate (e.g. if you want to match a
228-
substring), use self.page.expect_console_message directly.
229228
230229
timeout is expressed in milliseconds. If it's None, it will use
231-
playwright's own default value, which is 30 seconds).
230+
the same default as playwright, which is 30 seconds.
232231
233232
If check_js_errors is True (the default), it also checks that no JS
234233
errors were raised during the waiting.
235-
"""
236-
237-
def pred(msg):
238-
return msg.text == text
239234
235+
Return the elapsed time in ms.
236+
"""
237+
if timeout is None:
238+
timeout = 30 * 1000
239+
# NOTE: we cannot use playwright's own page.expect_console_message(),
240+
# because if you call it AFTER the text has already been emitted, it
241+
# waits forever. Instead, we have to use our own custom logic.
240242
try:
241-
with self.page.expect_console_message(pred, timeout=timeout):
242-
pass
243+
t0 = time.time()
244+
while True:
245+
elapsed_ms = (time.time() - t0) * 1000
246+
if elapsed_ms > timeout:
247+
raise TimeoutError(f"{elapsed_ms:.2f} ms")
248+
#
249+
if text in self.console.all.lines:
250+
# found it!
251+
return elapsed_ms
252+
#
253+
self.page.wait_for_timeout(50)
243254
finally:
244255
# raise JsError if there were any javascript exception. Note that
245256
# this might happen also in case of a TimeoutError. In that case,
@@ -260,16 +271,21 @@ def wait_for_pyscript(self, *, timeout=None, check_js_errors=True):
260271
errors were raised during the waiting.
261272
"""
262273
# this is printed by interpreter.ts:Interpreter.initialize
263-
self.wait_for_console(
274+
elapsed_ms = self.wait_for_console(
264275
"[pyscript/main] PyScript page fully initialized",
265276
timeout=timeout,
266277
check_js_errors=check_js_errors,
267278
)
279+
self.logger.log(
280+
"wait_for_pyscript", f"Waited for {elapsed_ms/1000:.2f} s", color="yellow"
281+
)
268282
# We still don't know why this wait is necessary, but without it
269283
# events aren't being triggered in the tests.
270284
self.page.wait_for_timeout(100)
271285

272-
def pyscript_run(self, snippet, *, extra_head="", wait_for_pyscript=True):
286+
def pyscript_run(
287+
self, snippet, *, extra_head="", wait_for_pyscript=True, timeout=None
288+
):
273289
"""
274290
Main entry point for pyscript tests.
275291
@@ -295,11 +311,13 @@ def pyscript_run(self, snippet, *, extra_head="", wait_for_pyscript=True):
295311
</body>
296312
</html>
297313
"""
314+
if not wait_for_pyscript and timeout is not None:
315+
raise ValueError("Cannot set a timeout if wait_for_pyscript=False")
298316
filename = f"{self.testname}.html"
299317
self.writefile(filename, doc)
300318
self.goto(filename)
301319
if wait_for_pyscript:
302-
self.wait_for_pyscript()
320+
self.wait_for_pyscript(timeout=timeout)
303321

304322
def iter_locator(self, loc):
305323
"""
@@ -594,7 +612,7 @@ def colorize_prefix(self, text, *, color):
594612
def log(self, category, text, *, color=None):
595613
delta = time.time() - self.start_time
596614
text = self.colorize_prefix(text, color="teal")
597-
line = f"[{delta:6.2f} {category:16}] {text}"
615+
line = f"[{delta:6.2f} {category:17}] {text}"
598616
if color:
599617
line = Color.set(color, line)
600618
print(line)

pyscriptjs/tests/integration/test_00_support.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import textwrap
33

44
import pytest
5-
from playwright import sync_api
65

76
from .support import JsErrors, JsErrorsDidNotRaise, PyScriptTest
87

@@ -266,7 +265,7 @@ def test_clear_js_errors(self):
266265
# cleared
267266
self.check_js_errors()
268267

269-
def test_wait_for_console(self):
268+
def test_wait_for_console_simple(self):
270269
"""
271270
Test that self.wait_for_console actually waits.
272271
If it's buggy, the test will try to read self.console.log BEFORE the
@@ -285,11 +284,46 @@ def test_wait_for_console(self):
285284
"""
286285
self.writefile("mytest.html", doc)
287286
self.goto("mytest.html")
288-
# we use a timeout of 500ms to give plenty of time to the page to
287+
# we use a timeout of 200ms to give plenty of time to the page to
289288
# actually run the setTimeout callback
290289
self.wait_for_console("Page loaded!", timeout=200)
291290
assert self.console.log.lines[-1] == "Page loaded!"
292291

292+
def test_wait_for_console_timeout(self):
293+
doc = """
294+
<html>
295+
<body>
296+
</body>
297+
</html>
298+
"""
299+
self.writefile("mytest.html", doc)
300+
self.goto("mytest.html")
301+
with pytest.raises(TimeoutError):
302+
self.wait_for_console("This text will never be printed", timeout=200)
303+
304+
def test_wait_for_console_dont_wait_if_already_emitted(self):
305+
"""
306+
If the text is already on the console, wait_for_console() should return
307+
immediately without waiting.
308+
"""
309+
doc = """
310+
<html>
311+
<body>
312+
<script>
313+
console.log('Hello world')
314+
console.log('Page loaded!');
315+
</script>
316+
</body>
317+
</html>
318+
"""
319+
self.writefile("mytest.html", doc)
320+
self.goto("mytest.html")
321+
self.wait_for_console("Page loaded!", timeout=200)
322+
assert self.console.log.lines[-2] == "Hello world"
323+
assert self.console.log.lines[-1] == "Page loaded!"
324+
# the following call should return immediately without waiting
325+
self.wait_for_console("Hello world", timeout=1)
326+
293327
def test_wait_for_console_exception_1(self):
294328
"""
295329
Test that if a JS exception is raised while waiting for the console, we
@@ -316,12 +350,12 @@ def test_wait_for_console_exception_1(self):
316350
with pytest.raises(JsErrors) as exc:
317351
self.wait_for_console("Page loaded!", timeout=200)
318352
assert "this is an error" in str(exc.value)
319-
assert isinstance(exc.value.__context__, sync_api.TimeoutError)
353+
assert isinstance(exc.value.__context__, TimeoutError)
320354
#
321355
# if we use check_js_errors=False, the error are ignored, but we get the
322356
# Timeout anyway
323357
self.goto("mytest.html")
324-
with pytest.raises(sync_api.TimeoutError):
358+
with pytest.raises(TimeoutError):
325359
self.wait_for_console("Page loaded!", timeout=200, check_js_errors=False)
326360
# we still got a JsErrors, so we need to manually clear it, else the
327361
# test fails at teardown

pyscriptjs/tests/integration/test_zz_examples.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test_antigravity(self):
102102
def test_bokeh(self):
103103
# XXX improve this test
104104
self.goto("examples/bokeh.html")
105-
self.wait_for_pyscript()
105+
self.wait_for_pyscript(timeout=90 * 1000)
106106
assert self.page.title() == "Bokeh Example"
107107
wait_for_render(self.page, "*", '<div.*?class="bk.*".*?>')
108108
self.assert_no_banners()
@@ -111,7 +111,7 @@ def test_bokeh(self):
111111
def test_bokeh_interactive(self):
112112
# XXX improve this test
113113
self.goto("examples/bokeh_interactive.html")
114-
self.wait_for_pyscript()
114+
self.wait_for_pyscript(timeout=90 * 1000)
115115
assert self.page.title() == "Bokeh Example"
116116
wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
117117
self.assert_no_banners()
@@ -248,7 +248,7 @@ def test_numpy_canvas_fractals(self):
248248

249249
def test_panel(self):
250250
self.goto("examples/panel.html")
251-
self.wait_for_pyscript()
251+
self.wait_for_pyscript(timeout=90 * 1000)
252252
assert self.page.title() == "Panel Example"
253253
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
254254
slider_title = self.page.wait_for_selector(".bk-slider-title")
@@ -268,7 +268,7 @@ def test_panel(self):
268268
def test_panel_deckgl(self):
269269
# XXX improve this test
270270
self.goto("examples/panel_deckgl.html")
271-
self.wait_for_pyscript()
271+
self.wait_for_pyscript(timeout=90 * 1000)
272272
assert self.page.title() == "PyScript/Panel DeckGL Demo"
273273
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
274274
self.assert_no_banners()
@@ -277,7 +277,7 @@ def test_panel_deckgl(self):
277277
def test_panel_kmeans(self):
278278
# XXX improve this test
279279
self.goto("examples/panel_kmeans.html")
280-
self.wait_for_pyscript()
280+
self.wait_for_pyscript(timeout=90 * 1000)
281281
assert self.page.title() == "Pyscript/Panel KMeans Demo"
282282
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
283283
self.assert_no_banners()
@@ -286,7 +286,7 @@ def test_panel_kmeans(self):
286286
def test_panel_stream(self):
287287
# XXX improve this test
288288
self.goto("examples/panel_stream.html")
289-
self.wait_for_pyscript()
289+
self.wait_for_pyscript(timeout=90 * 1000)
290290
assert self.page.title() == "PyScript/Panel Streaming Demo"
291291
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
292292
self.assert_no_banners()

0 commit comments

Comments
 (0)
0