Initial commit
This commit is contained in:
commit
97e799fd52
61 changed files with 2252 additions and 0 deletions
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
52
tests/settings.py
Normal file
52
tests/settings.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
"""A minimal service, standing in for tdas/odms/ops."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
SECRET_KEY = "test-only"
|
||||
DEBUG = True
|
||||
USE_TZ = True
|
||||
USE_I18N = True
|
||||
LANGUAGE_CODE = "en"
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"yksa_kit",
|
||||
"yksa_web",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"yksa_kit.middleware.UserTimezoneMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
]
|
||||
|
||||
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}
|
||||
|
||||
ROOT_URLCONF = "tests.urls"
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [BASE_DIR / "templates"],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"yksa_kit.context_processors.ui_preferences",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
12
tests/templates/base.html
Normal file
12
tests/templates/base.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{% extends "yksa/base.html" %}
|
||||
{% load i18n %}
|
||||
{% comment %}
|
||||
The three-line base.html every service is expected to keep. If a service needs
|
||||
more than blocks to fit the chrome, the chrome is wrong.
|
||||
{% endcomment %}
|
||||
{% block title %}{% trans "Test service" %}{% endblock title %}
|
||||
{% block navbar_items %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link nav-full-height border border-top-0" href="/">{% trans "Home" %}</a>
|
||||
</li>
|
||||
{% endblock navbar_items %}
|
||||
145
tests/test_ui_kit.py
Normal file
145
tests/test_ui_kit.py
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
"""The kit's snippets, the chrome, and the rules the estate agreed to.
|
||||
|
||||
This module used to be copied into every service. It lives here now; a service
|
||||
keeps only the part that lists its own pages.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from django.template.loader import render_to_string
|
||||
from django.test import Client
|
||||
|
||||
from yksa_web.states import STATES
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent / "yksa_web"
|
||||
TEMPLATES = sorted((ROOT / "templates").rglob("*.html"))
|
||||
STYLESHEETS = [ROOT / "static" / "yksa" / "css" / "kit.css"]
|
||||
|
||||
|
||||
# --- the chrome -------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_the_shared_pages_render():
|
||||
client = Client()
|
||||
for url in ("/preferences/timezone/", "/privacy/", "/cookies/"):
|
||||
assert client.get(url).status_code == 200, url
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_the_navbar_and_footer_are_on_every_page():
|
||||
"""Both are stated requirements, and both were hand-copied before this."""
|
||||
html = Client().get("/privacy/").content.decode()
|
||||
assert "custom-navbar" in html
|
||||
assert "yksa/img/logo-full-en.svg" in html
|
||||
assert 'href="/cookies/"' in html # footer link
|
||||
assert "yksa/css/kit.css" in html
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_the_service_cannot_reorder_the_chrome():
|
||||
"""navbar, then main, then footer — a service that wants otherwise has to
|
||||
stop extending the kit, which is the point."""
|
||||
html = Client().get("/privacy/").content.decode()
|
||||
assert html.index("<nav") < html.index("<main") < html.index("<footer")
|
||||
|
||||
|
||||
# --- the snippets -----------------------------------------------------------
|
||||
|
||||
|
||||
def test_state_carries_a_colour_an_icon_and_a_word():
|
||||
"""Colour is never the only signal: the badge has to survive a colour-blind
|
||||
reader and a greyscale print."""
|
||||
html = render_to_string("yksa/ui/_state.html", {"state": "failed"})
|
||||
assert "yksa-state-failed" in html
|
||||
assert "bi-x-lg" in html
|
||||
assert "failed" in html
|
||||
|
||||
|
||||
def test_every_state_in_the_vocabulary_renders():
|
||||
for name in STATES:
|
||||
assert f"yksa-state-{name}" in render_to_string(
|
||||
"yksa/ui/_state.html", {"state": name}
|
||||
)
|
||||
|
||||
|
||||
def test_an_unknown_state_still_renders():
|
||||
"""A status the reader can see beats one that silently disappears."""
|
||||
html = render_to_string("yksa/ui/_state.html", {"state": "nonsense"})
|
||||
assert "yksa-state-unknown" in html
|
||||
|
||||
|
||||
def test_chip_is_colourless():
|
||||
"""A chip is a fact, not a status. If it needs a colour it is a state."""
|
||||
html = render_to_string("yksa/ui/_chip.html", {"text": "v3"})
|
||||
for variant in ("success", "danger", "warning", "primary"):
|
||||
assert variant not in html
|
||||
|
||||
|
||||
def test_page_title_is_always_h1_h3():
|
||||
"""Pages cannot drift apart visually if none of them picks its own size."""
|
||||
html = render_to_string("yksa/ui/_page_title.html", {"title": "Widgets"})
|
||||
assert '<h1 class="h3' in html
|
||||
|
||||
|
||||
def test_page_title_links_its_parent():
|
||||
html = render_to_string("yksa/ui/_page_title.html", {
|
||||
"title": "W-1", "parent": "Widgets", "parent_url": "/widgets/",
|
||||
})
|
||||
assert 'href="/widgets/"' in html
|
||||
assert "W-1" in html
|
||||
|
||||
|
||||
# --- the estate-wide rules --------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("banned", ["fw-semibold", "fw-medium", "fw-light", "fw-bolder"])
|
||||
def test_no_banned_font_weights_in_templates(banned):
|
||||
"""Weights are 400 and 700. Hierarchy comes from weight, colour and size."""
|
||||
assert [str(p) for p in TEMPLATES if banned in p.read_text(encoding="utf-8")] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("banned", [r"font-weight:\s*[56]00", r"letter-spacing"])
|
||||
def test_no_banned_typography_in_css(banned):
|
||||
"""The template rule was enforced and the CSS rule never was, which is how
|
||||
main.css came to carry font-weight: 500."""
|
||||
offenders = [
|
||||
str(p) for p in STYLESHEETS
|
||||
if re.search(banned, _without_comments(p.read_text(encoding="utf-8")))
|
||||
]
|
||||
assert offenders == []
|
||||
|
||||
|
||||
def _without_comments(css: str) -> str:
|
||||
return re.sub(r"/\*.*?\*/", "", css, flags=re.S)
|
||||
|
||||
|
||||
def test_no_legacy_text_muted():
|
||||
"""Deprecated in Bootstrap 5.3. Muted text is text-body-secondary."""
|
||||
assert [str(p) for p in TEMPLATES if "text-muted" in p.read_text(encoding="utf-8")] == []
|
||||
|
||||
|
||||
def test_no_display_classes():
|
||||
"""`.display-*` is a marketing scale; headings take `.h1`-`.h6`."""
|
||||
offenders = [
|
||||
str(p) for p in TEMPLATES
|
||||
if re.search(r"\bdisplay-[1-6]\b", p.read_text(encoding="utf-8"))
|
||||
]
|
||||
assert offenders == []
|
||||
|
||||
|
||||
def test_no_django_comment_syntax():
|
||||
"""`{# #}` breaks across edits and leaks into rendered output."""
|
||||
assert [str(p) for p in TEMPLATES if "{#" in p.read_text(encoding="utf-8")] == []
|
||||
|
||||
|
||||
def test_bootstrap_is_at_least_5_3():
|
||||
"""text-body-secondary and --bs-*-bg-subtle arrived in 5.3. On 5.2.3 every
|
||||
rule above passes and the page still renders wrong, which is what happened
|
||||
across the estate before this package owned the bundle."""
|
||||
bundle = ROOT / "static" / "yksa" / "css" / "bootstrap.min.css"
|
||||
assert "--bs-success-bg-subtle" in bundle.read_text(encoding="utf-8")
|
||||
6
tests/urls.py
Normal file
6
tests/urls.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path("", include("yksa_kit.urls")),
|
||||
path("i18n/", include("django.conf.urls.i18n")),
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue