10000 Update test suite by ntoll · Pull Request #2181 · pyscript/pyscript · GitHub
[go: up one dir, main page]

Skip to content

Update test suite #2181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6650809
WiP pyscript.web tests pass with upytest.
ntoll Sep 10, 2024
ec20298
Test refactoring. WiP
ntoll Sep 13, 2024
762ae19
Completed refactor of old integration tests to new Python tests.
ntoll Sep 17, 2024
b35fb8f
Added comprehensive test suite for Python based pyodide module.
ntoll Sep 19, 2024
806e574
Black.
ntoll Sep 19, 2024
177cbb9
Rebuld and check websocket attribute assignment via init and as attri…
ntoll Sep 23, 2024
091da6e
Update when tests to allow for worker round trips.
ntoll Sep 23, 2024
4a6c5fa
Post build
ntoll Sep 23, 2024
790fd26
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 23, 2024
ae66d13
Fixed config issue via polyscript update (#2182)
WebReflection Sep 24, 2024
e3c572d
WiP pyscript.web tests pass with upytest.
ntoll Sep 10, 2024
aa83e6c
Test refactoring. WiP
ntoll Sep 13, 2024
263f1fc
Completed refactor of old integration tests to new Python tests.
ntoll Sep 17, 2024
0eb8c43
Added comprehensive test suite for Python based pyodide module.
ntoll Sep 19, 2024
6c81b5f
Black.
ntoll Sep 19, 2024
43f25fd
Rebuld and check websocket attribute assignment via init and as attri…
ntoll Sep 23, 2024
344e54a
Update when tests to allow for worker round trips.
ntoll Sep 23, 2024
f24a2cf
Post build
ntoll Sep 23, 2024
6e52918
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 23, 2024
06216ac
Merge branch 'update-test-suite' of github.com:pyscript/pyscript into…
ntoll Sep 24, 2024
abc3a3b
Fix a couple of timing issues in display and web tests.
ntoll Sep 24, 2024
0d7ef9d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 24, 2024
57e023e
Black
ntoll Sep 24, 2024
58d905d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 24, 2024
7da2ba6
Add integration tests to Makefile (and CI)
ntoll Sep 24, 2024
f826d0b
Remove un-needed upload action.
ntoll Sep 24, 2024
af6cc2b
Ensure fails are properly logged as an array. Remove the explicit tes…
ntoll Sep 24, 2024
aa70295
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 24, 2024
19bc612
Directory reorg/refactor. Updated docs.
ntoll Sep 25, 2024
60622c9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 25, 2024
c52a695
Bump polyscript.
ntoll Sep 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Completed refactor of old integration tests to new Python tests.
  • Loading branch information
ntoll committed Sep 23, 2024
commit 762ae191ad2af8c3e031c4600678c744a2b053bf
4 changes: 4 additions & 0 deletions pyscript.core/tests/integration/python/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ const config = qs.has('config') ? qs.get('config') : './settings.json';
// terminal=0 to NOT have a terminal
const terminal = qs.get('terminal') !== '0';

// worker=1 to have a worker
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find these two a bit confusing ... we could as well just use has('terminal') or has('worker') ... not sure why we need =0 on terminal and =1 on worker ... this is a nit though, still maybe you agree.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has more to do with my shonky JS "skillz". 😉 I'll fix this up... I agree with your proposed change. It's much more readable. 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was probably me suggesting these two but that's because I forgot that ?terminal&worker are perfectly valid too as query strings parameters

const worker = qs.get('worker') == '1';

const script = document.createElement('script');
script.type = qs.get('type') || 'mpy';
if (src) script.src = src;
if (config) script.setAttribute('config', config);
script.toggleAttribute('terminal', terminal);
script.toggleAttribute('worker', worker);

document.write(script.outerHTML);
8 changes: 4 additions & 4 deletions pyscript.core/tests/integration/python/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pyscript import window
from pyscript.ffi import to_js

from pyscript import web
import upytest
import json

result = await upytest.run("./tests")
window.console.log(to_js(result))
output = web.div(json.dumps(result), id="result")
web.page.append(output)
2 changes: 1 addition & 1 deletion pyscript.core/tests/integration/python/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"files": {
"https://raw.githubusercontent.com/ntoll/upytest/1.0.4/upytest.py": "",
"https://raw.githubusercontent.com/ntoll/upytest/1.0.5/upytest.py": "",
"./tests/test_web.py": "tests/test_web.py",
"./tests/test_display.py": "tests/test_display.py",
"./tests/test_when.py": "tests/test_when.py"
Expand Down
135 changes: 85 additions & 50 deletions pyscript.core/tests/integration/python/tests/test_display.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""
Tests for the display function in PyScript.
"""

import re
import upytest
import asyncio


from pyscript import display, web, HTML, RUNNING_IN_WORKER, py_import
Expand All @@ -21,46 +23,60 @@ def get_display_container():
return None


def setup():
async def setup():
"""
Setup function for the test_display.py module. Remove all references to the
display output in the DOM so we always start from a clean state.
"""
container = get_display_container()
container.replaceChildren()
if container:
container.replaceChildren()
target_container = web.page.find("#test-element-container")[0]
target_container.innerHTML = ""
await asyncio.sleep(0.01)


def teardown():
async def teardown():
"""
Like setup.
"""
container = get_display_container()
container.replaceChildren()
if container:
container.replaceChildren()
target_container = web.page.find("#test-element-container")[0]
target_container.innerHTML = ""
await asyncio.sleep(0.01)


def test_simple_display():
"""
Test the display function with a simple string.
"""
display("Hello, world")
container = get_display_container()
assert len(container.children) == 1, "Expected one child in the display container."
assert container.children[0].tagName == "DIV", "Expected a div element in the display container."
assert (
len(container.children) == 1
), "Expected one child in the display container."
assert (
container.children[0].tagName == "DIV"
), "Expected a div element in the display container."
assert container.children[0].innerHTML == "Hello, world"


def test_consecutive_display():
"""
Display order should be preserved.
"""
display("hello 1")
display("hello 2")
container = get_display_container()
assert len(container.children) == 2, "Expected two children in the display container."
assert (
len(container.children) == 2
), "Expected two children in the display container."
assert container.children[0].innerHTML == "hello 1"
assert container.children[1].innerHTML == "hello 2"


def test_target_parameter():
""&qu F438 ot;
The output from display is placed in the target element.
Expand All @@ -69,6 +85,7 @@ def test_target_parameter():
target = web.page.find("#test-element-container")[0]
assert target.innerText == "hello world"


def test_target_parameter_with_hash():
"""
The target parameter can have a hash in front of it.
Expand All @@ -77,13 +94,15 @@ def test_target_parameter_with_hash():
target = web.page.find("#test-element-container")[0]
assert target.innerText == "hello world"


def test_non_existing_id_target_raises_value_error():
"""
If the target parameter is set to a non-existing element, a ValueError should be raised.
"""
with upytest.raises(ValueError):
display("hello world", target="non-existing")


def test_empty_string_target_raises_value_error():
"""
If the target parameter is an empty string, a ValueError should be raised.
Expand All @@ -92,17 +111,19 @@ def test_empty_string_target_raises_value_error():
display("hello world", target="")
assert str(exc.exception) == "Cannot have an empty target"


def test_non_string_target_values_raise_typerror():
"""
The target parameter must be a string.
"""
with upytest.raises(TypeError) as exc:
display("hello world", target=True)
assert str(exc.exception) == "target must be str or None, not bool"

with upytest.raises(TypeError) as exc:
display("hello world", target=123)
assert str(exc.exception) == "target must be str or None, not int"
assert str(exc.exception) == "target must be str or None, not int"


def test_tag_target_attribute():
"""
Expand All @@ -112,12 +133,15 @@ def test_tag_target_attribute():
display("item 2", target="test-element-container")
display("item 3")
container = get_display_container()
assert len(container.children) == 2, "Expected two children in the display container."
assert (
len(container.children) == 2
), "Expected two children in the display container."
assert container.children[0].innerHTML == "item 1"
assert container.children[1].innerHTML == "item 3"
target = web.page.find("#test-element-container")[0]
assert target.innerText == "item 2"


@upytest.skip("CHECK: test consecutive script tags with display in JS")
def test_consecutive_display_target():
self.pyscript_run(
Expand All @@ -140,7 +164,13 @@ def test_consecutive_display_target():
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"]
assert lines == [
"hello 1",
"hello in between 1 and 2",
"hello 2",
"hello 3",
]


def test_multiple_display_calls_same_tag():
"""
Expand All @@ -149,31 +179,12 @@ def test_multiple_display_calls_same_tag():
display("item 1")
display("item 2")
container = get_display_container()
assert len(container.children) == 2, "Expected two children in the display container."
assert (
len(container.children) == 2
), "Expected two children in the display container."
assert container.children[0].innerHTML == "item 1"
assert container.children[1].innerHTML == "item 2"

@upytest.skip("CHECK: test implicit target from a different tag, both main and in worker in JS?")
def test_implicit_target_from_a_different_tag():
self.pyscript_run(
"""
<script type="py">
from pyscript import display
def say_hello():
display('hello')
</script>

<script type="py">
from pyscript import display
say_hello()
</script>
"""
)
elems = self.page.locator("script-py")
py0 = elems.nth(0)
py1 = elems.nth(1)
assert py0.inner_text() == ""
assert py1.inner_text() == "hello"

def test_append_true():
"""
Expand All @@ -182,10 +193,13 @@ def test_append_true():
display("item 1", append=True)
display("item 2", append=True)
container = get_display_container()
assert len(container.children) == 2, "Expected two children in the display container."
assert (
len(container.children) == 2
), "Expected two children in the display container."
assert container.children[0].innerHTML == "item 1"
assert container.children[1].innerHTML == "item 2"


def test_append_false():
"""
Explicit append flag as false should replace the expected container element.
Expand All @@ -195,94 +209,115 @@ def test_append_false():
container = get_display_container()
assert container.innerText == "item 2"

def test_display_multiple_values():

async def test_display_multiple_values():
"""
Display multiple values in the same call.
"""
display("hello", "world")
await asyncio.sleep(0.01)
container = get_display_container()
assert container.innerText == "hello\nworld"
assert container.innerText == "hello\nworld", container.innerText


def test_display_multiple_append_false():
display("hello", "world", append=False)
container = get_display_container()
assert container.innerText == "world"

# 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():
"""

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
"""

class Circle:
r = 0

def _repr_svg_(self):
return (
f'<svg height="{self.r*2}" width="{self.r*2}">'
f'<circle cx="{self.r}" cy="{self.r}" r="{self.r}" fill="red"></circle></svg>'
)

circle = Circle()
circle.r += 5
display(circle, circle, target="test-element-container", append=False)
target = web.page.find("#test-element-container")[0]
assert target.innerHTML == circle._repr_svg_()

def test_display_list_dict_tuple():

async def test_display_list_dict_tuple():
"""
Display a list, dictionary, and tuple with the expected __repr__.

NOTE: MicroPython doesn't (yet) have ordered dicts. Hence the rather odd
check that the dictionary is displayed as a string.
"""
l = ['A', 1, '!']
d = {'B': 2, 'List': l}
t = ('C', 3, '!')
l = ["A", 1, "!"]
d = {"B": 2, "List": l}
t = ("C", 3, "!")
display(l, d, t)
await asyncio.sleep(0.01)
container = get_display_container()
l2, d2, t2 = container.innerText.split('\n')
l2, d2, t2 = container.innerText.split("\n")
assert l == eval(l2)
assert d == eval(d2)
assert t == eval(t2)


def test_display_should_escape():
display("<p>hello world</p>")
container = get_display_container()
assert container[0].innerHTML == "&lt;p&gt;hello world&lt;/p&gt;"
assert container.innerText == "<p>hello world</p>"


def test_display_HTML():
display(HTML("<p>hello world</p>"))
container = get_display_container()
assert container[0].innerHTML == "<p>hello world</p>"
assert container.innerText == "hello world"

@upytest.skip("Pyodide main thread only", skip_when=upytest.is_micropython or RUNNING_IN_WORKER)

@upytest.skip(
"Pyodide main thread only",
skip_when=upytest.is_micropython or RUNNING_IN_WORKER,
)
async def test_image_display():
"""
Check an image is displayed correctly.
"""
mpl = await py_import("matplotlib")
import matplotlib.pyplot as plt

xpoints = [3, 6, 9]
ypoints = [1, 2, 3]
plt.plot(xpoints, ypoints)
display(plt)
container = get_display_container()
img = container.find("img")[0]
img_src = img.getAttribute("src").replace("data:image/png;charset=utf-8;base64,", "")
img_src = img.getAttribute("src").replace(
"data:image/png;charset=utf-8;base64,", ""
)
assert len(img_src) > 0

@upytest.skip("Pyodide main thread only", skip_when=upytest.is_micropython or RUNNING_IN_WORKER)

@upytest.skip(
"Pyodide main thread only",
skip_when=upytest.is_micropython or RUNNING_IN_WORKER,
)
async def test_image_renders_correctly():
"""
This is just a sanity check to make sure that images are rendered
in a reasonable way.
"""
from PIL import Image

img = Image.new("RGB", (4, 4), color=(0, 0, 0))
display(img, target="test-element-container", append=False)
target = web.page.find("#test-element-container")[0]
img = target.find("img")[0]
assert img.src.startswith("data:image/png;charset=utf-8;base64")
assert img.src.startswith("data:image/png;charset=utf-8;base64")
Loading
0