Skip to content

Commit

Permalink
Add tests to achieve 100% Test Coverage (#91)
Browse files Browse the repository at this point in the history
* create patch_settings fixture

* 100% test coverage

---------

Co-authored-by: Ben Beecher <[email protected]>
Co-authored-by: Eddy Brown <[email protected]>
Co-authored-by: Thijs Kramer <[email protected]>
Co-authored-by: Niicck <[email protected]>
  • Loading branch information
5 people committed Oct 16, 2023
1 parent b9c94ad commit 5cf2c06
Show file tree
Hide file tree
Showing 21 changed files with 819 additions and 196 deletions.
5 changes: 3 additions & 2 deletions django_vite/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.apps import AppConfig
from django.core.checks import Warning, register

from .exceptions import DjangoViteManifestError
from .templatetags.django_vite import DjangoViteAssetLoader


Expand All @@ -11,7 +12,7 @@ class DjangoViteAppConfig(AppConfig):
verbose_name = "Django Vite"

def ready(self) -> None:
with suppress(RuntimeError):
with suppress(DjangoViteManifestError):
# Create Loader instance at startup to prevent threading problems,
# but do not crash while doing so.
DjangoViteAssetLoader.instance()
Expand All @@ -25,7 +26,7 @@ def check_loader_instance(**kwargs):
# Make Loader instance at startup to prevent threading problems
DjangoViteAssetLoader.instance()
return []
except RuntimeError as exception:
except DjangoViteManifestError as exception:
return [
Warning(
exception,
Expand Down
10 changes: 10 additions & 0 deletions django_vite/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class DjangoViteManifestError(RuntimeError):
"""Manifest parsing failed."""

pass


class DjangoViteAssetNotFoundError(RuntimeError):
"""Vite Asset could not be found."""

pass
53 changes: 22 additions & 31 deletions django_vite/templatetags/django_vite.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import json
from pathlib import Path
from typing import Dict, List, Callable
from typing import Callable, Dict, List
from urllib.parse import urljoin

from django import template
from django.apps import apps
from django.conf import settings
from django.utils.safestring import mark_safe

register = template.Library()
from django_vite.exceptions import DjangoViteAssetNotFoundError, DjangoViteManifestError

register = template.Library()

# If using in development or production mode.
DJANGO_VITE_DEV_MODE = getattr(settings, "DJANGO_VITE_DEV_MODE", False)
Expand Down Expand Up @@ -104,7 +105,7 @@ def generate_vite_asset(
script tags.
Raises:
RuntimeError: If cannot find the file path in the
DjangoViteAssetNotFoundError: If cannot find the file path in the
manifest (only in production).
Returns:
Expand All @@ -119,7 +120,7 @@ def generate_vite_asset(
)

if not self._manifest or path not in self._manifest:
raise RuntimeError(
raise DjangoViteAssetNotFoundError(
f"Cannot find {path} in Vite manifest "
f"at {DJANGO_VITE_MANIFEST_PATH}"
)
Expand Down Expand Up @@ -152,9 +153,7 @@ def generate_vite_asset(
for dep in manifest_entry.get("imports", []):
dep_manifest_entry = self._manifest[dep]
dep_file = dep_manifest_entry["file"]
url = DjangoViteAssetLoader._generate_production_server_url(
dep_file
)
url = DjangoViteAssetLoader._generate_production_server_url(dep_file)
tags.append(
DjangoViteAssetLoader._generate_preload_tag(
url,
Expand Down Expand Up @@ -182,7 +181,7 @@ def preload_vite_asset(
str -- All tags to preload this file in your HTML page.
Raises:
RuntimeError: If cannot find the file path in the
DjangoViteAssetNotFoundError: if cannot find the file path in the
manifest.
Returns:
Expand All @@ -193,7 +192,7 @@ def preload_vite_asset(
return ""

if not self._manifest or path not in self._manifest:
raise RuntimeError(
raise DjangoViteAssetNotFoundError(
f"Cannot find {path} in Vite manifest "
f"at {DJANGO_VITE_MANIFEST_PATH}"
)
Expand All @@ -210,9 +209,7 @@ def preload_vite_asset(
}

manifest_file = manifest_entry["file"]
url = DjangoViteAssetLoader._generate_production_server_url(
manifest_file
)
url = DjangoViteAssetLoader._generate_production_server_url(manifest_file)
tags.append(
DjangoViteAssetLoader._generate_preload_tag(
url,
Expand All @@ -227,9 +224,7 @@ def preload_vite_asset(
for dep in manifest_entry.get("imports", []):
dep_manifest_entry = self._manifest[dep]
dep_file = dep_manifest_entry["file"]
url = DjangoViteAssetLoader._generate_production_server_url(
dep_file
)
url = DjangoViteAssetLoader._generate_production_server_url(dep_file)
tags.append(
DjangoViteAssetLoader._generate_preload_tag(
url,
Expand Down Expand Up @@ -285,10 +280,8 @@ def _generate_css_files_of_asset(
if "css" in manifest_entry:
for css_path in manifest_entry["css"]:
if css_path not in already_processed:
url = (
DjangoViteAssetLoader._generate_production_server_url(
css_path
)
url = DjangoViteAssetLoader._generate_production_server_url(
css_path
)
tags.append(tag_generator(url))

Expand All @@ -305,7 +298,7 @@ def generate_vite_asset_url(self, path: str) -> str:
path {str} -- Path to a Vite asset.
Raises:
RuntimeError: If cannot find the asset path in the
DjangoViteAssetNotFoundError: If cannot find the asset path in the
manifest (only in production).
Returns:
Expand All @@ -316,7 +309,7 @@ def generate_vite_asset_url(self, path: str) -> str:
return DjangoViteAssetLoader._generate_vite_server_url(path)

if not self._manifest or path not in self._manifest:
raise RuntimeError(
raise DjangoViteAssetNotFoundError(
f"Cannot find {path} in Vite manifest "
f"at {DJANGO_VITE_MANIFEST_PATH}"
)
Expand All @@ -340,7 +333,7 @@ def generate_vite_legacy_polyfills(
script tags.
Raises:
RuntimeError: If polyfills path not found inside
DjangoViteAssetNotFoundError: If polyfills path not found inside
the 'manifest.json' (only in production).
Returns:
Expand All @@ -361,7 +354,7 @@ def generate_vite_legacy_polyfills(
attrs=scripts_attrs,
)

raise RuntimeError(
raise DjangoViteAssetNotFoundError(
f"Vite legacy polyfills not found in manifest "
f"at {DJANGO_VITE_MANIFEST_PATH}"
)
Expand All @@ -385,7 +378,7 @@ def generate_vite_legacy_asset(
script tags.
Raises:
RuntimeError: If cannot find the asset path in the
DjangoViteAssetNotFoundError: If cannot find the asset path in the
manifest (only in production).
Returns:
Expand All @@ -396,7 +389,7 @@ def generate_vite_legacy_asset(
return ""

if not self._manifest or path not in self._manifest:
raise RuntimeError(
raise DjangoViteAssetNotFoundError(
f"Cannot find {path} in Vite manifest "
f"at {DJANGO_VITE_MANIFEST_PATH}"
)
Expand All @@ -416,15 +409,16 @@ def _parse_manifest(self) -> None:
Read and parse the Vite manifest file.
Raises:
RuntimeError: if cannot load the file or JSON in file is malformed.
DjangoViteManifestError: if cannot load the file or JSON in file is
malformed.
"""

try:
with open(DJANGO_VITE_MANIFEST_PATH, "r") as manifest_file:
manifest_content = manifest_file.read()
self._manifest = json.loads(manifest_content)
except Exception as error:
raise RuntimeError(
raise DjangoViteManifestError(
f"Cannot read Vite manifest file at "
f"{DJANGO_VITE_MANIFEST_PATH} : {str(error)}"
) from error
Expand Down Expand Up @@ -523,9 +517,7 @@ def _generate_stylesheet_preload_tag(href: str) -> str:

@staticmethod
def _generate_preload_tag(href: str, attrs: Dict[str, str]) -> str:
attrs_str = " ".join(
[f'{key}="{value}"' for key, value in attrs.items()]
)
attrs_str = " ".join([f'{key}="{value}"' for key, value in attrs.items()])

return f'<link href="{href}" {attrs_str} />'

Expand Down Expand Up @@ -673,7 +665,6 @@ def vite_preload_asset(
manifest (only in production).
"""

assert path is not None

return DjangoViteAssetLoader.instance().preload_vite_asset(path)
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ addopts = '''
--cov-report html
--cov-report term-missing
--cov-branch
--cov-fail-under=100
'''

[tool.black]
line-length = 88

[tool.ruff]
select = [
"E", # pycodestyle
Expand Down
46 changes: 46 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from typing import Dict, Any
import pytest
from importlib import reload
from django.apps import apps
from django_vite.templatetags import django_vite


def reload_django_vite():
reload(django_vite)
django_vite_app_config = apps.get_app_config("django_vite")
django_vite_app_config.ready()


@pytest.fixture()
def patch_settings(settings):
"""
1. Patch new settings into django.conf.settings.
2. Reload django_vite module so that variables on the module level that use settings
get recalculated.
3. Restore the original settings once the test is over.
TODO: refactor django_vite so that we don't set variables on the module level using
settings.
"""
__PYTEST_EMPTY__ = "__PYTEST_EMPTY__"
original_settings_cache = {}

def _patch_settings(new_settings: Dict[str, Any]):
for key, value in new_settings.items():
original_settings_cache[key] = getattr(settings, key, __PYTEST_EMPTY__)
setattr(settings, key, value)
reload_django_vite()

yield _patch_settings

for key, value in original_settings_cache.items():
if value == __PYTEST_EMPTY__:
delattr(settings, key)
else:
setattr(settings, key, value)
reload_django_vite()


@pytest.fixture()
def dev_mode_off(patch_settings):
patch_settings({"DJANGO_VITE_DEV_MODE": False})
24 changes: 24 additions & 0 deletions tests/data/staticfiles/custom-motif-polyfills-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"../../vite/custom-motif-legacy": {
"file": "assets/polyfills-legacy-6e7a4b9c.js",
"isEntry": true,
"src": "../../vite/custom-motif-legacy"
},
"src/entry-legacy.ts": {
"file": "assets/entry-legacy-4c50596f.js",
"isEntry": true,
"src": "src/entry-legacy.ts"
},
"src/entry.css": {
"file": "assets/entry-5e7d9c21.css",
"src": "src/entry.css"
},
"src/entry.ts": {
"css": [
"assets/entry-5e7d9c21.css"
],
"file": "assets/entry-8a2f6b3d.js",
"isEntry": true,
"src": "src/entry.ts"
}
}
72 changes: 72 additions & 0 deletions tests/data/staticfiles/custom/prefix/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"src/entry.ts": {
"css": ["assets/entry-74d0d5dd.css"],
"file": "assets/entry-29e38a60.js",
"imports": [
"_vue.esm-bundler-96356fb1.js",
"_index-62f37ad0.js",
"_vue.esm-bundler-49e6b475.js",
"__plugin-vue_export-helper-c27b6911.js",
"_messages-a0a9e13b.js",
"_use-outside-click-224980bf.js",
"_use-resolve-button-type-c5656cba.js",
"_use-event-listener-153dc639.js",
"_hidden-84ddb9a5.js",
"_apiClient-01d20438.js",
"_pinia-5d7892fd.js"
],
"isEntry": true,
"src": "entry.ts"
},
"src/entry.css": {
"file": "assets/entry-74d0d5dd.css",
"src": "entry.css"
},
"src/extra.css": {
"file": "assets/extra-a9f3b2c1.css",
"src": "extra.css"
},
"_vue.esm-bundler-96356fb1.js": {
"file": "vue.esm-bundler-96356fb1.js"
},
"_index-62f37ad0.js": {
"file": "index-62f37ad0.js",
"imports": ["_vue.esm-bundler-49e6b475.js"]
},
"_vue.esm-bundler-49e6b475.js": {
"file": "vue.esm-bundler-49e6b475.js",
"imports": ["_vue.esm-bundler-96356fb1.js"]
},
"__plugin-vue_export-helper-c27b6911.js": {
"file": "_plugin-vue_export-helper-c27b6911.js"
},
"_messages-a0a9e13b.js": {
"css": ["assets/extra-a9f3b2c1.css"],
"file": "messages-a0a9e13b.js",
"imports": ["_pinia-5d7892fd.js", "_vue.esm-bundler-96356fb1.js"]
},
"_pinia-5d7892fd.js": {
"file": "pinia-5d7892fd.js",
"imports": ["_vue.esm-bundler-96356fb1.js"]
},
"_use-outside-click-224980bf.js": {
"file": "use-outside-click-224980bf.js",
"imports": ["_vue.esm-bundler-96356fb1.js"]
},
"_use-resolve-button-type-c5656cba.js": {
"file": "use-resolve-button-type-c5656cba.js",
"imports": ["_vue.esm-bundler-96356fb1.js", "_use-outside-click-224980bf.js"]
},
"_use-event-listener-153dc639.js": {
"file": "use-event-listener-153dc639.js",
"imports": ["_vue.esm-bundler-96356fb1.js", "_use-outside-click-224980bf.js"]
},
"_hidden-84ddb9a5.js": {
"file": "hidden-84ddb9a5.js",
"imports": ["_vue.esm-bundler-96356fb1.js", "_use-outside-click-224980bf.js"]
},
"_apiClient-01d20438.js": {
"css": ["assets/extra-a9f3b2c1.css"],
"file": "apiClient-01d20438.js"
}
}
Loading

0 comments on commit 5cf2c06

Please sign in to comment.