@pyscript/core 0.1.22 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/core.css +1 -1
  2. package/dist/core.js +2 -2
  3. package/dist/core.js.map +1 -1
  4. package/dist/error-e4fe78fd.js +2 -0
  5. package/dist/error-e4fe78fd.js.map +1 -0
  6. package/package.json +2 -2
  7. package/src/core.css +3 -1
  8. package/src/core.js +170 -161
  9. package/src/plugins/error.js +2 -2
  10. package/src/stdlib/pyscript/__init__.py +10 -1
  11. package/src/stdlib/pyscript/display.py +7 -0
  12. package/src/stdlib/pyscript/util.py +3 -4
  13. package/src/stdlib/pyscript.js +3 -3
  14. package/dist/error-87e0706c.js +0 -2
  15. package/dist/error-87e0706c.js.map +0 -1
  16. package/tests/integration/__init__.py +0 -0
  17. package/tests/integration/conftest.py +0 -184
  18. package/tests/integration/support.py +0 -1038
  19. package/tests/integration/test_00_support.py +0 -495
  20. package/tests/integration/test_01_basic.py +0 -353
  21. package/tests/integration/test_02_display.py +0 -452
  22. package/tests/integration/test_03_element.py +0 -303
  23. package/tests/integration/test_assets/line_plot.png +0 -0
  24. package/tests/integration/test_assets/tripcolor.png +0 -0
  25. package/tests/integration/test_async.py +0 -197
  26. package/tests/integration/test_event_handling.py +0 -193
  27. package/tests/integration/test_importmap.py +0 -66
  28. package/tests/integration/test_interpreter.py +0 -98
  29. package/tests/integration/test_plugins.py +0 -419
  30. package/tests/integration/test_py_config.py +0 -294
  31. package/tests/integration/test_py_repl.py +0 -663
  32. package/tests/integration/test_py_terminal.py +0 -270
  33. package/tests/integration/test_runtime_attributes.py +0 -64
  34. package/tests/integration/test_script_type.py +0 -121
  35. package/tests/integration/test_shadow_root.py +0 -33
  36. package/tests/integration/test_splashscreen.py +0 -124
  37. package/tests/integration/test_stdio_handling.py +0 -370
  38. package/tests/integration/test_style.py +0 -47
  39. package/tests/integration/test_warnings_and_banners.py +0 -32
  40. package/tests/integration/test_zz_examples.py +0 -419
  41. package/tests/integration/test_zzz_docs_snippets.py +0 -305
