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:
Antonio Cuni
2022-10-24 16:24:52 +02:00
committed by GitHub
parent f9194cc833
commit 58f7c2137d
6 changed files with 280 additions and 106 deletions

View File

@@ -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()