Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/hyperweave/compose/resolvers/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,18 @@ def _text_surface(layout: MatrixLayout) -> list[str]:
strings.append(text_spec.text)
for header in layout.colheaders:
strings.append(header.label.text)
strings.extend(line.text for line in header.lines)
if header.sublabel is not None:
strings.append(header.sublabel.text)
for band in layout.section_bands:
strings.append(band.label.text)
for cell in layout.cells:
if cell.text:
strings.append(cell.text)
strings.extend(line.text for line in cell.text_lines)
if cell.sub_text:
strings.append(cell.sub_text)
strings.extend(line.text for line in cell.sub_lines)
for chip in cell.chips:
strings.append(chip.text)
if layout.axis is not None:
Expand Down
5 changes: 3 additions & 2 deletions tests/compose/test_chart_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from hyperweave.compose.engine import compose
from hyperweave.core.models import ComposeSpec
from tests.helpers import rendered_text

MOCK_POINTS = [
{"date": "2025-01-01T00:00:00Z", "count": 100},
Expand Down Expand Up @@ -129,7 +130,7 @@ def test_chart_graceful_degradation_without_data() -> None:
assert 'data-hw-status="stale"' in result.svg
# No fabricated polyline and no leak of the old placeholder numbers.
assert "<polyline" not in result.svg
assert "1200" not in result.svg and "1,200" not in result.svg
assert "1200" not in rendered_text(result.svg) and "1,200" not in rendered_text(result.svg)
# Overlay communicates the truthful unavailable state.
assert "DATA UNAVAILABLE" in result.svg

Expand All @@ -151,7 +152,7 @@ def test_chart_zero_stars_renders_empty_state() -> None:
assert 'data-hw-status="empty"' in svg
assert "NEW REPO" in svg
# No placeholder leakage.
assert "1200" not in svg and "1,200" not in svg
assert "1200" not in rendered_text(svg) and "1,200" not in rendered_text(svg)
# No fabricated polyline.
assert "<polyline" not in svg
# Hero shows 0 stars truthfully (not a comma-formatted 1,200).
Expand Down
5 changes: 3 additions & 2 deletions tests/compose/test_stats_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from hyperweave.compose.engine import compose
from hyperweave.core.models import ComposeSpec
from tests.helpers import rendered_text

MOCK_STATS = {
"username": "eli64s",
Expand Down Expand Up @@ -148,8 +149,8 @@ def test_stats_chrome_zero_stars_does_not_synthesize_placeholder() -> None:
)
svg = compose(spec).svg
# No placeholder leakage in the embedded chart.
assert "1200" not in svg
assert "1,200" not in svg
assert "1200" not in rendered_text(svg)
assert "1,200" not in rendered_text(svg)
# Compose still completes successfully with data-hw-frame marker (public name).
assert 'data-hw-frame="card"' in svg

Expand Down
13 changes: 12 additions & 1 deletion tests/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Test-only helpers for constructing inline genomes.
"""Test-only helpers: inline genomes and the rendered text layer.

``build_minimal_genome_for_testing`` emits a COMPLETE minimal genome — one
that passes the full custom-genome boundary (GenomeSpec grammar, the
Expand All @@ -13,6 +13,7 @@

from __future__ import annotations

import xml.etree.ElementTree as ET
from typing import Any

_CHROMATIC_PREFIXES = ("#", "rgba(", "rgb(", "linear-gradient", "radial-gradient")
Expand Down Expand Up @@ -64,3 +65,13 @@ def build_minimal_genome_for_testing(**overrides: Any) -> dict[str, Any]:
]
defaults["roles"] = {"core": chromatic}
return defaults


def rendered_text(svg: str) -> str:
"""Every string the SVG paints, joined — the text layer only.

Provenance timestamps, digests, and base64 font bytes are volatile and can
spell any digit run; assertions about what a reader sees must look here.
"""
ns = "{http://www.w3.org/2000/svg}"
return "\n".join(el.text for el in ET.fromstring(svg).iter() if el.tag in (f"{ns}text", f"{ns}tspan") and el.text)
49 changes: 49 additions & 0 deletions tests/render/test_tofu_and_destinations.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,37 @@
_NS = "{http://www.w3.org/2000/svg}"
_UNCOVERABLE = {0x25AE} # marquee separator bar: absent from every shipped font

_WRAPPED_MATRIX = {
"title": "Ømega ledger",
"subtitle": "wrapped runs carry glyphs the header set never sees",
"headline": {"value": "99.9%", "label": "Ωmega availability"},
"columns": [
{"id": "l", "label": "Ünit", "role": "label"},
{"id": "t", "label": "DESCRIPTION OF THE BEHAVIOUR UNDER TEST", "kind": "text", "width": 120},
{"id": "c", "label": "TAGS", "kind": "chip", "width": 90},
{"id": "k", "label": "STATE", "kind": "check"},
{"id": "b", "label": "SIZE", "kind": "bar"},
],
"rows": [
{
"label": "Straße parsing of unicode text",
"sublabel": "μ-benchmark",
"cells": [
{
"value": "Ωmega binding digits map every sigma into the ledger " * 2,
"note": "naïve first pass over the corpus",
},
{"chips": ["none (opaque html)", "ß"]},
{"state": "partial"},
{"value": 3420},
],
}
],
}

_TOFU_SPECS: tuple[tuple[str, ComposeSpec], ...] = (
("badge", ComposeSpec(type="badge", genome_id="brutalist", title="BUILD", value="passing")),
("matrix", ComposeSpec(type="matrix", genome_id="primer", matrix=_WRAPPED_MATRIX)),
("strip", ComposeSpec(type="strip", genome_id="primer", title="repo", value="STARS:1")),
("marquee", ComposeSpec(type="marquee", genome_id="brutalist", title="HW|TEST")),
(
Expand Down Expand Up @@ -85,6 +114,26 @@ def test_every_rendered_codepoint_is_in_the_embedded_subset(self, label: str, sp
missing = _rendered_codepoints(svg) - cmap - _UNCOVERABLE
assert not missing, f"{label}: tofu — rendered but not in the subset: {[hex(cp) for cp in sorted(missing)]}"

def test_the_matrix_corpus_is_tofu_free(self) -> None:
"""Every matrix fixture and every audit probe: wrapped cell runs,
note runs, and wrapped header runs all ride the subset surface."""
import json
from pathlib import Path

root = Path(__file__).parents[1] / "fixtures"
specs = {p.stem: json.loads(p.read_text()) for p in sorted((root / "matrix").glob("*.json"))}
for probe in sorted((root / "spatial" / "probes").glob("mat-audit-*.json")):
specs[probe.stem] = json.loads(probe.read_text())["compose"]["matrix"]
failures: list[str] = []
for name, payload in specs.items():
svg = compose(ComposeSpec(type="matrix", genome_id="primer", matrix=payload)).svg
cmap = _embedded_cmap(svg)
assert cmap, f"{name}: matrix must embed its fonts"
missing = _rendered_codepoints(svg) - cmap - _UNCOVERABLE
if missing:
failures.append(f"{name}: {[hex(cp) for cp in sorted(missing)]}")
assert not failures, "tofu in the matrix corpus:\n" + "\n".join(failures)

def test_the_full_preset_corpus_is_tofu_free(self) -> None:
# The FULL corpus, not a sample — a 3-preset spot check missed five
# presets whose chip, legend, and tint text rode fields the subsetter
Expand Down