Files
rendercv/tests/test_pyodide.py
2026-02-17 17:04:12 +03:00

131 lines
3.4 KiB
Python

import pathlib
import shutil
import subprocess
import sys
import pytest
PROJECT_ROOT = pathlib.Path(__file__).parent.parent
@pytest.fixture
def js_runtime(tmp_path: pathlib.Path) -> str:
"""Provide a JS runtime with pyodide installed in tmp.
Why:
The Pyodide test needs a JS runtime with the pyodide npm package.
Node.js is preferred, but if unavailable, bun is used (or installed
on the fly since it's a single ~13MB download). Pyodide is installed
into tmp_path so node_modules never touches the repo.
Returns:
Absolute path to the JS runtime binary.
"""
node = shutil.which("node")
if node is not None:
# On Windows, npm is a .cmd batch script that requires shell=True
shell = sys.platform == "win32"
subprocess.run(
["npm", "init", "-y"],
cwd=tmp_path,
check=True,
capture_output=True,
timeout=120,
shell=shell,
)
subprocess.run(
["npm", "install", "pyodide"],
cwd=tmp_path,
check=True,
capture_output=True,
timeout=300,
shell=shell,
)
return node
bun = shutil.which("bun")
if bun is None:
# Install bun into tmp
bun_dir = tmp_path / "bun"
subprocess.run(
[
"bash",
"-c",
f"curl -fsSL https://bun.sh/install | BUN_INSTALL={bun_dir} bash",
],
check=True,
capture_output=True,
timeout=120,
)
bun = str(bun_dir / "bin" / "bun")
subprocess.run(
[bun, "add", "pyodide"],
cwd=tmp_path,
check=True,
capture_output=True,
timeout=120,
)
return bun
def test_rendercv_installs_in_pyodide(tmp_path: pathlib.Path, js_runtime: str) -> None:
# Build the wheel into tmp
subprocess.run(
["uv", "build", "--wheel", "--out-dir", str(tmp_path / "dist")],
cwd=PROJECT_ROOT,
check=True,
capture_output=True,
timeout=60,
)
wheel = next((tmp_path / "dist").glob("rendercv-*-py3-none-any.whl"))
# Use forward slashes so the path works on Windows too
wheel_posix_path = wheel.resolve().as_posix()
script = tmp_path / "test.mjs"
script.write_text(f"""\
import fs from "node:fs";
import {{ loadPyodide }} from "pyodide";
async function main() {{
const pyodide = await loadPyodide();
// Copy wheel into Pyodide's virtual filesystem to avoid file:// URL
// issues on Windows
const wheelData = fs.readFileSync("{wheel_posix_path}");
pyodide.FS.writeFile("/tmp/{wheel.name}", wheelData);
await pyodide.loadPackage("micropip");
await pyodide.runPythonAsync(`
import micropip
# Pre-install email-validator because micropip may not resolve
# pydantic[email] extras notation correctly
await micropip.install("email-validator")
# Install rendercv from Pyodide's virtual filesystem
await micropip.install("emfs:/tmp/{wheel.name}")
# Verify import works
import rendercv
`);
}}
main().catch(err => {{
console.error(err.message);
process.exit(1);
}});
""")
# Run test
result = subprocess.run(
[js_runtime, str(script)],
capture_output=True,
text=True,
timeout=300,
check=False,
)
assert result.returncode == 0, f"Pyodide install failed:\n{result.stderr}"