Files
pyscript/pyscriptjs/tests/integration/test_py_repl.py
Antonio Cuni 214e39537b Simplify pyrepl.ts and kill base.ts (#884)
Major highlights:

1. Merge&simplify base.ts and pyrepl.ts; kill base.ts
2. improve and extente the py-repl integration tests
3. Reorder the code in pyrepl.ts. This part of the PR doesn't change much of the concrete logic: it's just a sequence of renaming variables, moving code around, group code into functions, killing code which is no longer needed. But the end result is much better and nicer to read, IMHO.

Minor highlights:

1. py-repl now uses the new logic in pyexec.ts to run the code

2. after PR Add display impl, rm outputManage, print and console.log default to browser console #749 py-repl no longer displayed the result of the last evaluated expression (e.g. if you typed 42 and run it, it displayed nothing). This PR re-introduces this behavior, which is what you would expect by a REPL.

3. improve the pytest --dev option: now it implies --no-fake-server so that sourcemaps works automatically

4. improve the names of the CSS classes to be more consistent

5. kill pyrepl.test.ts: the old tests didn't check anything useful,  this style of unit test doesn't really add much value if you have good integration tests (which now we have) and trying to revive them was not worth the hassle
2022-10-27 10:10:57 +02:00

232 lines
7.6 KiB
Python

from .support import PyScriptTest
class TestPyRepl(PyScriptTest):
def _replace(self, py_repl, newcode):
"""
Clear the editor and write new code in it.
WARNING: this assumes that the textbox has already the focus
"""
# clear the editor, write new code
self.page.keyboard.press("Control+A")
self.page.keyboard.press("Backspace")
self.page.keyboard.type(newcode)
def test_repl_loads(self):
self.pyscript_run(
"""
<py-repl></py-repl>
"""
)
py_repl = self.page.query_selector("py-repl")
assert py_repl
assert "Python" in py_repl.inner_text()
def test_execute_preloaded_source(self):
"""
Unfortunately it tests two things at once, but it's impossible to write a
smaller test. I think this is the most basic test that we can write.
We test that:
1. the source code that we put in the tag is loaded inside the editor
2. clicking the button executes it
"""
self.pyscript_run(
"""
<py-repl>
print('hello from py-repl')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
src = py_repl.inner_text()
assert "print('hello from py-repl')" in src
py_repl.locator("button").click()
assert self.console.log.lines[-1] == "hello from py-repl"
def test_execute_code_typed_by_the_user(self):
self.pyscript_run(
"""
<py-repl></py-repl>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.type('print("hello")')
py_repl.locator("button").click()
assert self.console.log.lines[-1] == "hello"
def test_execute_on_shift_enter(self):
self.pyscript_run(
"""
<py-repl>
print("hello world")
</py-repl>
"""
)
self.page.keyboard.press("Shift+Enter")
# when we use locator('button').click() the message appears
# immediately, with keyboard.press we need to wait for it. I don't
# really know why it has a different behavior, I didn't investigate
# further.
self.wait_for_console("hello world")
def test_display(self):
self.pyscript_run(
"""
<py-repl>
display('hello world')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.locator("button").click()
out_div = py_repl.locator("div.py-repl-output")
assert out_div.inner_text() == "hello world"
def test_show_last_expression(self):
"""
Test that we display() the value of the last expression, as you would
expect by a REPL
"""
self.pyscript_run(
"""
<py-repl>
42
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.locator("button").click()
out_div = py_repl.locator("div.py-repl-output")
assert out_div.inner_text() == "42"
def test_run_clears_previous_output(self):
"""
Check that we clear the previous output of the cell before executing it
again
"""
self.pyscript_run(
"""
<py-repl>
display('hello world')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
out_div = py_repl.locator("div.py-repl-output")
self.page.keyboard.press("Shift+Enter")
assert out_div.inner_text() == "hello world"
#
# clear the editor, write new code, execute
self._replace(py_repl, "display('another output')")
self.page.keyboard.press("Shift+Enter")
assert out_div.inner_text() == "another output"
def test_python_exception(self):
"""
See also test01_basic::test_python_exception, since it's very similar
"""
self.pyscript_run(
"""
<py-repl>
raise Exception('this is an error')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.locator("button").click()
#
# check that we sent the traceback to the console
tb_lines = self.console.error.lines[-1].splitlines()
assert tb_lines[0] == "[pyexec] Python exception:"
assert tb_lines[1] == "Traceback (most recent call last):"
assert tb_lines[-1] == "Exception: this is an error"
#
# check that we show the traceback in the page
err_pre = py_repl.locator("div.py-repl-output > pre.py-error")
tb_lines = err_pre.inner_text().splitlines()
assert tb_lines[0] == "Traceback (most recent call last):"
assert tb_lines[-1] == "Exception: this is an error"
def test_python_exception_after_previous_output(self):
self.pyscript_run(
"""
<py-repl>
display('hello world')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
out_div = py_repl.locator("div.py-repl-output")
self.page.keyboard.press("Shift+Enter")
assert out_div.inner_text() == "hello world"
#
# clear the editor, write new code, execute
self._replace(py_repl, "0/0")
self.page.keyboard.press("Shift+Enter")
assert "hello world" not in out_div.inner_text()
assert "ZeroDivisionError" in out_div.inner_text()
def test_hide_previous_error_after_successful_run(self):
"""
this tests the fact that a new error div should be created once there's an
error but also that it should disappear automatically once the error
is fixed
"""
self.pyscript_run(
"""
<py-repl>
raise Exception('this is an error')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
out_div = py_repl.locator("div.py-repl-output")
self.page.keyboard.press("Shift+Enter")
assert "this is an error" in out_div.inner_text()
#
self._replace(py_repl, "display('hello')")
self.page.keyboard.press("Shift+Enter")
assert out_div.inner_text() == "hello"
def test_output_attribute(self):
self.pyscript_run(
"""
<py-repl output="mydiv">
display('hello world')
</py-repl>
<hr>
<div id="mydiv"></div>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.locator("button").click()
#
# check that we did NOT write to py-repl-output
out_div = py_repl.locator("div.py-repl-output")
assert out_div.inner_text() == ""
# check that we are using mydiv instead
mydiv = self.page.locator("#mydiv")
assert mydiv.inner_text() == "hello world"
def test_output_attribute_does_not_exist(self):
"""
If we try to use an attribute which doesn't exist, we display an error
instead
"""
self.pyscript_run(
"""
<py-repl output="I-dont-exist">
print('I will not be executed')
</py-repl>
"""
)
py_repl = self.page.locator("py-repl")
py_repl.locator("button").click()
#
out_div = py_repl.locator("div.py-repl-output")
msg = "py-repl ERROR: cannot find the output element #I-dont-exist in the DOM"
assert out_div.inner_text() == msg
assert "I will not be executed" not in self.console.log.text