Skip to content

Commit

Permalink
Setup tests, linting and codestyle checks (#77)
Browse files Browse the repository at this point in the history
* setup tests, linting and codestyle checks

* add a simple smoke test with some standard django settings

* add Github Action to run Tox test matrix

* fix codestyle warning

* update pre-commit config

* re-format code with black

* add troves to clarify for which django and python versions Django-Vite is intended

* fix linting errors

* add tests for templatetags

* Add tests to achieve 100% Test Coverage (#91)

* create patch_settings fixture

* 100% test coverage

* add Python 3.12 to test matrix

* do not fail tests under 100%; also ensure imports are sorted

---------

Co-authored-by: Nick Ivons <[email protected]>
  • Loading branch information
thijskramer and Niicck authored Oct 16, 2023
1 parent 57156f4 commit 4108c13
Show file tree
Hide file tree
Showing 24 changed files with 985 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ jobs:
- uses: "actions/setup-python@v4"
with:
python-version: 3.x
- uses: "pre-commit/action@v2.0.3"
- uses: "pre-commit/action@v3.0.0"
31 changes: 31 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Run Test Matrix

on:
push:
branches: [master]
pull_request:
branches: [master]

concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox tox-gh-actions
- name: Test with tox
run: tox
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
__pycache__/
.tox
.vscode
.coverage
*.egg-info
9 changes: 2 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: "https://github.com/pre-commit/pre-commit-hooks"
rev: "v4.1.0"
rev: "v4.4.0"
hooks:
- id: "check-toml"
- id: "check-yaml"
Expand All @@ -10,15 +10,10 @@ repos:
- id: "trailing-whitespace"

- repo: "https://github.com/psf/black"
rev: "22.3.0"
rev: "23.3.0"
hooks:
- id: "black"

- repo: "https://github.com/PyCQA/flake8"
rev: "4.0.1"
hooks:
- id: "flake8"

- repo: "https://github.com/commitizen-tools/commitizen"
rev: master
hooks:
Expand Down
13 changes: 7 additions & 6 deletions django_vite/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from contextlib import suppress

from django.apps import AppConfig
from django.core.checks import Warning, register

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


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

def ready(self) -> None:
try:
# Create Loader instance at startup to prevent threading problems.
with suppress(DjangoViteManifestError):
# Create Loader instance at startup to prevent threading problems,
# but do not crash while doing so.
DjangoViteAssetLoader.instance()
except RuntimeError:
# Just continue, the system check below outputs a warning.
pass


@register
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
86 changes: 32 additions & 54 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 All @@ -25,9 +26,7 @@
)

# Default Vite server port.
DJANGO_VITE_DEV_SERVER_PORT = getattr(
settings, "DJANGO_VITE_DEV_SERVER_PORT", 3000
)
DJANGO_VITE_DEV_SERVER_PORT = getattr(settings, "DJANGO_VITE_DEV_SERVER_PORT", 3000)

# Default Vite server path to HMR script.
DJANGO_VITE_WS_CLIENT_URL = getattr(
Expand All @@ -42,12 +41,10 @@
# Must be included in your "STATICFILES_DIRS".
# In Django production mode this folder need to be collected as static
# files using "python manage.py collectstatic".
DJANGO_VITE_ASSETS_PATH = Path(getattr(settings, "DJANGO_VITE_ASSETS_PATH"))
DJANGO_VITE_ASSETS_PATH = Path(settings.DJANGO_VITE_ASSETS_PATH)

# Prefix for STATIC_URL
DJANGO_VITE_STATIC_URL_PREFIX = getattr(
settings, "DJANGO_VITE_STATIC_URL_PREFIX", ""
)
DJANGO_VITE_STATIC_URL_PREFIX = getattr(settings, "DJANGO_VITE_STATIC_URL_PREFIX", "")

DJANGO_VITE_STATIC_ROOT = (
DJANGO_VITE_ASSETS_PATH
Expand All @@ -68,9 +65,7 @@
settings, "DJANGO_VITE_LEGACY_POLYFILLS_MOTIF", "legacy-polyfills"
)

DJANGO_VITE_STATIC_URL = urljoin(
settings.STATIC_URL, DJANGO_VITE_STATIC_URL_PREFIX
)
DJANGO_VITE_STATIC_URL = urljoin(settings.STATIC_URL, DJANGO_VITE_STATIC_URL_PREFIX)

# Make sure 'DJANGO_VITE_STATIC_URL' finish with a '/'
if DJANGO_VITE_STATIC_URL[-1] != "/":
Expand Down Expand Up @@ -110,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 @@ -125,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 @@ -158,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 @@ -188,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 @@ -199,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 @@ -216,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 @@ -233,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 @@ -291,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 @@ -311,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 @@ -322,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 @@ -346,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 @@ -367,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 @@ -391,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 @@ -402,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 @@ -422,19 +409,19 @@ 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:
manifest_file = open(DJANGO_VITE_MANIFEST_PATH, "r")
manifest_content = manifest_file.read()
manifest_file.close()
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

@classmethod
def instance(cls):
Expand Down Expand Up @@ -496,9 +483,7 @@ def _generate_script_tag(src: str, attrs: Dict[str, str]) -> str:
str -- The script tag.
"""

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'<script {attrs_str} src="{src}"></script>'

Expand Down Expand Up @@ -532,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 @@ -682,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 Expand Up @@ -731,9 +713,7 @@ def vite_legacy_polyfills(**kwargs: Dict[str, str]) -> str:
str -- The script tag to the polyfills.
"""

return DjangoViteAssetLoader.instance().generate_vite_legacy_polyfills(
**kwargs
)
return DjangoViteAssetLoader.instance().generate_vite_legacy_polyfills(**kwargs)


@register.simple_tag
Expand Down Expand Up @@ -765,9 +745,7 @@ def vite_legacy_asset(

assert path is not None

return DjangoViteAssetLoader.instance().generate_vite_legacy_asset(
path, **kwargs
)
return DjangoViteAssetLoader.instance().generate_vite_legacy_asset(path, **kwargs)


@register.simple_tag
Expand Down
Loading

0 comments on commit 4108c13

Please sign in to comment.