mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-14 02:42:22 +02:00
test(css): pin computed styles against a committed baseline
static/style.css is 51,425 lines in one file. Hundreds of selectors are declared more than once and !important is used throughout, so the rendered result is a function of source order. Extracting a block into its own file changes that order, and nothing in the suite would notice - which makes a 51k-line split unfalsifiable and "looks fine to me" the only available evidence. This moves no CSS. It captures getComputedStyle over a fixed inventory of 676 elements across three pages, four viewports, both themes and the three density modes - 16,224 element snapshots - hashes them, and compares against tests/css_snapshot/baseline.json. A capture takes about 21 seconds. The bench page synthesises one element per selector from an evidence-driven list: every selector declared more than once in style.css that can be expressed as a static compound chain, plus a curated set per feature area. Redeclared selectors are the ones a reorder can flip. The bench loads whatever stylesheets index.html ships, so it keeps measuring the real set once the file is split. tests/test_css_computed_style_snapshot.py also carries a self-test that swaps two conflicting .attach-strip declarations and asserts the digest moves, so the harness cannot silently stop watching. The second half is the asset-manifest check specs/frontend.md asks for, scoped to stylesheets: every stylesheet referenced by shipped HTML and by the sw.js precache exists, and index.html and sw.js agree on the ?v= string. They hardcode it independently today, so a split that updates one and not the other ships an offline cache nobody notices until a plane.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""Computed-style snapshot regression for ``static/style.css``.
|
||||
|
||||
The stylesheet is one 51k-line file whose result depends on source order, so an
|
||||
extraction that "looks fine" can still change which declaration wins. These
|
||||
tests capture ``getComputedStyle`` over a fixed element inventory across pages,
|
||||
viewports, themes and density modes, and compare the hash against
|
||||
``tests/css_snapshot/baseline.json``.
|
||||
|
||||
Run ``python scripts/css_snapshot.py --write-baseline`` to re-record the
|
||||
baseline, and only after confirming the change is intended - see
|
||||
``tests/css_snapshot/README.md``.
|
||||
"""
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.helpers.cli_loader import load_script
|
||||
|
||||
snapshot = load_script("css_snapshot.py")
|
||||
|
||||
_HAS_BROWSER = snapshot.playwright_available()
|
||||
_requires_browser = pytest.mark.skipif(
|
||||
not _HAS_BROWSER,
|
||||
reason="node with the playwright package is required (npm ci)",
|
||||
)
|
||||
|
||||
# The session static server publishes its ephemeral origin here. The literal
|
||||
# fallback is the fixed port the fixture used before it moved to an ephemeral
|
||||
# one, so this module works on either revision.
|
||||
STATIC_ORIGIN = os.environ.get("ODYSSEUS_TEST_STATIC_ORIGIN", "http://127.0.0.1:7011")
|
||||
|
||||
# One conflicting selector used to prove the harness is actually sensitive to
|
||||
# source order. `.attach-strip` is declared three times at the top level of
|
||||
# style.css with different margin, min-height and padding, so swapping the
|
||||
# first two changes which declaration wins without changing a single byte of
|
||||
# any individual rule.
|
||||
CONFLICTING_SELECTOR = ".attach-strip"
|
||||
|
||||
|
||||
def test_baseline_covers_every_inventory_entry():
|
||||
"""The committed baseline and the inventory describe the same surface.
|
||||
|
||||
Cheap and browserless: it catches an inventory entry added without
|
||||
re-recording the baseline, which would otherwise look like a pass because
|
||||
nothing compares an absent key.
|
||||
"""
|
||||
inventory = snapshot.load_inventory()
|
||||
baseline = snapshot.load_baseline()
|
||||
|
||||
variant_names = {variant["name"] for variant in inventory["variants"]}
|
||||
for page in inventory["pages"]:
|
||||
name = page["name"]
|
||||
assert name in baseline["elements"], f"{name} missing from the baseline"
|
||||
expected_keys = {entry["key"] for entry in page.get("elements", [])}
|
||||
expected_keys |= set(page.get("bench", []))
|
||||
assert set(baseline["elements"][name]) == expected_keys, (
|
||||
f"{name}: baseline elements differ from the inventory; "
|
||||
"re-record with scripts/css_snapshot.py --write-baseline"
|
||||
)
|
||||
assert set(baseline["variants"][name]) == variant_names
|
||||
|
||||
|
||||
@_requires_browser
|
||||
def test_computed_styles_match_the_committed_baseline():
|
||||
captured = snapshot.capture(STATIC_ORIGIN)
|
||||
|
||||
assert captured["missing"] == {}, (
|
||||
"inventory entries matched no element - the markup moved under the "
|
||||
f"harness: {captured['missing']}"
|
||||
)
|
||||
|
||||
summary = snapshot.summarize(captured["snapshot"])
|
||||
drift = snapshot.compare(snapshot.load_baseline(), summary)
|
||||
assert not drift["elements"] and not drift["variants"] and not drift["digest_changed"], (
|
||||
"computed styles moved against tests/css_snapshot/baseline.json.\n"
|
||||
f"elements: {drift['elements']}\n"
|
||||
f"variants: {drift['variants']}\n"
|
||||
"If the change is intended, re-record with "
|
||||
"`python scripts/css_snapshot.py --write-baseline`; if it is not, the "
|
||||
"restructuring changed which declaration wins."
|
||||
)
|
||||
|
||||
|
||||
@_requires_browser
|
||||
def test_reordering_two_conflicting_declarations_moves_the_digest():
|
||||
"""The harness has to fail when the cascade changes, or it proves nothing.
|
||||
|
||||
Captures one variant twice - once normally, once with the first two
|
||||
top-level `.attach-strip` blocks swapped - and asserts the digest moves and
|
||||
points at the affected element.
|
||||
"""
|
||||
variants = ["desktop-dark-comfortable"]
|
||||
unchanged = snapshot.summarize(
|
||||
snapshot.capture(STATIC_ORIGIN, variants=variants)["snapshot"]
|
||||
)
|
||||
reordered = snapshot.summarize(
|
||||
snapshot.capture(STATIC_ORIGIN, variants=variants,
|
||||
swap_rule=CONFLICTING_SELECTOR)["snapshot"]
|
||||
)
|
||||
|
||||
assert unchanged["digest"] != reordered["digest"]
|
||||
drift = snapshot.compare(unchanged, reordered)
|
||||
assert "app-shell/attach-strip" in drift["elements"]
|
||||
assert "bench/.attach-strip" in drift["elements"]
|
||||
Reference in New Issue
Block a user