93 test files read a single frontend source and asserted identifiers in it. The
MapWorkspace split showed what that costs: 24 tests went red for a move that
changed no behaviour at all. A contract belongs to the feature — a container,
its hooks, its domain layer — not to whichever file currently holds it.
232 read sites now resolve through read_feature(). The distinction that makes
this safe is direction: a *positive* contract ("this is wired") may widen,
because the identifier must still exist somewhere in the feature; a *negative*
one ("this component performs no transport") is a statement about one file, and
widening it would quietly weaken the check. The 73 single-file reads that
remain are exactly those, and a guard now enforces the rule for new tests.
Verified rather than assumed: of the 732 migrated positive assertions, 644 still
match exactly one module — as specific as before — and the other 86 already
spanned a container and its hook by nature. Two apparent misses are an artefact
of the checking regex reading an escaped newline literally.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
92 lines
4.1 KiB
Python
92 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from tests.frontend_contract import read_feature
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def read(path: str) -> str:
|
|
return (ROOT / path).read_text(encoding="utf-8")
|
|
|
|
|
|
def test_workbench_uses_grouped_navigation_and_optional_inspector() -> None:
|
|
app = read_feature("shell")
|
|
inspector = read_feature("shell")
|
|
|
|
assert "import './styles/premium.css'" in app
|
|
assert "const workspaceNavGroups" in app
|
|
assert "['map', 'data']" in app
|
|
assert "['analysis', 'ai']" in app
|
|
assert "['overview', 'system']" in app
|
|
assert "const [inspectorOpen, setInspectorOpen] = useState(false)" in app
|
|
assert "{inspectorOpen ? (" in app
|
|
assert 'id="workbench-inspector"' in app
|
|
assert "onClose={() => setInspectorOpen(false)}" in app
|
|
assert 'className="inspector-close"' in inspector
|
|
|
|
|
|
def test_data_creation_forms_are_progressively_disclosed() -> None:
|
|
project = read("frontend/src/components/project/ProjectPanel.tsx")
|
|
area = read("frontend/src/components/project/AreaPanel.tsx")
|
|
dataset = read_feature("datasets")
|
|
css = read("frontend/src/styles/premium.css")
|
|
|
|
assert '<details className="data-panel-form-block technical-management-block">' in project
|
|
assert '<summary>Geavanceerd projectbeheer</summary>' in project
|
|
assert '<details className="data-panel-form-block">' in area
|
|
assert '<summary>Eigen gebied toevoegen (GeoJSON)</summary>' in area
|
|
assert '<details className="dataset-upload-block data-panel-form-block">' in dataset
|
|
assert '<summary>Eigen bronbestand toevoegen</summary>' in dataset
|
|
assert "details.data-panel-form-block > summary" in css
|
|
assert ".workspace-grid-data > section:nth-child(n)" in css
|
|
assert "max-height: calc(100dvh - 10.5rem);" in css
|
|
|
|
|
|
def test_map_prioritizes_controls_map_and_collapsed_diagnostics() -> None:
|
|
map_workspace = read_feature("map_workspace")
|
|
css = read("frontend/src/styles/premium.css")
|
|
|
|
control_index = map_workspace.index('className="map-control-surface"')
|
|
map_index = map_workspace.index('className="map-frame-surface"')
|
|
detail_index = map_workspace.index('className="map-layer-details"')
|
|
assert control_index < map_index < detail_index
|
|
assert '<details className="map-layer-details">' in map_workspace
|
|
assert '<details className="map-advanced-tools">' in map_workspace
|
|
assert 'className="map-advanced-tools-body"' in map_workspace
|
|
assert ".map-control-surface {\n order: 1;" in css
|
|
assert ".map-frame-surface {\n order: 2;" in css
|
|
assert ".map-layer-details {\n order: 3;" in css
|
|
|
|
|
|
def test_ai_workspaces_prioritize_runs_and_collapse_registry_detail() -> None:
|
|
detection = "\n".join(
|
|
(
|
|
read("frontend/src/components/detection/DetectionLab.tsx"),
|
|
read("frontend/src/components/detection/DetectionModelManagement.tsx"),
|
|
)
|
|
)
|
|
segmentation = read_feature("segmentation")
|
|
css = read("frontend/src/styles/premium.css")
|
|
|
|
assert '<details className="ai-lab-model-surface" aria-label="Technische modelmogelijkheden">' in detection
|
|
assert '<details className="ai-lab-model-surface" aria-label="Technische YOLO-runtimecontrole">' in detection
|
|
assert '<details className="ai-lab-model-surface" aria-label="Technische informatie over segmentatiemodellen">' in segmentation
|
|
assert ".ai-lab-shell > .lab-block {\n order: 2;" in css
|
|
assert ".ai-lab-shell > .ai-lab-model-surface {\n order: 7;" in css
|
|
assert "details.ai-lab-model-surface > summary" in css
|
|
|
|
|
|
def test_mobile_shell_uses_full_width_main_and_horizontal_navigation() -> None:
|
|
css = read("frontend/src/styles/premium.css")
|
|
|
|
assert "@media (max-width: 920px)" in css
|
|
assert ".app-shell > header.workbench-topbar" in css
|
|
assert ".workbench-layout {\n display: block;" in css
|
|
assert ".workbench-sidebar nav {\n display: flex;" in css
|
|
assert ".nav-group {\n display: contents;" in css
|
|
assert ".workbench-inspector {\n top: 0;\n width: 100vw;" in css
|
|
assert "grid-auto-flow: column;" in css
|
|
assert "overflow-x: auto;" in css
|