import base64
import io
import os
import re
import numpy as np
import pytest
from PIL import Image
from .support import (
PyScriptTest,
filter_inner_text,
filter_page_content,
wait_for_render,
)
DISPLAY_OUTPUT_ID_PATTERN = r'[id^="py-"]'
class TestDisplay(PyScriptTest):
def test_simple_display(self):
self.pyscript_run(
"""
print('ciao')
from pyscript import display
display("hello world")
""",
timeout=20000,
)
node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
pattern = r"
hello world
"
assert node_list[0].inner_html() == pattern
assert len(node_list) == 1
def test_consecutive_display(self):
self.pyscript_run(
"""
from pyscript import display
display('hello 1')
hello 2
from pyscript import display
display('hello 3')
"""
)
inner_text = self.page.inner_text("body")
lines = inner_text.splitlines()
lines = [line for line in filter_page_content(lines)] # remove empty lines
assert lines == ["hello 1", "hello 2", "hello 3"]
def test_target_attribute(self):
self.pyscript_run(
"""
from pyscript import display
display('hello world', target="mydiv")
"""
)
mydiv = self.page.locator("#mydiv")
assert mydiv.inner_text() == "hello world"
def test_consecutive_display_target(self):
self.pyscript_run(
"""
from pyscript import display
display('hello 1')
hello in between 1 and 2
from pyscript import display
display('hello 2', target="second")
from pyscript import display
display('hello 3')
"""
)
inner_text = self.page.inner_text("body")
lines = inner_text.splitlines()
lines = [line for line in filter_page_content(lines)] # 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(
"""
from pyscript import display
display('hello')
display('world')
"""
)
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(
"""
from pyscript import display
def say_hello():
display('hello')
from pyscript import display
say_hello()
"""
)
py1 = self.page.locator("#py1")
py2 = self.page.locator("#py2")
assert py1.inner_text() == ""
assert py2.inner_text() == "hello"
def test_no_explicit_target(self):
self.pyscript_run(
"""
from pyscript import display
def display_hello(error):
display('hello world')
Click me
"""
)
self.page.locator("button").click()
text = self.page.locator("py-script").text_content()
assert "hello world" in text
def test_explicit_target_pyscript_tag(self):
self.pyscript_run(
"""
from pyscript import display
def display_hello():
display('hello', target='second-pyscript-tag')
display_hello()
"""
)
text = self.page.locator("id=second-pyscript-tag").inner_text()
assert text == "hello"
def test_explicit_target_on_button_tag(self):
self.pyscript_run(
"""
from pyscript import display
def display_hello(error):
display('hello', target='my-button')
Click me
"""
)
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(
"""
from pyscript import display
def display_hello():
display('hello', target='second-pyscript-tag')
print('nothing to see here')
display_hello()
"""
)
text = self.page.locator("id=second-pyscript-tag").all_inner_texts()
assert "hello" in text
def test_append_true(self):
self.pyscript_run(
"""
from pyscript import display
display('hello world', append=True)
"""
)
node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
pattern = r"hello world
"
assert node_list[0].inner_html() == pattern
assert len(node_list) == 1
def test_append_false(self):
self.pyscript_run(
"""
from pyscript import display
display('hello world', append=False)
"""
)
inner_html = self.page.content()
pattern = r'hello world '
assert re.search(pattern, inner_html)
def test_display_multiple_values(self):
self.pyscript_run(
"""
from pyscript import display
hello = 'hello'
world = 'world'
display(hello, world)
"""
)
inner_text = self.page.inner_text("html")
assert inner_text == "hello\nworld"
def test_display_multiple_append_false(self):
self.pyscript_run(
"""
from pyscript import display
display('hello', append=False)
display('world', append=False)
"""
)
inner_html = self.page.content()
pattern = r'world '
assert re.search(pattern, inner_html)
# TODO: this is a display.py issue to fix when append=False is used
# do not use the first element, just clean up and then append
# remove the # display comment once that's done
def test_display_multiple_append_false_with_target(self):
self.pyscript_run(
"""
"""
)
innerhtml = self.page.locator("id=circle-div").inner_html()
assert (
innerhtml
== ' ' # noqa: E501
)
assert self.console.error.lines == []
def test_display_list_dict_tuple(self):
self.pyscript_run(
"""
from pyscript import display
l = ['A', 1, '!']
d = {'B': 2, 'List': l}
t = ('C', 3, '!')
display(l, d, t)
"""
)
inner_text = self.page.inner_text("html")
filtered_inner_text = filter_inner_text(inner_text)
print(filtered_inner_text)
assert (
filtered_inner_text
== "['A', 1, '!']\n{'B': 2, 'List': ['A', 1, '!']}\n('C', 3, '!')"
)
def test_display_should_escape(self):
self.pyscript_run(
"""
from pyscript import display
display("hello world
")
"""
)
# out = self.page.locator("py-script > div")
node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
node_list[0]
# assert out.inner_html() == html.escape("hello world
")
# assert out.inner_text() == "hello world
"
@pytest.mark.skip("FIXME: HTML has been removed from pyscript")
def test_display_HTML(self):
self.pyscript_run(
"""
from pyscript import display, HTML
display(HTML("hello world
"))
"""
)
# out = self.page.locator("py-script > div")
node_list = self.page.query_selector_all(DISPLAY_OUTPUT_ID_PATTERN)
node_list[0]
# assert out.inner_html() == "hello world
"
# assert out.inner_text() == "hello world"
@pytest.mark.skip(
"FIX TEST: Works correctly in Chrome, but fails in TEST with the error:\n\n"
"It's likely that the Test framework injections in config are causing"
"this error."
)
def test_image_display(self):
self.pyscript_run(
"""
packages = ["matplotlib"]
from pyscript import display
import matplotlib.pyplot as plt
xpoints = [3, 6, 9]
ypoints = [1, 2, 3]
plt.plot(xpoints, ypoints)
display(plt)
"""
)
wait_for_render(self.page, "*", "
from pyscript import display
import js
print('print from python')
js.console.log('print from js')
js.console.error('error from js');
"""
)
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(
"""
from pyscript import display
import js
display('this goes to the DOM')
print('print from python')
js.console.log('print from js')
js.console.error('error from js');
"""
)
inner_text = self.page.inner_text("py-script")
assert inner_text == "this goes to the DOM"
assert self.console.log.lines[-2:] == [
"print from python",
"print from js",
]
print(self.console.error.lines)
assert self.console.error.lines[-1] == "error from js"
def test_console_line_break(self):
self.pyscript_run(
"""
print('1print\\n2print')
print('1console\\n2console')
"""
)
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)
@pytest.mark.skip(
"FIX TEST: Works correctly in Chrome, but fails in TEST with the error:\n\n"
"It's likely that the Test framework injections in config are causing"
"this error."
)
def test_image_renders_correctly(self):
"""This is just a sanity check to make sure that images are rendered correctly."""
buffer = io.BytesIO()
img = Image.new("RGB", (4, 4), color=(0, 0, 0))
img.save(buffer, format="PNG")
b64_img = base64.b64encode(buffer.getvalue()).decode("utf-8")
expected_img_src = f"data:image/png;charset=utf-8;base64,{b64_img}"
self.pyscript_run(
"""
packages = ["pillow"]
from pyscript import display
from PIL import Image
img = Image.new("RGB", (4, 4), color=(0, 0, 0))
display(img, target='img-target', append=False)
"""
)
rendered_img_src = self.page.locator("img").get_attribute("src")
assert rendered_img_src == expected_img_src