Don't create custom elements in main and fix various small issues on tests (#747)

* Create custom elements when the runtime finishes loading

* Remove xfails and fix repl integration test

* Fix commented ignore

* Address Antonio's comments

* Fix bad rebase

* Make ure to wait for repl to be in attached state before asserting content

* Move createCustomeElement up so it runs before we close the loader, xfail flaky d3 test

* Fix xfail
This commit is contained in:
Fábio Rosado
2022-09-13 15:59:33 +01:00
committed by GitHub
parent 5c1b785b4b
commit c566977749
8 changed files with 59 additions and 95 deletions

View File

@@ -0,0 +1,35 @@
import { PyRepl } from './pyrepl';
import { PyBox } from './pybox';
import { PyButton } from './pybutton';
import { PyTitle } from './pytitle';
import { PyInputBox } from './pyinputbox';
import { PyWidget } from './base';
/*
These were taken from main.js because some of our components call
runAfterRuntimeInitialized immediately when we are creating the custom
element, this was causing tests to fail since runAfterRuntimeInitialized
expects the runtime to have been loaded before being called.
This function is now called from within the `runtime.initialize`. Once
the runtime finished initializing, then we will create the custom elements
so they are rendered in the page and we will always have a runtime available.
Ideally, this would live under utils.js, but importing all the components in
the utils.js file was causing jest to fail with weird errors such as:
"ReferenceError: Cannot access 'BaseEvalElement' before initialization" coming
from the PyScript class.
*/
function createCustomElements() {
/* eslint-disable @typescript-eslint/no-unused-vars */
const xPyRepl = customElements.define('py-repl', PyRepl);
const xPyBox = customElements.define('py-box', PyBox);
const xPyTitle = customElements.define('py-title', PyTitle);
const xPyWidget = customElements.define('py-register-widget', PyWidget);
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
const xPyButton = customElements.define('py-button', PyButton);
/* eslint-enable @typescript-eslint/no-unused-vars */
}
export { createCustomElements };

View File

@@ -1,32 +1,20 @@
import './styles/pyscript_base.css';
import { PyScript } from './components/pyscript';
import { PyRepl } from './components/pyrepl';
import { PyEnv } from './components/pyenv';
import { PyBox } from './components/pybox';
import { PyButton } from './components/pybutton';
import { PyTitle } from './components/pytitle';
import { PyInputBox } from './components/pyinputbox';
import { PyWidget } from './components/base';
import { PyLoader } from './components/pyloader';
import { globalLoader } from './stores';
import { PyConfig } from './components/pyconfig';
import { getLogger } from './logger';
import { globalLoader } from './stores';
const logger = getLogger('pyscript/main');
/* eslint-disable @typescript-eslint/no-unused-vars */
const xPyScript = customElements.define('py-script', PyScript);
const xPyRepl = customElements.define('py-repl', PyRepl);
const xPyEnv = customElements.define('py-env', PyEnv);
const xPyBox = customElements.define('py-box', PyBox);
const xPyButton = customElements.define('py-button', PyButton);
const xPyTitle = customElements.define('py-title', PyTitle);
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
const xPyWidget = customElements.define('py-register-widget', PyWidget);
const xPyLoader = customElements.define('py-loader', PyLoader);
const xPyConfig = customElements.define('py-config', PyConfig);
/* eslint-enable @typescript-eslint/no-unused-vars */
const xPyEnv = customElements.define('py-env', PyEnv);
/* eslint-disable @typescript-eslint/no-unused-vars */
// As first thing, loop for application configs
logger.info('checking for py-confing');

View File