@@ -1,419 +0,0 @@
1
- import base64
2
- import io
3
- import os
4
- import re
5
- import time
6
-
7
- import numpy as np
8
- import pytest
9
- from PIL import Image
10
-
11
- from .support import ROOT, PyScriptTest, wait_for_render, with_execution_thread
12
-
13
-
14
- @pytest.mark.skip(
15
- reason="SKIPPING EXAMPLES: these should be moved elsewhere and updated"
16
- )
17
- @with_execution_thread(None)
18
- @pytest.mark.usefixtures("chdir")
19
- class TestExamples(PyScriptTest):
20
- """
21
- Each example requires the same three tests:
22
-
23
- - Test that the initial markup loads properly (currently done by
24
- testing the <title> tag's content)
25
- - Testing that pyscript is loading properly
26
- - Testing that the page contains appropriate content after rendering
27
- """
28
-
29
- @pytest.fixture()
30
- def chdir(self):
31
- # make sure that the http server serves from the right directory
32
- ROOT.join("pyscriptjs").chdir()
33
-
34
- def test_hello_world(self):
35
- self.goto("examples/hello_world.html")
36
- self.wait_for_pyscript()
37
- assert self.page.title() == "PyScript Hello World"
38
- content = self.page.content()
39
- pattern = "\\d+/\\d+/\\d+, \\d+:\\d+:\\d+" # e.g. 08/09/2022 15:57:32
40
- assert re.search(pattern, content)
41
- self.assert_no_banners()
42
- self.check_tutor_generated_code()
43
-
44
- def test_simple_clock(self):
45
- self.goto("examples/simple_clock.html")
46
- self.wait_for_pyscript()
47
- assert self.page.title() == "Simple Clock Demo"
48
- pattern = r"\d{2}/\d{2}/\d{4}, \d{2}:\d{2}:\d{2}"
49
- # run for 5 seconds to be sure that we see the page with "It's
50
- # espresso time!"
51
- for _ in range(5):
52
- content = self.page.inner_html("#outputDiv2")
53
- if re.match(pattern, content) and int(content[-1]) in (0, 4, 8):
54
- assert self.page.inner_html("#outputDiv3") == "It's espresso time!"
55
- break
56
- else:
57
- time.sleep(1)
58
- else:
59
- raise AssertionError("Espresso time not found :(")
60
- self.assert_no_banners()
61
- self.check_tutor_generated_code()
62
-
63
- def test_altair(self):
64
- self.goto("examples/altair.html")
65
- self.wait_for_pyscript(timeout=90 * 1000)
66
- assert self.page.title() == "Altair"
67
- wait_for_render(self.page, "*", '<canvas.*?class=\\"marks\\".*?>')
68
- save_as_png_link = self.page.locator("text=Save as PNG")
69
- see_source_link = self.page.locator("text=View Source")
70
- # These shouldn't be visible since we didn't click the menu
71
- assert not save_as_png_link.is_visible()
72
- assert not see_source_link.is_visible()
73
-
74
- self.page.locator("summary").click()
75
-
76
- # Let's confirm that the links are visible now after clicking the menu
77
- assert save_as_png_link.is_visible()
78
- assert see_source_link.is_visible()
79
- self.assert_no_banners()
80
- self.check_tutor_generated_code()
81
-
82
- def test_antigravity(self):
83
- self.goto("examples/antigravity.html")
84
- self.wait_for_pyscript()
85
- assert self.page.title() == "Antigravity"
86
-
87
- # confirm that svg added to page
88
- wait_for_render(self.page, "*", '<svg.*id="svg8".*>')
89
-
90
- # Get svg layer of flying character
91
- char = self.page.wait_for_selector("#python")
92
- assert char is not None
93
-
94
- # check that character moves in negative-y direction over time
95
- ycoord_pattern = r"translate\(-?\d*\.\d*,\s(?P<ycoord>-?[\d.]+)\)"
96
- starting_y_coord = float(
97
- re.match(ycoord_pattern, char.get_attribute("transform")).group("ycoord")
98
- )
99
- time.sleep(2)
100
- later_y_coord = float(
101
- re.match(ycoord_pattern, char.get_attribute("transform")).group("ycoord")
102
- )
103
- assert later_y_coord < starting_y_coord
104
- self.check_tutor_generated_code(modules_to_check=["antigravity.py"])
105
-
106
- def test_bokeh(self):
107
- # XXX improve this test
108
- self.goto("examples/bokeh.html")
109
- self.wait_for_pyscript(timeout=90 * 1000)
110
- assert self.page.title() == "Bokeh Example"
111
- wait_for_render(self.page, "*", '<div.*?class="bk.*".*?>')
112
- self.assert_no_banners()
113
- self.check_tutor_generated_code()
114
-
115
- def test_bokeh_interactive(self):
116
- # XXX improve this test
117
- self.goto("examples/bokeh_interactive.html")
118
- self.wait_for_pyscript(timeout=90 * 1000)
119
- assert self.page.title() == "Bokeh Example"
120
- wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
121
- self.assert_no_banners()
122
- self.check_tutor_generated_code()
123
-
124
- @pytest.mark.skip("flaky, see issue 759")
125
- def test_d3(self):
126
- self.goto("examples/d3.html")
127
- self.wait_for_pyscript()
128
- assert (
129
- self.page.title() == "d3: JavaScript & PyScript visualizations side-by-side"
130
- )
131
- wait_for_render(self.page, "*", "<svg.*?>")
132
- assert "PyScript version" in self.page.content()
133
- pyscript_chart = self.page.wait_for_selector("#py")
134
-
135
- # Let's simply assert that the text of the chart is as expected which
136
- # means that the chart rendered successfully and with the right text
137
- assert "🍊21\n🍇13\n🍏8\n🍌5\n🍐3\n🍋2\n🍎1\n🍉1" in pyscript_chart.inner_text()
138
- self.assert_no_banners()
139
- self.check_tutor_generated_code(modules_to_check=["d3.py"])
140
-
141
- def test_folium(self):
142
- self.goto("examples/folium.html")
143
- self.wait_for_pyscript(timeout=90 * 1000)
144
- assert self.page.title() == "Folium"
145
- wait_for_render(self.page, "*", "<iframe srcdoc=")
146
-
147
- # We need to look into the iframe first
148
- iframe = self.page.frame_locator("iframe")
149
-
150
- # Just checking that legend was rendered correctly
151
- legend = iframe.locator("#legend")
152
- assert "Unemployment Rate (%)" in legend.inner_html()
153
-
154
- # Let's check that the zoom buttons are rendered and clickable
155
- # Note: if element is not clickable it will timeout
156
- zoom_in = iframe.locator("[aria-label='Zoom in']")
157
- assert "+" in zoom_in.inner_text()
158
- zoom_in.click()
159
- zoom_out = iframe.locator("[aria-label='Zoom out']")
160
- assert "−" in zoom_out.inner_text()
161
- zoom_out.click()
162
- self.assert_no_banners()
163
- self.check_tutor_generated_code()
164
-
165
- def test_markdown_plugin(self):
166
- # Given the example page with:
167
- # * <title>PyMarkdown</title>
168
- # * <py-md>#Hello world!</py-md>
169
- self.goto("examples/markdown-plugin.html")
170
- self.wait_for_pyscript()
171
- # ASSERT title is rendered correctly
172
- assert self.page.title() == "PyMarkdown"
173
- # ASSERT markdown is rendered to the corresponding HTML tag
174
- wait_for_render(self.page, "*", "<h1>Hello world!</h1>")
175
- self.check_tutor_generated_code()
176
-
177
- def test_matplotlib(self):
178
- self.goto("examples/matplotlib.html")
179
- self.wait_for_pyscript(timeout=90 * 1000)
180
- assert self.page.title() == "Matplotlib"
181
- wait_for_render(self.page, "*", "<img src=['\"]data:image")
182
- # The image is being rended using base64, lets fetch its source
183
- # and replace everything but the actual base64 string.
184
- # Note: The first image on the page is the logo, so we are lookin
185
- # at the mpl-1 div which is rendered once the image is in the page
186
- # if this test fails, confirm that the div has the right id using
187
- # the --dev flag when running the tests
188
- test = self.page.wait_for_selector("#mpl >> img")
189
- img_src = test.get_attribute("src").replace(
190
- "data:image/png;charset=utf-8;base64,", ""
191
- )
192
- # Finally, let's get the np array from the previous data
193
- img_data = np.asarray(Image.open(io.BytesIO(base64.b64decode(img_src))))
194
- with Image.open(
195
- os.path.join(os.path.dirname(__file__), "test_assets", "tripcolor.png"),
196
- ) as image:
197
- ref_data = np.asarray(image)
198
- # Now that we have both images data as a numpy array
199
- # let's confirm that they are the same
200
- deviation = np.mean(np.abs(img_data - ref_data))
201
- assert deviation == 0.0
202
- self.assert_no_banners()
203
- self.check_tutor_generated_code()
204
-
205
- def test_numpy_canvas_fractals(self):
206
- self.goto("examples/numpy_canvas_fractals.html")
207
- self.wait_for_pyscript(timeout=90 * 1000)
208
- assert (
209
- self.page.title()
210
- == "Visualization of Mandelbrot, Julia and Newton sets with NumPy and HTML5 canvas"
211
- )
212
- wait_for_render(
213
- self.page, "*", "<div.*?id=['\"](mandelbrot|julia|newton)['\"].*?>"
214
- )
215
-
216
- # Assert that we get the title and canvas for each element
217
- mandelbrot = self.page.wait_for_selector("#mandelbrot")
218
- assert "Mandelbrot set" in mandelbrot.inner_text()
219
- assert "<canvas" in mandelbrot.inner_html()
220
-
221
- julia = self.page.wait_for_selector("#julia")
222
- assert "Julia set" in julia.inner_text()
223
- assert "<canvas" in julia.inner_html()
224
-
225
- newton = self.page.wait_for_selector("#newton")
226
- assert "Newton set" in newton.inner_text()
227
- assert "<canvas" in newton.inner_html()
228
-
229
- # Confirm that all fieldsets are rendered correctly
230
- poly = newton.wait_for_selector("#poly")
231
- assert poly.input_value() == "z**3 - 2*z + 2"
232
-
233
- coef = newton.wait_for_selector("#coef")
234
- assert coef.input_value() == "1"
235
-
236
- # Let's now change some x/y values to confirm that they
237
- # are editable (is it the best way to test this?)
238
- x0 = newton.wait_for_selector("#x0")
239
- y0 = newton.wait_for_selector("#y0")
240
-
241
- x0.fill("50")
242
- assert x0.input_value() == "50"
243
- y0.fill("-25")
244
- assert y0.input_value() == "-25"
245
-
246
- # This was the first computation with the default values
247
- assert self.console.log.lines[-2] == "Computing Newton set ..."
248
- # Confirm that changing the input values, triggered a new computation
249
- assert self.console.log.lines[-1] == "Computing Newton set ..."
250
- self.assert_no_banners()
251
- self.check_tutor_generated_code()
252
-
253
- def test_panel(self):
254
- self.goto("examples/panel.html")
255
- self.wait_for_pyscript(timeout=90 * 1000)
256
- assert self.page.title() == "Panel Example"
257
- wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
258
- slider_title = self.page.wait_for_selector(".bk-slider-title")
259
- assert slider_title.inner_text() == "Amplitude: 0"
260
-
261
- slider_result = self.page.wait_for_selector(".bk-clearfix")
262
- assert slider_result.inner_text() == "Amplitude is: 0"
263
-
264
- amplitude_bar = self.page.wait_for_selector(".noUi-connects")
265
- amplitude_bar.click()
266
-
267
- # Let's confirm that slider title changed
268
- assert slider_title.inner_text() == "Amplitude: 5"
269
- self.assert_no_banners()
270
- self.check_tutor_generated_code()
271
-
272
- def test_panel_deckgl(self):
273
- # XXX improve this test
274
- self.goto("examples/panel_deckgl.html")
275
- self.wait_for_pyscript(timeout=90 * 1000)
276
- assert self.page.title() == "PyScript/Panel DeckGL Demo"
277
- wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
278
- self.assert_no_banners()
279
- self.check_tutor_generated_code()
280
-
281
- def test_panel_kmeans(self):
282
- # XXX improve this test>>>>>>> main
283
- self.goto("examples/panel_kmeans.html")
284
- self.wait_for_pyscript(timeout=120 * 1000)
285
- assert self.page.title() == "Pyscript/Panel KMeans Demo"
286
- wait_for_render(
287
- self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>", timeout_seconds=60 * 2
288
- )
289
- self.assert_no_banners()
290
- self.check_tutor_generated_code()
291
-
292
- def test_panel_stream(self):
293
- # XXX improve this test
294
- self.goto("examples/panel_stream.html")
295
- self.wait_for_pyscript(timeout=3 * 60 * 1000)
296
- assert self.page.title() == "PyScript/Panel Streaming Demo"
297
- wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
298
- self.assert_no_banners()
299
- self.check_tutor_generated_code()
300
-
301
- def test_repl(self):
302
- self.goto("examples/repl.html")
303
- self.wait_for_pyscript()
304
- assert self.page.title() == "REPL"
305
- self.page.wait_for_selector("py-repl")
306
-
307
- self.page.locator("py-repl").type("display('Hello, World!')")
308
- self.page.wait_for_selector(".py-repl-run-button").click()
309
- self.page.wait_for_selector("#my-repl-repl-output")
310
- assert (
311
- self.page.locator("#my-repl-repl-output").text_content() == "Hello, World!"
312
- )
313
-
314
- # Confirm that using the second repl still works properly
315
- self.page.locator("#my-repl-1").type("display(2*2)")
316
- self.page.keyboard.press("Shift+Enter")
317
- my_repl_1 = self.page.wait_for_selector("#my-repl-1-repl-output")
318
- assert my_repl_1.inner_text() == "4"
319
- self.assert_no_banners()
320
- self.check_tutor_generated_code(modules_to_check=["antigravity.py"])
321
-
322
- def test_repl2(self):
323
- self.goto("examples/repl2.html")
324
- self.wait_for_pyscript(timeout=1.5 * 60 * 1000)
325
- assert self.page.title() == "Custom REPL Example"
326
- wait_for_render(self.page, "*", "<py-repl.*?>")
327
- # confirm we can import utils and run one command
328
- self.page.locator("py-repl").type("import utils\ndisplay(utils.now())")
329
- self.page.wait_for_selector("py-repl .py-repl-run-button").click()
330
- # Make sure the output is in the page
331
- self.page.wait_for_selector("#my-repl-1")
332
- # utils.now returns current date time
333
- content = self.page.content()
334
- pattern = "\\d+/\\d+/\\d+, \\d+:\\d+:\\d+" # e.g. 08/09/2022 15:57:32
335
- assert re.search(pattern, content)
336
- self.assert_no_banners()
337
- self.check_tutor_generated_code(modules_to_check=["antigravity.py"])
338
-
339
- def test_todo(self):
340
- self.goto("examples/todo.html")
341
- self.wait_for_pyscript()
342
- assert self.page.title() == "Todo App"
343
- wait_for_render(self.page, "*", "<input.*?id=['\"]new-task-content['\"].*?>")
344
- todo_input = self.page.locator("input")
345
- submit_task_button = self.page.locator("button")
346
-
347
- todo_input.type("Fold laundry")
348
- submit_task_button.click()
349
-
350
- first_task = self.page.locator("#task-0")
351
- assert "Fold laundry" in first_task.inner_text()
352
-
353
- task_checkbox = first_task.locator("input")
354
- # Confirm that the new task isn't checked
355
- assert not task_checkbox.is_checked()
356
-
357
- # Let's mark it as done now
358
- task_checkbox.check()
359
-
360
- # Basic check that the task has the line-through class
361
- assert (
362
- '<p class="m-0 inline line-through">Fold laundry</p>'
363
- in first_task.inner_html()
364
- )
365
- self.assert_no_banners()
366
- self.check_tutor_generated_code(modules_to_check=["./utils.py", "./todo.py"])
367
-
368
- def test_todo_pylist(self):
369
- self.goto("examples/todo-pylist.html")
370
- self.wait_for_pyscript()
371
- assert self.page.title() == "Todo App"
372
- wait_for_render(self.page, "*", "<input.*?id=['\"]new-task-content['\"].*?>")
373
- todo_input = self.page.locator("input")
374
- submit_task_button = self.page.locator("button#new-task-btn")
375
-
376
- todo_input.type("Fold laundry")
377
- submit_task_button.click()
378
-
379
- first_task = self.page.locator("div#myList-c-0")
380
- assert "Fold laundry" in first_task.inner_text()
381
-
382
- task_checkbox = first_task.locator("input")
383
- # Confirm that the new task isn't checked
384
- assert not task_checkbox.is_checked()
385
-
386
- # Let's mark it as done now
387
- task_checkbox.check()
388
-
389
- # Basic check that the task has the line-through class
390
- assert "line-through" in first_task.get_attribute("class")
391
- self.assert_no_banners()
392
- self.check_tutor_generated_code(modules_to_check=["utils.py"])
393
-
394
- @pytest.mark.xfail(reason="To be moved to collective and updated, see issue #686")
395
- def test_toga_freedom(self):
396
- self.goto("examples/toga/freedom.html")
397
- self.wait_for_pyscript()
398
- assert self.page.title() in ["Loading...", "Freedom Units"]
399
- wait_for_render(self.page, "*", "<(main|div).*?id=['\"]toga_\\d+['\"].*?>")
400
-
401
- page_content = self.page.content()
402
-
403
- assert "Fahrenheit" in page_content
404
- assert "Celsius" in page_content
405
-
406
- self.page.locator("#toga_f_input").fill("105")
407
- self.page.locator("button#toga_calculate").click()
408
- result = self.page.locator("#toga_c_input")
409
- assert "40.555" in result.input_value()
410
- self.assert_no_banners()
411
- self.check_tutor_generated_code()
412
-
413
- def test_webgl_raycaster_index(self):
414
- # XXX improve this test
415
- self.goto("examples/webgl/raycaster/index.html")
416
- self.wait_for_pyscript()
417
- assert self.page.title() == "Raycaster"
418
- wait_for_render(self.page, "*", "<canvas.*?>")
419
- self.assert_no_banners()