Coverage for tests/tests_vite_integration.py: 100%
102 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-17 22:07 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-17 22:07 +0000
1from unittest import TestCase
2import json
3from shutil import rmtree
4from typing import cast
5from urllib.parse import urlparse
6from pathlib import Path
7from webapp.vite_integration.impl import (
8 ProdViteIntegration,
9 DevViteIntegration,
10)
11import webapp.vite_integration.exceptions as vite_exceptions
13MOCK_OUTPUT_PATH = "/tmp/python_vite_test"
14MOCK_ASSET_PATH = "test/path/for/asset.ts"
15MOCK_SCSS_PATH = "test/path/for/styles.scss"
16MOCK_MANIFEST = {
17 "_dependency.js": {"file": "chunks/dependency.js", "name": "dependency"},
18 "_chunk.js": {
19 "file": "chunks/chunk.js",
20 "name": "chunk",
21 "imports": ["_dependency.js"],
22 "css": ["assets/styles.css"],
23 },
24 "test/path/for/asset.ts": {
25 "file": "asset.js",
26 "name": "asset",
27 "src": "test/path/for/asset.ts",
28 "isEntry": True,
29 "imports": [
30 "_chunk.js",
31 ],
32 },
33 "test/path/for/styles.scss": {
34 "file": "assets/styles.css",
35 "src": "test/path/for/styles.scss",
36 "isEntry": True,
37 "names": ["styles.css"],
38 },
39}
42class TestsDevViteIntegration(TestCase):
43 def setUp(self):
44 self.vite = DevViteIntegration()
46 def tests_dev_tools(self):
47 dev_tools = self.vite.get_dev_tools()
48 assert "@vite/client" in dev_tools
49 assert "@react-refresh" in dev_tools
51 def tests_get_asset_url(self):
52 url = self.vite.get_asset_url(MOCK_ASSET_PATH)
53 assert MOCK_ASSET_PATH in url
54 parsed = urlparse(url)
55 assert parsed.scheme == "http"
56 assert parsed.netloc.startswith("localhost:")
57 assert parsed.path == f"/{MOCK_ASSET_PATH}"
58 assert parsed.params == ""
59 assert parsed.query == ""
60 assert parsed.fragment == ""
62 def tests_get_imported_chunks(self):
63 assert len(self.vite.get_imported_chunks(MOCK_ASSET_PATH)) == 0
65 def tests_get_imported_css(self):
66 assert len(self.vite.get_imported_css(MOCK_ASSET_PATH)) == 0
69class TestsProdViteIntegration(TestCase):
70 def setUp(self):
71 # create a fake Vite output directory
72 manifest_path = Path(f"{MOCK_OUTPUT_PATH}/.vite/manifest.json")
73 manifest_path.parent.mkdir(exist_ok=True, parents=True)
74 with manifest_path.open("w+") as file:
75 file.write(json.dumps(MOCK_MANIFEST))
77 for entry in MOCK_MANIFEST.values():
78 file = cast(dict, entry).get("file", "")
79 file_path = Path(f"{MOCK_OUTPUT_PATH}/{file}")
80 file_path.parent.mkdir(exist_ok=True, parents=True)
81 with file_path.open("w+") as file:
82 file.write("")
84 # inject the mocks in the static class scope before initalizing
85 ProdViteIntegration.OUT_DIR = MOCK_OUTPUT_PATH
87 def tearDown(self):
88 rmtree(MOCK_OUTPUT_PATH)
90 def tests_good_manifest_file(self):
91 # attempt to init
92 ProdViteIntegration()
94 def tests_bad_manifest_file(self):
95 # try to init a ProdViteIntegration instance with a bad manifest file
97 ProdViteIntegration.manifest = None # reset the manifest instance
98 old_manifest_name = ProdViteIntegration.BUILD_MANIFEST
100 ProdViteIntegration.BUILD_MANIFEST = "file/that/does/not/exist"
102 with self.assertRaises(vite_exceptions.ManifestPathException):
103 self.vite = ProdViteIntegration()
105 ProdViteIntegration.BUILD_MANIFEST = old_manifest_name
107 def tests_dev_tools(self):
108 vite = ProdViteIntegration()
109 dev_tools = vite.get_dev_tools()
110 assert dev_tools == ""
112 def tests_get_asset_url__bad_asset(self):
113 vite = ProdViteIntegration()
114 with self.assertRaises(vite_exceptions.ManifestContentException):
115 vite.get_asset_url("this_asset_does_not_exist.ts")
117 def tests_get_asset_url__bad_path(self):
118 # try to load an asset declared in the manifest but without a real
119 # file backing it
120 # load a proper manifest...
121 ProdViteIntegration.manifest = MOCK_MANIFEST
122 # but also load a broken OUT_DIR path
123 ProdViteIntegration.OUT_DIR = "/tmp/path/does/not/exist"
125 vite = ProdViteIntegration()
126 with self.assertRaises(vite_exceptions.AssetPathException):
127 vite.get_asset_url(MOCK_ASSET_PATH)
129 # cleanup
130 ProdViteIntegration.OUT_DIR = MOCK_OUTPUT_PATH
131 ProdViteIntegration.manifest = None
133 def tests_get_asset_url__is_not_ts(self):
134 vite = ProdViteIntegration()
135 url = vite.get_asset_url(MOCK_ASSET_PATH)
136 assert MOCK_ASSET_PATH not in url # source asset is a .ts file
137 assert url.endswith(".js") # dist asset is a .js file
139 def tests_get_asset_url__is_not_scss(self):
140 vite = ProdViteIntegration()
141 url = vite.get_asset_url(MOCK_SCSS_PATH)
142 assert MOCK_SCSS_PATH not in url # source asset is a .scss file
143 assert url.endswith(".css") # dist asset is a .css file
145 def tests_get_imported_chunks__bad_asset(self):
146 vite = ProdViteIntegration()
147 with self.assertRaises(vite_exceptions.ManifestContentException):
148 vite.get_imported_chunks("this_asset_does_not_exist.ts")
150 def tests_get_imported_chunks__bad_path(self):
151 # try to load chunks for an asset declared in the manifest but
152 # without a real file backing it
153 # load a proper manifest...
154 ProdViteIntegration.manifest = MOCK_MANIFEST
155 # but also load a broken OUT_DIR path
156 ProdViteIntegration.OUT_DIR = "/tmp/path/does/not/exist"
158 vite = ProdViteIntegration()
159 with self.assertRaises(vite_exceptions.AssetPathException):
160 vite.get_imported_chunks(MOCK_ASSET_PATH)
162 # cleanup
163 ProdViteIntegration.OUT_DIR = MOCK_OUTPUT_PATH
164 ProdViteIntegration.manifest = None
166 def tests_get_imported_chunks(self):
167 vite = ProdViteIntegration()
168 js_entries = filter(
169 lambda x: x["file"].endswith(".js"), MOCK_MANIFEST.values()
170 )
171 assert len(vite.get_imported_chunks(MOCK_ASSET_PATH)) == (
172 len(list(js_entries)) - 1
173 )
175 def tests_get_imported_css(self):
176 vite = ProdViteIntegration()
177 assert len(vite.get_imported_css(MOCK_ASSET_PATH)) == 1