forked from lektor/lektor
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_build_frontend.py
131 lines (100 loc) · 4.3 KB
/
test_build_frontend.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""Tests for the hatch build hooks in ../build_frontend.py."""
import shutil
import sys
from importlib.util import module_from_spec
from importlib.util import spec_from_file_location
from pathlib import Path
from unittest.mock import Mock
import pytest
@pytest.fixture(scope="session")
def frontend_build_module():
return import_module_from_file("build_frontend", "../build_frontend.py")
@pytest.fixture
def tmp_root(tmp_path):
root = tmp_path / "root"
lektor_admin = root / "lektor/admin"
lektor_admin.mkdir(parents=True)
return root
@pytest.fixture
def frontend_src(tmp_root):
"""Copy the frontend source to tmp_root"""
top = Path(__file__).parent / ".."
for path in "frontend", "lektor/translations":
dst = tmp_root / path
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(top / path, dst, ignore=shutil.ignore_patterns("node_modules"))
@pytest.fixture
def compiled_output(tmp_root):
app_js = tmp_root / "lektor/admin/static/app.js"
app_js.parent.mkdir(parents=True, exist_ok=True)
app_js.touch()
return app_js
@pytest.fixture
def frontend_build_hook(frontend_build_module, tmp_root, tmp_path):
return frontend_build_module.FrontendBuildHook(
root=tmp_root,
config={},
build_config=Mock(name="BuilderConfig"),
metadata=Mock(name="ProjectMetadata"),
directory=tmp_path / "dist",
target_name="wheel",
)
@pytest.mark.usefixtures("frontend_src")
def test_clean(frontend_build_hook, compiled_output):
frontend_build_hook.clean(("standard",))
assert not compiled_output.parent.exists()
@pytest.mark.usefixtures("frontend_src")
def test_clean_idempotent(frontend_build_hook, compiled_output):
frontend_build_hook.clean(("standard",))
assert not compiled_output.parent.exists()
frontend_build_hook.clean(("standard",))
assert not compiled_output.parent.exists()
def test_clean_skipped_if_no_frontend(frontend_build_hook, compiled_output):
frontend_build_hook.clean(("standard",))
assert compiled_output.parent.exists()
@pytest.mark.usefixtures("frontend_src")
def test_initialize_skips_build_if_output_exists(
frontend_build_hook, compiled_output, mocker
):
mocker.patch("shutil.which").return_value = None
frontend_build_hook.initialize("standard", build_data={})
assert compiled_output.exists()
@pytest.mark.usefixtures("frontend_src")
def test_initialize_aborts_if_no_npm(
frontend_build_hook, frontend_build_module, monkeypatch
):
monkeypatch.setattr(frontend_build_module, "NPM", "NPM_IS_MISSING")
with pytest.raises(SystemExit) as exc_info:
frontend_build_hook.initialize("standard", build_data={})
assert exc_info.value.args[0] == 1
def test_initialize_aborts_if_no_source(frontend_build_hook, mocker):
mocker.patch("shutil.which").return_value = "/bin/false"
with pytest.raises(SystemExit) as exc_info:
frontend_build_hook.initialize("standard", build_data={})
assert exc_info.value.args[0] == 1
@pytest.mark.skipif(shutil.which("npm") is None, reason="npm not installed")
@pytest.mark.requiresinternet
@pytest.mark.slowtest
@pytest.mark.usefixtures("frontend_src")
def test_initialize_builds_frontend(frontend_build_hook, tmp_root):
frontend_build_hook.initialize("standard", build_data={})
app_js = tmp_root / "lektor/admin/static/app.js"
assert app_js.stat().st_size > 1024
def import_module_from_file(module_name: str, path: str) -> None:
"""Import a module or package from a specific source (``.py``) file
This bypasses the normal search of ``sys.path``, etc., directly
importing the module from the specified python source file.
The imported module is registered, as usual, in ``sys.modules``.
If the path to the source file is relative, it is interpreted
relative to the directory containing this script.
"""
# Copied more-or-less verbatim from:
# https://docs.python.org/3/library/importlib.html?highlight=import#importing-a-source-file-directly
here = Path(__file__).parent
spec = spec_from_file_location(module_name, here / path)
if spec is None or spec.loader is None:
raise ModuleNotFoundError(f"Can not find {module_name}")
module = module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module