mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-20 02:37:41 -05:00
* add test and example files * update config to include python plugins in build * add markdown plugin * remove full pyscript execution from pyodide * move loading of pyscript.py from pyodide loagInterpreter to main setupVirtualEnv and add function to create python CE plugins * add plugin class to pyscript.py * add missing import * fix plugin path * add fetchPythonPlugins to PyScriptApp * remove old comments * fix test * add support for python plugins beyond custom elements and add app to python namespace in main * inject reference to PyScript app onto python plugins * add example hook onto markdown plugin * change plugin events logs * remove unused PyPlugin * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix type import * add docstring to fetchPythonPlugins * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename addPythonPlugin method * address PR comment * call python plugins on hooks after the interpreted is ready * add test for event hooks and split the test in 2 separate plugins to isolte type of plugins tests * change python plugins initialization and registration, to inject the app from app itself instead of on the plugins themselves * handle case when plugin cannot load due to missing plugin attribute * add test for fail scenario when a plugin module does not have a plugin attribute * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add deprecation warning for pyscript objects loaded in global namespace * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove all from global scope * remove create_custom_element from global scope * rename create_custom_element to define_custom_element * rename attributes in define_custom_element and add docstrings * better handle connect event output * add warning to py_markdown plugin * remove debugging logs * improve tests * remove debugging log * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove unused import * add executable shebang * add pyodide mock module * fmt and lint * Update to pyodide.ffi.create_proxy per pyodide v21 api change * Mock pyodide as package instead of mdoule * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add __init__ to pyodide package * Update pyscriptjs/src/plugin.ts fix logger name Co-authored-by: Antonio Cuni <anto.cuni@gmail.com> * fix pyodide import but handling the diff in their API change * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * oops, conflict resolution blooper * Fix failing integration tests Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jeff Glass <glass.jeffrey@gmail.com> Co-authored-by: Antonio Cuni <anto.cuni@gmail.com> Co-authored-by: FabioRosado <fabiorosado@outlook.com>
323 lines
10 KiB
Python
323 lines
10 KiB
Python
import html
|
|
import re
|
|
|
|
from .support import PyScriptTest
|
|
|
|
|
|
class TestOutput(PyScriptTest):
|
|
def test_simple_display(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello world')
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_html = self.page.content()
|
|
pattern = r'<div id="py-.*">hello world</div>'
|
|
assert re.search(pattern, inner_html)
|
|
|
|
def test_consecutive_display(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello 1')
|
|
</py-script>
|
|
<p>hello 2</p>
|
|
<py-script>
|
|
display('hello 3')
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_text = self.page.inner_text("body")
|
|
lines = inner_text.splitlines()
|
|
lines = [line for line in lines if line != ""] # remove empty lines
|
|
assert lines == ["hello 1", "hello 2", "hello 3"]
|
|
|
|
def test_output_attribute_shows_deprecated_warning(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script output="myDiv">
|
|
display('hello world')
|
|
</py-script>
|
|
<div id="mydiv"></div>
|
|
"""
|
|
)
|
|
warning_banner = self.page.locator(".alert-banner")
|
|
assert "The 'output' attribute is deprecated" in warning_banner.inner_text()
|
|
|
|
def test_target_attribute(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello world', target="mydiv")
|
|
</py-script>
|
|
<div id="mydiv"></div>
|
|
"""
|
|
)
|
|
mydiv = self.page.locator("#mydiv")
|
|
assert mydiv.inner_text() == "hello world"
|
|
|
|
def test_consecutive_display_target(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script id="first">
|
|
display('hello 1')
|
|
</py-script>
|
|
<p>hello in between 1 and 2</p>
|
|
<py-script id="second">
|
|
display('hello 2', target="second")
|
|
</py-script>
|
|
<py-script id="third">
|
|
display('hello 3')
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_text = self.page.inner_text("body")
|
|
lines = inner_text.splitlines()
|
|
lines = [line for line in lines if line != ""] # remove empty lines
|
|
assert lines == ["hello 1", "hello in between 1 and 2", "hello 2", "hello 3"]
|
|
|
|
def test_multiple_display_calls_same_tag(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello')
|
|
display('world')
|
|
</py-script>
|
|
"""
|
|
)
|
|
tag = self.page.locator("py-script")
|
|
lines = tag.inner_text().splitlines()
|
|
assert lines == ["hello", "world"]
|
|
|
|
def test_implicit_target_from_a_different_tag(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script id="py1">
|
|
def say_hello():
|
|
display('hello')
|
|
</py-script>
|
|
|
|
<py-script id="py2">
|
|
say_hello()
|
|
</py-script>
|
|
"""
|
|
)
|
|
py1 = self.page.locator("#py1")
|
|
py2 = self.page.locator("#py2")
|
|
assert py1.inner_text() == ""
|
|
assert py2.inner_text() == "hello"
|
|
|
|
def test_no_implicit_target(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
def display_hello():
|
|
# this fails because we don't have any implicit target
|
|
# from event handlers
|
|
display('hello')
|
|
</py-script>
|
|
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
|
"""
|
|
)
|
|
self.page.locator("text=Click me").click()
|
|
text = self.page.text_content("body")
|
|
assert "hello" not in text
|
|
self.check_js_errors(
|
|
"Implicit target not allowed here. Please use display(..., target=...)"
|
|
)
|
|
|
|
def test_explicit_target_pyscript_tag(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
def display_hello():
|
|
display('hello', target='second-pyscript-tag')
|
|
</py-script>
|
|
<py-script id="second-pyscript-tag">
|
|
display_hello()
|
|
</py-script>
|
|
"""
|
|
)
|
|
text = self.page.locator("id=second-pyscript-tag").inner_text()
|
|
assert text == "hello"
|
|
|
|
def test_explicit_target_on_button_tag(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
def display_hello():
|
|
display('hello', target='my-button')
|
|
</py-script>
|
|
<button id="my-button" py-onClick="display_hello()">Click me</button>
|
|
"""
|
|
)
|
|
self.page.locator("text=Click me").click()
|
|
text = self.page.locator("id=my-button").inner_text()
|
|
assert "hello" in text
|
|
|
|
def test_explicit_different_target_from_call(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script id="first-pyscript-tag">
|
|
def display_hello():
|
|
display('hello', target='second-pyscript-tag')
|
|
</py-script>
|
|
<py-script id="second-pyscript-tag">
|
|
print('nothing to see here')
|
|
</py-script>
|
|
<py-script>
|
|
display_hello()
|
|
</py-script>
|
|
"""
|
|
)
|
|
text = self.page.locator("id=second-pyscript-tag").all_inner_texts()
|
|
assert "hello" in text
|
|
|
|
def test_append_true(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello world', append=True)
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_html = self.page.content()
|
|
pattern = r'<div id="py-.*">hello world</div>'
|
|
assert re.search(pattern, inner_html)
|
|
|
|
def test_append_false(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('hello world', append=False)
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_html = self.page.content()
|
|
pattern = r'<py-script id="py-.*">hello world</py-script>'
|
|
assert re.search(pattern, inner_html)
|
|
|
|
def test_display_multiple_values(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
hello = 'hello'
|
|
world = 'world'
|
|
display(hello, world)
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_text = self.page.inner_text("html")
|
|
assert inner_text == "hello\nworld"
|
|
|
|
def test_display_list_dict_tuple(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
l = ['A', 1, '!']
|
|
d = {'B': 2, 'List': l}
|
|
t = ('C', 3, '!')
|
|
display(l, d, t)
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_text = self.page.inner_text("html")
|
|
print(inner_text)
|
|
assert (
|
|
inner_text
|
|
== "['A', 1, '!']\n{'B': 2, 'List': ['A', 1, '!']}\n('C', 3, '!')"
|
|
)
|
|
|
|
def test_display_should_escape(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display("<p>hello world</p>")
|
|
</py-script>
|
|
"""
|
|
)
|
|
out = self.page.locator("py-script > div")
|
|
assert out.inner_html() == html.escape("<p>hello world</p>")
|
|
assert out.inner_text() == "<p>hello world</p>"
|
|
|
|
def test_display_HTML(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display(HTML("<p>hello world</p>"))
|
|
</py-script>
|
|
"""
|
|
)
|
|
out = self.page.locator("py-script > div")
|
|
assert out.inner_html() == "<p>hello world</p>"
|
|
assert out.inner_text() == "hello world"
|
|
|
|
def test_image_display(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-config> packages = [ "matplotlib"] </py-config>
|
|
<py-script>
|
|
import matplotlib.pyplot as plt
|
|
xpoints = [3, 6, 9]
|
|
ypoints = [1, 2, 3]
|
|
plt.plot(xpoints, ypoints)
|
|
plt.show()
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_html = self.page.content()
|
|
pattern = r'<style id="matplotlib-figure-styles">'
|
|
assert re.search(pattern, inner_html)
|
|
|
|
def test_empty_HTML_and_console_output(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
print('print from python')
|
|
console.log('print from js')
|
|
console.error('error from js');
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_html = self.page.content()
|
|
assert re.search("", inner_html)
|
|
console_text = self.console.all.lines
|
|
assert "print from python" in console_text
|
|
assert "print from js" in console_text
|
|
assert "error from js" in console_text
|
|
|
|
def test_text_HTML_and_console_output(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
display('this goes to the DOM')
|
|
print('print from python')
|
|
console.log('print from js')
|
|
console.error('error from js');
|
|
</py-script>
|
|
"""
|
|
)
|
|
inner_text = self.page.inner_text("py-script")
|
|
assert inner_text == "this goes to the DOM"
|
|
assert self.console.log.lines[0] == self.PY_COMPLETE
|
|
assert self.console.log.lines[-2:] == [
|
|
"print from python",
|
|
"print from js",
|
|
]
|
|
assert self.console.error.lines[-1] == "error from js"
|
|
|
|
def test_console_line_break(self):
|
|
self.pyscript_run(
|
|
"""
|
|
<py-script>
|
|
print('1print\\n2print')
|
|
print('1console\\n2console')
|
|
</py-script>
|
|
"""
|
|
)
|
|
console_text = self.console.all.lines
|
|
assert console_text.index("1print") == (console_text.index("2print") - 1)
|
|
assert console_text.index("1console") == (console_text.index("2console") - 1)
|