mirror of
https://github.com/pyscript/pyscript.git
synced 2026-02-23 08:03:01 -05:00
Introduce/improve check_js_errors and improve test_no_implicit_target (#874)
Until now, we didn't have a nice way to check that we expect a specific JS error in the web page. This PR improves check_js_errors() so that now you can pass a list of error messages that you expect. It is tricky because we need to handle (and test!) all various combinations of cases: - errors expected and found / expected but not found - unexpected errors found / not found Moreover, JS exceptions now are logged in the special category console.js_error, which means that the printed text is also available using e.g. self.console.js_error.text or self.console.all.text. However, this should never be required and it's preferred to use self.check_js_errors to check for exceptions. This fixes #795 . Finally, use the new logic to improve test_no_implicit_target.
This commit is contained in:
@@ -165,7 +165,15 @@ function parseConfig(configText: string, configType = "toml") {
|
||||
catch (err) {
|
||||
const errMessage: string = err.toString();
|
||||
showError(`<p>config supplied: ${configText} is an invalid TOML and cannot be parsed: ${errMessage}</p>`);
|
||||
throw err;
|
||||
// we cannot easily just "throw err" here, because for some reason
|
||||
// playwright gets confused by it and cannot print it
|
||||
// correctly. It is just displayed as an empty error.
|
||||
// If you print err in JS, you get something like this:
|
||||
// n {message: '...', offset: 19, line: 2, column: 19}
|
||||
// I think that 'n' is the minified name?
|
||||
// The workaround is to re-wrap the message into SyntaxError(), so that
|
||||
// it's correctly handled by playwright.
|
||||
throw SyntaxError(errMessage);
|
||||
}
|
||||
}
|
||||
else if (configType === "json") {
|
||||
|
||||
@@ -28,9 +28,9 @@ class PyScriptTest:
|
||||
- self.console collects all the JS console.* messages. Look at the doc
|
||||
of ConsoleMessageCollection for more details.
|
||||
|
||||
- self.check_errors() checks that no JS errors have been thrown
|
||||
- self.check_js_errors() checks that no JS errors have been thrown
|
||||
|
||||
- after each test, self.check_errors() is automatically run to ensure
|
||||
- after each test, self.check_js_errors() is automatically run to ensure
|
||||
that no JS error passes uncaught.
|
||||
|
||||
- self.wait_for_console waits until the specified message appears in the
|
||||
@@ -113,47 +113,74 @@ class PyScriptTest:
|
||||
page.set_default_timeout(60000)
|
||||
|
||||
self.console = ConsoleMessageCollection(self.logger)
|
||||
self._page_errors = []
|
||||
page.on("console", self.console.add_message)
|
||||
self._js_errors = []
|
||||
page.on("console", self._on_console)
|
||||
page.on("pageerror", self._on_pageerror)
|
||||
|
||||
def teardown_method(self):
|
||||
# we call check_errors on teardown: this means that if there are still
|
||||
# we call check_js_errors on teardown: this means that if there are still
|
||||
# non-cleared errors, the test will fail. If you expect errors in your
|
||||
# page and they should not cause the test to fail, you should call
|
||||
# self.check_errors() in the test itself.
|
||||
self.check_errors()
|
||||
# self.check_js_errors() in the test itself.
|
||||
self.check_js_errors()
|
||||
|
||||
def _on_console(self, msg):
|
||||
self.console.add_message(msg.type, msg.text)
|
||||
|
||||
def _on_pageerror(self, error):
|
||||
self.logger.log("JS exception", error.stack, color="red")
|
||||
self._page_errors.append(error)
|
||||
self.console.add_message("js_error", error.stack)
|
||||
self._js_errors.append(error)
|
||||
|
||||
def check_errors(self):
|
||||
def check_js_errors(self, *expected_messages):
|
||||
"""
|
||||
Check whether JS errors were reported.
|
||||
|
||||
If it finds a single JS error, raise JsError.
|
||||
If it finds multiple JS errors, raise JsMultipleErrors.
|
||||
expected_messages is a list of strings of errors that you expect they
|
||||
were raised in the page. They are checked using a simple 'in' check,
|
||||
equivalent to this:
|
||||
if expected_message in actual_error_message:
|
||||
...
|
||||
|
||||
If an error was expected but not found, it raises
|
||||
DidNotRaiseJsError().
|
||||
|
||||
If there are MORE errors other than the expected ones, it raises JsErrors.
|
||||
|
||||
Upon return, all the errors are cleared, so a subsequent call to
|
||||
check_errors will not raise, unless NEW JS errors have been reported
|
||||
check_js_errors will not raise, unless NEW JS errors have been reported
|
||||
in the meantime.
|
||||
"""
|
||||
exc = None
|
||||
if len(self._page_errors) == 1:
|
||||
# if there is a single error, wrap it
|
||||
exc = JsError(self._page_errors[0])
|
||||
elif len(self._page_errors) >= 2:
|
||||
exc = JsMultipleErrors(self._page_errors)
|
||||
self._page_errors = []
|
||||
if exc:
|
||||
raise exc
|
||||
expected_messages = list(expected_messages)
|
||||
js_errors = self._js_errors[:]
|
||||
|
||||
def clear_errors(self):
|
||||
for i, msg in enumerate(expected_messages):
|
||||
for j, error in enumerate(js_errors):
|
||||
if msg is not None and error is not None and msg in error.message:
|
||||
# we matched one expected message with an error, remove both
|
||||
expected_messages[i] = None
|
||||
js_errors[j] = None
|
||||
|
||||
# if everything is find, now expected_messages and js_errors contains
|
||||
# only Nones. If they contain non-None elements, it means that we
|
||||
# either have messages which are expected-but-not-found or errors
|
||||
# which are found-but-not-expected.
|
||||
expected_messages = [msg for msg in expected_messages if msg is not None]
|
||||
js_errors = [err for err in js_errors if err is not None]
|
||||
self.clear_js_errors()
|
||||
|
||||
if expected_messages:
|
||||
# expected-but-not-found
|
||||
raise JsErrorsDidNotRaise(expected_messages, js_errors)
|
||||
|
||||
if js_errors:
|
||||
# found-but-not-expected
|
||||
raise JsErrors(js_errors)
|
||||
|
||||
def clear_js_errors(self):
|
||||
"""
|
||||
Clear all JS errors.
|
||||
"""
|
||||
self._page_errors = []
|
||||
self._js_errors = []
|
||||
|
||||
def writefile(self, filename, content):
|
||||
"""
|
||||
@@ -168,7 +195,7 @@ class PyScriptTest:
|
||||
url = f"{self.http_server}/{path}"
|
||||
self.page.goto(url, timeout=0)
|
||||
|
||||
def wait_for_console(self, text, *, timeout=None, check_errors=True):
|
||||
def wait_for_console(self, text, *, timeout=None, check_js_errors=True):
|
||||
"""
|
||||
Wait until the given message appear in the console.
|
||||
|
||||
@@ -179,7 +206,7 @@ class PyScriptTest:
|
||||
timeout is expressed in milliseconds. If it's None, it will use
|
||||
playwright's own default value, which is 30 seconds).
|
||||
|
||||
If check_errors is True (the default), it also checks that no JS
|
||||
If check_js_errors is True (the default), it also checks that no JS
|
||||
errors were raised during the waiting.
|
||||
"""
|
||||
pred = lambda msg: msg.text == text
|
||||
@@ -192,24 +219,24 @@ class PyScriptTest:
|
||||
# the JsError will shadow the TimeoutError but this is correct,
|
||||
# because it's very likely that the console message never appeared
|
||||
# precisely because of the exception in JS.
|
||||
if check_errors:
|
||||
self.check_errors()
|
||||
if check_js_errors:
|
||||
self.check_js_errors()
|
||||
|
||||
def wait_for_pyscript(self, *, timeout=None, check_errors=True):
|
||||
def wait_for_pyscript(self, *, timeout=None, check_js_errors=True):
|
||||
"""
|
||||
Wait until pyscript has been fully loaded.
|
||||
|
||||
Timeout is expressed in milliseconds. If it's None, it will use
|
||||
playwright's own default value, which is 30 seconds).
|
||||
|
||||
If check_errors is True (the default), it also checks that no JS
|
||||
If check_js_errors is True (the default), it also checks that no JS
|
||||
errors were raised during the waiting.
|
||||
"""
|
||||
# this is printed by runtime.ts:Runtime.initialize
|
||||
self.wait_for_console(
|
||||
"[pyscript/main] PyScript page fully initialized",
|
||||
timeout=timeout,
|
||||
check_errors=check_errors,
|
||||
check_js_errors=check_js_errors,
|
||||
)
|
||||
# We still don't know why this wait is necessary, but without it
|
||||
# events aren't being triggered in the tests.
|
||||
@@ -251,9 +278,9 @@ class PyScriptTest:
|
||||
# ============== Helpers and utility functions ==============
|
||||
|
||||
|
||||
class JsError(Exception):
|
||||
class JsErrors(Exception):
|
||||
"""
|
||||
Represent an exception which happened in JS.
|
||||
Represent one or more exceptions which happened in JS.
|
||||
|
||||
It's a thin wrapper around playwright.sync_api.Error, with two important
|
||||
differences:
|
||||
@@ -265,9 +292,15 @@ class JsError(Exception):
|
||||
playwright.sync_api.Error
|
||||
"""
|
||||
|
||||
def __init__(self, error):
|
||||
super().__init__(self.format_playwright_error(error))
|
||||
self.error = error
|
||||
def __init__(self, errors):
|
||||
n = len(errors)
|
||||
assert n != 0
|
||||
lines = [f"JS errors found: {n}"]
|
||||
for err in errors:
|
||||
lines.append(self.format_playwright_error(err))
|
||||
msg = "\n".join(lines)
|
||||
super().__init__(msg)
|
||||
self.errors = errors
|
||||
|
||||
@staticmethod
|
||||
def format_playwright_error(error):
|
||||
@@ -278,17 +311,24 @@ class JsError(Exception):
|
||||
return error.stack or str(error)
|
||||
|
||||
|
||||
class JsMultipleErrors(Exception):
|
||||
class JsErrorsDidNotRaise(Exception):
|
||||
"""
|
||||
This is raised in case we get multiple JS errors in the page
|
||||
Exception raised by check_js_errors when the expected JS error messages
|
||||
are not found.
|
||||
"""
|
||||
|
||||
def __init__(self, errors):
|
||||
lines = ["Multiple JS errors found:"]
|
||||
for err in errors:
|
||||
lines.append(JsError.format_playwright_error(err))
|
||||
def __init__(self, expected_messages, errors):
|
||||
lines = ["The following JS errors were expected but could not be found:"]
|
||||
for msg in expected_messages:
|
||||
lines.append(" - " + msg)
|
||||
if errors:
|
||||
lines.append("---")
|
||||
lines.append("The following JS errors were raised but not expected:")
|
||||
for err in errors:
|
||||
lines.append(JsErrors.format_playwright_error(err))
|
||||
msg = "\n".join(lines)
|
||||
super().__init__(msg)
|
||||
self.expected_messages = expected_messages
|
||||
self.errors = errors
|
||||
|
||||
|
||||
@@ -307,9 +347,18 @@ class ConsoleMessageCollection:
|
||||
console.error.*
|
||||
console.warning.*
|
||||
|
||||
console.all.* same as above, but considering all messages, no filters
|
||||
console.js_error.* this is a special category which does not exist in the
|
||||
browser: it prints uncaught JS exceptions
|
||||
|
||||
console.all.* same as the individual categories but considering
|
||||
all messages which were sent to the console
|
||||
"""
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
type: str # 'log', 'info', 'debug', etc.
|
||||
text: str
|
||||
|
||||
class View:
|
||||
"""
|
||||
Filter console messages by the given msg_type
|
||||
@@ -337,8 +386,9 @@ class ConsoleMessageCollection:
|
||||
return "\n".join(self.lines)
|
||||
|
||||
_COLORS = {
|
||||
"error": "red",
|
||||
"warning": "brown",
|
||||
"error": "darkred",
|
||||
"js_error": "red",
|
||||
}
|
||||
|
||||
def __init__(self, logger):
|
||||
@@ -350,10 +400,12 @@ class ConsoleMessageCollection:
|
||||
self.info = self.View(self, "info")
|
||||
self.error = self.View(self, "error")
|
||||
self.warning = self.View(self, "warning")
|
||||
self.js_error = self.View(self, "js_error")
|
||||
|
||||
def add_message(self, msg):
|
||||
# log the message: pytest will capute the output and display the
|
||||
def add_message(self, type, text):
|
||||
# log the message: pytest will capture the output and display the
|
||||
# messages if the test fails.
|
||||
msg = self.Message(type=type, text=text)
|
||||
category = f"console.{msg.type}"
|
||||
color = self._COLORS.get(msg.type)
|
||||
self.logger.log(category, msg.text, color=color)
|
||||
@@ -389,7 +441,7 @@ class Logger:
|
||||
def log(self, category, text, *, color=None):
|
||||
delta = time.time() - self.start_time
|
||||
text = self.colorize_prefix(text, color="teal")
|
||||
line = f"[{delta:6.2f} {category:15}] {text}"
|
||||
line = f"[{delta:6.2f} {category:16}] {text}"
|
||||
if color:
|
||||
line = Color.set(color, line)
|
||||
print(line)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
from playwright import sync_api
|
||||
|
||||
from .support import JsError, JsMultipleErrors, PyScriptTest
|
||||
from .support import JsErrors, JsErrorsDidNotRaise, PyScriptTest
|
||||
|
||||
|
||||
class TestSupport(PyScriptTest):
|
||||
@@ -75,7 +76,7 @@ class TestSupport(PyScriptTest):
|
||||
assert self.console.log.lines == ["my log 1", "my log 2"]
|
||||
assert self.console.debug.lines == ["my debug"]
|
||||
|
||||
def test_check_errors(self):
|
||||
def test_check_js_errors_simple(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
@@ -85,18 +86,68 @@ class TestSupport(PyScriptTest):
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsError) as exc:
|
||||
self.check_errors()
|
||||
with pytest.raises(JsErrors) as exc:
|
||||
self.check_js_errors()
|
||||
# check that the exception message contains the error message and the
|
||||
# stack trace
|
||||
msg = str(exc.value)
|
||||
assert "Error: this is an error" in msg
|
||||
assert f"at {self.http_server}/mytest.html" in msg
|
||||
expected = textwrap.dedent(
|
||||
f"""
|
||||
JS errors found: 1
|
||||
Error: this is an error
|
||||
at {self.http_server}/mytest.html:.*
|
||||
"""
|
||||
).strip()
|
||||
assert re.search(expected, msg)
|
||||
#
|
||||
# after a call to check_errors, the errors are cleared
|
||||
self.check_errors()
|
||||
# after a call to check_js_errors, the errors are cleared
|
||||
self.check_js_errors()
|
||||
#
|
||||
# JS exceptions are also available in self.console.js_error
|
||||
assert self.console.js_error.lines[0].startswith("Error: this is an error")
|
||||
|
||||
def test_check_errors_multiple(self):
|
||||
def test_check_js_errors_expected(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
<script>throw new Error('this is an error');</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
self.check_js_errors("this is an error")
|
||||
|
||||
def test_check_js_errors_expected_but_didnt_raise(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
<script>throw new Error('this is an error 2');</script>
|
||||
<script>throw new Error('this is an error 4');</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsErrorsDidNotRaise) as exc:
|
||||
self.check_js_errors(
|
||||
"this is an error 1",
|
||||
"this is an error 2",
|
||||
"this is an error 3",
|
||||
"this is an error 4",
|
||||
)
|
||||
#
|
||||
msg = str(exc.value)
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
The following JS errors were expected but could not be found:
|
||||
- this is an error 1
|
||||
- this is an error 3
|
||||
"""
|
||||
).strip()
|
||||
assert re.search(expected, msg)
|
||||
|
||||
def test_check_js_errors_multiple(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
@@ -107,15 +158,82 @@ class TestSupport(PyScriptTest):
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsMultipleErrors) as exc:
|
||||
self.check_errors()
|
||||
assert "error 1" in str(exc.value)
|
||||
assert "error 2" in str(exc.value)
|
||||
with pytest.raises(JsErrors) as exc:
|
||||
self.check_js_errors()
|
||||
#
|
||||
msg = str(exc.value)
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
JS errors found: 2
|
||||
Error: error 1
|
||||
at http://fake_server/mytest.html:.*
|
||||
Error: error 2
|
||||
at http://fake_server/mytest.html:.*
|
||||
"""
|
||||
).strip()
|
||||
assert re.search(expected, msg)
|
||||
#
|
||||
# check that errors are cleared
|
||||
self.check_errors()
|
||||
self.check_js_errors()
|
||||
|
||||
def test_clear_errors(self):
|
||||
def test_check_js_errors_some_expected_but_others_not(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
<script>throw new Error('expected 1');</script>
|
||||
<script>throw new Error('NOT expected 2');</script>
|
||||
<script>throw new Error('expected 3');</script>
|
||||
<script>throw new Error('NOT expected 4');</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsErrors) as exc:
|
||||
self.check_js_errors("expected 1", "expected 3")
|
||||
#
|
||||
msg = str(exc.value)
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
JS errors found: 2
|
||||
Error: NOT expected 2
|
||||
at http://fake_server/mytest.html:.*
|
||||
Error: NOT expected 4
|
||||
at http://fake_server/mytest.html:.*
|
||||
"""
|
||||
).strip()
|
||||
assert re.search(expected, msg)
|
||||
|
||||
def test_check_js_errors_expected_not_found_but_other_errors(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
<script>throw new Error('error 1');</script>
|
||||
<script>throw new Error('error 2');</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsErrorsDidNotRaise) as exc:
|
||||
self.check_js_errors("this is not going to be found")
|
||||
#
|
||||
msg = str(exc.value)
|
||||
expected = textwrap.dedent(
|
||||
"""
|
||||
The following JS errors were expected but could not be found:
|
||||
- this is not going to be found
|
||||
---
|
||||
The following JS errors were raised but not expected:
|
||||
Error: error 1
|
||||
at http://fake_server/mytest.html:.*
|
||||
Error: error 2
|
||||
at http://fake_server/mytest.html:.*
|
||||
"""
|
||||
).strip()
|
||||
assert re.search(expected, msg)
|
||||
|
||||
def test_clear_js_errors(self):
|
||||
doc = """
|
||||
<html>
|
||||
<body>
|
||||
@@ -125,10 +243,10 @@ class TestSupport(PyScriptTest):
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
self.clear_errors()
|
||||
# self.check_errors does not raise, because the errors have been
|
||||
self.clear_js_errors()
|
||||
# self.check_js_errors does not raise, because the errors have been
|
||||
# cleared
|
||||
self.check_errors()
|
||||
self.check_js_errors()
|
||||
|
||||
def test_wait_for_console(self):
|
||||
"""
|
||||
@@ -177,19 +295,19 @@ class TestSupport(PyScriptTest):
|
||||
self.writefile("mytest.html", doc)
|
||||
# "Page loaded!" will never appear, of course.
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsError) as exc:
|
||||
with pytest.raises(JsErrors) as exc:
|
||||
self.wait_for_console("Page loaded!", timeout=200)
|
||||
assert "this is an error" in str(exc.value)
|
||||
assert isinstance(exc.value.__context__, sync_api.TimeoutError)
|
||||
#
|
||||
# if we use check_errors=False, the error are ignored, but we get the
|
||||
# if we use check_js_errors=False, the error are ignored, but we get the
|
||||
# Timeout anyway
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(sync_api.TimeoutError):
|
||||
self.wait_for_console("Page loaded!", timeout=200, check_errors=False)
|
||||
# we still got a JsError, so we need to manually clear it, else the
|
||||
self.wait_for_console("Page loaded!", timeout=200, check_js_errors=False)
|
||||
# we still got a JsErrors, so we need to manually clear it, else the
|
||||
# test fails at teardown
|
||||
self.clear_errors()
|
||||
self.clear_js_errors()
|
||||
|
||||
def test_wait_for_console_exception_2(self):
|
||||
"""
|
||||
@@ -210,13 +328,13 @@ class TestSupport(PyScriptTest):
|
||||
"""
|
||||
self.writefile("mytest.html", doc)
|
||||
self.goto("mytest.html")
|
||||
with pytest.raises(JsError) as exc:
|
||||
with pytest.raises(JsErrors) as exc:
|
||||
self.wait_for_console("Page loaded!", timeout=200)
|
||||
assert "this is an error" in str(exc.value)
|
||||
#
|
||||
# with check_errors=False, the Error is ignored and the
|
||||
# with check_js_errors=False, the Error is ignored and the
|
||||
# wait_for_console succeeds
|
||||
self.goto("mytest.html")
|
||||
self.wait_for_console("Page loaded!", timeout=200, check_errors=False)
|
||||
self.wait_for_console("Page loaded!", timeout=200, check_js_errors=False)
|
||||
# clear the errors, else the test fails at teardown
|
||||
self.clear_errors()
|
||||
self.clear_js_errors()
|
||||
|
||||
@@ -95,17 +95,9 @@ class TestOutput(PyScriptTest):
|
||||
self.page.locator("text=Click me").click()
|
||||
text = self.page.text_content("body")
|
||||
assert "hello" not in text
|
||||
|
||||
# currently the test infrastructure doesn't allow to easily assert that
|
||||
# js exceptions were raised this is a workaround but we need a better fix.
|
||||
# Antonio promised to write it
|
||||
assert len(self._page_errors) == 1
|
||||
console_text = self._page_errors
|
||||
assert (
|
||||
self.check_js_errors(
|
||||
"Implicit target not allowed here. Please use display(..., target=...)"
|
||||
in console_text[0].message
|
||||
)
|
||||
self._page_errors = []
|
||||
|
||||
def test_explicit_target_pyscript_tag(self):
|
||||
self.pyscript_run(
|
||||
|
||||
@@ -5,7 +5,7 @@ import tempfile
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from .support import JsError, PyScriptTest
|
||||
from .support import PyScriptTest
|
||||
|
||||
URL = "https://github.com/pyodide/pyodide/releases/download/0.20.0/pyodide-build-0.20.0.tar.bz2"
|
||||
TAR_NAME = "pyodide-build-0.20.0.tar.bz2"
|
||||
@@ -105,29 +105,32 @@ class TestConfig(PyScriptTest):
|
||||
assert version == "0.20.0"
|
||||
|
||||
def test_invalid_json_config(self):
|
||||
with pytest.raises(JsError) as exc:
|
||||
self.pyscript_run(
|
||||
snippet="""
|
||||
<py-config type="json">
|
||||
[[
|
||||
</py-config>
|
||||
"""
|
||||
)
|
||||
|
||||
msg = str(exc.value)
|
||||
assert "SyntaxError" in msg
|
||||
# we need wait_for_pyscript=False because we bail out very soon,
|
||||
# before being able to write 'PyScript page fully initialized'
|
||||
self.pyscript_run(
|
||||
"""
|
||||
<py-config type="json">
|
||||
[[
|
||||
</py-config>
|
||||
""",
|
||||
wait_for_pyscript=False,
|
||||
)
|
||||
self.page.wait_for_selector(".py-error")
|
||||
self.check_js_errors("Unexpected end of JSON input")
|
||||
|
||||
def test_invalid_toml_config(self):
|
||||
with pytest.raises(JsError) as exc:
|
||||
self.pyscript_run(
|
||||
snippet="""
|
||||
<py-config>
|
||||
[[
|
||||
</py-config>
|
||||
"""
|
||||
)
|
||||
msg = str(exc)
|
||||
assert "<ExceptionInfo JsError" in msg
|
||||
# we need wait_for_pyscript=False because we bail out very soon,
|
||||
# before being able to write 'PyScript page fully initialized'
|
||||
self.pyscript_run(
|
||||
"""
|
||||
<py-config>
|
||||
[[
|
||||
</py-config>
|
||||
""",
|
||||
wait_for_pyscript=False,
|
||||
)
|
||||
self.page.wait_for_selector(".py-error")
|
||||
self.check_js_errors("SyntaxError: Expected DoubleQuote")
|
||||
|
||||
def test_multiple_py_config(self):
|
||||
self.pyscript_run(
|
||||
|
||||
@@ -109,6 +109,7 @@ class TestExamples(PyScriptTest):
|
||||
assert self.page.title() == "Bokeh Example"
|
||||
wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
|
||||
|
||||
@pytest.mark.skip("flaky, see issue 759")
|
||||
def test_d3(self):
|
||||
self.goto("examples/d3.html")
|
||||
self.wait_for_pyscript()
|
||||
|
||||
Reference in New Issue
Block a user