@@ -8,8 +8,9 @@ import {
postInitializers,
Initializer,
scriptsQueue,
appConfig
} from './stores'
appConfig,
} from './stores';
import { createCustomElements } from './components/elements';
import type { PyScript } from './components/pyscript';
import { getLogger } from './logger';
@@ -167,6 +168,9 @@ export abstract class Runtime extends Object {
// now we call all post initializers AFTER we actually executed all page scripts
loader?.log('Running post initializers...');
// Finally create the custom elements for pyscript such as pybutton
createCustomElements();
if (appConfig_ && appConfig_.autoclose_loader) {
loader?.close();
}

View File

@@ -191,6 +191,9 @@ class PyScriptTest:
timeout=timeout,
check_errors=check_errors,
)
# We still don't know why this wait is necessary, but without it
# events aren't being triggered in the tests.
self.page.wait_for_timeout(100)
def pyscript_run(self, snippet, *, extra_head=""):
"""

View File

@@ -1,24 +1,8 @@
import pytest
from .support import PyScriptTest
class TestPyButton(PyScriptTest):
@pytest.mark.xfail
def test_on_click(self):
"""
currently this test fails for a bad reason which is unrelated to
py-button. During the page loading, the following JS exception occur,
in base.ts:BaseEvalElement.evaluate
[JS exception ] TypeError: Cannot use 'in' operator to search for 'runPythonAsync' in undefined
at http://127.0.0.1:8080/build/pyscript.js:305:38
at Object.subscribe (http://127.0.0.1:8080/build/pyscript.js:46:13)
at PyButton.runAfterRuntimeInitialized (http://127.0.0.1:8080/build/pyscript.js:304:27)
at PyButton.connectedCallback (http://127.0.0.1:8080/build/pyscript.js:26856:18)
at http://127.0.0.1:8080/build/pyscript.js:27075:20
at http://127.0.0.1:8080/build/pyscript.js:27093:3
""" # noqa: E501
self.pyscript_run(
"""
<py-button label="my button">

View File

@@ -1,25 +1,8 @@
import time
import pytest
from .support import PyScriptTest
class TestPyInputBox(PyScriptTest):
@pytest.mark.xfail
def test_input_box_typing(self):
"""
This test fails in a similar fashion as the pybutton
test so it's xfailed for now.
[JS exception ] TypeError: Cannot use 'in' operator to search for 'runPythonAsync' in undefined
at http://127.0.0.1:8080/build/pyscript.js:305:38
at Object.subscribe (http://127.0.0.1:8080/build/pyscript.js:46:13)
at PyButton.runAfterRuntimeInitialized (http://127.0.0.1:8080/build/pyscript.js:304:27)
at PyButton.connectedCallback (http://127.0.0.1:8080/build/pyscript.js:26856:18)
at http://127.0.0.1:8080/build/pyscript.js:27075:20
at http://127.0.0.1:8080/build/pyscript.js:27093:3
""" # noqa: E501
self.pyscript_run(
"""
<py-inputbox label="my input">
@@ -32,10 +15,7 @@ class TestPyInputBox(PyScriptTest):
)
assert self.console.log.lines == [self.PY_COMPLETE]
input = self.page.locator("input")
# We need to wait some time before we can type any text
# otherwise it won't be registered. This was the smallest
# amount that seems to work.
time.sleep(0.1)
input.type("Hello")
input.press("Enter")

View File

@@ -1,5 +1,3 @@
import pytest
from .support import PyScriptTest
@@ -15,22 +13,7 @@ class TestPyRepl(PyScriptTest):
assert py_repl
assert "Python" in py_repl.inner_text()
@pytest.mark.xfail
def test_repl_runs_on_button_press(self):
"""
Current this test fails due to an exception when we iterate over
'importmaps'
[ 2.28 JS exception ] TypeError: importmaps is not iterable
at PyRepl._register_esm (http://127.0.0.1:8080/build/pyscript.js:227:37)
at PyRepl.evaluate (http://127.0.0.1:8080/build/pyscript.js:252:22)
at http://127.0.0.1:8080/build/pyscript.js:23678:21
at runFor (http://127.0.0.1:8080/build/pyscript.js:11780:25)
at runHandlers (http://127.0.0.1:8080/build/pyscript.js:11789:17)
at Object.keydown (http://127.0.0.1:8080/build/pyscript.js:11691:20)
at InputState.runCustomHandlers (http://127.0.0.1:8080/build/pyscript.js:8194:37)
at HTMLDivElement.<anonymous> (http://127.0.0.1:8080/build/pyscript.js:8156:30)
"""
self.pyscript_run(
"""
<py-repl id="my-repl" auto-generate="true"> </py-repl>
@@ -43,26 +26,11 @@ class TestPyRepl(PyScriptTest):
self.page.locator("button").click()
# The result gets the id of the repl + n
repl_result = self.page.locator("#my-repl-1")
repl_result = self.page.wait_for_selector("#my-repl-1", state="attached")
assert repl_result.inner_text() == "4"
@pytest.mark.xfail
def test_repl_runs_with_shift_enter(self):
"""
Current this test fails due to an exception when we iterate over
'importmaps'
[ 2.28 JS exception ] TypeError: importmaps is not iterable
at PyRepl._register_esm (http://127.0.0.1:8080/build/pyscript.js:227:37)
at PyRepl.evaluate (http://127.0.0.1:8080/build/pyscript.js:252:22)
at http://127.0.0.1:8080/build/pyscript.js:23678:21
at runFor (http://127.0.0.1:8080/build/pyscript.js:11780:25)
at runHandlers (http://127.0.0.1:8080/build/pyscript.js:11789:17)
at Object.keydown (http://127.0.0.1:8080/build/pyscript.js:11691:20)
at InputState.runCustomHandlers (http://127.0.0.1:8080/build/pyscript.js:8194:37)
at HTMLDivElement.<anonymous> (http://127.0.0.1:8080/build/pyscript.js:8156:30)
"""
self.pyscript_run(
"""
<py-repl id="my-repl" auto-generate="true"> </py-repl>
@@ -72,6 +40,6 @@ class TestPyRepl(PyScriptTest):
# Confirm that we get a result by using the keys shortcut
self.page.keyboard.press("Shift+Enter")
repl_result = self.page.locator("#my-repl-1")
repl_result = self.page.wait_for_selector("#my-repl-1", state="attached")
assert repl_result.text_content() == "4"

View File

@@ -105,6 +105,7 @@ class TestExamples(PyScriptTest):
assert self.page.title() == "Bokeh Example"
wait_for_render(self.page, "*", '<div.*?class=\\"bk\\".*?>')
@pytest.mark.xfail(reason="Flaky test #759")
def test_d3(self):
# XXX improve this test
self.goto("examples/d3.html")
@@ -161,7 +162,6 @@ class TestExamples(PyScriptTest):
assert self.page.title() == "Pyscript/Panel KMeans Demo"
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
@pytest.mark.xfail(reason="JsError: issue #677")
def test_panel_stream(self):
# XXX improve this test
self.goto("examples/panel_stream.html")
@@ -169,9 +169,6 @@ class TestExamples(PyScriptTest):
assert self.page.title() == "PyScript/Panel Streaming Demo"
wait_for_render(self.page, "*", "<div.*?class=['\"]bk-root['\"].*?>")
@pytest.mark.xfail(
reason="Test seems flaky, sometimes it doesn't return result from second repl"
)
def test_repl(self):
self.goto("examples/repl.html")
self.wait_for_pyscript()
@@ -186,8 +183,12 @@ class TestExamples(PyScriptTest):
# Confirm that using the second repl still works properly
self.page.locator("#my-repl-2").type("2*2")
self.page.keyboard.press("Shift+Enter")
assert self.page.locator("#my-repl-2-2").text_content() == "4"
# Make sure that the child of the second repl is attached properly
# before looking into the text_content
second_repl_result = self.page.wait_for_selector(
"#my-repl-2-2", state="attached"
)
assert second_repl_result.text_content() == "4"
@pytest.mark.xfail(reason="Test seems flaky")
def test_repl2(self):
@@ -198,6 +199,8 @@ class TestExamples(PyScriptTest):
# confirm we can import utils and run one command
self.page.locator("py-repl").type("import utils\nutils.now()")
self.page.locator("button").click()
# Make sure the output is in the page
self.page.wait_for_selector("#output")
# utils.now returns current date time
content = self.page.content()
pattern = "\\d+/\\d+/\\d+, \\d+:\\d+:\\d+" # e.g. 08/09/2022 15:57:32
@@ -230,7 +233,6 @@ class TestExamples(PyScriptTest):
in first_task.inner_html()
)
@pytest.mark.xfail(reason="JsError, issue #673")
def test_todo_pylist(self):
# XXX improve this test
self.goto("examples/todo-pylist.html")