mirror of https://github.com/pyodide/pyodide.git
Set up pytest node tests (#1717)
This commit is contained in:
parent
c8436c33a7
commit
f0bd568a31
|
@ -3,7 +3,7 @@ version: 2.1
|
|||
defaults: &defaults
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: pyodide/pyodide-env:18
|
||||
- image: pyodide/pyodide-env:19
|
||||
environment:
|
||||
- EMSDK_NUM_CORES: 3
|
||||
EMCC_CORES: 3
|
||||
|
@ -26,11 +26,6 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install prerequisites
|
||||
command: |
|
||||
pip install -r docs/requirements-doc.txt
|
||||
|
||||
- run:
|
||||
name: Test docs
|
||||
command: |
|
||||
|
@ -166,7 +161,7 @@ jobs:
|
|||
- run:
|
||||
name: stack-size
|
||||
command: |
|
||||
pytest -s benchmark/stack_usage.py | sed -n 's/## //pg'
|
||||
pytest -s benchmark/stack_usage.py | sed -n 's/## //pg' || true
|
||||
|
||||
test-emsdk:
|
||||
<<: *defaults
|
||||
|
@ -188,11 +183,10 @@ jobs:
|
|||
name: test
|
||||
command: |
|
||||
mkdir test-results
|
||||
pip install ruamel.yaml
|
||||
pytest \
|
||||
--junitxml=test-results/junit.xml \
|
||||
--verbose \
|
||||
-k 'not (chrome or firefox)' \
|
||||
-k 'not (chrome or firefox or node)' \
|
||||
--cov=pyodide_build --cov=pyodide \
|
||||
src pyodide-build packages/micropip/
|
||||
|
||||
|
@ -313,6 +307,11 @@ workflows:
|
|||
test-params: -k firefox src packages/micropip
|
||||
requires:
|
||||
- build-core
|
||||
- test-main:
|
||||
name: test-core-node
|
||||
test-params: -k node src packages/micropip
|
||||
requires:
|
||||
- build-core
|
||||
- test-main:
|
||||
name: test-packages-chrome
|
||||
test-params: -k chrome packages/test* packages/*/test*
|
||||
|
@ -323,6 +322,11 @@ workflows:
|
|||
test-params: -k firefox packages/test* packages/*/test*
|
||||
requires:
|
||||
- build-packages
|
||||
- test-main:
|
||||
name: test-packages-node
|
||||
test-params: -k "node and not numpy" packages/test* packages/*/test*
|
||||
requires:
|
||||
- build-packages
|
||||
- test-emsdk:
|
||||
requires:
|
||||
- build-core
|
||||
|
|
32
Dockerfile
32
Dockerfile
|
@ -12,34 +12,10 @@ RUN apt-get update \
|
|||
libgconf-2-4 "chromium=90.*" \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pip3 --no-cache-dir install \
|
||||
black \
|
||||
"cython<3.0" \
|
||||
packaging \
|
||||
flake8 \
|
||||
hypothesis \
|
||||
"mypy==0.812" \
|
||||
pytest \
|
||||
pytest-asyncio \
|
||||
pytest-cov \
|
||||
pytest-httpserver \
|
||||
pytest-instafail \
|
||||
pytest-rerunfailures \
|
||||
pytest-xdist \
|
||||
pyyaml \
|
||||
"selenium==4.0.0.b3" \
|
||||
# Docs requirements
|
||||
sphinx \
|
||||
sphinx_book_theme \
|
||||
myst-parser==0.13.3 \
|
||||
sphinxcontrib-napoleon \
|
||||
packaging \
|
||||
sphinx-js==3.1 \
|
||||
autodocsumm \
|
||||
docutils==0.16 \
|
||||
sphinx-argparse-cli~=1.6.0 \
|
||||
sphinx-version-warning~=1.1.2 \
|
||||
sphinx-issues
|
||||
ADD docs/requirements-doc.txt requirements.txt /
|
||||
|
||||
RUN pip3 --no-cache-dir install -r /requirements.txt \
|
||||
&& pip3 --no-cache-dir install -r /requirements-doc.txt
|
||||
|
||||
# Get firefox 70.0.1 and geckodriver
|
||||
RUN wget -qO- https://ftp.mozilla.org/pub/firefox/releases/87.0/linux-x86_64/en-US/firefox-87.0.tar.bz2 | tar jx \
|
||||
|
|
218
conftest.py
218
conftest.py
|
@ -3,12 +3,14 @@ Various common utilities for testing.
|
|||
"""
|
||||
|
||||
import contextlib
|
||||
import json
|
||||
import multiprocessing
|
||||
import textwrap
|
||||
import tempfile
|
||||
import time
|
||||
import os
|
||||
import pathlib
|
||||
import pexpect
|
||||
import queue
|
||||
import sys
|
||||
import shutil
|
||||
|
@ -78,29 +80,24 @@ class SeleniumWrapper:
|
|||
server_port,
|
||||
server_hostname="127.0.0.1",
|
||||
server_log=None,
|
||||
build_dir=None,
|
||||
load_pyodide=True,
|
||||
script_timeout=20,
|
||||
):
|
||||
if build_dir is None:
|
||||
build_dir = BUILD_PATH
|
||||
|
||||
self.driver = self.get_driver()
|
||||
self.server_port = server_port
|
||||
self.server_hostname = server_hostname
|
||||
self.base_url = f"http://{self.server_hostname}:{self.server_port}"
|
||||
self.server_log = server_log
|
||||
|
||||
if not (pathlib.Path(build_dir) / "test.html").exists():
|
||||
# selenium does not expose HTTP response codes
|
||||
raise ValueError(
|
||||
f"{(build_dir / 'test.html').resolve()} " f"does not exist!"
|
||||
)
|
||||
self.driver.get(f"http://{server_hostname}:{server_port}/test.html")
|
||||
self.driver = self.get_driver()
|
||||
self.set_script_timeout(script_timeout)
|
||||
self.script_timeout = script_timeout
|
||||
self.prepare_driver()
|
||||
self.javascript_setup()
|
||||
if load_pyodide:
|
||||
self.run_js(
|
||||
"""
|
||||
window.pyodide = await loadPyodide({ indexURL : './', fullStdLib: false });
|
||||
let pyodide = await loadPyodide({ indexURL : './', fullStdLib: false, jsglobals : self });
|
||||
self.pyodide = pyodide;
|
||||
globalThis.pyodide = pyodide;
|
||||
pyodide.globals.get;
|
||||
pyodide.pyodide_py.eval_code;
|
||||
pyodide.pyodide_py.eval_code_async;
|
||||
|
@ -108,88 +105,41 @@ class SeleniumWrapper:
|
|||
pyodide.pyodide_py.unregister_js_module;
|
||||
pyodide.pyodide_py.find_imports;
|
||||
pyodide.runPython("");
|
||||
"""
|
||||
""",
|
||||
)
|
||||
self.save_state()
|
||||
self.restore_state()
|
||||
self.script_timeout = script_timeout
|
||||
self.driver.set_script_timeout(script_timeout)
|
||||
|
||||
SETUP_CODE = pathlib.Path(ROOT_PATH / "tools/testsetup.js").read_text()
|
||||
|
||||
def prepare_driver(self):
|
||||
self.driver.get(f"{self.base_url}/test.html")
|
||||
|
||||
def set_script_timeout(self, timeout):
|
||||
self.driver.set_script_timeout(timeout)
|
||||
|
||||
def quit(self):
|
||||
self.driver.quit()
|
||||
|
||||
def refresh(self):
|
||||
self.driver.refresh()
|
||||
self.javascript_setup()
|
||||
|
||||
def javascript_setup(self):
|
||||
self.run_js("Error.stackTraceLimit = Infinity;", pyodide_checks=False)
|
||||
self.run_js(
|
||||
"""
|
||||
window.assert = function(cb, message=""){
|
||||
if(message !== ""){
|
||||
message = "\\n" + message;
|
||||
}
|
||||
if(cb() !== true){
|
||||
throw new Error(`Assertion failed: ${cb.toString().slice(6)}${message}`);
|
||||
}
|
||||
};
|
||||
window.assertAsync = async function(cb, message=""){
|
||||
if(message !== ""){
|
||||
message = "\\n" + message;
|
||||
}
|
||||
if(await cb() !== true){
|
||||
throw new Error(`Assertion failed: ${cb.toString().slice(12)}${message}`);
|
||||
}
|
||||
};
|
||||
function checkError(err, errname, pattern, pat_str, thiscallstr){
|
||||
if(typeof pattern === "string"){
|
||||
pattern = new RegExp(pattern);
|
||||
}
|
||||
if(!err){
|
||||
throw new Error(`${thiscallstr} failed, no error thrown`);
|
||||
}
|
||||
if(err.constructor.name !== errname){
|
||||
throw new Error(
|
||||
`${thiscallstr} failed, expected error ` +
|
||||
`of type '${errname}' got type '${err.constructor.name}'`
|
||||
);
|
||||
}
|
||||
if(!pattern.test(err.message)){
|
||||
throw new Error(
|
||||
`${thiscallstr} failed, expected error ` +
|
||||
`message to match pattern ${pat_str} got:\n${err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
window.assertThrows = function(cb, errname, pattern){
|
||||
let pat_str = typeof pattern === "string" ? `"${pattern}"` : `${pattern}`;
|
||||
let thiscallstr = `assertThrows(${cb.toString()}, "${errname}", ${pat_str})`;
|
||||
let err = undefined;
|
||||
try {
|
||||
cb();
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
checkError(err, errname, pattern, pat_str, thiscallstr);
|
||||
};
|
||||
window.assertThrowsAsync = async function(cb, errname, pattern){
|
||||
let pat_str = typeof pattern === "string" ? `"${pattern}"` : `${pattern}`;
|
||||
let thiscallstr = `assertThrowsAsync(${cb.toString()}, "${errname}", ${pat_str})`;
|
||||
let err = undefined;
|
||||
try {
|
||||
await cb();
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
checkError(err, errname, pattern, pat_str, thiscallstr);
|
||||
};
|
||||
""",
|
||||
SeleniumWrapper.SETUP_CODE,
|
||||
pyodide_checks=False,
|
||||
)
|
||||
|
||||
@property
|
||||
def logs(self):
|
||||
logs = self.driver.execute_script("return window.logs;")
|
||||
logs = self.run_js("return self.logs;", pyodide_checks=False)
|
||||
if logs is not None:
|
||||
return "\n".join(str(x) for x in logs)
|
||||
return ""
|
||||
|
||||
def clean_logs(self):
|
||||
self.driver.execute_script("window.logs = []")
|
||||
self.run_js("self.logs = []", pyodide_checks=False)
|
||||
|
||||
def run(self, code):
|
||||
return self.run_js(
|
||||
|
@ -236,7 +186,7 @@ class SeleniumWrapper:
|
|||
try {
|
||||
pyodide._module._pythonexc2js();
|
||||
} catch(e){
|
||||
console.error(`Python exited with error flag set! Error was:\n{e.message}`);
|
||||
console.error(`Python exited with error flag set! Error was:\n${e.message}`);
|
||||
// Don't put original error message in new one: we want
|
||||
// "pytest.raises(xxx, match=msg)" to fail
|
||||
throw new Error(`Python exited with error flag set!`);
|
||||
|
@ -245,7 +195,9 @@ class SeleniumWrapper:
|
|||
"""
|
||||
else:
|
||||
check_code = ""
|
||||
return self.run_js_inner(code, check_code)
|
||||
|
||||
def run_js_inner(self, code, check_code):
|
||||
wrapper = """
|
||||
let cb = arguments[arguments.length - 1];
|
||||
let run = async () => { %s }
|
||||
|
@ -259,9 +211,7 @@ class SeleniumWrapper:
|
|||
}
|
||||
})()
|
||||
"""
|
||||
|
||||
retval = self.driver.execute_async_script(wrapper % (code, check_code))
|
||||
|
||||
if retval[0] == 0:
|
||||
return retval[1]
|
||||
else:
|
||||
|
@ -350,6 +300,84 @@ class ChromeWrapper(SeleniumWrapper):
|
|||
options.add_argument("--js-flags=--expose-gc")
|
||||
return Chrome(options=options)
|
||||
|
||||
def collect_garbage(self):
|
||||
self.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})
|
||||
|
||||
|
||||
class NodeWrapper(SeleniumWrapper):
|
||||
browser = "node"
|
||||
|
||||
def init_node(self):
|
||||
os.chdir("build")
|
||||
self.p = pexpect.spawn(
|
||||
f"node --expose-gc ../tools/node_test_driver.js {self.base_url}", timeout=60
|
||||
)
|
||||
self.p.setecho(False)
|
||||
self.p.delaybeforesend = None
|
||||
os.chdir("..")
|
||||
|
||||
def get_driver(self):
|
||||
self._logs = []
|
||||
self.init_node()
|
||||
|
||||
class NodeDriver:
|
||||
def __getattr__(self, x):
|
||||
raise NotImplementedError()
|
||||
|
||||
return NodeDriver()
|
||||
|
||||
def prepare_driver(self):
|
||||
pass
|
||||
|
||||
def set_script_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def quit(self):
|
||||
self.p.sendeof()
|
||||
|
||||
def refresh(self):
|
||||
self.quit()
|
||||
self.init_node()
|
||||
self.javascript_setup()
|
||||
|
||||
def collect_garbage(self):
|
||||
self.run_js("gc()")
|
||||
|
||||
@property
|
||||
def logs(self):
|
||||
return "\n".join(self._logs)
|
||||
|
||||
def clean_logs(self):
|
||||
self._logs = []
|
||||
|
||||
def run_js_inner(self, code, check_code):
|
||||
check_code = ""
|
||||
wrapped = """
|
||||
let result = await (async () => { %s })();
|
||||
%s
|
||||
return result;
|
||||
""" % (
|
||||
code,
|
||||
check_code,
|
||||
)
|
||||
from uuid import uuid4
|
||||
|
||||
cmd_id = str(uuid4())
|
||||
self.p.sendline(cmd_id)
|
||||
self.p.sendline(wrapped)
|
||||
self.p.sendline(cmd_id)
|
||||
self.p.expect_exact(f"{cmd_id}:UUID\r\n", timeout=self._timeout)
|
||||
self.p.expect_exact(f"{cmd_id}:UUID\r\n")
|
||||
if self.p.before:
|
||||
self._logs.append(self.p.before.decode()[:-2].replace("\r", ""))
|
||||
self.p.expect(f"[01]\r\n")
|
||||
success = int(self.p.match[0].decode()[0]) == 0
|
||||
self.p.expect_exact(f"\r\n{cmd_id}:UUID\r\n")
|
||||
if success:
|
||||
return json.loads(self.p.before.decode().replace("undefined", "null"))
|
||||
else:
|
||||
raise JavascriptException("", self.p.before.decode())
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_call(item):
|
||||
|
@ -388,12 +416,17 @@ def test_wrapper_check_for_memory_leaks(selenium, trace_hiwire_refs, trace_pypro
|
|||
selenium.enable_pyproxy_tracing()
|
||||
init_num_proxies = selenium.get_num_proxies()
|
||||
a = yield
|
||||
selenium.disable_pyproxy_tracing()
|
||||
selenium.restore_state()
|
||||
# if there was an error in the body of the test, flush it out by calling
|
||||
# get_result (we don't want to override the error message by raising a
|
||||
# different error here.)
|
||||
a.get_result()
|
||||
try:
|
||||
# If these guys cause a crash because the test really screwed things up,
|
||||
# we override the error message with the better message returned by
|
||||
# a.result() in the finally block.
|
||||
selenium.disable_pyproxy_tracing()
|
||||
selenium.restore_state()
|
||||
finally:
|
||||
# if there was an error in the body of the test, flush it out by calling
|
||||
# get_result (we don't want to override the error message by raising a
|
||||
# different error here.)
|
||||
a.get_result()
|
||||
if trace_pyproxies and trace_hiwire_refs:
|
||||
delta_proxies = selenium.get_num_proxies() - init_num_proxies
|
||||
delta_keys = selenium.get_num_hiwire_keys() - init_num_keys
|
||||
|
@ -410,10 +443,11 @@ def selenium_common(request, web_server_main, load_pyodide=True):
|
|||
cls = FirefoxWrapper
|
||||
elif request.param == "chrome":
|
||||
cls = ChromeWrapper
|
||||
elif request.param == "node":
|
||||
cls = NodeWrapper
|
||||
else:
|
||||
assert False
|
||||
selenium = cls(
|
||||
build_dir=request.config.option.build_dir,
|
||||
server_port=server_port,
|
||||
server_hostname=server_hostname,
|
||||
server_log=server_log,
|
||||
|
@ -422,10 +456,10 @@ def selenium_common(request, web_server_main, load_pyodide=True):
|
|||
try:
|
||||
yield selenium
|
||||
finally:
|
||||
selenium.driver.quit()
|
||||
selenium.quit()
|
||||
|
||||
|
||||
@pytest.fixture(params=["firefox", "chrome"], scope="function")
|
||||
@pytest.fixture(params=["firefox", "chrome", "node"], scope="function")
|
||||
def selenium_standalone(request, web_server_main):
|
||||
with selenium_common(request, web_server_main) as selenium:
|
||||
with set_webdriver_script_timeout(
|
||||
|
@ -450,7 +484,7 @@ def selenium_webworker_standalone(request, web_server_main):
|
|||
|
||||
|
||||
# selenium instance cached at the module level
|
||||
@pytest.fixture(params=["firefox", "chrome"], scope="module")
|
||||
@pytest.fixture(params=["firefox", "chrome", "node"], scope="module")
|
||||
def selenium_module_scope(request, web_server_main):
|
||||
with selenium_common(request, web_server_main) as selenium:
|
||||
yield selenium
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
sphinx
|
||||
sphinx_book_theme
|
||||
myst-parser==0.13.3
|
||||
sphinxcontrib-napoleon
|
||||
packaging # required by micropip
|
||||
sphinx-js==3.1
|
||||
autodocsumm
|
||||
docutils==0.16
|
||||
myst-parser==0.13.3
|
||||
packaging # required by micropip at import time
|
||||
sphinx
|
||||
sphinx-argparse-cli~=1.6.0
|
||||
sphinx-version-warning~=1.1.2
|
||||
sphinx_book_theme
|
||||
sphinxcontrib-napoleon
|
||||
sphinx-issues
|
||||
sphinx-js==3.1
|
||||
sphinx-version-warning~=1.1.2
|
||||
|
|
|
@ -13,9 +13,9 @@ def get_backend(selenium_standalone):
|
|||
selenium = selenium_standalone
|
||||
return selenium.run(
|
||||
"""
|
||||
import matplotlib
|
||||
matplotlib.get_backend()
|
||||
"""
|
||||
import matplotlib
|
||||
matplotlib.get_backend()
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
|
@ -58,8 +58,9 @@ def check_comparison(selenium, prefix, num_fonts):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_matplotlib(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_matplotlib(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
selenium.run(
|
||||
"""
|
||||
|
@ -74,6 +75,8 @@ def test_matplotlib(selenium_standalone):
|
|||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_svg(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
selenium.run("from matplotlib import pyplot as plt")
|
||||
selenium.run("plt.figure(); pass")
|
||||
|
@ -88,6 +91,8 @@ def test_svg(selenium):
|
|||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pdf(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
selenium.run("from matplotlib import pyplot as plt")
|
||||
selenium.run("plt.figure(); pass")
|
||||
|
@ -131,8 +136,9 @@ def test_font_manager(selenium):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_rendering(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_rendering(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
@ -164,8 +170,9 @@ def test_rendering(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_draw_image(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_draw_image(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
@ -205,8 +212,9 @@ def test_draw_image(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_draw_image_affine_transform(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_draw_image_affine_transform(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
@ -276,8 +284,9 @@ def test_draw_image_affine_transform(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_draw_text_rotated(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_draw_text_rotated(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
if selenium.browser == "chrome":
|
||||
pytest.xfail(f"high recursion limit not supported for {selenium.browser}")
|
||||
selenium.load_package("matplotlib")
|
||||
|
@ -331,8 +340,9 @@ def test_draw_text_rotated(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_draw_math_text(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_draw_math_text(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
if selenium.browser == "chrome":
|
||||
pytest.xfail(f"high recursion limit not supported for {selenium.browser}")
|
||||
selenium.load_package("matplotlib")
|
||||
|
@ -454,8 +464,9 @@ def test_draw_math_text(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_custom_font_text(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_custom_font_text(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
@ -466,22 +477,22 @@ def test_custom_font_text(selenium_standalone):
|
|||
try:
|
||||
selenium.run(
|
||||
"""
|
||||
from js import window
|
||||
window.testing = True
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from js import window
|
||||
window.testing = True
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
f = {'fontname': 'cmsy10'}
|
||||
f = {'fontname': 'cmsy10'}
|
||||
|
||||
t = np.arange(0.0, 2.0, 0.01)
|
||||
s = 1 + np.sin(2 * np.pi * t)
|
||||
plt.figure()
|
||||
plt.title('A simple Sine Curve', **f)
|
||||
plt.plot(t, s, linewidth=1.0, marker=11)
|
||||
plt.plot(t, t)
|
||||
plt.grid(True)
|
||||
plt.show()
|
||||
"""
|
||||
t = np.arange(0.0, 2.0, 0.01)
|
||||
s = 1 + np.sin(2 * np.pi * t)
|
||||
plt.figure()
|
||||
plt.title('A simple Sine Curve', **f)
|
||||
plt.plot(t, s, linewidth=1.0, marker=11)
|
||||
plt.plot(t, t)
|
||||
plt.grid(True)
|
||||
plt.show()
|
||||
"""
|
||||
)
|
||||
|
||||
check_comparison(selenium, "canvas-custom-font-text", 2)
|
||||
|
@ -491,8 +502,9 @@ def test_custom_font_text(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_zoom_on_polar_plot(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_zoom_on_polar_plot(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
@ -503,30 +515,30 @@ def test_zoom_on_polar_plot(selenium_standalone):
|
|||
try:
|
||||
selenium.run(
|
||||
"""
|
||||
from js import window
|
||||
window.testing = True
|
||||
from js import window
|
||||
window.testing = True
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
np.random.seed(42)
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
np.random.seed(42)
|
||||
|
||||
# Compute pie slices
|
||||
N = 20
|
||||
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
|
||||
radii = 10 * np.random.rand(N)
|
||||
width = np.pi / 4 * np.random.rand(N)
|
||||
# Compute pie slices
|
||||
N = 20
|
||||
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
|
||||
radii = 10 * np.random.rand(N)
|
||||
width = np.pi / 4 * np.random.rand(N)
|
||||
|
||||
ax = plt.subplot(111, projection='polar')
|
||||
bars = ax.bar(theta, radii, width=width, bottom=0.0)
|
||||
ax = plt.subplot(111, projection='polar')
|
||||
bars = ax.bar(theta, radii, width=width, bottom=0.0)
|
||||
|
||||
# Use custom colors and opacity
|
||||
for r, bar in zip(radii, bars):
|
||||
bar.set_facecolor(plt.cm.viridis(r / 10.))
|
||||
bar.set_alpha(0.5)
|
||||
# Use custom colors and opacity
|
||||
for r, bar in zip(radii, bars):
|
||||
bar.set_facecolor(plt.cm.viridis(r / 10.))
|
||||
bar.set_alpha(0.5)
|
||||
|
||||
ax.set_rlim([0,5])
|
||||
plt.show()
|
||||
"""
|
||||
ax.set_rlim([0,5])
|
||||
plt.show()
|
||||
"""
|
||||
)
|
||||
|
||||
check_comparison(selenium, "canvas-polar-zoom", 1)
|
||||
|
@ -536,8 +548,9 @@ def test_zoom_on_polar_plot(selenium_standalone):
|
|||
|
||||
@pytest.mark.skip_refcount_check
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_transparency(selenium_standalone):
|
||||
selenium = selenium_standalone
|
||||
def test_transparency(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No supported matplotlib backends on node")
|
||||
selenium.load_package("matplotlib")
|
||||
if get_backend(selenium) == "module://matplotlib.backends.wasm_backend":
|
||||
pytest.skip(
|
||||
|
|
|
@ -12,7 +12,7 @@ def test_numpy(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
let xpy = pyodide.runPython('x');
|
||||
window.x = xpy.toJs();
|
||||
self.x = xpy.toJs();
|
||||
xpy.destroy();
|
||||
"""
|
||||
)
|
||||
|
@ -37,7 +37,7 @@ def test_typed_arrays(selenium):
|
|||
("Float32Array", "float32"),
|
||||
("Float64Array", "float64"),
|
||||
):
|
||||
selenium.run_js(f"window.array = new {jstype}([1, 2, 3, 4]);\n")
|
||||
selenium.run_js(f"self.array = new {jstype}([1, 2, 3, 4]);\n")
|
||||
assert selenium.run(
|
||||
"from js import array\n"
|
||||
"npyarray = numpy.asarray(array.to_py())\n"
|
||||
|
@ -264,7 +264,7 @@ def test_get_buffer_roundtrip(selenium, arg):
|
|||
import numpy as np
|
||||
x = {arg}
|
||||
`);
|
||||
window.x_js_buf = pyodide.globals.get("x").getBuffer();
|
||||
self.x_js_buf = pyodide.globals.get("x").getBuffer();
|
||||
x_js_buf.length = x_js_buf.data.length;
|
||||
"""
|
||||
)
|
||||
|
@ -303,7 +303,7 @@ def test_get_buffer_big_endian(selenium):
|
|||
selenium.run_js(
|
||||
"""
|
||||
await pyodide.loadPackage(['numpy']);
|
||||
window.a = pyodide.runPython(`
|
||||
self.a = pyodide.runPython(`
|
||||
import numpy as np
|
||||
np.arange(24, dtype="int16").byteswap().newbyteorder()
|
||||
`);
|
||||
|
|
|
@ -55,6 +55,8 @@ def test_load_largish_file(selenium_standalone, request, httpserver):
|
|||
pytest.xfail(
|
||||
"test_load_largish_file triggers a fatal runtime error in Chrome 89 see #1495"
|
||||
)
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("open_url doesn't work in node")
|
||||
|
||||
selenium.load_package("pandas")
|
||||
selenium.load_package("matplotlib")
|
||||
|
|
|
@ -2,7 +2,8 @@ from pyodide_build.testing import run_in_pyodide
|
|||
|
||||
|
||||
@run_in_pyodide(
|
||||
packages=["pillow"], xfail_browsers={"firefox": "timeout", "chrome": ""}
|
||||
packages=["pillow"],
|
||||
xfail_browsers={"firefox": "timeout", "chrome": "", "node": "timeout"},
|
||||
)
|
||||
def test_pillow():
|
||||
from PIL import Image, ImageDraw, ImageOps
|
||||
|
|
|
@ -29,6 +29,7 @@ def registered_packages_meta():
|
|||
UNSUPPORTED_PACKAGES: dict = {
|
||||
"chrome": ["scikit-image", "statsmodels"],
|
||||
"firefox": [],
|
||||
"node": ["scikit-image", "statsmodels"],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
|||
import inspect
|
||||
from typing import Callable, Dict, List, Optional, Union
|
||||
import contextlib
|
||||
from base64 import b64encode
|
||||
|
||||
|
||||
def _run_in_pyodide_get_source(f):
|
||||
|
@ -17,6 +18,13 @@ def _run_in_pyodide_get_source(f):
|
|||
return "".join(lines)
|
||||
|
||||
|
||||
def chunkstring(string, length):
|
||||
return (string[0 + i : length + i] for i in range(0, len(string), length))
|
||||
|
||||
|
||||
from pprint import pformat
|
||||
|
||||
|
||||
def run_in_pyodide(
|
||||
_function: Optional[Callable] = None,
|
||||
*,
|
||||
|
@ -64,13 +72,17 @@ def run_in_pyodide(
|
|||
await_kw = ""
|
||||
source = _run_in_pyodide_get_source(f)
|
||||
filename = inspect.getsourcefile(f)
|
||||
encoded = pformat(
|
||||
list(chunkstring(b64encode(source.encode()).decode(), 100))
|
||||
)
|
||||
|
||||
selenium.run_js(
|
||||
f"""
|
||||
let eval_code = pyodide.pyodide_py.eval_code;
|
||||
try {{
|
||||
eval_code.callKwargs(
|
||||
{{
|
||||
source : {source!r},
|
||||
source : atob({encoded}.join("")),
|
||||
globals : pyodide._module.globals,
|
||||
filename : {filename!r}
|
||||
}}
|
||||
|
@ -125,11 +137,11 @@ def set_webdriver_script_timeout(selenium, script_timeout: Optional[Union[int, f
|
|||
value of the timeout in seconds
|
||||
"""
|
||||
if script_timeout is not None:
|
||||
selenium.driver.set_script_timeout(script_timeout)
|
||||
selenium.set_script_timeout(script_timeout)
|
||||
yield
|
||||
# revert to the initial value
|
||||
if script_timeout is not None:
|
||||
selenium.driver.set_script_timeout(selenium.script_timeout)
|
||||
selenium.set_script_timeout(selenium.script_timeout)
|
||||
|
||||
|
||||
def parse_driver_timeout(request) -> Optional[Union[int, float]]:
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
from pyodide_build.testing import set_webdriver_script_timeout
|
||||
|
||||
|
||||
class _MockDriver:
|
||||
def set_script_timeout(self, value):
|
||||
self._timeout = value
|
||||
|
||||
|
||||
class _MockSelenium:
|
||||
script_timeout = 2
|
||||
driver = _MockDriver()
|
||||
|
||||
def set_script_timeout(self, value):
|
||||
self._timeout = value
|
||||
|
||||
|
||||
def test_set_webdriver_script_timeout():
|
||||
selenium = _MockSelenium()
|
||||
assert not hasattr(selenium.driver, "_timeout")
|
||||
assert not hasattr(selenium, "_timeout")
|
||||
with set_webdriver_script_timeout(selenium, script_timeout=10):
|
||||
assert selenium.driver._timeout == 10
|
||||
assert selenium.driver._timeout == 2
|
||||
assert selenium._timeout == 10
|
||||
assert selenium._timeout == 2
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# core
|
||||
cython<3.0
|
||||
packaging
|
||||
pyyaml
|
||||
ruamel.yaml
|
||||
# lint
|
||||
black
|
||||
flake8
|
||||
mypy==0.812
|
||||
# testing
|
||||
hypothesis
|
||||
pexpect
|
||||
pytest
|
||||
pytest-asyncio
|
||||
pytest-cov
|
||||
pytest-httpserver
|
||||
pytest-instafail
|
||||
pytest-rerunfailures
|
||||
pytest-xdist
|
||||
selenium==4.0.0.b3
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PYODIDE_IMAGE_REPO="pyodide"
|
||||
PYODIDE_IMAGE_TAG="18"
|
||||
PYODIDE_IMAGE_TAG="19"
|
||||
PYODIDE_PREBUILT_IMAGE_TAG="0.17.0"
|
||||
DEFAULT_PYODIDE_DOCKER_IMAGE="${PYODIDE_IMAGE_REPO}/pyodide-env:${PYODIDE_IMAGE_TAG}"
|
||||
DEFAULT_PYODIDE_SYSTEM_PORT="8000"
|
||||
|
|
|
@ -52,7 +52,22 @@ if (globalThis.document) {
|
|||
globalThis.importScripts(url);
|
||||
};
|
||||
} else if (typeof process !== "undefined" && process.release.name === "node") {
|
||||
loadScript = async (url) => import(path.resolve(url));
|
||||
const pathPromise = import("path").then((M) => M.default);
|
||||
const fetchPromise = import("node-fetch").then((M) => M.default);
|
||||
const vmPromise = import("vm").then((M) => M.default);
|
||||
loadScript = async (url) => {
|
||||
if (url.includes("://")) {
|
||||
// If it's a url, have to load it with fetch and then eval it.
|
||||
const fetch = await fetchPromise;
|
||||
const vm = await vmPromise;
|
||||
vm.runInThisContext(await (await fetch(url)).text());
|
||||
} else {
|
||||
// Otherwise, hopefully it is a relative path we can load from the file
|
||||
// system.
|
||||
const path = await pathPromise;
|
||||
await import(path.resolve(url));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error("Cannot determine runtime environment");
|
||||
}
|
||||
|
|
|
@ -288,6 +288,11 @@
|
|||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"base-64": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
|
||||
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"base-64": "^1.0.0",
|
||||
"node-fetch": "^2.6.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ function fixRecursionLimit() {
|
|||
* @async
|
||||
*/
|
||||
export async function loadPyodide(config) {
|
||||
const default_config = { fullStdLib: true };
|
||||
const default_config = { fullStdLib: true, jsglobals: globalThis };
|
||||
config = Object.assign(default_config, config);
|
||||
if (globalThis.__pyodide_module) {
|
||||
if (globalThis.languagePluginURL) {
|
||||
|
@ -243,7 +243,7 @@ def temp(pyodide_js, Module, jsglobals):
|
|||
print("Python initialization complete")
|
||||
`);
|
||||
|
||||
Module.init_dict.get("temp")(pyodide, Module, globalThis);
|
||||
Module.init_dict.get("temp")(pyodide, Module, config.jsglobals);
|
||||
// Module.runPython works starting from here!
|
||||
|
||||
// Wrap "globals" in a special Proxy that allows `pyodide.globals.x` access.
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
<!-- Bootstrap HTML for running the unit tests. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script type="text/javascript">
|
||||
function sleep(s) {
|
||||
return new Promise((resolve) => setTimeout(resolve, s));
|
||||
}
|
||||
window.logs = [];
|
||||
console.log = function (message) {
|
||||
window.logs.push(message);
|
||||
|
@ -19,7 +16,6 @@
|
|||
console.error = function (message) {
|
||||
window.logs.push(message);
|
||||
};
|
||||
// set the pyodide files URL (packages.json, pyodide.asm.data etc)
|
||||
</script>
|
||||
<script src="./pyodide.js"></script>
|
||||
</head>
|
||||
|
|
|
@ -140,7 +140,7 @@ def test_then_jsproxy(selenium):
|
|||
selenium.run(
|
||||
"""
|
||||
p = Promise.new(create_once_callable(prom))
|
||||
p.finally_(onfinally)
|
||||
p.finally_(onfinally).catch(onrejected) # node gets angry if we don't catch it!
|
||||
reject(10)
|
||||
"""
|
||||
)
|
||||
|
@ -149,6 +149,8 @@ def test_then_jsproxy(selenium):
|
|||
"""
|
||||
assert finally_occurred
|
||||
finally_occurred = False
|
||||
assert err == 10
|
||||
err = None
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -189,11 +191,11 @@ def test_await_error(selenium):
|
|||
async function async_js_raises(){
|
||||
throw new Error("This is an error message!");
|
||||
}
|
||||
window.async_js_raises = async_js_raises;
|
||||
self.async_js_raises = async_js_raises;
|
||||
function js_raises(){
|
||||
throw new Error("This is an error message!");
|
||||
}
|
||||
window.js_raises = js_raises;
|
||||
self.js_raises = js_raises;
|
||||
pyodide.runPython(`
|
||||
from js import async_js_raises, js_raises
|
||||
async def test():
|
||||
|
@ -309,7 +311,7 @@ def test_eval_code_await_error(selenium):
|
|||
console.log("Hello there???");
|
||||
throw new Error("This is an error message!");
|
||||
}
|
||||
window.async_js_raises = async_js_raises;
|
||||
self.async_js_raises = async_js_raises;
|
||||
pyodide.runPython(`
|
||||
from js import async_js_raises
|
||||
from pyodide import eval_code_async
|
||||
|
@ -336,7 +338,7 @@ def test_eval_code_await_error(selenium):
|
|||
def test_ensure_future_memleak(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.o = { "xxx" : 777 };
|
||||
self.o = { "xxx" : 777 };
|
||||
pyodide.runPython(`
|
||||
import asyncio
|
||||
from js import o
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import pytest
|
||||
from pathlib import Path
|
||||
import time
|
||||
import sys
|
||||
|
||||
from conftest import selenium_common
|
||||
|
@ -293,8 +294,12 @@ def test_interactive_console_top_level_await(selenium, safe_selenium_sys_redirec
|
|||
"""
|
||||
)
|
||||
selenium.run("shell.push('from js import fetch')")
|
||||
selenium.run("shell.push('await (await fetch(`packages.json`)).json()')")
|
||||
assert selenium.run("result") == None
|
||||
time.sleep(0.2)
|
||||
selenium.run("""shell.push("await (await fetch('packages.json')).json()")""")
|
||||
time.sleep(0.2)
|
||||
res = selenium.run("result")
|
||||
assert isinstance(res, dict)
|
||||
assert res["dependencies"]["micropip"] == ["pyparsing", "packaging", "distutils"]
|
||||
|
||||
|
||||
@pytest.fixture(params=["firefox", "chrome"], scope="function")
|
||||
|
|
|
@ -10,7 +10,7 @@ def test_cpython_core(python_test, selenium, request):
|
|||
name, error_flags = python_test
|
||||
|
||||
# keep only flags related to the current browser
|
||||
flags_to_remove = ["firefox", "chrome"]
|
||||
flags_to_remove = ["firefox", "chrome", "node"]
|
||||
flags_to_remove.remove(selenium.browser)
|
||||
for flag in flags_to_remove:
|
||||
if "crash-" + flag in error_flags:
|
||||
|
|
|
@ -10,11 +10,15 @@ import pytest
|
|||
def test_idbfs_persist_code(selenium_standalone):
|
||||
"""can we persist files created by user python code?"""
|
||||
selenium = selenium_standalone
|
||||
if selenium.browser == "node":
|
||||
fstype = "NODEFS"
|
||||
else:
|
||||
fstype = "IDBFS"
|
||||
# create mount
|
||||
selenium.run_js(
|
||||
"""
|
||||
f"""
|
||||
pyodide.FS.mkdir('/lib/python3.9/site-packages/test_idbfs');
|
||||
pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, "/lib/python3.9/site-packages/test_idbfs")
|
||||
pyodide.FS.mount(pyodide.FS.filesystems.{fstype}, {{root : "."}}, "/lib/python3.9/site-packages/test_idbfs");
|
||||
"""
|
||||
)
|
||||
# create file in mount
|
||||
|
@ -41,15 +45,13 @@ def test_idbfs_persist_code(selenium_standalone):
|
|||
"""
|
||||
)
|
||||
# refresh page and re-fixture
|
||||
selenium.driver.refresh()
|
||||
selenium.javascript_setup()
|
||||
selenium.refresh()
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.pyodide = await loadPyodide({ indexURL : './', fullStdLib: false });
|
||||
self.pyodide = await loadPyodide({ indexURL : './', fullStdLib: false });
|
||||
"""
|
||||
)
|
||||
selenium.save_state()
|
||||
selenium.restore_state()
|
||||
# idbfs isn't magically loaded
|
||||
selenium.run_js(
|
||||
"""
|
||||
|
@ -67,9 +69,9 @@ def test_idbfs_persist_code(selenium_standalone):
|
|||
)
|
||||
# re-mount
|
||||
selenium.run_js(
|
||||
"""
|
||||
f"""
|
||||
pyodide.FS.mkdir('/lib/python3.9/site-packages/test_idbfs');
|
||||
pyodide.FS.mount(pyodide.FS.filesystems.IDBFS, {}, "/lib/python3.9/site-packages/test_idbfs");
|
||||
pyodide.FS.mount(pyodide.FS.filesystems.{fstype}, {{root : "."}}, "/lib/python3.9/site-packages/test_idbfs");
|
||||
"""
|
||||
)
|
||||
# sync FROM idbfs
|
||||
|
@ -92,3 +94,7 @@ def test_idbfs_persist_code(selenium_standalone):
|
|||
`);
|
||||
"""
|
||||
)
|
||||
# remove file
|
||||
selenium.run_js(
|
||||
"""pyodide.FS.unlink("/lib/python3.9/site-packages/test_idbfs/__init__.py")"""
|
||||
)
|
||||
|
|
|
@ -6,8 +6,8 @@ from pyodide_build.testing import run_in_pyodide
|
|||
def test_jsproxy_dir(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
window.a = { x : 2, y : "9" };
|
||||
window.b = function(){};
|
||||
self.a = { x : 2, y : "9" };
|
||||
self.b = function(){};
|
||||
let pyresult = pyodide.runPython(`
|
||||
from js import a
|
||||
from js import b
|
||||
|
@ -43,7 +43,7 @@ def test_jsproxy_dir(selenium):
|
|||
assert set1.isdisjoint(a_items)
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = [0,1,2,3,4,5,6,7,8,9];
|
||||
self.a = [0,1,2,3,4,5,6,7,8,9];
|
||||
a[27] = 0;
|
||||
a[":"] = 0;
|
||||
a["/"] = 0;
|
||||
|
@ -69,7 +69,7 @@ def test_jsproxy_getattr(selenium):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = { x : 2, y : "9", typeof : 7 };
|
||||
self.a = { x : 2, y : "9", typeof : 7 };
|
||||
let pyresult = pyodide.runPython(`
|
||||
from js import a
|
||||
[ a.x, a.y, a.typeof ]
|
||||
|
@ -83,7 +83,9 @@ def test_jsproxy_getattr(selenium):
|
|||
)
|
||||
|
||||
|
||||
def test_jsproxy(selenium):
|
||||
def test_jsproxy_document(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No document in node")
|
||||
selenium.run("from js import document")
|
||||
assert (
|
||||
selenium.run(
|
||||
|
@ -97,13 +99,50 @@ def test_jsproxy(selenium):
|
|||
)
|
||||
assert selenium.run("document.body.children[0].tagName") == "DIV"
|
||||
assert selenium.run("repr(document)") == "[object HTMLDocument]"
|
||||
|
||||
selenium.run_js("window.square = function (x) { return x*x; }")
|
||||
assert selenium.run("from js import square\n" "square(2)") == 4
|
||||
assert (
|
||||
selenium.run("from js import ImageData\n" "ImageData.new(64, 64).width") == 64
|
||||
selenium.run(
|
||||
"""
|
||||
from js import document
|
||||
el = document.createElement('div')
|
||||
len(dir(el)) >= 200 and 'appendChild' in dir(el)
|
||||
"""
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert selenium.run("from js import ImageData\n" "ImageData.typeof") == "function"
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import ImageData
|
||||
ImageData.new(64, 64).width
|
||||
"""
|
||||
)
|
||||
== 64
|
||||
)
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import ImageData
|
||||
ImageData.typeof
|
||||
"""
|
||||
)
|
||||
== "function"
|
||||
)
|
||||
|
||||
|
||||
def test_jsproxy_function(selenium):
|
||||
selenium.run_js("self.square = function (x) { return x*x; };")
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import square
|
||||
square(2)
|
||||
"""
|
||||
)
|
||||
== 4
|
||||
)
|
||||
|
||||
|
||||
def test_jsproxy_class(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
class Point {
|
||||
|
@ -112,7 +151,8 @@ def test_jsproxy(selenium):
|
|||
this.y = y;
|
||||
}
|
||||
}
|
||||
window.TEST = new Point(42, 43);"""
|
||||
self.TEST = new Point(42, 43);
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
selenium.run(
|
||||
|
@ -124,9 +164,12 @@ def test_jsproxy(selenium):
|
|||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_jsproxy_map(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.TEST = new Map([["x", 42], ["y", 43]]);
|
||||
self.TEST = new Map([["x", 42], ["y", 43]]);
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
|
@ -153,7 +196,7 @@ def test_jsproxy(selenium):
|
|||
)
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.TEST = {foo: 'bar', baz: 'bap'}
|
||||
self.TEST = {foo: 'bar', baz: 'bap'}
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
|
@ -165,16 +208,6 @@ def test_jsproxy(selenium):
|
|||
)
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import document
|
||||
el = document.createElement('div')
|
||||
len(dir(el)) >= 200 and 'appendChild' in dir(el)
|
||||
"""
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_jsproxy_iter(selenium):
|
||||
|
@ -190,7 +223,7 @@ def test_jsproxy_iter(selenium):
|
|||
}
|
||||
};
|
||||
}
|
||||
window.ITER = makeIterator([1, 2, 3]);"""
|
||||
self.ITER = makeIterator([1, 2, 3]);"""
|
||||
)
|
||||
assert selenium.run("from js import ITER\n" "list(ITER)") == [1, 2, 3]
|
||||
|
||||
|
@ -198,7 +231,7 @@ def test_jsproxy_iter(selenium):
|
|||
def test_jsproxy_implicit_iter(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.ITER = [1, 2, 3];
|
||||
self.ITER = [1, 2, 3];
|
||||
"""
|
||||
)
|
||||
assert selenium.run("from js import ITER, Object\n" "list(ITER)") == [1, 2, 3]
|
||||
|
@ -216,7 +249,7 @@ def test_jsproxy_call(selenium):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.f = function(){ return arguments.length; };
|
||||
self.f = function(){ return arguments.length; };
|
||||
let pyresult = pyodide.runPython(
|
||||
`
|
||||
from js import f
|
||||
|
@ -236,7 +269,7 @@ def test_jsproxy_call_kwargs(selenium):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.kwarg_function = ({ a = 1, b = 1 }) => {
|
||||
self.kwarg_function = ({ a = 1, b = 1 }) => {
|
||||
return [a, b];
|
||||
};
|
||||
return pyodide.runPython(
|
||||
|
@ -255,7 +288,7 @@ def test_jsproxy_call_kwargs(selenium):
|
|||
def test_jsproxy_call_meth_py(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
window.a = {};
|
||||
self.a = {};
|
||||
return pyodide.runPython(
|
||||
`
|
||||
from js import a
|
||||
|
@ -272,7 +305,7 @@ def test_jsproxy_call_meth_py(selenium):
|
|||
def test_jsproxy_call_meth_js(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
window.a = {};
|
||||
self.a = {};
|
||||
function f(){return this;}
|
||||
a.f = f;
|
||||
return pyodide.runPython(
|
||||
|
@ -288,7 +321,7 @@ def test_jsproxy_call_meth_js(selenium):
|
|||
def test_jsproxy_call_meth_js_kwargs(selenium):
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
window.a = {};
|
||||
self.a = {};
|
||||
function f({ x = 1, y = 1 }){
|
||||
return [this, x, y];
|
||||
}
|
||||
|
@ -327,23 +360,23 @@ def test_import_bind():
|
|||
@run_in_pyodide
|
||||
def test_nested_attribute_access():
|
||||
import js
|
||||
from js import window
|
||||
from js import self
|
||||
|
||||
js.URL.createObjectURL
|
||||
window.URL.createObjectURL
|
||||
assert js.Float64Array.BYTES_PER_ELEMENT == 8
|
||||
assert self.Float64Array.BYTES_PER_ELEMENT == 8
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
def test_window_isnt_super_weird_anymore():
|
||||
import js
|
||||
from js import window, Array
|
||||
from js import self, Array
|
||||
|
||||
assert window.Array != window
|
||||
assert window.Array == Array
|
||||
assert window.window.window.window == window
|
||||
assert js.window.Array == Array
|
||||
assert js.window.window.window.window == window
|
||||
assert window.window.window.window.Array == Array
|
||||
assert self.Array != self
|
||||
assert self.Array == Array
|
||||
assert self.self.self.self == self
|
||||
assert js.self.Array == Array
|
||||
assert js.self.self.self.self == self
|
||||
assert self.self.self.self.Array == Array
|
||||
|
||||
|
||||
@pytest.mark.skip_refcount_check
|
||||
|
@ -437,7 +470,7 @@ def test_nested_import(selenium_standalone):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = { b : { c : { d : 2 } } };
|
||||
self.a = { b : { c : { d : 2 } } };
|
||||
return pyodide.runPython("from js.a.b import c; c.d");
|
||||
"""
|
||||
)
|
||||
|
@ -493,7 +526,7 @@ def test_register_jsmodule_docs_example(selenium_standalone):
|
|||
def test_object_entries_keys_values(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.x = { a : 2, b : 3, c : 4 };
|
||||
self.x = { a : 2, b : 3, c : 4 };
|
||||
pyodide.runPython(`
|
||||
from js import x
|
||||
assert x.object_entries().to_py() == [["a", 2], ["b", 3], ["c", 4]]
|
||||
|
@ -551,7 +584,7 @@ def test_mixins_feature_presence(selenium):
|
|||
def test_mixins_calls(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
window.testObjects = {};
|
||||
self.testObjects = {};
|
||||
testObjects.iterable = { *[Symbol.iterator](){
|
||||
yield 3; yield 5; yield 7;
|
||||
} };
|
||||
|
@ -604,8 +637,8 @@ def test_mixins_calls(selenium):
|
|||
def test_mixins_errors(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = [];
|
||||
window.b = {
|
||||
self.a = [];
|
||||
self.b = {
|
||||
has(){ return false; },
|
||||
get(){ return undefined; },
|
||||
set(){ return false; },
|
||||
|
@ -625,7 +658,7 @@ def test_mixins_errors(selenium):
|
|||
del b[0]
|
||||
`);
|
||||
|
||||
window.c = {
|
||||
self.c = {
|
||||
next(){},
|
||||
length : 1,
|
||||
get(){},
|
||||
|
@ -633,7 +666,7 @@ def test_mixins_errors(selenium):
|
|||
has(){},
|
||||
then(){}
|
||||
};
|
||||
window.d = {
|
||||
self.d = {
|
||||
[Symbol.iterator](){},
|
||||
};
|
||||
pyodide.runPython("from js import c, d");
|
||||
|
@ -670,8 +703,8 @@ def test_mixins_errors(selenium):
|
|||
await c
|
||||
`);
|
||||
|
||||
window.l = [0, false, NaN, undefined, null];
|
||||
window.l[6] = 7;
|
||||
self.l = [0, false, NaN, undefined, null];
|
||||
self.l[6] = 7;
|
||||
await pyodide.runPythonAsync(`
|
||||
from unittest import TestCase
|
||||
raises = TestCase().assertRaises
|
||||
|
@ -691,11 +724,11 @@ def test_mixins_errors(selenium):
|
|||
l[3]; l[4]
|
||||
`);
|
||||
|
||||
window.l = [0, false, NaN, undefined, null];
|
||||
window.l[6] = 7;
|
||||
let a = Array.from(window.l.entries());
|
||||
self.l = [0, false, NaN, undefined, null];
|
||||
self.l[6] = 7;
|
||||
let a = Array.from(self.l.entries());
|
||||
a.splice(5, 1);
|
||||
window.m = new Map(a);
|
||||
self.m = new Map(a);
|
||||
await pyodide.runPythonAsync(`
|
||||
from js import m
|
||||
from unittest import TestCase
|
||||
|
@ -791,7 +824,7 @@ def test_memory_leaks(selenium):
|
|||
# refcounts are tested automatically in conftest by default
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = [1,2,3];
|
||||
self.a = [1,2,3];
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
repr(a)
|
||||
|
|
|
@ -5,15 +5,17 @@ from pathlib import Path
|
|||
|
||||
@pytest.mark.parametrize("active_server", ["main", "secondary"])
|
||||
def test_load_from_url(selenium_standalone, web_server_secondary, active_server):
|
||||
|
||||
selenium = selenium_standalone
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("Loading urls in node seems to time out right now")
|
||||
if active_server == "secondary":
|
||||
url, port, log_main = web_server_secondary
|
||||
log_backup = selenium_standalone.server_log
|
||||
log_backup = selenium.server_log
|
||||
elif active_server == "main":
|
||||
_, _, log_backup = web_server_secondary
|
||||
log_main = selenium_standalone.server_log
|
||||
url = selenium_standalone.server_hostname
|
||||
port = selenium_standalone.server_port
|
||||
log_main = selenium.server_log
|
||||
url = selenium.server_hostname
|
||||
port = selenium.server_port
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
|
@ -23,26 +25,26 @@ def test_load_from_url(selenium_standalone, web_server_secondary, active_server)
|
|||
fh_main.seek(0, 2)
|
||||
fh_backup.seek(0, 2)
|
||||
|
||||
selenium_standalone.load_package(f"http://{url}:{port}/pyparsing.js")
|
||||
assert "Skipping unknown package" not in selenium_standalone.logs
|
||||
selenium.load_package(f"http://{url}:{port}/pyparsing.js")
|
||||
assert "Skipping unknown package" not in selenium.logs
|
||||
|
||||
# check that all ressources were loaded from the active server
|
||||
# check that all resources were loaded from the active server
|
||||
txt = fh_main.read()
|
||||
assert '"GET /pyparsing.js HTTP/1.1" 200' in txt
|
||||
assert '"GET /pyparsing.data HTTP/1.1" 200' in txt
|
||||
|
||||
# no additional ressources were loaded from the other server
|
||||
# no additional resources were loaded from the other server
|
||||
assert len(fh_backup.read()) == 0
|
||||
|
||||
selenium_standalone.run(
|
||||
selenium.run(
|
||||
"""
|
||||
from pyparsing import Word, alphas
|
||||
repr(Word(alphas).parseString('hello'))
|
||||
"""
|
||||
)
|
||||
|
||||
selenium_standalone.load_package(f"http://{url}:{port}/pytz.js")
|
||||
selenium_standalone.run("import pytz")
|
||||
selenium.load_package(f"http://{url}:{port}/pytz.js")
|
||||
selenium.run("import pytz")
|
||||
|
||||
|
||||
def test_load_relative_url(selenium_standalone):
|
||||
|
@ -146,13 +148,13 @@ def test_load_package_unknown(selenium_standalone):
|
|||
shutil.copyfile(build_dir / "pyparsing.data", build_dir / "pyparsing-custom.data")
|
||||
|
||||
try:
|
||||
selenium_standalone.load_package(f"http://{url}:{port}/pyparsing-custom.js")
|
||||
selenium_standalone.load_package(f"./pyparsing-custom.js")
|
||||
finally:
|
||||
(build_dir / "pyparsing-custom.js").unlink()
|
||||
(build_dir / "pyparsing-custom.data").unlink()
|
||||
|
||||
assert selenium_standalone.run_js(
|
||||
"return window.pyodide.loadedPackages.hasOwnProperty('pyparsing-custom')"
|
||||
"return pyodide.loadedPackages.hasOwnProperty('pyparsing-custom')"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -177,7 +177,6 @@ def test_hiwire_is_promise(selenium):
|
|||
"1",
|
||||
"'x'",
|
||||
"''",
|
||||
"document.all",
|
||||
"false",
|
||||
"undefined",
|
||||
"null",
|
||||
|
@ -202,6 +201,11 @@ def test_hiwire_is_promise(selenium):
|
|||
f"return pyodide._module.hiwire.isPromise({s}) === false;"
|
||||
)
|
||||
|
||||
if not selenium.browser == "node":
|
||||
assert selenium.run_js(
|
||||
f"return pyodide._module.hiwire.isPromise(document.all) === false;"
|
||||
)
|
||||
|
||||
assert selenium.run_js(
|
||||
"return pyodide._module.hiwire.isPromise(Promise.resolve()) === true;"
|
||||
)
|
||||
|
@ -229,7 +233,7 @@ def test_keyboard_interrupt(selenium):
|
|||
"""
|
||||
x = new Int8Array(1)
|
||||
pyodide._module.setInterruptBuffer(x)
|
||||
window.triggerKeyboardInterrupt = function(){
|
||||
self.triggerKeyboardInterrupt = function(){
|
||||
x[0] = 2;
|
||||
}
|
||||
try {
|
||||
|
@ -311,7 +315,7 @@ def test_run_python_js_error(selenium):
|
|||
function throwError(){
|
||||
throw new Error("blah!");
|
||||
}
|
||||
window.throwError = throwError;
|
||||
self.throwError = throwError;
|
||||
pyodide.runPython(`
|
||||
from js import throwError
|
||||
from unittest import TestCase
|
||||
|
@ -327,7 +331,7 @@ def test_run_python_js_error(selenium):
|
|||
def test_create_once_callable(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.call7 = function call7(f){
|
||||
self.call7 = function call7(f){
|
||||
return f(7);
|
||||
}
|
||||
pyodide.runPython(`
|
||||
|
@ -364,14 +368,14 @@ def test_create_once_callable(selenium):
|
|||
def test_create_proxy(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.testAddListener = function(f){
|
||||
window.listener = f;
|
||||
self.testAddListener = function(f){
|
||||
self.listener = f;
|
||||
}
|
||||
window.testCallListener = function(f){
|
||||
return window.listener();
|
||||
self.testCallListener = function(f){
|
||||
return self.listener();
|
||||
}
|
||||
window.testRemoveListener = function(f){
|
||||
return window.listener === f;
|
||||
self.testRemoveListener = function(f){
|
||||
return self.listener === f;
|
||||
}
|
||||
pyodide.runPython(`
|
||||
from pyodide import create_proxy
|
||||
|
@ -428,7 +432,7 @@ def test_docstrings_b(selenium):
|
|||
sig_then_should_equal = "(onfulfilled, onrejected)"
|
||||
ds_once_should_equal = dedent_docstring(create_once_callable.__doc__)
|
||||
sig_once_should_equal = "(obj)"
|
||||
selenium.run_js("window.a = Promise.resolve();")
|
||||
selenium.run_js("self.a = Promise.resolve();")
|
||||
[ds_then, sig_then, ds_once, sig_once] = selenium.run(
|
||||
"""
|
||||
from js import a
|
||||
|
@ -508,6 +512,10 @@ def test_fatal_error(selenium_standalone):
|
|||
def strip_stack_trace(x):
|
||||
x = re.sub("\n.*site-packages.*", "", x)
|
||||
x = re.sub("/lib/python.*/", "", x)
|
||||
x = re.sub("/lib/python.*/", "", x)
|
||||
x = re.sub("warning: no [bB]lob.*\n", "", x)
|
||||
x = re.sub("Error: intentionally triggered fatal error!", "{}", x)
|
||||
x = re.sub(" +at .*\n", "", x)
|
||||
return x
|
||||
|
||||
assert (
|
||||
|
@ -515,18 +523,18 @@ def test_fatal_error(selenium_standalone):
|
|||
== dedent(
|
||||
strip_stack_trace(
|
||||
"""
|
||||
Python initialization complete
|
||||
Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers.
|
||||
The cause of the fatal error was:
|
||||
{}
|
||||
Stack (most recent call first):
|
||||
File "<exec>", line 8 in h
|
||||
File "<exec>", line 6 in g
|
||||
File "<exec>", line 4 in f
|
||||
File "<exec>", line 9 in <module>
|
||||
File "/lib/pythonxxx/site-packages/pyodide/_base.py", line 242 in run
|
||||
File "/lib/pythonxxx/site-packages/pyodide/_base.py", line 344 in eval_code
|
||||
"""
|
||||
Python initialization complete
|
||||
Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers.
|
||||
The cause of the fatal error was:
|
||||
{}
|
||||
Stack (most recent call first):
|
||||
File "<exec>", line 8 in h
|
||||
File "<exec>", line 6 in g
|
||||
File "<exec>", line 4 in f
|
||||
File "<exec>", line 9 in <module>
|
||||
File "/lib/pythonxxx/site-packages/pyodide/_base.py", line 242 in run
|
||||
File "/lib/pythonxxx/site-packages/pyodide/_base.py", line 344 in eval_code
|
||||
"""
|
||||
)
|
||||
).strip()
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# See also test_typeconversions, and test_python.
|
||||
import pytest
|
||||
import time
|
||||
|
||||
|
||||
def test_pyproxy_class(selenium):
|
||||
|
@ -12,7 +13,7 @@ def test_pyproxy_class(selenium):
|
|||
return value * 64
|
||||
f = Foo()
|
||||
`);
|
||||
window.f = pyodide.globals.get('f');
|
||||
self.f = pyodide.globals.get('f');
|
||||
assert(() => f.type === "Foo");
|
||||
let f_get_value = f.get_value
|
||||
assert(() => f_get_value(2) === 128);
|
||||
|
@ -22,7 +23,7 @@ def test_pyproxy_class(selenium):
|
|||
f.baz = 32;
|
||||
assert(() => f.baz === 32);
|
||||
pyodide.runPython(`assert hasattr(f, 'baz')`)
|
||||
window.f_props = Object.getOwnPropertyNames(f);
|
||||
self.f_props = Object.getOwnPropertyNames(f);
|
||||
delete f.baz
|
||||
pyodide.runPython(`assert not hasattr(f, 'baz')`)
|
||||
assert(() => f.toString().startsWith("<Foo"));
|
||||
|
@ -87,10 +88,10 @@ def test_pyproxy_refcount(selenium):
|
|||
return pyodide.runPython("sys.getrefcount(pyfunc)");
|
||||
}
|
||||
let result = [];
|
||||
window.jsfunc = function (f) { f(); };
|
||||
self.jsfunc = function (f) { f(); };
|
||||
pyodide.runPython(`
|
||||
import sys
|
||||
from js import window
|
||||
from js import self
|
||||
|
||||
def pyfunc(*args, **kwargs):
|
||||
print(*args, **kwargs)
|
||||
|
@ -110,13 +111,13 @@ def test_pyproxy_refcount(selenium):
|
|||
// 3. pyfunc is referenced from the sys.getrefcount()-test below
|
||||
|
||||
pyodide.runPython(`
|
||||
window.jsfunc(pyfunc) # creates new PyProxy
|
||||
self.jsfunc(pyfunc) # creates new PyProxy
|
||||
`);
|
||||
|
||||
result.push([getRefCount(), 3])
|
||||
pyodide.runPython(`
|
||||
window.jsfunc(pyfunc) # create new PyProxy
|
||||
window.jsfunc(pyfunc) # create new PyProxy
|
||||
self.jsfunc(pyfunc) # create new PyProxy
|
||||
self.jsfunc(pyfunc) # create new PyProxy
|
||||
`)
|
||||
|
||||
// the refcount should be 3 because:
|
||||
|
@ -294,7 +295,7 @@ def test_get_empty_buffer(selenium):
|
|||
def test_pyproxy_get_buffer_type_argument(selenium, array_type):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = pyodide.runPython("bytes(range(256))");
|
||||
self.a = pyodide.runPython("bytes(range(256))");
|
||||
"""
|
||||
)
|
||||
try:
|
||||
|
@ -324,7 +325,7 @@ def test_pyproxy_get_buffer_type_argument(selenium, array_type):
|
|||
else:
|
||||
assert result == list(mv.cast(fmt))
|
||||
finally:
|
||||
selenium.run_js("a.destroy(); window.a = undefined;")
|
||||
selenium.run_js("a.destroy(); self.a = undefined;")
|
||||
|
||||
|
||||
def test_pyproxy_mixins(selenium):
|
||||
|
@ -528,7 +529,7 @@ def test_pyproxy_mixins6(selenium):
|
|||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pyproxy_gc(selenium):
|
||||
if selenium.browser != "chrome":
|
||||
if not hasattr(selenium, "collect_garbage"):
|
||||
pytest.skip("No gc exposed")
|
||||
|
||||
# Two ways to trigger garbage collection in Chrome:
|
||||
|
@ -542,16 +543,22 @@ def test_pyproxy_gc(selenium):
|
|||
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.x = new FinalizationRegistry((val) => { window.val = val; });
|
||||
self.x = new FinalizationRegistry((val) => { self.val = val; });
|
||||
x.register({}, 77);
|
||||
gc();
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js("return window.val;") == 77
|
||||
time.sleep(0.1)
|
||||
selenium.run_js(
|
||||
"""
|
||||
gc();
|
||||
"""
|
||||
)
|
||||
assert selenium.run_js("return self.val;") == 77
|
||||
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.res = new Map();
|
||||
self.res = new Map();
|
||||
|
||||
let d = pyodide.runPython(`
|
||||
from js import res
|
||||
|
@ -579,7 +586,7 @@ def test_pyproxy_gc(selenium):
|
|||
d.destroy()
|
||||
"""
|
||||
)
|
||||
selenium.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})
|
||||
selenium.collect_garbage()
|
||||
|
||||
selenium.run(
|
||||
"""
|
||||
|
@ -587,19 +594,19 @@ def test_pyproxy_gc(selenium):
|
|||
del d
|
||||
"""
|
||||
)
|
||||
selenium.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})
|
||||
selenium.collect_garbage()
|
||||
a = selenium.run_js("return Array.from(res.entries());")
|
||||
assert dict(a) == {0: 2, 1: 3, 2: 4, 3: 2, "destructor_ran": True}
|
||||
|
||||
|
||||
@pytest.mark.skip_pyproxy_check
|
||||
def test_pyproxy_gc_destroy(selenium):
|
||||
if selenium.browser != "chrome":
|
||||
if not hasattr(selenium, "collect_garbage"):
|
||||
pytest.skip("No gc exposed")
|
||||
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.res = new Map();
|
||||
self.res = new Map();
|
||||
let d = pyodide.runPython(`
|
||||
from js import res
|
||||
def get_ref_count(x):
|
||||
|
@ -627,7 +634,7 @@ def test_pyproxy_gc_destroy(selenium):
|
|||
get_ref_count.destroy();
|
||||
"""
|
||||
)
|
||||
selenium.driver.execute_cdp_cmd("HeapProfiler.collectGarbage", {})
|
||||
selenium.collect_garbage()
|
||||
selenium.run(
|
||||
"""
|
||||
get_ref_count(4)
|
||||
|
@ -783,7 +790,7 @@ def test_pyproxy_call(selenium):
|
|||
def f(x=2, y=3):
|
||||
return to_js([x, y])
|
||||
`);
|
||||
window.f = pyodide.globals.get("f");
|
||||
self.f = pyodide.globals.get("f");
|
||||
"""
|
||||
)
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@ import pytest
|
|||
|
||||
def test_init(selenium_standalone):
|
||||
assert "Python initialization complete" in selenium_standalone.logs.splitlines()
|
||||
assert len(selenium_standalone.driver.window_handles) == 1
|
||||
|
||||
|
||||
def test_webbrowser(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("Webbrowser doesn't work in node")
|
||||
selenium.run_async("import antigravity")
|
||||
assert len(selenium.driver.window_handles) == 2
|
||||
|
||||
|
@ -17,6 +18,8 @@ def test_print(selenium):
|
|||
|
||||
|
||||
def test_import_js(selenium):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("No window in node")
|
||||
result = selenium.run(
|
||||
"""
|
||||
import js
|
||||
|
@ -47,6 +50,8 @@ def test_globals_get_multiple(selenium):
|
|||
|
||||
|
||||
def test_open_url(selenium, httpserver):
|
||||
if selenium.browser == "node":
|
||||
pytest.xfail("XMLHttpRequest not available in node")
|
||||
httpserver.expect_request("/data").respond_with_data(
|
||||
b"HELLO", content_type="text/text", headers={"Access-Control-Allow-Origin": "*"}
|
||||
)
|
||||
|
|
|
@ -7,14 +7,14 @@ from conftest import selenium_context_manager
|
|||
|
||||
|
||||
@given(s=text())
|
||||
@settings(deadline=600)
|
||||
@settings(deadline=2000)
|
||||
def test_string_conversion(selenium_module_scope, s):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
# careful string escaping here -- hypothesis will fuzz it.
|
||||
sbytes = list(s.encode())
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.sjs = (new TextDecoder("utf8")).decode(new Uint8Array({sbytes}));
|
||||
self.sjs = (new TextDecoder("utf8")).decode(new Uint8Array({sbytes}));
|
||||
pyodide.runPython('spy = bytes({sbytes}).decode()');
|
||||
"""
|
||||
)
|
||||
|
@ -33,7 +33,7 @@ def test_string_conversion(selenium_module_scope, s):
|
|||
strategies.floats(allow_nan=False),
|
||||
)
|
||||
)
|
||||
@settings(deadline=600)
|
||||
@settings(deadline=2000)
|
||||
def test_number_conversions(selenium_module_scope, n):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
import json
|
||||
|
@ -41,7 +41,7 @@ def test_number_conversions(selenium_module_scope, n):
|
|||
s = json.dumps(n)
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.x_js = eval({s!r}); // JSON.parse apparently doesn't work
|
||||
self.x_js = eval({s!r}); // JSON.parse apparently doesn't work
|
||||
pyodide.runPython(`
|
||||
import json
|
||||
x_py = json.loads({s!r})
|
||||
|
@ -60,7 +60,7 @@ def test_number_conversions(selenium_module_scope, n):
|
|||
def test_nan_conversions(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = NaN;
|
||||
self.a = NaN;
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
from math import isnan, nan
|
||||
|
@ -75,11 +75,11 @@ def test_nan_conversions(selenium):
|
|||
|
||||
|
||||
@given(n=strategies.integers())
|
||||
@settings(deadline=600)
|
||||
@settings(deadline=2000)
|
||||
def test_bigint_conversions(selenium_module_scope, n):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
h = hex(n)
|
||||
selenium.run_js(f"window.h = {h!r};")
|
||||
selenium.run_js(f"self.h = {h!r};")
|
||||
selenium.run_js(
|
||||
"""
|
||||
let negative = false;
|
||||
|
@ -88,9 +88,9 @@ def test_bigint_conversions(selenium_module_scope, n):
|
|||
h2 = h2.slice(1);
|
||||
negative = true;
|
||||
}
|
||||
window.n = BigInt(h2);
|
||||
self.n = BigInt(h2);
|
||||
if(negative){
|
||||
window.n = -n;
|
||||
self.n = -n;
|
||||
}
|
||||
pyodide.runPython(`
|
||||
from js import n, h
|
||||
|
@ -112,7 +112,7 @@ def test_bigint_conversions(selenium_module_scope, n):
|
|||
|
||||
# Generate an object of any type
|
||||
@given(obj=from_type(type).flatmap(from_type))
|
||||
@settings(deadline=600)
|
||||
@settings(deadline=2000)
|
||||
def test_hyp_py2js2py(selenium_module_scope, obj):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
import pickle
|
||||
|
@ -143,7 +143,7 @@ def test_hyp_py2js2py(selenium_module_scope, obj):
|
|||
)
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.x2 = pyodide.globals.get("x1");
|
||||
self.x2 = pyodide.globals.get("x1");
|
||||
pyodide.runPython(`
|
||||
from js import x2
|
||||
if x1 != x2:
|
||||
|
@ -158,7 +158,7 @@ def test_big_integer_py2js2py(selenium):
|
|||
a = 9992361673228537
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.a = pyodide.runPython("{a}")
|
||||
self.a = pyodide.runPython("{a}")
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
assert a == {a}
|
||||
|
@ -168,7 +168,7 @@ def test_big_integer_py2js2py(selenium):
|
|||
a = -a
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.a = pyodide.runPython("{a}")
|
||||
self.a = pyodide.runPython("{a}")
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
assert a == {a}
|
||||
|
@ -179,7 +179,7 @@ def test_big_integer_py2js2py(selenium):
|
|||
|
||||
# Generate an object of any type
|
||||
@given(obj=from_type(type).flatmap(from_type))
|
||||
@settings(deadline=600)
|
||||
@settings(deadline=2000)
|
||||
def test_hyp_tojs_no_crash(selenium_module_scope, obj):
|
||||
with selenium_context_manager(selenium_module_scope) as selenium:
|
||||
import pickle
|
||||
|
@ -227,7 +227,7 @@ def test_python2js2(selenium):
|
|||
let xpy = pyodide.runPython("b'bytes'");
|
||||
let x = xpy.toJs();
|
||||
xpy.destroy();
|
||||
return (x instanceof window.Uint8Array) &&
|
||||
return (x.constructor.name === "Uint8Array") &&
|
||||
(x.length === 5) &&
|
||||
(x[0] === 98)
|
||||
"""
|
||||
|
@ -241,7 +241,7 @@ def test_python2js3(selenium):
|
|||
let typename = proxy.type;
|
||||
let x = proxy.toJs();
|
||||
proxy.destroy();
|
||||
return ((typename === "list") && (x instanceof window.Array) &&
|
||||
return ((typename === "list") && (x.constructor.name === "Array") &&
|
||||
(x.length === 3) && (x[0] == 1) && (x[1] == 2) && (x[2] == 3));
|
||||
"""
|
||||
)
|
||||
|
@ -280,24 +280,24 @@ def test_wrong_way_conversions(selenium):
|
|||
let t = new Test();
|
||||
assert(() => pyodide.toPy(t) === t);
|
||||
|
||||
window.a1 = [1,2,3];
|
||||
window.b1 = pyodide.toPy(a1);
|
||||
window.a2 = { a : 1, b : 2, c : 3};
|
||||
window.b2 = pyodide.toPy(a2);
|
||||
self.a1 = [1,2,3];
|
||||
self.b1 = pyodide.toPy(a1);
|
||||
self.a2 = { a : 1, b : 2, c : 3};
|
||||
self.b2 = pyodide.toPy(a2);
|
||||
pyodide.runPython(`
|
||||
from js import a1, b1, a2, b2
|
||||
assert a1.to_py() == b1
|
||||
assert a2.to_py() == b2
|
||||
`);
|
||||
window.b1.destroy();
|
||||
window.b2.destroy();
|
||||
self.b1.destroy();
|
||||
self.b2.destroy();
|
||||
"""
|
||||
)
|
||||
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = [1,2,3];
|
||||
window.b = pyodide.runPython(`
|
||||
self.a = [1,2,3];
|
||||
self.b = pyodide.runPython(`
|
||||
import pyodide
|
||||
pyodide.to_js([1, 2, 3])
|
||||
`);
|
||||
|
@ -307,7 +307,7 @@ def test_wrong_way_conversions(selenium):
|
|||
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.t3 = pyodide.runPython(`
|
||||
self.t3 = pyodide.runPython(`
|
||||
class Test: pass
|
||||
t1 = Test()
|
||||
t2 = pyodide.to_js(t1)
|
||||
|
@ -367,7 +367,7 @@ def test_run_python_simple_error(selenium):
|
|||
def test_js2python(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.test_objects = {
|
||||
self.test_objects = {
|
||||
jsstring_ucs1 : "pyodidé",
|
||||
jsstring_ucs2 : "碘化物",
|
||||
jsstring_ucs4 : "🐍",
|
||||
|
@ -382,7 +382,7 @@ def test_js2python(selenium):
|
|||
jspython : pyodide.globals.get("open"),
|
||||
jsbytes : new Uint8Array([1, 2, 3]),
|
||||
jsfloats : new Float32Array([1, 2, 3]),
|
||||
jsobject : new XMLHttpRequest(),
|
||||
jsobject : new TextDecoder(),
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
@ -412,7 +412,7 @@ def test_js2python(selenium):
|
|||
(jsfloats.tolist() == [1, 2, 3]) and (jsfloats.tobytes() == expected)
|
||||
"""
|
||||
)
|
||||
assert selenium.run('str(t.jsobject) == "[object XMLHttpRequest]"')
|
||||
assert selenium.run('str(t.jsobject) == "[object TextDecoder]"')
|
||||
assert selenium.run("bool(t.jsobject) == True")
|
||||
assert selenium.run("bool(t.jsarray0) == False")
|
||||
assert selenium.run("bool(t.jsarray1) == True")
|
||||
|
@ -422,17 +422,17 @@ def test_js2python(selenium):
|
|||
def test_js2python_bool(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.f = ()=>{}
|
||||
window.m0 = new Map();
|
||||
window.m1 = new Map([[0, 1]]);
|
||||
window.s0 = new Set();
|
||||
window.s1 = new Set([0]);
|
||||
self.f = ()=>{}
|
||||
self.m0 = new Map();
|
||||
self.m1 = new Map([[0, 1]]);
|
||||
self.s0 = new Set();
|
||||
self.s1 = new Set([0]);
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
selenium.run(
|
||||
"""
|
||||
from js import window, f, m0, m1, s0, s1
|
||||
from js import self, f, m0, m1, s0, s1
|
||||
[bool(x) for x in [f, m0, m1, s0, s1]]
|
||||
"""
|
||||
)
|
||||
|
@ -457,7 +457,7 @@ def test_js2python_bool(selenium):
|
|||
def test_typed_arrays(selenium, jstype, pytype):
|
||||
assert selenium.run_js(
|
||||
f"""
|
||||
window.array = new {jstype}([1, 2, 3, 4]);
|
||||
self.array = new {jstype}([1, 2, 3, 4]);
|
||||
return pyodide.runPython(`
|
||||
from js import array
|
||||
array = array.to_py()
|
||||
|
@ -477,7 +477,7 @@ def test_array_buffer(selenium):
|
|||
assert (
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.array = new ArrayBuffer(100);
|
||||
self.array = new ArrayBuffer(100);
|
||||
return pyodide.runPython(`
|
||||
from js import array
|
||||
array = array.to_py()
|
||||
|
@ -490,7 +490,7 @@ def test_array_buffer(selenium):
|
|||
|
||||
|
||||
def assert_js_to_py_to_js(selenium, name):
|
||||
selenium.run_js(f"window.obj = {name};")
|
||||
selenium.run_js(f"self.obj = {name};")
|
||||
selenium.run("from js import obj")
|
||||
assert selenium.run_js(
|
||||
"""
|
||||
|
@ -503,7 +503,7 @@ def assert_js_to_py_to_js(selenium, name):
|
|||
def assert_py_to_js_to_py(selenium, name):
|
||||
selenium.run_js(
|
||||
f"""
|
||||
window.obj = pyodide.runPython('{name}');
|
||||
self.obj = pyodide.runPython('{name}');
|
||||
pyodide.runPython(`
|
||||
from js import obj
|
||||
assert obj is {name}
|
||||
|
@ -532,17 +532,17 @@ def test_recursive_dict_to_js():
|
|||
|
||||
|
||||
def test_list_js2py2js(selenium):
|
||||
selenium.run_js("window.x = [1,2,3];")
|
||||
selenium.run_js("self.x = [1,2,3];")
|
||||
assert_js_to_py_to_js(selenium, "x")
|
||||
|
||||
|
||||
def test_dict_js2py2js(selenium):
|
||||
selenium.run_js("window.x = { a : 1, b : 2, 0 : 3 };")
|
||||
selenium.run_js("self.x = { a : 1, b : 2, 0 : 3 };")
|
||||
assert_js_to_py_to_js(selenium, "x")
|
||||
|
||||
|
||||
def test_error_js2py2js(selenium):
|
||||
selenium.run_js("window.err = new Error('hello there?');")
|
||||
selenium.run_js("self.err = new Error('hello there?');")
|
||||
assert_js_to_py_to_js(selenium, "err")
|
||||
|
||||
|
||||
|
@ -570,7 +570,7 @@ def test_jsproxy_attribute_error(selenium):
|
|||
this.y = y;
|
||||
}
|
||||
}
|
||||
window.point = new Point(42, 43);
|
||||
self.point = new Point(42, 43);
|
||||
"""
|
||||
)
|
||||
selenium.run(
|
||||
|
@ -607,7 +607,7 @@ def test_javascript_error(selenium):
|
|||
def test_javascript_error_back_to_js(selenium):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.err = new Error("This is a js error");
|
||||
self.err = new Error("This is a js error");
|
||||
"""
|
||||
)
|
||||
assert (
|
||||
|
@ -853,7 +853,7 @@ def test_tojs9(selenium):
|
|||
def test_to_py(selenium):
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
window.a = new Map([[1, [1,2,new Set([1,2,3])]], [2, new Map([[1,2],[2,7]])]]);
|
||||
self.a = new Map([[1, [1,2,new Set([1,2,3])]], [2, new Map([[1,2],[2,7]])]]);
|
||||
a.get(2).set("a", a);
|
||||
let result = [];
|
||||
for(let i = 0; i < 4; i++){
|
||||
|
@ -874,7 +874,7 @@ def test_to_py(selenium):
|
|||
|
||||
result = selenium.run_js(
|
||||
"""
|
||||
window.a = { "x" : 2, "y" : 7, "z" : [1,2] };
|
||||
self.a = { "x" : 2, "y" : 7, "z" : [1,2] };
|
||||
a.z.push(a);
|
||||
let result = [];
|
||||
for(let i = 0; i < 4; i++){
|
||||
|
@ -901,7 +901,7 @@ def test_to_py(selenium):
|
|||
this.y = 7;
|
||||
}
|
||||
}
|
||||
window.a = new Temp();
|
||||
self.a = new Temp();
|
||||
let result = pyodide.runPython(`
|
||||
from js import a
|
||||
b = a.to_py()
|
||||
|
@ -916,7 +916,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Map([[[1,1], 2]]);
|
||||
self.a = new Map([[[1,1], 2]]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
@ -928,7 +928,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Set([[1,1]]);
|
||||
self.a = new Set([[1,1]]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
@ -940,7 +940,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Map([[0, 2], [false, 3]]);
|
||||
self.a = new Map([[0, 2], [false, 3]]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
@ -952,7 +952,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Map([[1, 2], [true, 3]]);
|
||||
self.a = new Map([[1, 2], [true, 3]]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
@ -964,7 +964,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Set([0, false]);
|
||||
self.a = new Set([0, false]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
@ -976,7 +976,7 @@ def test_to_py(selenium):
|
|||
with pytest.raises(selenium.JavascriptException, match=msg):
|
||||
selenium.run_js(
|
||||
"""
|
||||
window.a = new Set([1, true]);
|
||||
self.a = new Set([1, true]);
|
||||
pyodide.runPython(`
|
||||
from js import a
|
||||
a.to_py()
|
||||
|
|
|
@ -5,11 +5,11 @@ def run_with_resolve(selenium, code):
|
|||
selenium.run_js(
|
||||
f"""
|
||||
try {{
|
||||
let promise = new Promise((resolve) => window.resolve = resolve);
|
||||
let promise = new Promise((resolve) => self.resolve = resolve);
|
||||
pyodide.runPython({code!r});
|
||||
await promise;
|
||||
}} finally {{
|
||||
delete window.resolve;
|
||||
delete self.resolve;
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
@ -173,13 +173,13 @@ def test_run_in_executor(selenium):
|
|||
|
||||
|
||||
def test_webloop_exception_handler(selenium):
|
||||
selenium.run(
|
||||
selenium.run_async(
|
||||
"""
|
||||
import asyncio
|
||||
async def test():
|
||||
raise Exception("test")
|
||||
asyncio.ensure_future(test())
|
||||
pass
|
||||
await asyncio.sleep(0.2)
|
||||
"""
|
||||
)
|
||||
assert "Task exception was never retrieved" in selenium.logs
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
const vm = require("vm");
|
||||
const readline = require("readline");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const node_fetch = require("node-fetch");
|
||||
const base64 = require("base-64");
|
||||
|
||||
require(path.resolve("./pyodide.js"));
|
||||
let base_url = process.argv[2];
|
||||
// node requires full paths.
|
||||
function fetch(path) {
|
||||
return node_fetch(new URL(path, base_url).toString());
|
||||
}
|
||||
|
||||
const context = Object.assign({}, globalThis, {
|
||||
path,
|
||||
process,
|
||||
require,
|
||||
fetch,
|
||||
TextDecoder: util.TextDecoder,
|
||||
TextEncoder: util.TextEncoder,
|
||||
URL,
|
||||
atob: base64.decode,
|
||||
btoa: base64.encode,
|
||||
});
|
||||
vm.createContext(context);
|
||||
vm.runInContext("globalThis.self = globalThis;", context);
|
||||
|
||||
// Get rid of all colors in output of console.log, they mess us up.
|
||||
for (let key of Object.keys(util.inspect.styles)) {
|
||||
util.inspect.styles[key] = undefined;
|
||||
}
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
let cur_code = "";
|
||||
let cur_uuid;
|
||||
rl.on("line", async function (line) {
|
||||
if (!cur_uuid) {
|
||||
cur_uuid = line;
|
||||
return;
|
||||
}
|
||||
if (line !== cur_uuid) {
|
||||
cur_code += line + "\n";
|
||||
} else {
|
||||
evalCode(cur_uuid, cur_code, context);
|
||||
cur_code = "";
|
||||
cur_uuid = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
async function evalCode(uuid, code, eval_context) {
|
||||
let p = new Promise((resolve, reject) => {
|
||||
eval_context.___outer_resolve = resolve;
|
||||
eval_context.___outer_reject = reject;
|
||||
});
|
||||
let wrapped_code = `
|
||||
(async function(){
|
||||
${code}
|
||||
})().then(___outer_resolve).catch(___outer_reject);
|
||||
`;
|
||||
let delim = uuid + ":UUID";
|
||||
console.log(delim);
|
||||
try {
|
||||
vm.runInContext(wrapped_code, eval_context);
|
||||
let result = JSON.stringify(await p);
|
||||
console.log(`${delim}\n0\n${result}\n${delim}`);
|
||||
} catch (e) {
|
||||
console.log(`${delim}\n1\n${e.stack}\n${delim}`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
Error.stackTraceLimit = Infinity;
|
||||
|
||||
// Fix globalThis is messed up in firefox see facebook/react#16606.
|
||||
// Replace it with window.
|
||||
globalThis.globalThis = globalThis.window || globalThis;
|
||||
|
||||
globalThis.sleep = function (s) {
|
||||
return new Promise((resolve) => setTimeout(resolve, s));
|
||||
};
|
||||
|
||||
globalThis.assert = function (cb, message = "") {
|
||||
if (message !== "") {
|
||||
message = "\n" + message;
|
||||
}
|
||||
if (cb() !== true) {
|
||||
throw new Error(
|
||||
`Assertion failed: ${cb.toString().slice(6)}${message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
globalThis.assertAsync = async function (cb, message = "") {
|
||||
if (message !== "") {
|
||||
message = "\n" + message;
|
||||
}
|
||||
if ((await cb()) !== true) {
|
||||
throw new Error(
|
||||
`Assertion failed: ${cb.toString().slice(12)}${message}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function checkError(err, errname, pattern, pat_str, thiscallstr) {
|
||||
if (typeof pattern === "string") {
|
||||
pattern = new RegExp(pattern);
|
||||
}
|
||||
if (!err) {
|
||||
throw new Error(`${thiscallstr} failed, no error thrown`);
|
||||
}
|
||||
if (err.constructor.name !== errname) {
|
||||
throw new Error(
|
||||
`${thiscallstr} failed, expected error ` +
|
||||
`of type '${errname}' got type '${err.constructor.name}'`
|
||||
);
|
||||
}
|
||||
if (!pattern.test(err.message)) {
|
||||
throw new Error(
|
||||
`${thiscallstr} failed, expected error ` +
|
||||
`message to match pattern ${pat_str} got:\n${err.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.assertThrows = function (cb, errname, pattern) {
|
||||
let pat_str = typeof pattern === "string" ? `"${pattern}"` : `${pattern}`;
|
||||
let thiscallstr = `assertThrows(${cb.toString()}, "${errname}", ${pat_str})`;
|
||||
let err = undefined;
|
||||
try {
|
||||
cb();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
checkError(err, errname, pattern, pat_str, thiscallstr);
|
||||
};
|
||||
|
||||
globalThis.assertThrowsAsync = async function (cb, errname, pattern) {
|
||||
let pat_str = typeof pattern === "string" ? `"${pattern}"` : `${pattern}`;
|
||||
let thiscallstr = `assertThrowsAsync(${cb.toString()}, "${errname}", ${pat_str})`;
|
||||
let err = undefined;
|
||||
try {
|
||||
await cb();
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
checkError(err, errname, pattern, pat_str, thiscallstr);
|
||||
};
|
Loading…
Reference in New Issue