The Monitors list now opens with a count strip (down, up with how many degraded, pending, paused) and sorts down, degraded, pending, up, paused, by name within each. Degraded is up but worth a look: uptime at or below its warning floor, or the latest response past its critical cutoff, both from Settings → Thresholds; a slow-but-tolerable reply is coloured, not sorted. A monitor had no page of its own: every link went to the list. Now /monitors/<id> shows its state and since when, latency, uptime for 24h/7d/ 30d, TLS days, the last checks, recent failures with their errors, and its settings (HTTP header names, never their values). Status links to it, and adding or editing returns to it. Adding is its own page (/monitors/new, form.html shared with edit) behind an "Add monitor" button; a refused add re-renders the form with the error and what was typed instead of a bare 400 text. Row actions are labelled icon buttons, type names read Ping/TCP/DNS/HTTP (|monitor_type), and the heartbeat uses pill-up/pill-down, so HEX_CEILING drops to 56. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
70 lines
2.7 KiB
Python
70 lines
2.7 KiB
Python
"""Every colour a page draws comes from the design-system tokens (#5092).
|
|
|
|
- Every var(--x) a template or stylesheet uses is defined somewhere Steward
|
|
serves, so nothing silently falls back to the browser default.
|
|
- The base stylesheet states no colour or heavy weight of its own: values
|
|
are theme.css tokens (generated from design system 6) or aliases of them.
|
|
- Hex colours written into templates can only go down: each page moved onto
|
|
the tokens lowers HEX_CEILING (milestone 477 retires the rest).
|
|
"""
|
|
import pathlib
|
|
import re
|
|
|
|
import steward
|
|
from tests._templates import template_files as _templates
|
|
|
|
_REPO = pathlib.Path(steward.__file__).parent.parent
|
|
_STATIC = _REPO / "steward" / "static"
|
|
_COMMENTS = re.compile(r"\{#.*?#\}|<!--.*?-->|/\*.*?\*/", re.S)
|
|
_DEFINED = re.compile(r"(--[\w-]+)\s*:")
|
|
_USED = re.compile(r"var\(\s*(--[\w-]+)")
|
|
# A hex colour where a colour goes: after a CSS ':', '(' or ',', or as a quoted JS
|
|
# string -- but not an HTML attribute's value (href="#abc" is a link to an id).
|
|
_HEX = re.compile(r"""(?:[:(,]\s*|(?<![\w-]=)['"])#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{3})(?![0-9a-zA-Z_-])""")
|
|
|
|
HEX_CEILING = 56 # 75 at #5092, 58 at #5095, 56 with the Monitors list on tokens (#5121); only lower it
|
|
|
|
|
|
def _sheets():
|
|
return [p for p in _STATIC.rglob("*.css") if "vendor" not in p.parts]
|
|
|
|
|
|
def _text(p):
|
|
return _COMMENTS.sub("", p.read_text())
|
|
|
|
|
|
def undefined_vars(texts) -> set[str]:
|
|
defined, used = set(), set()
|
|
for t in texts:
|
|
defined |= set(_DEFINED.findall(t))
|
|
used |= set(_USED.findall(t))
|
|
return used - defined
|
|
|
|
|
|
def hex_colours(text: str) -> list[str]:
|
|
return _HEX.findall(_COMMENTS.sub("", text))
|
|
|
|
|
|
def test_the_guards_can_fail():
|
|
assert undefined_vars([":root { --a: 1; } .x { color: var(--a); background: var(--b); }"]) == {"--b"}
|
|
page = '<div style="color:#fff;background: rgb(0 0 0)" hx-target="#add-form"></div><script>c = "#4aa86a"</script>'
|
|
assert len(hex_colours(page)) == 2
|
|
assert hex_colours("{# color:#fff #}<a href=\"#abc\">") == []
|
|
|
|
|
|
def test_every_css_variable_used_is_defined():
|
|
texts = [_text(p) for p in [*_templates(), *_sheets()]]
|
|
assert not undefined_vars(texts)
|
|
|
|
|
|
def test_the_base_stylesheet_states_no_colour_or_heavy_weight_of_its_own():
|
|
css = _text(_STATIC / "steward.css")
|
|
assert not re.findall(r"#[0-9a-fA-F]{3,8}\b|rgba?\(|hsla?\(", css)
|
|
assert not re.findall(r"font-weight\s*:\s*(?:[6-9]00|bold)", css)
|
|
|
|
|
|
def test_hex_colours_in_templates_only_go_down():
|
|
count = sum(len(hex_colours(p.read_text())) for p in _templates())
|
|
assert count <= HEX_CEILING, (
|
|
f"{count} hex colours in templates, ceiling {HEX_CEILING}: use a token (theme.css / steward.css)")
|