scope frontend contracts to the feature, not to one file

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>
This commit is contained in:
Jens
2026-08-22 22:05:43 +02:00
co-authored by Claude Opus 5
parent a2a8775df1
commit 6572e4ad5f
95 changed files with 454 additions and 444 deletions
+84 -15
View File
@@ -22,20 +22,77 @@ ROOT = Path(__file__).resolve().parents[2]
FRONTEND_SRC = ROOT / "frontend" / "src" FRONTEND_SRC = ROOT / "frontend" / "src"
# The map workspace is one feature split across several modules: the container # A feature is one behaviour spread over several modules: a container, its
# component, its domain layer and its pure helpers. A contract belongs to the # hooks, its domain layer, its pure helpers. A contract belongs to the feature,
# feature, not to whichever file currently holds it, so splitting a 4.000-line # not to whichever file currently holds it, so moving code between siblings
# component must not red the suite. # must not red the suite. Missing entries are skipped, so a group survives a
MAP_WORKSPACE_SOURCES = ( # module being split further, renamed or merged back.
"components/map/MapWorkspace.tsx", #
"components/map/mapWorkspaceThemes.ts", # Use these for *positive* contracts ("this is wired"). A negative contract
"components/map/mapWorkspaceUtils.ts", # ("this component performs no transport") is a statement about one file and
"hooks/useMapImageOverlays.ts", # must keep reading that file, or widening it would quietly weaken the check.
"hooks/useMapRectangleSelection.ts", FEATURE_SOURCES: dict[str, tuple[str, ...]] = {
"hooks/useFullGisWorkflow.ts", "map_workspace": (
"components/map/MapExplorerView.tsx", "components/map/MapWorkspace.tsx",
"components/map/MapAdvancedWorkbench.tsx", "components/map/mapWorkspaceThemes.ts",
) "components/map/mapWorkspaceUtils.ts",
"components/map/MapExplorerView.tsx",
"components/map/MapAdvancedWorkbench.tsx",
"hooks/useMapImageOverlays.ts",
"hooks/useMapRectangleSelection.ts",
"hooks/useFullGisWorkflow.ts",
"hooks/useMapThemeSelectionInsights.ts",
"hooks/useMapSelectionExtract.ts",
"hooks/useMapWorkspaceState.ts",
"hooks/useMapSelectionDataset.ts",
"hooks/useMapSelectionQa.ts",
"hooks/useMapThemeSelectionInsights.ts",
"hooks/useTemporalComparison.ts",
"hooks/useCoverageResolver.ts",
"hooks/useOfficialMapProducts.ts",
),
"detection": (
"components/detection/DetectionLab.tsx",
"components/detection/DetectionModelManagement.tsx",
"components/detection/detectionProfiles.ts",
"components/models/ModelSelector.tsx",
"components/models/modelOptions.ts",
"hooks/useDetectionWorkflow.ts",
),
"segmentation": (
"components/segmentation/SegmentationLab.tsx",
"hooks/useSegmentationWorkflow.ts",
),
"quality": (
"components/quality/QualityResultsPanel.tsx",
"components/quality/DetectionReviewPanel.tsx",
"hooks/useQualityWorkflow.ts",
),
"datasets": (
"components/datasets/DatasetPanel.tsx",
"components/datasets/DatasetDetailPanel.tsx",
"components/datasets/RasterControls.tsx",
"components/datasets/VectorControls.tsx",
"components/datasets/SourceCatalogPanel.tsx",
"hooks/useDatasetWorkflow.ts",
"services/api/datasets.ts",
),
"exports": (
"components/exports/ExportCenter.tsx",
"hooks/useExportWorkflow.ts",
),
"shell": (
"App.tsx",
"components/shell/WorkbenchNavigation.tsx",
"components/shell/SecondaryDisplay.tsx",
"components/inspector/WorkbenchInspector.tsx",
"components/overview/OverviewWorkspace.tsx",
"hooks/useProjectWorkspace.ts",
"hooks/useWorkbenchBootstrap.ts",
),
}
MAP_WORKSPACE_SOURCES = FEATURE_SOURCES["map_workspace"]
def read_frontend(relative_path: str) -> str: def read_frontend(relative_path: str) -> str:
@@ -59,10 +116,22 @@ def read_frontend_area(*relative_paths: str) -> str:
return chr(10).join(parts) return chr(10).join(parts)
def read_feature(name: str) -> str:
"""Every module of one feature, whichever files it is currently split into."""
try:
sources = FEATURE_SOURCES[name]
except KeyError: # pragma: no cover - a typo should fail loudly
raise AssertionError(
f"Unknown frontend feature {name!r}; known: {sorted(FEATURE_SOURCES)}"
) from None
return read_frontend_area(*sources)
def read_map_workspace() -> str: def read_map_workspace() -> str:
"""The whole map workspace feature, whichever modules it is split into.""" """The whole map workspace feature, whichever modules it is split into."""
return read_frontend_area(*MAP_WORKSPACE_SOURCES) return read_feature("map_workspace")
def assert_wired(source: str, *identifiers: str, context: str = "frontend source") -> None: def assert_wired(source: str, *identifiers: str, context: str = "frontend source") -> None:
@@ -73,3 +73,48 @@ def test_frontend_contract_helpers_are_available() -> None:
assert_wired(source, "analyzeSelection") assert_wired(source, "analyzeSelection")
assert_calls(source, "analyzeSelection", first_argument="bbox") assert_calls(source, "analyzeSelection", first_argument="bbox")
assert_mentions(source, "AREAIDFORSELECTION") assert_mentions(source, "AREAIDFORSELECTION")
FRONTEND_READ = re.compile(
r'(?P<var>\w+)\s*=\s*\(?\s*(?:ROOT|root|REPO_ROOT)\s*/\s*'
r'(?P<path>"[^"]+"(?:\s*/\s*"[^"]+")*)\s*\)?\.read_text\('
)
NEGATIVE_ASSERT = re.compile(r"assert\s+[^\n]*not in\s+(\w+)")
def _feature_owner() -> dict[str, str]:
from tests.frontend_contract import FEATURE_SOURCES
return {source: feature for feature, sources in FEATURE_SOURCES.items() for source in sources}
def test_a_positive_contract_reads_the_feature_not_one_file() -> None:
"""Moving code between sibling modules must not red the suite.
A single-file read is right for a *negative* contract — "this component
performs no transport" is a statement about that file, and widening it
would quietly weaken the check. For a positive contract it pins the
contract to whichever file happens to hold it today.
"""
owner = _feature_owner()
offenders: list[str] = []
for path, source in _test_sources():
if "frontend" not in source:
continue
negatives = set(NEGATIVE_ASSERT.findall(source))
for match in FRONTEND_READ.finditer(source):
if match.group("var") in negatives:
continue
joined = re.sub(r'["\s/]+', "/", match.group("path")).strip("/")
if "frontend/src/" not in joined:
continue
relative = joined.split("frontend/src/", 1)[1]
if relative in owner:
offenders.append(f"{path.name}: {match.group('var')} -> {relative}")
assert not offenders, (
"These read one file of a multi-module feature for a positive contract. "
f"Use read_feature() from tests/frontend_contract.py instead: {sorted(offenders)}"
)
+3 -3
View File
@@ -13,7 +13,7 @@ from app.models import Area, Dataset, Project
from app.schemas.coverage import CoverageBBox from app.schemas.coverage import CoverageBBox
from app.services.coverage_registry_service import CoverageRegistryService, THEMES, ZONES from app.services.coverage_registry_service import CoverageRegistryService, THEMES, ZONES
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
class FakeQuery: class FakeQuery:
@@ -473,8 +473,8 @@ def test_outside_scope_and_unknown_theme_are_explicit() -> None:
def test_frontend_prefers_materialized_national_workspace_and_resolves_drawn_bbox() -> None: def test_frontend_prefers_materialized_national_workspace_and_resolves_drawn_bbox() -> None:
root = Path(__file__).parents[2] root = Path(__file__).parents[2]
focus = (root / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(encoding="utf-8") focus = (root / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(encoding="utf-8")
workspace_hook = (root / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(encoding="utf-8") workspace_hook = read_feature("shell")
coverage_hook = (root / "frontend" / "src" / "hooks" / "useCoverageResolver.ts").read_text(encoding="utf-8") coverage_hook = read_feature("map_workspace")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
assert "Belgium and North Sea Workbench" in focus assert "Belgium and North Sea Workbench" in focus
@@ -2,7 +2,7 @@ from __future__ import annotations
import json import json
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -20,7 +20,7 @@ def test_rc9_ux_audit_is_wired_into_frontend_and_readiness() -> None:
def test_rc9_loading_and_accessibility_states_are_explicit() -> None: def test_rc9_loading_and_accessibility_states_are_explicit() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
geo_map = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text( geo_map = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(
encoding="utf-8" encoding="utf-8"
@@ -1,17 +1,16 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_raster_tile_manifest_can_handoff_to_segmentation_lab() -> None: def test_raster_tile_manifest_can_handoff_to_segmentation_lab() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
hook = (ROOT / "frontend" / "src" / "hooks" / "useSegmentationWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("segmentation")
detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(encoding="utf-8") detail_panel = read_feature("datasets")
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(encoding="utf-8") raster_controls = read_feature("datasets")
segmentation_lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( segmentation_lab = read_feature("segmentation")
encoding="utf-8",
)
assert "segmentationTileManifestPath" in hook assert "segmentationTileManifestPath" in hook
assert "setSegmentationTileManifestPath" in hook assert "setSegmentationTileManifestPath" in hook
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_detection_lab_exposes_run_readiness_contract() -> None: def test_detection_lab_exposes_run_readiness_contract() -> None:
lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( lab = read_feature("detection")
encoding="utf-8"
)
assert "selectedDetectionModel = detectionModels.find" in lab assert "selectedDetectionModel = detectionModels.find" in lab
assert "detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'" in lab assert "detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'" in lab
@@ -24,9 +23,7 @@ def test_detection_lab_exposes_run_readiness_contract() -> None:
def test_segmentation_lab_exposes_run_readiness_contract() -> None: def test_segmentation_lab_exposes_run_readiness_contract() -> None:
lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( lab = read_feature("segmentation")
encoding="utf-8"
)
assert "segmentationHasDataset" in lab assert "segmentationHasDataset" in lab
assert "segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0" in lab assert "segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0" in lab
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_detection_lab_distinguishes_configured_model_from_ui_runnable_action() -> None: def test_detection_lab_distinguishes_configured_model_from_ui_runnable_action() -> None:
lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( lab = read_feature("detection")
encoding="utf-8"
)
assert "detectionModelUiRunnable" in lab assert "detectionModelUiRunnable" in lab
assert "selectedDetectionModelId !== 'manual-fixture-detector'" in lab assert "selectedDetectionModelId !== 'manual-fixture-detector'" in lab
@@ -20,9 +19,7 @@ def test_detection_lab_distinguishes_configured_model_from_ui_runnable_action()
def test_segmentation_lab_distinguishes_configured_model_from_ui_runnable_action() -> None: def test_segmentation_lab_distinguishes_configured_model_from_ui_runnable_action() -> None:
lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( lab = read_feature("segmentation")
encoding="utf-8"
)
assert "segmentationModelUiRunnable" in lab assert "segmentationModelUiRunnable" in lab
assert "selectedSegmentationModelId !== 'fixture-segmenter'" in lab assert "selectedSegmentationModelId !== 'fixture-segmenter'" in lab
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -23,7 +23,7 @@ def test_map_workspace_exposes_feature_extract_actions() -> None:
def test_geomap_highlights_selected_feature_layer() -> None: def test_geomap_highlights_selected_feature_layer() -> None:
geomap = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") geomap = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "selectedFeature?: GeoJSON.Feature | null" in geomap assert "selectedFeature?: GeoJSON.Feature | null" in geomap
assert "selected-feature" in geomap assert "selected-feature" in geomap
@@ -11,7 +11,7 @@ from shapely.geometry import Polygon, box
from app.core.errors import AppError from app.core.errors import AppError
from app.models import Dataset, VectorFeature from app.models import Dataset, VectorFeature
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -337,12 +337,12 @@ def test_vector_select_route_rejects_area_from_another_project(monkeypatch) -> N
def test_frontend_exposes_map_bbox_selection_contracts() -> None: def test_frontend_exposes_map_bbox_selection_contracts() -> None:
api_client = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") api_client = read_feature("datasets")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
geomap = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") geomap = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
extract_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapSelectionExtract.ts").read_text(encoding="utf-8") extract_hook = read_feature("map_workspace")
theme_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") theme_hook = read_feature("map_workspace")
assert "selectVectorFeatures" in api_client assert "selectVectorFeatures" in api_client
assert "Area selection" in map_workspace assert "Area selection" in map_workspace
@@ -16,6 +16,7 @@ from app.schemas.export import ExportCreateResponse
from app.services.export_service import ExportService from app.services.export_service import ExportService
from app.services.storage_service import StorageService from app.services.storage_service import StorageService
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -287,9 +288,9 @@ def test_area_constrained_bbox_rejects_selection_outside_work_area() -> None:
def test_frontend_exposes_map_selection_export_action() -> None: def test_frontend_exposes_map_selection_export_action() -> None:
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
exports_api = (ROOT / "frontend" / "src" / "services" / "api" / "exports.ts").read_text(encoding="utf-8") exports_api = (ROOT / "frontend" / "src" / "services" / "api" / "exports.ts").read_text(encoding="utf-8")
export_hook = (ROOT / "frontend" / "src" / "hooks" / "useExportWorkflow.ts").read_text(encoding="utf-8") export_hook = read_feature("exports")
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "'vector_selection'" in types assert "'vector_selection'" in types
assert "bbox?: VectorSelectionBBox" in exports_api assert "bbox?: VectorSelectionBBox" in exports_api
@@ -12,6 +12,7 @@ from app.schemas.dataset import DatasetCreateResponse
from app.services.storage_service import StorageService from app.services.storage_service import StorageService
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from app.services.vector_operations_service import VectorOperationsService from app.services.vector_operations_service import VectorOperationsService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -213,9 +214,9 @@ def test_vector_selection_derive_endpoint_returns_canonical_dataset_envelope(mon
def test_frontend_exposes_map_selection_derive_action() -> None: def test_frontend_exposes_map_selection_derive_action() -> None:
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
datasets_api = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") datasets_api = read_feature("datasets")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
assert "VectorSelectionDeriveRequest" in types assert "VectorSelectionDeriveRequest" in types
assert "deriveVectorSelection" in datasets_api assert "deriveVectorSelection" in datasets_api
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -6,7 +7,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None: def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None:
hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts" hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts"
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
assert hook_path.exists() assert hook_path.exists()
hook = hook_path.read_text(encoding="utf-8") hook = hook_path.read_text(encoding="utf-8")
@@ -12,6 +12,7 @@ from app.db.session import get_db
from app.main import app from app.main import app
from app.models import QualityCheck, VectorFeature from app.models import QualityCheck, VectorFeature
from app.services.quality_evidence_service import QualityEvidenceService from app.services.quality_evidence_service import QualityEvidenceService
from tests.frontend_contract import read_feature
class FakeQuery: class FakeQuery:
@@ -169,7 +170,7 @@ def test_frontend_quality_evidence_overlay_contract_is_wired() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
geo_map = (root / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") geo_map = (root / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
map_workspace = (root / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
qa_api = (root / "frontend" / "src" / "services" / "api" / "qa.ts").read_text(encoding="utf-8") qa_api = (root / "frontend" / "src" / "services" / "api" / "qa.ts").read_text(encoding="utf-8")
assert "qaEvidenceData" in geo_map assert "qaEvidenceData" in geo_map
@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
REPO_ROOT = Path(__file__).resolve().parents[2] REPO_ROOT = Path(__file__).resolve().parents[2]
@@ -21,7 +21,7 @@ def test_map_uses_road_basemap_with_attribution_and_env_override() -> None:
def test_map_workspace_can_select_persisted_database_layer_and_run_query() -> None: def test_map_workspace_can_select_persisted_database_layer_and_run_query() -> None:
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
app_shell = (REPO_ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app_shell = read_feature("shell")
styles = (REPO_ROOT / "frontend/src/styles/app.css").read_text(encoding="utf-8") styles = (REPO_ROOT / "frontend/src/styles/app.css").read_text(encoding="utf-8")
assert "map-database-layer-select" in map_workspace assert "map-database-layer-select" in map_workspace
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -11,10 +12,10 @@ def test_detection_lab_surfaces_yolo_runtime_preflight() -> None:
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"),
) )
) )
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("detection")
api = (ROOT / "frontend" / "src" / "services" / "api" / "detection.ts").read_text(encoding="utf-8") api = (ROOT / "frontend" / "src" / "services" / "api" / "detection.ts").read_text(encoding="utf-8")
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "Technische YOLO-runtimecontrole" in lab assert "Technische YOLO-runtimecontrole" in lab
assert "torch_version" in lab assert "torch_version" in lab
@@ -35,10 +36,10 @@ def test_detection_lab_surfaces_local_model_asset_selection() -> None:
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"),
) )
) )
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("detection")
api = (ROOT / "frontend" / "src" / "services" / "api" / "detection.ts").read_text(encoding="utf-8") api = (ROOT / "frontend" / "src" / "services" / "api" / "detection.ts").read_text(encoding="utf-8")
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text( provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
@@ -1,11 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_raster_workflow_exposes_structured_tile_manifest_handoff() -> None: def test_raster_workflow_exposes_structured_tile_manifest_handoff() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("datasets")
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
assert "interface RasterTileHandoff" in types assert "interface RasterTileHandoff" in types
@@ -16,9 +17,7 @@ def test_raster_workflow_exposes_structured_tile_manifest_handoff() -> None:
def test_raster_controls_show_manifest_details_and_ai_handoff_action() -> None: def test_raster_controls_show_manifest_details_and_ai_handoff_action() -> None:
controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( controls = read_feature("datasets")
encoding="utf-8"
)
assert "latestRasterTileManifest" in controls assert "latestRasterTileManifest" in controls
assert "Aantal tegels" in controls assert "Aantal tegels" in controls
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -52,7 +53,7 @@ def test_detection_lab_surfaces_profiles_as_deliberate_operator_actions() -> Non
def test_detection_workflow_applies_profiles_without_selecting_the_first_arbitrary_asset() -> None: def test_detection_workflow_applies_profiles_without_selecting_the_first_arbitrary_asset() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "applyDetectionOperatorProfile" in hook assert "applyDetectionOperatorProfile" in hook
assert "setSelectedDetectionModelId('yolo-configured')" in hook assert "setSelectedDetectionModelId('yolo-configured')" in hook
@@ -1,11 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_long_context_names_remain_compact_and_inspectable() -> None: def test_long_context_names_remain_compact_and_inspectable() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
status = ( status = (
ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx" ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx"
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -14,14 +15,7 @@ def test_frontend_declares_national_scope_as_primary_operating_focus() -> None:
map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text( map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
navigation = ( navigation = read_feature("shell")
ROOT
/ "frontend"
/ "src"
/ "components"
/ "shell"
/ "WorkbenchNavigation.tsx"
).read_text(encoding="utf-8")
assert "NATIONAL_WORKSPACE_PROJECT_NAME = 'Belgium and North Sea Workbench'" in focus assert "NATIONAL_WORKSPACE_PROJECT_NAME = 'Belgium and North Sea Workbench'" in focus
assert "NATIONAL_WORKSPACE_REGION = 'Belgie en Belgische Noordzee'" in focus assert "NATIONAL_WORKSPACE_REGION = 'Belgie en Belgische Noordzee'" in focus
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -11,8 +12,8 @@ def read(path: str) -> str:
def test_workbench_uses_grouped_navigation_and_optional_inspector() -> None: def test_workbench_uses_grouped_navigation_and_optional_inspector() -> None:
app = read("frontend/src/App.tsx") app = read_feature("shell")
inspector = read("frontend/src/components/inspector/WorkbenchInspector.tsx") inspector = read_feature("shell")
assert "import './styles/premium.css'" in app assert "import './styles/premium.css'" in app
assert "const workspaceNavGroups" in app assert "const workspaceNavGroups" in app
@@ -29,7 +30,7 @@ def test_workbench_uses_grouped_navigation_and_optional_inspector() -> None:
def test_data_creation_forms_are_progressively_disclosed() -> None: def test_data_creation_forms_are_progressively_disclosed() -> None:
project = read("frontend/src/components/project/ProjectPanel.tsx") project = read("frontend/src/components/project/ProjectPanel.tsx")
area = read("frontend/src/components/project/AreaPanel.tsx") area = read("frontend/src/components/project/AreaPanel.tsx")
dataset = read("frontend/src/components/datasets/DatasetPanel.tsx") dataset = read_feature("datasets")
css = read("frontend/src/styles/premium.css") css = read("frontend/src/styles/premium.css")
assert '<details className="data-panel-form-block technical-management-block">' in project assert '<details className="data-panel-form-block technical-management-block">' in project
@@ -44,7 +45,7 @@ def test_data_creation_forms_are_progressively_disclosed() -> None:
def test_map_prioritizes_controls_map_and_collapsed_diagnostics() -> None: def test_map_prioritizes_controls_map_and_collapsed_diagnostics() -> None:
map_workspace = read("frontend/src/components/map/MapWorkspace.tsx") map_workspace = read_feature("map_workspace")
css = read("frontend/src/styles/premium.css") css = read("frontend/src/styles/premium.css")
control_index = map_workspace.index('className="map-control-surface"') control_index = map_workspace.index('className="map-control-surface"')
@@ -66,7 +67,7 @@ def test_ai_workspaces_prioritize_runs_and_collapse_registry_detail() -> None:
read("frontend/src/components/detection/DetectionModelManagement.tsx"), read("frontend/src/components/detection/DetectionModelManagement.tsx"),
) )
) )
segmentation = read("frontend/src/components/segmentation/SegmentationLab.tsx") segmentation = read_feature("segmentation")
css = read("frontend/src/styles/premium.css") 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 modelmogelijkheden">' in detection
@@ -10,6 +10,7 @@ from shapely.geometry import Polygon, shape
from app.models import VectorFeature from app.models import VectorFeature
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -147,9 +148,9 @@ def test_municipality_workspace_remains_a_regression_fixture_without_frontend_pr
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8") dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
focus = (ROOT / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(encoding="utf-8") focus = (ROOT / "frontend" / "src" / "config" / "primaryFocus.ts").read_text(encoding="utf-8")
project_hook = (ROOT / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(encoding="utf-8") project_hook = (ROOT / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(encoding="utf-8")
dataset_hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") dataset_hook = read_feature("datasets")
map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") map_source = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
assert "py_compile scripts/provision_mol_municipality_workspace.py" in readiness assert "py_compile scripts/provision_mol_municipality_workspace.py" in readiness
assert "COPY scripts/provision_mol_municipality_workspace.py" in dockerfile assert "COPY scripts/provision_mol_municipality_workspace.py" in dockerfile
@@ -4,6 +4,7 @@ import pytest
from pydantic import ValidationError from pydantic import ValidationError
from app.schemas.operations import VectorSelectionBBox, VectorSelectionRequest from app.schemas.operations import VectorSelectionBBox, VectorSelectionRequest
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -22,7 +23,7 @@ def test_vector_selection_contract_keeps_an_explicit_bounded_limit() -> None:
def test_large_vector_delivery_uses_existing_postgis_bbox_contract() -> None: def test_large_vector_delivery_uses_existing_postgis_bbox_contract() -> None:
config = (ROOT / "frontend" / "src" / "config" / "vectorDelivery.ts").read_text(encoding="utf-8") config = (ROOT / "frontend" / "src" / "config" / "vectorDelivery.ts").read_text(encoding="utf-8")
viewport_hook = (ROOT / "frontend" / "src" / "hooks" / "useViewportVectorLayer.ts").read_text(encoding="utf-8") viewport_hook = (ROOT / "frontend" / "src" / "hooks" / "useViewportVectorLayer.ts").read_text(encoding="utf-8")
dataset_hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") dataset_hook = read_feature("datasets")
assert "VECTOR_VIEWPORT_FEATURE_THRESHOLD = 5_000" in config assert "VECTOR_VIEWPORT_FEATURE_THRESHOLD = 5_000" in config
assert "VECTOR_VIEWPORT_MIN_ZOOM = 14" in config assert "VECTOR_VIEWPORT_MIN_ZOOM = 14" in config
@@ -35,9 +36,9 @@ def test_large_vector_delivery_uses_existing_postgis_bbox_contract() -> None:
def test_map_reports_viewport_and_does_not_refit_each_slice() -> None: def test_map_reports_viewport_and_does_not_refit_each_slice() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
geo_map = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") geo_map = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") workspace = read_feature("map_workspace")
assert "useViewportVectorLayer" in app assert "useViewportVectorLayer" in app
assert "fitMapDataOnChange={!viewportVectorLayerActive}" in app assert "fitMapDataOnChange={!viewportVectorLayerActive}" in app
@@ -1,12 +1,13 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_map_source_mode_keeps_database_layers_distinct_from_analysis_results() -> None: def test_map_source_mode_keeps_database_layers_distinct_from_analysis_results() -> None:
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") workspace = read_feature("map_workspace")
assert "const [mapContentMode, setMapContentMode]" in app assert "const [mapContentMode, setMapContentMode]" in app
assert "mapContentMode === 'analysis' && analysisMapLayerAvailable" in app assert "mapContentMode === 'analysis' && analysisMapLayerAvailable" in app
@@ -3,7 +3,7 @@ from __future__ import annotations
import re import re
from pathlib import Path from pathlib import Path
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -14,7 +14,7 @@ def read(path: str) -> str:
def test_map_first_explorer_is_the_default_product_flow() -> None: def test_map_first_explorer_is_the_default_product_flow() -> None:
app = read("frontend/src/App.tsx") app = read_feature("shell")
workspace = read_map_workspace() workspace = read_map_workspace()
assert "useState<WorkspaceKey>('map')" in app assert "useState<WorkspaceKey>('map')" in app
@@ -69,7 +69,7 @@ def test_map_rectangle_drag_is_wired_to_automatic_analysis() -> None:
def test_dataset_detail_responses_cannot_overwrite_the_latest_map_layer() -> None: def test_dataset_detail_responses_cannot_overwrite_the_latest_map_layer() -> None:
workflow = read("frontend/src/hooks/useDatasetWorkflow.ts") workflow = read_feature("datasets")
assert "const datasetDetailRequestSequence = useRef(0)" in workflow assert "const datasetDetailRequestSequence = useRef(0)" in workflow
assert "const detailRequestId = ++datasetDetailRequestSequence.current" in workflow assert "const detailRequestId = ++datasetDetailRequestSequence.current" in workflow
+6 -5
View File
@@ -5,6 +5,7 @@ from pathlib import Path
import sys import sys
from shapely.geometry import Polygon, shape from shapely.geometry import Polygon, shape
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -127,7 +128,7 @@ def test_kempen_scope_operator_is_packaged_and_exposed_in_map_flow() -> None:
(ROOT / "frontend/src/components/map/mapWorkspaceUtils.ts").read_text(encoding="utf-8"), (ROOT / "frontend/src/components/map/mapWorkspaceUtils.ts").read_text(encoding="utf-8"),
) )
) )
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "COPY scripts/geographic_scopes.py" in dockerfile assert "COPY scripts/geographic_scopes.py" in dockerfile
assert "COPY scripts/provision_geographic_scope.py" in dockerfile assert "COPY scripts/provision_geographic_scope.py" in dockerfile
@@ -143,10 +144,10 @@ def test_kempen_scope_operator_is_packaged_and_exposed_in_map_flow() -> None:
def test_project_switches_reset_scoped_state_and_prefer_the_regional_context() -> None: def test_project_switches_reset_scoped_state_and_prefer_the_regional_context() -> None:
bootstrap = (ROOT / "frontend/src/hooks/useWorkbenchBootstrap.ts").read_text(encoding="utf-8") bootstrap = read_feature("shell")
project_workspace = (ROOT / "frontend/src/hooks/useProjectWorkspace.ts").read_text(encoding="utf-8") project_workspace = read_feature("shell")
map_state = (ROOT / "frontend/src/hooks/useMapWorkspaceState.ts").read_text(encoding="utf-8") map_state = read_feature("map_workspace")
dataset_workflow = (ROOT / "frontend/src/hooks/useDatasetWorkflow.ts").read_text(encoding="utf-8") dataset_workflow = read_feature("datasets")
selected_project_branch = bootstrap.split("if (!selectedProjectId)", maxsplit=1)[1] selected_project_branch = bootstrap.split("if (!selectedProjectId)", maxsplit=1)[1]
assert "resetProjectData()" in selected_project_branch assert "resetProjectData()" in selected_project_branch
@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -20,8 +20,8 @@ def test_work_area_change_clears_stale_spatial_results_before_switching_area() -
def test_cancelled_selection_requests_cannot_restore_stale_results() -> None: def test_cancelled_selection_requests_cannot_restore_stale_results() -> None:
selection_hook = read("frontend/src/hooks/useMapSelectionExtract.ts") selection_hook = read_feature("map_workspace")
themes_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") themes_hook = read_feature("map_workspace")
assert "const requestSequence = useRef(0)" in selection_hook assert "const requestSequence = useRef(0)" in selection_hook
assert "requestSequence.current += 1\n setMapSelectionBbox(null)" in selection_hook assert "requestSequence.current += 1\n setMapSelectionBbox(null)" in selection_hook
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -24,7 +25,7 @@ def test_national_workspace_is_automatic_and_map_has_one_scope_selector() -> Non
def test_primary_navigation_uses_end_user_language_and_keeps_management_secondary() -> None: def test_primary_navigation_uses_end_user_language_and_keeps_management_secondary() -> None:
app = read("frontend/src/App.tsx") app = read_feature("shell")
for label in ("Kaart", "Bronnen", "Kwaliteit", "Beeldanalyse", "Downloads"): for label in ("Kaart", "Bronnen", "Kwaliteit", "Beeldanalyse", "Downloads"):
assert f"label: '{label}'" in app assert f"label: '{label}'" in app
@@ -35,7 +36,7 @@ def test_primary_navigation_uses_end_user_language_and_keeps_management_secondar
def test_technical_projects_and_metadata_are_progressively_disclosed() -> None: def test_technical_projects_and_metadata_are_progressively_disclosed() -> None:
projects = read("frontend/src/components/project/ProjectPanel.tsx") projects = read("frontend/src/components/project/ProjectPanel.tsx")
areas = read("frontend/src/components/project/AreaPanel.tsx") areas = read("frontend/src/components/project/AreaPanel.tsx")
datasets = read("frontend/src/components/datasets/DatasetPanel.tsx") datasets = read_feature("datasets")
dataset_names = read("frontend/src/lib/datasetDisplay.ts") dataset_names = read("frontend/src/lib/datasetDisplay.ts")
assert "TECHNICAL_PROJECT_PATTERN" in projects assert "TECHNICAL_PROJECT_PATTERN" in projects
@@ -50,8 +51,8 @@ def test_technical_projects_and_metadata_are_progressively_disclosed() -> None:
def test_configured_yolo_and_active_asset_are_selected_without_hiding_limitations() -> None: def test_configured_yolo_and_active_asset_are_selected_without_hiding_limitations() -> None:
hook = read("frontend/src/hooks/useDetectionWorkflow.ts") hook = read_feature("detection")
lab = read("frontend/src/components/detection/DetectionLab.tsx") lab = read_feature("detection")
assert "useState('yolo-configured')" in hook assert "useState('yolo-configured')" in hook
assert "useState(0.15)" in hook assert "useState(0.15)" in hook
@@ -66,8 +67,8 @@ def test_configured_yolo_and_active_asset_are_selected_without_hiding_limitation
def test_visible_ai_and_quality_labels_are_end_user_facing() -> None: def test_visible_ai_and_quality_labels_are_end_user_facing() -> None:
profiles = read("frontend/src/components/detection/detectionProfiles.ts") profiles = read_feature("detection")
quality = read("frontend/src/components/quality/QualityResultsPanel.tsx") quality = read_feature("quality")
export_preview = read("frontend/src/components/exports/ExportPreview.tsx") export_preview = read("frontend/src/components/exports/ExportPreview.tsx")
providers = read("frontend/src/components/providers/ProviderPanel.tsx") providers = read("frontend/src/components/providers/ProviderPanel.tsx")
@@ -16,6 +16,7 @@ from rasterio.transform import from_origin
from app.models import Dataset from app.models import Dataset
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -240,11 +241,11 @@ def test_regional_timeseries_operator_is_packaged_and_release_checked() -> None:
def test_end_user_dataset_sources_are_human_readable() -> None: def test_end_user_dataset_sources_are_human_readable() -> None:
display = (ROOT / "frontend/src/lib/datasetDisplay.ts").read_text(encoding="utf-8") display = (ROOT / "frontend/src/lib/datasetDisplay.ts").read_text(encoding="utf-8")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") workspace = read_feature("map_workspace")
catalog = (ROOT / "frontend/src/components/datasets/DatasetPanel.tsx").read_text(encoding="utf-8") catalog = read_feature("datasets")
status = (ROOT / "frontend/src/components/WorkbenchStatusStrip.tsx").read_text(encoding="utf-8") status = (ROOT / "frontend/src/components/WorkbenchStatusStrip.tsx").read_text(encoding="utf-8")
detection = (ROOT / "frontend/src/components/detection/DetectionLab.tsx").read_text(encoding="utf-8") detection = read_feature("detection")
exports = (ROOT / "frontend/src/components/exports/ExportCenter.tsx").read_text(encoding="utf-8") exports = read_feature("exports")
assert "department_omgeving_land_use: 'Departement Omgeving'" in display assert "department_omgeving_land_use: 'Departement Omgeving'" in display
assert "statbel: 'Statbel'" in display assert "statbel: 'Statbel'" in display
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -11,7 +12,7 @@ def read(path: str) -> str:
def test_guided_detection_reuses_canonical_raster_and_detection_apis() -> None: def test_guided_detection_reuses_canonical_raster_and_detection_apis() -> None:
hook = read("frontend/src/hooks/useDetectionWorkflow.ts") hook = read_feature("detection")
assert "prepareAndRunDetection" in hook assert "prepareAndRunDetection" in hook
assert "datasetsApi.rasterTile" in hook assert "datasetsApi.rasterTile" in hook
@@ -30,7 +31,7 @@ def test_guided_detection_reuses_canonical_raster_and_detection_apis() -> None:
def test_guided_detection_upload_uses_existing_dataset_persistence_boundary() -> None: def test_guided_detection_upload_uses_existing_dataset_persistence_boundary() -> None:
hook = read("frontend/src/hooks/useDetectionWorkflow.ts") hook = read_feature("detection")
assert "uploadDetectionRaster" in hook assert "uploadDetectionRaster" in hook
assert "datasetsApi.upload" in hook assert "datasetsApi.upload" in hook
@@ -41,8 +42,8 @@ def test_guided_detection_upload_uses_existing_dataset_persistence_boundary() ->
def test_detection_lab_hides_manifest_plumbing_and_exposes_map_first_result_flow() -> None: def test_detection_lab_hides_manifest_plumbing_and_exposes_map_first_result_flow() -> None:
lab = read("frontend/src/components/detection/DetectionLab.tsx") lab = read_feature("detection")
app = read("frontend/src/App.tsx") app = read_feature("shell")
assert "Gebouwen zoeken en op kaart tonen" in lab assert "Gebouwen zoeken en op kaart tonen" in lab
assert 'aria-label="Luchtbeeld toevoegen"' in lab assert 'aria-label="Luchtbeeld toevoegen"' in lab
@@ -56,8 +57,8 @@ def test_detection_lab_hides_manifest_plumbing_and_exposes_map_first_result_flow
def test_detection_qa_remains_persisted_and_primary_not_parallel() -> None: def test_detection_qa_remains_persisted_and_primary_not_parallel() -> None:
lab = read("frontend/src/components/detection/DetectionLab.tsx") lab = read_feature("detection")
hook = read("frontend/src/hooks/useDetectionWorkflow.ts") hook = read_feature("detection")
assert 'aria-label="Kwaliteitscontrole gebouwdetectie"' in lab assert 'aria-label="Kwaliteitscontrole gebouwdetectie"' in lab
assert "als kwaliteitscontrole in de database bewaard" in lab assert "als kwaliteitscontrole in de database bewaard" in lab
@@ -68,7 +69,7 @@ def test_detection_qa_remains_persisted_and_primary_not_parallel() -> None:
def test_active_analysis_is_not_presented_as_the_underlying_source_dataset() -> None: def test_active_analysis_is_not_presented_as_the_underlying_source_dataset() -> None:
app = read("frontend/src/App.tsx") app = read_feature("shell")
assert "analysisMapLayerActive && mapFeatureCollection" in app assert "analysisMapLayerActive && mapFeatureCollection" in app
assert "`${mapLayerLabel} · controle vereist`" in app assert "`${mapLayerLabel} · controle vereist`" in app
@@ -21,6 +21,7 @@ from app.main import app
from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegistry, SourceSnapshot from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegistry, SourceSnapshot
from app.schemas.orthophoto import OrthophotoAcquireRequest from app.schemas.orthophoto import OrthophotoAcquireRequest
from app.services.orthophoto_acquisition_service import OrthophotoAcquisitionService from app.services.orthophoto_acquisition_service import OrthophotoAcquisitionService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -518,9 +519,9 @@ def test_orthophoto_product_endpoint_returns_canonical_envelope() -> None:
def test_frontend_connects_map_selection_to_existing_detection_and_qa_flows() -> None: def test_frontend_connects_map_selection_to_existing_detection_and_qa_flows() -> None:
app_source = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app_source = read_feature("shell")
hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapOrthophotoAnalysis.ts").read_text(encoding="utf-8") hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapOrthophotoAnalysis.ts").read_text(encoding="utf-8")
map_source = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_source = read_feature("map_workspace")
assert "useMapOrthophotoAnalysis" in app_source assert "useMapOrthophotoAnalysis" in app_source
assert "onRunOrthophotoAnalysis={mapOrthophotoAnalysis.run}" in app_source assert "onRunOrthophotoAnalysis={mapOrthophotoAnalysis.run}" in app_source
@@ -19,6 +19,7 @@ from app.schemas.detection_review import (
DetectionReviewUpsert, DetectionReviewUpsert,
) )
from app.services.detection_review_service import DetectionReviewService from app.services.detection_review_service import DetectionReviewService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -241,7 +242,7 @@ def test_detection_review_endpoints_use_canonical_envelopes(monkeypatch) -> None
def test_map_detection_qa_uses_documented_footprint_threshold_and_honest_labels() -> None: def test_map_detection_qa_uses_documented_footprint_threshold_and_honest_labels() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useMapOrthophotoAnalysis.ts").read_text(encoding="utf-8") hook = (ROOT / "frontend" / "src" / "hooks" / "useMapOrthophotoAnalysis.ts").read_text(encoding="utf-8")
app_source = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app_source = read_feature("shell")
evidence_service = (ROOT / "backend" / "app" / "services" / "quality_evidence_service.py").read_text(encoding="utf-8") evidence_service = (ROOT / "backend" / "app" / "services" / "quality_evidence_service.py").read_text(encoding="utf-8")
assert "MAP_BUILDING_QA_IOU_THRESHOLD = 0.25" in hook assert "MAP_BUILDING_QA_IOU_THRESHOLD = 0.25" in hook
+3 -2
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -22,8 +23,8 @@ def test_geomap_exposes_v1_layer_controls_and_feature_inspection_contract() -> N
def test_app_wires_map_workbench_component() -> None: def test_app_wires_map_workbench_component() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
component = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") component = read_feature("map_workspace")
assert "mapLayerVisible" in app assert "mapLayerVisible" in app
assert "mapLayerOpacity" in app assert "mapLayerOpacity" in app
@@ -6,6 +6,7 @@ from uuid import uuid4
from app.models import Dataset from app.models import Dataset
from app.schemas.operations import VectorSelectionSummary from app.schemas.operations import VectorSelectionSummary
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).parents[2] ROOT = Path(__file__).parents[2]
@@ -174,7 +175,7 @@ def test_regional_historical_polygons_do_not_emit_irrelevant_line_metrics() -> N
def test_future_regional_imports_persist_semantic_aggregation_configuration() -> None: def test_future_regional_imports_persist_semantic_aggregation_configuration() -> None:
buildings = (ROOT / "scripts/provision_regional_grb_buildings.py").read_text(encoding="utf-8") buildings = (ROOT / "scripts/provision_regional_grb_buildings.py").read_text(encoding="utf-8")
context = (ROOT / "scripts/provision_regional_grb_context.py").read_text(encoding="utf-8") context = (ROOT / "scripts/provision_regional_grb_context.py").read_text(encoding="utf-8")
frontend = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") frontend = read_feature("map_workspace")
assert '"method": "intersection_area"' in buildings assert '"method": "intersection_area"' in buildings
assert '"label": "Bebouwde grondoppervlakte"' in buildings assert '"label": "Bebouwde grondoppervlakte"' in buildings
@@ -12,6 +12,7 @@ from app.main import app
from app.schemas.assistant import AssistantContextMetric, AssistantModelRead, AssistantQueryRequest, AssistantStatus, AssistantTemporalSeries from app.schemas.assistant import AssistantContextMetric, AssistantModelRead, AssistantQueryRequest, AssistantStatus, AssistantTemporalSeries
from app.services.geo_assistant_service import GeoAssistantService from app.services.geo_assistant_service import GeoAssistantService
from app.services.temporal_analysis_service import TemporalAnalysisService from app.services.temporal_analysis_service import TemporalAnalysisService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -408,9 +409,9 @@ def test_landuse_operator_exposes_more_honest_historical_themes() -> None:
def test_frontend_exposes_source_inventory_timeline_and_ai_window() -> None: def test_frontend_exposes_source_inventory_timeline_and_ai_window() -> None:
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") workspace = read_feature("map_workspace")
catalog = (ROOT / "frontend/src/components/datasets/SourceCatalogPanel.tsx").read_text(encoding="utf-8") catalog = read_feature("datasets")
assistant_hook = (ROOT / "frontend/src/hooks/useGeoAssistant.ts").read_text(encoding="utf-8") assistant_hook = (ROOT / "frontend/src/hooks/useGeoAssistant.ts").read_text(encoding="utf-8")
assert "SourceCatalogPanel" in app assert "SourceCatalogPanel" in app
@@ -5,6 +5,7 @@ import sys
from pathlib import Path from pathlib import Path
from shapely.geometry import box from shapely.geometry import box
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -137,7 +138,7 @@ def test_waterinfo_operator_is_packaged_and_readiness_checked() -> None:
def test_frontend_loads_all_dataset_pages_after_temporal_import_expansion() -> None: def test_frontend_loads_all_dataset_pages_after_temporal_import_expansion() -> None:
client = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") client = read_feature("datasets")
assert "DATASET_PAGE_SIZE = 200" in client assert "DATASET_PAGE_SIZE = 200" in client
assert "offset < (total ?? 0)" in client assert "offset < (total ?? 0)" in client
@@ -13,7 +13,7 @@ from shapely.ops import transform as transform_geometry
from app.models import Dataset from app.models import Dataset
from app.schemas.operations import VectorSelectionSummary from app.schemas.operations import VectorSelectionSummary
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -262,7 +262,7 @@ def test_operator_is_packaged_readiness_checked_and_wired_to_map() -> None:
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8") readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
service = (ROOT / "backend" / "app" / "services" / "vector_feature_service.py").read_text(encoding="utf-8") service = (ROOT / "backend" / "app" / "services" / "vector_feature_service.py").read_text(encoding="utf-8")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
source_catalog = (ROOT / "frontend" / "src" / "components" / "datasets" / "SourceCatalogPanel.tsx").read_text(encoding="utf-8") source_catalog = read_feature("datasets")
assert "COPY scripts/provision_mol_bwk_natura2000.py" in dockerfile assert "COPY scripts/provision_mol_bwk_natura2000.py" in dockerfile
assert "py_compile scripts/provision_mol_bwk_natura2000.py" in readiness assert "py_compile scripts/provision_mol_bwk_natura2000.py" in readiness
@@ -14,7 +14,7 @@ from shapely.ops import transform as transform_geometry
from app.models import Dataset from app.models import Dataset
from app.schemas.operations import VectorSelectionSummary from app.schemas.operations import VectorSelectionSummary
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -263,7 +263,7 @@ def test_operator_uses_canonical_upload_and_is_packaged_for_runtime() -> None:
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8") dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8") readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
source_catalog = (ROOT / "frontend" / "src" / "components" / "datasets" / "SourceCatalogPanel.tsx").read_text(encoding="utf-8") source_catalog = read_feature("datasets")
dataset_display = (ROOT / "frontend" / "src" / "lib" / "datasetDisplay.ts").read_text(encoding="utf-8") dataset_display = (ROOT / "frontend" / "src" / "lib" / "datasetDisplay.ts").read_text(encoding="utf-8")
assert "/datasets/upload" in operator assert "/datasets/upload" in operator
+3 -3
View File
@@ -21,7 +21,7 @@ from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegist
from app.schemas.dhmv import DhmvAcquireRequest, TerrainPartitionSelectionRequest, TerrainSelectionRequest from app.schemas.dhmv import DhmvAcquireRequest, TerrainPartitionSelectionRequest, TerrainSelectionRequest
from app.services.dhmv_acquisition_service import DhmvAcquisitionService from app.services.dhmv_acquisition_service import DhmvAcquisitionService
from app.services.terrain_analysis_service import TerrainAnalysisService from app.services.terrain_analysis_service import TerrainAnalysisService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -618,8 +618,8 @@ def test_frontend_and_runtime_expose_dhmv_workflow() -> None:
ROOT / "frontend" / "src" / "lib" / "datasetCapabilities.ts" ROOT / "frontend" / "src" / "lib" / "datasetCapabilities.ts"
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
map_source = read_map_workspace() map_source = read_map_workspace()
hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapSelectionExtract.ts").read_text(encoding="utf-8") hook_source = read_feature("map_workspace")
service_source = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") service_source = read_feature("datasets")
assert "digitaal_vlaanderen_dhmv" in capabilities_source assert "digitaal_vlaanderen_dhmv" in capabilities_source
assert "isMapRasterDataset" in capabilities_source assert "isMapRasterDataset" in capabilities_source
@@ -12,7 +12,7 @@ from shapely.ops import transform as transform_geometry
from app.models import Dataset from app.models import Dataset
from app.schemas.operations import VectorSelectionSummary from app.schemas.operations import VectorSelectionSummary
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -354,7 +354,7 @@ def test_operator_is_canonical_packaged_and_mol_scoped_in_explorer() -> None:
dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8") dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8")
readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8") readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8")
workspace = read_map_workspace() workspace = read_map_workspace()
catalog = (ROOT / "frontend/src/components/datasets/SourceCatalogPanel.tsx").read_text(encoding="utf-8") catalog = read_feature("datasets")
display = (ROOT / "frontend/src/lib/datasetDisplay.ts").read_text(encoding="utf-8") display = (ROOT / "frontend/src/lib/datasetDisplay.ts").read_text(encoding="utf-8")
assert "/datasets/upload" in operator assert "/datasets/upload" in operator
@@ -7,6 +7,7 @@ from pathlib import Path
import sys import sys
from shapely.geometry import box, mapping, shape from shapely.geometry import box, mapping, shape
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -239,8 +240,8 @@ def test_upload_contract_is_regional_temporal_and_partition_audited(tmp_path: Pa
def test_regional_historical_operator_is_packaged_and_release_checked() -> None: def test_regional_historical_operator_is_packaged_and_release_checked() -> None:
dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8") dockerfile = (ROOT / "deploy/unraid/Dockerfile.all-in-one").read_text(encoding="utf-8")
readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8") readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8")
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") workspace = read_feature("map_workspace")
assert "COPY scripts/provision_regional_historical_landuse.py" in dockerfile assert "COPY scripts/provision_regional_historical_landuse.py" in dockerfile
assert "py_compile scripts/provision_regional_historical_landuse.py" in readiness assert "py_compile scripts/provision_regional_historical_landuse.py" in readiness
@@ -7,6 +7,7 @@ from shapely.geometry import MultiPolygon, Polygon
from app.models import Area from app.models import Area
from app.services.area_service import AreaService from app.services.area_service import AreaService
from tests.frontend_contract import read_feature
def test_area_serializer_exposes_geojson_geometry_for_map_overlay() -> None: def test_area_serializer_exposes_geojson_geometry_for_map_overlay() -> None:
@@ -45,9 +46,9 @@ def test_area_serializer_exposes_geojson_geometry_for_map_overlay() -> None:
def test_frontend_wires_selected_area_map_overlay_contract() -> None: def test_frontend_wires_selected_area_map_overlay_contract() -> None:
root = __import__("pathlib").Path(__file__).resolve().parents[2] root = __import__("pathlib").Path(__file__).resolve().parents[2]
app = (root / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
geomap = (root / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") geomap = (root / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8")
map_workspace = (root / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
area_panel = (root / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text(encoding="utf-8") area_panel = (root / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text(encoding="utf-8")
assert "selectedMapAreaId" in app assert "selectedMapAreaId" in app
@@ -11,7 +11,7 @@ from shapely.geometry import box, mapping, shape
from app.models import Dataset from app.models import Dataset
from app.services.vector_feature_service import VectorFeatureService from app.services.vector_feature_service import VectorFeatureService
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -237,7 +237,7 @@ def test_regional_operator_is_packaged_release_checked_and_exact_area_is_preferr
readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8") readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8")
service = (ROOT / "backend/app/services/vector_feature_service.py").read_text(encoding="utf-8") service = (ROOT / "backend/app/services/vector_feature_service.py").read_text(encoding="utf-8")
workspace = read_map_workspace() workspace = read_map_workspace()
catalog = (ROOT / "frontend/src/components/datasets/SourceCatalogPanel.tsx").read_text(encoding="utf-8") catalog = read_feature("datasets")
model = (ROOT / "backend/app/models/entities.py").read_text(encoding="utf-8") model = (ROOT / "backend/app/models/entities.py").read_text(encoding="utf-8")
migration = ( migration = (
ROOT / "backend/alembic/versions/202607160001_vector_feature_municipality_index.py" ROOT / "backend/alembic/versions/202607160001_vector_feature_municipality_index.py"
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -30,7 +31,7 @@ def test_source_portfolio_covers_the_complete_platform() -> None:
def test_source_inventory_is_compact_honest_and_domain_driven() -> None: def test_source_inventory_is_compact_honest_and_domain_driven() -> None:
catalog = (ROOT / "frontend/src/components/datasets/SourceCatalogPanel.tsx").read_text(encoding="utf-8") catalog = read_feature("datasets")
styles = (ROOT / "frontend/src/styles/app.css").read_text(encoding="utf-8") styles = (ROOT / "frontend/src/styles/app.css").read_text(encoding="utf-8")
assert "Welke vragen kan GeoIntel beantwoorden?" in catalog assert "Welke vragen kan GeoIntel beantwoorden?" in catalog
@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -20,10 +20,10 @@ def test_partitioned_raster_routes_are_canonical_and_documented() -> None:
def test_regional_map_uses_logical_partition_groups_and_exact_analysis() -> None: def test_regional_map_uses_logical_partition_groups_and_exact_analysis() -> None:
app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
workspace = read_map_workspace() workspace = read_map_workspace()
hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") hook = read_feature("map_workspace")
api = (ROOT / "frontend/src/services/api/datasets.ts").read_text(encoding="utf-8") api = read_feature("datasets")
assert "regionalScopeSelected" in workspace assert "regionalScopeSelected" in workspace
assert "rasterPartitionsForDataset" in workspace assert "rasterPartitionsForDataset" in workspace
@@ -7,6 +7,7 @@ from types import SimpleNamespace
from app.models import Dataset, DatasetVersion from app.models import Dataset, DatasetVersion
from app.services.source_freshness_service import SourceFreshnessService from app.services.source_freshness_service import SourceFreshnessService
from tests.frontend_contract import read_feature
NOW = datetime(2026, 7, 16, 12, 0, tzinfo=timezone.utc) NOW = datetime(2026, 7, 16, 12, 0, tzinfo=timezone.utc)
@@ -231,8 +232,8 @@ def test_source_freshness_operator_and_ui_contract_are_read_only() -> None:
script = (root / "scripts" / "audit_source_freshness.py").read_text(encoding="utf-8") script = (root / "scripts" / "audit_source_freshness.py").read_text(encoding="utf-8")
dockerfile = (root / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8") dockerfile = (root / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
readiness = (root / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8") readiness = (root / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8")
app = (root / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") app = read_feature("shell")
api = (root / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") api = read_feature("datasets")
assert "Request(endpoint" in script assert "Request(endpoint" in script
assert "method=\"POST\"" not in script assert "method=\"POST\"" not in script
@@ -1,14 +1,15 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_frontend_wires_v1_workbench_status_strip() -> None: def test_frontend_wires_v1_workbench_status_strip() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") overview = read_feature("shell")
component = (ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx").read_text(encoding="utf-8") component = (ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx").read_text(encoding="utf-8")
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -9,7 +10,7 @@ def read(path: str) -> str:
def test_theme_overview_names_the_metric_instead_of_showing_a_bare_value() -> None: def test_theme_overview_names_the_metric_instead_of_showing_a_bare_value() -> None:
workspace = read("frontend/src/components/map/MapWorkspace.tsx") workspace = read_feature("map_workspace")
styles = read("frontend/src/styles/app.css") styles = read("frontend/src/styles/app.css")
assert 'className="geo-theme-result-value"' in workspace assert 'className="geo-theme-result-value"' in workspace
@@ -18,7 +19,7 @@ def test_theme_overview_names_the_metric_instead_of_showing_a_bare_value() -> No
def test_current_and_historical_results_are_downloadable() -> None: def test_current_and_historical_results_are_downloadable() -> None:
workspace = read("frontend/src/components/map/MapWorkspace.tsx") workspace = read_feature("map_workspace")
assert "activeTheme.id}-analysis.json" in workspace assert "activeTheme.id}-analysis.json" in workspace
assert "activeTheme.id}-selection.geojson" in workspace assert "activeTheme.id}-selection.geojson" in workspace
@@ -30,7 +31,7 @@ def test_current_and_historical_results_are_downloadable() -> None:
def test_completed_analysis_hands_off_to_ai_and_downloads_responsively() -> None: def test_completed_analysis_hands_off_to_ai_and_downloads_responsively() -> None:
app = read("frontend/src/App.tsx") app = read("frontend/src/App.tsx")
workspace = read("frontend/src/components/map/MapWorkspace.tsx") workspace = read_feature("map_workspace")
styles = read("frontend/src/styles/app.css") styles = read("frontend/src/styles/app.css")
assert "onOpenAssistant: () => void" in workspace assert "onOpenAssistant: () => void" in workspace
@@ -20,7 +20,7 @@ from app.services.storage_service import StorageService
from app.services.source_registry_service import SourceRegistryService from app.services.source_registry_service import SourceRegistryService
from app.services.temporal_analysis_service import TemporalAnalysisService from app.services.temporal_analysis_service import TemporalAnalysisService
from app.services.thematic_raster_analysis_service import ThematicRasterAnalysisService from app.services.thematic_raster_analysis_service import ThematicRasterAnalysisService
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
class FakeSession: class FakeSession:
@@ -365,7 +365,7 @@ def test_map_result_export_endpoint_uses_canonical_envelope(monkeypatch) -> None
def test_frontend_persists_map_result_before_opening_downloads() -> None: def test_frontend_persists_map_result_before_opening_downloads() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
workspace = read_map_workspace() workspace = read_map_workspace()
hook = (root / "frontend/src/hooks/useExportWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("exports")
api = (root / "frontend/src/services/api/exports.ts").read_text(encoding="utf-8") api = (root / "frontend/src/services/api/exports.ts").read_text(encoding="utf-8")
assert "persistActiveResultAndOpenDownloads" in workspace assert "persistActiveResultAndOpenDownloads" in workspace
@@ -409,7 +409,7 @@ def test_project_list_supports_exact_canonical_workspace_lookup(monkeypatch) ->
def test_frontend_fetches_canonical_workspace_outside_default_project_page() -> None: def test_frontend_fetches_canonical_workspace_outside_default_project_page() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
workflow = (root / "frontend/src/hooks/useProjectWorkspace.ts").read_text(encoding="utf-8") workflow = read_feature("shell")
api = (root / "frontend/src/services/api/projects.ts").read_text(encoding="utf-8") api = (root / "frontend/src/services/api/projects.ts").read_text(encoding="utf-8")
assert "projectsApi.list({ name: REGIONAL_WORKSPACE_PROJECT_NAME, limit: 1 })" in workflow assert "projectsApi.list({ name: REGIONAL_WORKSPACE_PROJECT_NAME, limit: 1 })" in workflow
@@ -419,7 +419,7 @@ def test_frontend_fetches_canonical_workspace_outside_default_project_page() ->
def test_theme_failures_name_the_source_and_reason() -> None: def test_theme_failures_name_the_source_and_reason() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
hook = (root / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") hook = read_feature("map_workspace")
assert "queries[index]?.dataset?.name" in hook assert "queries[index]?.dataset?.name" in hook
assert "queries[index]?.acquisition?.displayName" in hook assert "queries[index]?.acquisition?.displayName" in hook
@@ -429,7 +429,7 @@ def test_theme_failures_name_the_source_and_reason() -> None:
def test_workspace_navigation_resets_the_actual_scroll_container() -> None: def test_workspace_navigation_resets_the_actual_scroll_container() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
app = (root / "frontend/src/App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "const workbenchMainRef = useRef<HTMLElement | null>(null)" in app assert "const workbenchMainRef = useRef<HTMLElement | null>(null)" in app
assert "workbenchMainRef.current?.scrollTo({ top: 0, left: 0 })" in app assert "workbenchMainRef.current?.scrollTo({ top: 0, left: 0 })" in app
@@ -439,8 +439,8 @@ def test_workspace_navigation_resets_the_actual_scroll_container() -> None:
def test_quality_scores_have_plain_language_interpretation() -> None: def test_quality_scores_have_plain_language_interpretation() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
quality = (root / "frontend/src/components/quality/QualityResultsPanel.tsx").read_text(encoding="utf-8") quality = read_feature("quality")
detection = (root / "frontend/src/components/detection/DetectionLab.tsx").read_text(encoding="utf-8") detection = read_feature("detection")
assert "Laatste score (0-1)" in quality assert "Laatste score (0-1)" in quality
assert "Bruikbaar na controle" in quality assert "Bruikbaar na controle" in quality
@@ -465,7 +465,7 @@ def test_map_and_detection_workspaces_avoid_page_length_driven_layouts() -> None
def test_detection_lab_only_receives_operational_imagery_rasters() -> None: def test_detection_lab_only_receives_operational_imagery_rasters() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
app_source = (root / "frontend/src/App.tsx").read_text(encoding="utf-8") app_source = read_feature("shell")
capability_source = (root / "frontend/src/lib/datasetCapabilities.ts").read_text(encoding="utf-8") capability_source = (root / "frontend/src/lib/datasetCapabilities.ts").read_text(encoding="utf-8")
assert "department_omgeving_thematic_raster" in capability_source assert "department_omgeving_thematic_raster" in capability_source
@@ -480,7 +480,7 @@ def test_detection_lab_only_receives_operational_imagery_rasters() -> None:
def test_download_workspace_surfaces_map_results_in_plain_dutch() -> None: def test_download_workspace_surfaces_map_results_in_plain_dutch() -> None:
root = Path(__file__).resolve().parents[2] root = Path(__file__).resolve().parents[2]
exports = (root / "frontend/src/components/exports/ExportCenter.tsx").read_text(encoding="utf-8") exports = read_feature("exports")
assert "Gebiedsanalyse (JSON)" in exports assert "Gebiedsanalyse (JSON)" in exports
assert "Historische vergelijking (JSON)" in exports assert "Historische vergelijking (JSON)" in exports
@@ -15,6 +15,7 @@ from app.api.routes import projects as project_routes
from app.main import app from app.main import app
from app.schemas.project import ProjectRead, ProjectUpdate from app.schemas.project import ProjectRead, ProjectUpdate
from app.services.project_service import ProjectService from app.services.project_service import ProjectService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -149,11 +150,11 @@ def test_cleanup_script_can_start_as_a_direct_operator_command() -> None:
def test_frontend_lifecycle_and_component_boundaries_are_wired() -> None: def test_frontend_lifecycle_and_component_boundaries_are_wired() -> None:
app_source = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") app_source = read_feature("shell")
project_panel = (ROOT / "frontend/src/components/project/ProjectPanel.tsx").read_text(encoding="utf-8") project_panel = (ROOT / "frontend/src/components/project/ProjectPanel.tsx").read_text(encoding="utf-8")
detection_lab = (ROOT / "frontend/src/components/detection/DetectionLab.tsx").read_text(encoding="utf-8") detection_lab = read_feature("detection")
segmentation_lab = (ROOT / "frontend/src/components/segmentation/SegmentationLab.tsx").read_text(encoding="utf-8") segmentation_lab = read_feature("segmentation")
map_workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") map_workspace = read_feature("map_workspace")
premium_css = (ROOT / "frontend/src/styles/premium.css").read_text(encoding="utf-8") premium_css = (ROOT / "frontend/src/styles/premium.css").read_text(encoding="utf-8")
assert "OverviewWorkspace" in app_source assert "OverviewWorkspace" in app_source
@@ -31,7 +31,7 @@ if str(SCRIPTS) not in sys.path:
sys.path.insert(0, str(SCRIPTS)) sys.path.insert(0, str(SCRIPTS))
import provision_flanders_geographic_scope as flanders_scope # noqa: E402 import provision_flanders_geographic_scope as flanders_scope # noqa: E402
from tests.frontend_contract import read_map_workspace from tests.frontend_contract import read_map_workspace, read_feature
class BinaryResponse: class BinaryResponse:
@@ -433,9 +433,7 @@ def test_frontend_bounds_large_area_and_dataset_catalogs() -> None:
area_panel = ( area_panel = (
ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx" ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx"
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
dataset_panel = ( dataset_panel = read_feature("datasets")
ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx"
).read_text(encoding="utf-8")
assert "const AREA_CATALOG_PAGE_SIZE = 12" in area_panel assert "const AREA_CATALOG_PAGE_SIZE = 12" in area_panel
assert "{catalogOpen ? (" in area_panel assert "{catalogOpen ? (" in area_panel
@@ -459,18 +457,14 @@ def test_frontend_labels_flanders_scope_without_kempen_mislabeling() -> None:
def test_frontend_uses_partitioned_bathymetry_selection_for_regional_scope() -> None: def test_frontend_uses_partitioned_bathymetry_selection_for_regional_scope() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
index = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8") index = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8")
focus = ( focus = (
ROOT / "frontend" / "src" / "config" / "primaryFocus.ts" ROOT / "frontend" / "src" / "config" / "primaryFocus.ts"
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
map_workspace = read_map_workspace() map_workspace = read_map_workspace()
theme_hook = ( theme_hook = read_feature("map_workspace")
ROOT / "frontend" / "src" / "hooks" / "useMapThemeSelectionInsights.ts" dataset_api = read_feature("datasets")
).read_text(encoding="utf-8")
dataset_api = (
ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts"
).read_text(encoding="utf-8")
assert "isPartitionedBathymetry" in map_workspace assert "isPartitionedBathymetry" in map_workspace
assert "regionalPartitionedThemeActive" in map_workspace assert "regionalPartitionedThemeActive" in map_workspace
@@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -11,9 +11,9 @@ def read(path: str) -> str:
def test_flanders_workspace_exposes_governed_thematic_products_on_demand() -> None: def test_flanders_workspace_exposes_governed_thematic_products_on_demand() -> None:
workspace = read_map_workspace() workspace = read_map_workspace()
product_hook = read("frontend/src/hooks/useOfficialMapProducts.ts") product_hook = read_feature("map_workspace")
selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") selection_hook = read_feature("map_workspace")
api = read("frontend/src/services/api/datasets.ts") api = read_feature("datasets")
assert "activeScopeProject?.name === FLANDERS_WORKSPACE_PROJECT_NAME" in workspace assert "activeScopeProject?.name === FLANDERS_WORKSPACE_PROJECT_NAME" in workspace
assert "new Map<DataThemeId, OnDemandMapProduct>" in workspace assert "new Map<DataThemeId, OnDemandMapProduct>" in workspace
@@ -31,8 +31,8 @@ def test_flanders_workspace_exposes_governed_thematic_products_on_demand() -> No
def test_selection_reads_and_bounded_acquires_all_relevant_themes() -> None: def test_selection_reads_and_bounded_acquires_all_relevant_themes() -> None:
workspace = read_map_workspace() workspace = read_map_workspace()
app = read("frontend/src/App.tsx") app = read_feature("shell")
selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") selection_hook = read_feature("map_workspace")
# Selection reads the active theme; the loop form is incidental. # Selection reads the active theme; the loop form is incidental.
assert_wired(workspace, "activeTheme") assert_wired(workspace, "activeTheme")
@@ -2,6 +2,7 @@ from pathlib import Path
from app.schemas.flood_hazard import FloodHazardAcquireRequest from app.schemas.flood_hazard import FloodHazardAcquireRequest
from app.schemas.operations import VectorSelectionBBox from app.schemas.operations import VectorSelectionBBox
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -12,7 +13,7 @@ def read(path: str) -> str:
def test_official_map_catalog_hook_loads_all_governed_registries() -> None: def test_official_map_catalog_hook_loads_all_governed_registries() -> None:
hook = read("frontend/src/hooks/useOfficialMapProducts.ts") hook = read_feature("map_workspace")
assert "datasetsApi.listThematicRasterProducts" in hook assert "datasetsApi.listThematicRasterProducts" in hook
assert "datasetsApi.listDhmvProducts" in hook assert "datasetsApi.listDhmvProducts" in hook
@@ -22,8 +23,8 @@ def test_official_map_catalog_hook_loads_all_governed_registries() -> None:
def test_map_selection_can_acquire_dhmv_and_flood_hazard_products() -> None: def test_map_selection_can_acquire_dhmv_and_flood_hazard_products() -> None:
selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") selection_hook = read_feature("map_workspace")
workspace = read("frontend/src/components/map/MapWorkspace.tsx") workspace = read_feature("map_workspace")
for acquisition_kind in ( for acquisition_kind in (
"'thematic_raster'", "'thematic_raster'",
@@ -19,7 +19,7 @@ from app.models import Area, Dataset, Job, Project
from app.schemas.grb import GrbAcquireRequest from app.schemas.grb import GrbAcquireRequest
from app.services.dataset_service import DatasetService from app.services.dataset_service import DatasetService
from app.services.grb_acquisition_service import GrbAcquisitionService from app.services.grb_acquisition_service import GrbAcquisitionService
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_map_workspace, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -381,8 +381,8 @@ def test_system_capabilities_reports_bounded_grb_integration() -> None:
def test_grb_frontend_and_contracts_use_only_the_governed_backend_path() -> None: def test_grb_frontend_and_contracts_use_only_the_governed_backend_path() -> None:
selection_hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") selection_hook = read_feature("map_workspace")
catalog_hook = (ROOT / "frontend/src/hooks/useOfficialMapProducts.ts").read_text(encoding="utf-8") catalog_hook = read_feature("map_workspace")
workspace = read_map_workspace() workspace = read_map_workspace()
contracts = (ROOT / "docs/API_CONTRACTS.md").read_text(encoding="utf-8") contracts = (ROOT / "docs/API_CONTRACTS.md").read_text(encoding="utf-8")
@@ -23,6 +23,7 @@ from app.schemas.official_vector import OfficialVectorAcquireRequest
from app.services.dataset_service import DatasetService from app.services.dataset_service import DatasetService
from app.services.official_vector_acquisition_service import OfficialVectorAcquisitionService from app.services.official_vector_acquisition_service import OfficialVectorAcquisitionService
from app.services.thematic_raster_acquisition_service import ThematicRasterAcquisitionService from app.services.thematic_raster_acquisition_service import ThematicRasterAcquisitionService
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -412,8 +413,8 @@ def test_official_vector_routes_and_frontend_use_canonical_backend_path(monkeypa
assert acquire_response.json()["data"]["job_type"] == "vector.official.acquire" assert acquire_response.json()["data"]["job_type"] == "vector.official.acquire"
assert any(isinstance(item, Job) for item in db.added) assert any(isinstance(item, Job) for item in db.added)
selection_hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") selection_hook = read_feature("map_workspace")
catalog_hook = (ROOT / "frontend/src/hooks/useOfficialMapProducts.ts").read_text(encoding="utf-8") catalog_hook = read_feature("map_workspace")
workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8")
assert "datasetsApi.acquireOfficialVector" in selection_hook assert "datasetsApi.acquireOfficialVector" in selection_hook
assert "datasetsApi.listOfficialVectorProducts" in catalog_hook assert "datasetsApi.listOfficialVectorProducts" in catalog_hook
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -18,7 +19,7 @@ def test_app_uses_detection_and_segmentation_workflow_hooks() -> None:
def test_detection_workflow_hook_owns_detection_api_calls() -> None: def test_detection_workflow_hook_owns_detection_api_calls() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("detection")
assert "detectionApi.listModels" in hook assert "detectionApi.listModels" in hook
assert "detectionApi.listRuns" in hook assert "detectionApi.listRuns" in hook
@@ -28,7 +29,7 @@ def test_detection_workflow_hook_owns_detection_api_calls() -> None:
def test_segmentation_workflow_hook_owns_segmentation_api_calls() -> None: def test_segmentation_workflow_hook_owns_segmentation_api_calls() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useSegmentationWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("segmentation")
assert "segmentationApi.listModels" in hook assert "segmentationApi.listModels" in hook
assert "segmentationApi.listRuns" in hook assert "segmentationApi.listRuns" in hook
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -18,7 +19,7 @@ def test_app_uses_export_and_quality_workflow_hooks() -> None:
def test_export_workflow_hook_owns_export_api_calls() -> None: def test_export_workflow_hook_owns_export_api_calls() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useExportWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("exports")
assert "exportsApi.listProjectExports" in hook assert "exportsApi.listProjectExports" in hook
assert "exportsApi.exportGeojson" in hook assert "exportsApi.exportGeojson" in hook
@@ -30,7 +31,7 @@ def test_export_workflow_hook_owns_export_api_calls() -> None:
def test_quality_workflow_hook_owns_quality_api_calls() -> None: def test_quality_workflow_hook_owns_quality_api_calls() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useQualityWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("quality")
assert "qaApi.listQualityChecks" in hook assert "qaApi.listQualityChecks" in hook
assert "qaApi.runQa" in hook assert "qaApi.runQa" in hook
@@ -41,9 +42,7 @@ def test_quality_workflow_hook_owns_quality_api_calls() -> None:
def test_app_still_wires_quality_results_and_export_center() -> None: def test_app_still_wires_quality_results_and_export_center() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
quality_panel = ( quality_panel = read_feature("quality")
ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx"
).read_text(encoding="utf-8")
assert "<QualityResultsPanel" in app assert "<QualityResultsPanel" in app
assert "onRefresh={() => loadQualityChecks()}" in app assert "onRefresh={() => loadQualityChecks()}" in app
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -18,7 +19,7 @@ def test_app_uses_dataset_workflow_hook() -> None:
def test_dataset_workflow_hook_owns_dataset_api_calls() -> None: def test_dataset_workflow_hook_owns_dataset_api_calls() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("datasets")
assert "datasetsApi.upload" in hook assert "datasetsApi.upload" in hook
assert "datasetsApi.getContent" in hook assert "datasetsApi.getContent" in hook
@@ -41,12 +42,8 @@ def test_dataset_workflow_hook_owns_dataset_api_calls() -> None:
def test_app_still_wires_dataset_ui_callbacks() -> None: def test_app_still_wires_dataset_ui_callbacks() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8" detail_panel = read_feature("datasets")
)
detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(
encoding="utf-8"
)
assert "<DatasetPanel" in app assert "<DatasetPanel" in app
assert "onUploadDataset={uploadDataset}" in app assert "onUploadDataset={uploadDataset}" in app
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -8,9 +9,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_dataset_presentational_components() -> None: def test_app_uses_dataset_presentational_components() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
inspector = ( inspector = read_feature("shell")
ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx"
).read_text(encoding="utf-8")
assert "from './components/datasets/DatasetPanel'" in app assert "from './components/datasets/DatasetPanel'" in app
assert "from './components/inspector/WorkbenchInspector'" in app assert "from './components/inspector/WorkbenchInspector'" in app
@@ -24,7 +23,7 @@ def test_app_uses_dataset_presentational_components() -> None:
def test_dataset_panel_owns_upload_and_list_markup() -> None: def test_dataset_panel_owns_upload_and_list_markup() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text(encoding="utf-8") panel = read_feature("datasets")
assert "Eigen bronbestand toevoegen" in panel assert "Eigen bronbestand toevoegen" in panel
assert "Metadata vernieuwen" in panel assert "Metadata vernieuwen" in panel
@@ -34,15 +33,9 @@ def test_dataset_panel_owns_upload_and_list_markup() -> None:
def test_dataset_detail_panel_composes_raster_and_vector_controls() -> None: def test_dataset_detail_panel_composes_raster_and_vector_controls() -> None:
detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text( detail_panel = read_feature("datasets")
encoding="utf-8" raster_controls = read_feature("datasets")
) vector_controls = read_feature("datasets")
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(
encoding="utf-8"
)
vector_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "VectorControls.tsx").read_text(
encoding="utf-8"
)
assert "<RasterControls" in detail_panel assert "<RasterControls" in detail_panel
assert "<VectorControls" in detail_panel assert "<VectorControls" in detail_panel
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -61,7 +62,7 @@ def test_demo_workflow_hook_owns_demo_api_and_cross_module_selection() -> None:
def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None: def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
hook = (ROOT / "frontend" / "src" / "hooks" / "useWorkbenchBootstrap.ts").read_text(encoding="utf-8") hook = read_feature("shell")
assert "loadProjects().catch(() => null)" not in app assert "loadProjects().catch(() => null)" not in app
assert "loadDetectionResults().catch(() => null)" not in app assert "loadDetectionResults().catch(() => null)" not in app
@@ -78,7 +79,7 @@ def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None:
def test_project_workspace_hook_owns_project_area_dataset_loading() -> None: def test_project_workspace_hook_owns_project_area_dataset_loading() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useProjectWorkspace.ts").read_text(encoding="utf-8") hook = read_feature("shell")
assert "DEMO_PROJECT_NAME = 'GeoIntel Demo - Building QA'" in hook assert "DEMO_PROJECT_NAME = 'GeoIntel Demo - Building QA'" in hook
assert "pickInitialProjectId" in hook assert "pickInitialProjectId" in hook
@@ -113,7 +114,7 @@ def test_change_detection_hook_owns_change_detection_api_calls() -> None:
def test_map_workspace_state_hook_owns_derived_map_state() -> None: def test_map_workspace_state_hook_owns_derived_map_state() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useMapWorkspaceState.ts").read_text(encoding="utf-8") hook = read_feature("map_workspace")
assert "areaFeatureCollection" in hook assert "areaFeatureCollection" in hook
assert "mapFeatureCollection" in hook assert "mapFeatureCollection" in hook
@@ -122,8 +123,8 @@ def test_map_workspace_state_hook_owns_derived_map_state() -> None:
def test_area_selection_fallbacks_live_with_owning_hooks() -> None: def test_area_selection_fallbacks_live_with_owning_hooks() -> None:
dataset_hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") dataset_hook = read_feature("datasets")
map_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapWorkspaceState.ts").read_text(encoding="utf-8") map_hook = read_feature("map_workspace")
assert "setSelectedClipAreaId(areas[0].id)" in dataset_hook assert "setSelectedClipAreaId(areas[0].id)" in dataset_hook
assert "setSelectedMapAreaId(areas[0].id)" in map_hook assert "setSelectedMapAreaId(areas[0].id)" in map_hook
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -13,18 +14,10 @@ def test_workbench_components_expose_stable_interaction_test_ids() -> None:
area_panel = (ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text( area_panel = (ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8" dataset_panel = read_feature("datasets")
) quality_panel = read_feature("quality")
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
quality_panel = (
ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx"
).read_text(encoding="utf-8")
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text(
encoding="utf-8"
)
assert 'data-testid="project-panel"' in project_panel assert 'data-testid="project-panel"' in project_panel
assert 'data-testid={`project-select-${project.id}`}' in project_panel assert 'data-testid={`project-select-${project.id}`}' in project_panel
@@ -1,21 +1,15 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_task_based_workbench_shell() -> None: def test_app_uses_task_based_workbench_shell() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
navigation = ( navigation = read_feature("shell")
ROOT
/ "frontend"
/ "src"
/ "components"
/ "shell"
/ "WorkbenchNavigation.tsx"
).read_text(encoding="utf-8")
assert "type WorkspaceKey" in app assert "type WorkspaceKey" in app
assert "workspaceNavItems" in app assert "workspaceNavItems" in app
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -13,9 +14,7 @@ def test_data_workspace_panels_use_operator_friendly_cards_and_forms() -> None:
area_panel = (ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text( area_panel = (ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "compact-form" in project_panel assert "compact-form" in project_panel
assert "entity-card-active" in project_panel assert "entity-card-active" in project_panel
@@ -28,18 +27,14 @@ def test_data_workspace_panels_use_operator_friendly_cards_and_forms() -> None:
def test_map_and_ai_workspaces_use_task_blocks_not_raw_stacks() -> None: def test_map_and_ai_workspaces_use_task_blocks_not_raw_stacks() -> None:
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
detection_lab = "\n".join( detection_lab = "\n".join(
( (
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(encoding="utf-8"),
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"),
) )
) )
segmentation_lab = ( segmentation_lab = read_feature("segmentation")
ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx"
).read_text(encoding="utf-8")
styles = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") styles = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert "map-toolbar" in map_workspace assert "map-toolbar" in map_workspace
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -19,9 +20,7 @@ def test_quality_panel_uses_workbench_summary_and_cards() -> None:
def test_export_center_uses_artifact_actions_and_cards() -> None: def test_export_center_uses_artifact_actions_and_cards() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
export_preview = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportPreview.tsx").read_text( export_preview = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportPreview.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
@@ -1,11 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_app_wires_tabbed_workbench_inspector() -> None: def test_app_wires_tabbed_workbench_inspector() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "from './components/inspector/WorkbenchInspector'" in app assert "from './components/inspector/WorkbenchInspector'" in app
assert "<WorkbenchInspector" in app assert "<WorkbenchInspector" in app
@@ -35,9 +36,7 @@ def test_workbench_inspector_exposes_context_dataset_quality_and_ai_tabs() -> No
def test_dataset_detail_props_remain_exported_for_inspector_reuse() -> None: def test_dataset_detail_props_remain_exported_for_inspector_reuse() -> None:
dataset_panel = ( dataset_panel = read_feature("datasets")
ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx"
).read_text(encoding="utf-8")
assert "export interface DatasetDetailPanelProps" in dataset_panel assert "export interface DatasetDetailPanelProps" in dataset_panel
assert 'className="dataset-detail-panel"' in dataset_panel assert 'className="dataset-detail-panel"' in dataset_panel
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_dataset_panel_exposes_map_and_export_quick_actions() -> None: def test_dataset_panel_exposes_map_and_export_quick_actions() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( panel = read_feature("datasets")
encoding="utf-8"
)
assert "selectedDatasetId" in panel assert "selectedDatasetId" in panel
assert "dataset-card-active" in panel assert "dataset-card-active" in panel
@@ -29,7 +28,7 @@ def test_data_workspace_keeps_catalog_wide_enough_for_populated_state() -> None:
def test_shell_preserves_workspace_width_on_standard_desktop_viewports() -> None: def test_shell_preserves_workspace_width_on_standard_desktop_viewports() -> None:
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "grid-template-columns: 12.5rem minmax(0, 1fr) 21rem" in css assert "grid-template-columns: 12.5rem minmax(0, 1fr) 21rem" in css
assert "@media (max-width: 1360px)" in css assert "@media (max-width: 1360px)" in css
@@ -40,7 +39,7 @@ def test_shell_preserves_workspace_width_on_standard_desktop_viewports() -> None
def test_app_wires_dataset_quick_actions_to_existing_workspaces() -> None: def test_app_wires_dataset_quick_actions_to_existing_workspaces() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "const openDatasetInMap = (dataset: DatasetCreateResponse) => {" in app assert "const openDatasetInMap = (dataset: DatasetCreateResponse) => {" in app
assert "loadDatasetDetails(selectedProjectId, dataset)" in app assert "loadDatasetDetails(selectedProjectId, dataset)" in app
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -11,14 +12,7 @@ def test_frontend_shell_has_atlas_workbench_contracts() -> None:
css = (ROOT / "frontend" / "src" / "styles" / "atlas-workbench.css").read_text( css = (ROOT / "frontend" / "src" / "styles" / "atlas-workbench.css").read_text(
encoding="utf-8" encoding="utf-8"
) )
navigation = ( navigation = read_feature("shell")
ROOT
/ "frontend"
/ "src"
/ "components"
/ "shell"
/ "WorkbenchNavigation.tsx"
).read_text(encoding="utf-8")
assert "workspace-command-bar" not in app assert "workspace-command-bar" not in app
assert "workspace-nav-cluster" not in app assert "workspace-nav-cluster" not in app
@@ -36,15 +30,9 @@ def test_primary_panels_use_empty_state_components() -> None:
project_panel = (ROOT / "frontend" / "src" / "components" / "project" / "ProjectPanel.tsx").read_text( project_panel = (ROOT / "frontend" / "src" / "components" / "project" / "ProjectPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8" detection_lab = read_feature("detection")
) segmentation_lab = read_feature("segmentation")
detection_lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(
encoding="utf-8"
)
segmentation_lab = (
ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx"
).read_text(encoding="utf-8")
assert "empty-state" in project_panel assert "empty-state" in project_panel
assert "empty-state" in dataset_panel assert "empty-state" in dataset_panel
@@ -1,16 +1,15 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_map_workspace_exposes_layer_provenance_and_feature_summary() -> None: def test_map_workspace_exposes_layer_provenance_and_feature_summary() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert "mapLayerSourceLabel" in app assert "mapLayerSourceLabel" in app
@@ -24,9 +23,7 @@ def test_map_workspace_exposes_layer_provenance_and_feature_summary() -> None:
def test_map_workspace_has_clear_empty_result_layer_guidance() -> None: def test_map_workspace_has_clear_empty_result_layer_guidance() -> None:
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
assert "Geen actieve vector- of resultaatlaag" in map_workspace assert "Geen actieve vector- of resultaatlaag" in map_workspace
assert "Open een databron, beeldanalyse, segmentatie of veranderingsresultaat om het hier te tekenen." in map_workspace assert "Open een databron, beeldanalyse, segmentatie of veranderingsresultaat om het hier te tekenen." in map_workspace
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_export_center_surfaces_handoff_readiness_context() -> None: def test_export_center_surfaces_handoff_readiness_context() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
assert 'className="handoff-summary-card"' in export_center assert 'className="handoff-summary-card"' in export_center
assert 'className="handoff-readiness-grid"' in export_center assert 'className="handoff-readiness-grid"' in export_center
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_map_empty_state_surfaces_ready_vector_dataset_actions() -> None: def test_map_empty_state_surfaces_ready_vector_dataset_actions() -> None:
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
assert "availableMapDatasets" in map_workspace assert "availableMapDatasets" in map_workspace
assert "onOpenDatasetInMap" in map_workspace assert "onOpenDatasetInMap" in map_workspace
@@ -18,7 +17,7 @@ def test_map_empty_state_surfaces_ready_vector_dataset_actions() -> None:
def test_app_passes_available_vector_datasets_to_map_workspace() -> None: def test_app_passes_available_vector_datasets_to_map_workspace() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "availableMapDatasets" in app assert "availableMapDatasets" in app
assert "datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready')" in app assert "datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready')" in app
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_dataset_panel_surfaces_role_summary_and_badges() -> None: def test_dataset_panel_surfaces_role_summary_and_badges() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "roleSummaries" in dataset_panel assert "roleSummaries" in dataset_panel
assert "dataset-role-summary-grid" in dataset_panel assert "dataset-role-summary-grid" in dataset_panel
@@ -23,9 +22,7 @@ def test_dataset_panel_surfaces_role_summary_and_badges() -> None:
def test_dataset_cards_expose_scan_friendly_source_and_crs_context() -> None: def test_dataset_cards_expose_scan_friendly_source_and_crs_context() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "dataset-card-kicker" in dataset_panel assert "dataset-card-kicker" in dataset_panel
assert "Bron: {dataset.source_name ?? dataset.source}" in dataset_panel assert "Bron: {dataset.source_name ?? dataset.source}" in dataset_panel
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_dataset_panel_explains_recommended_actions() -> None: def test_dataset_panel_explains_recommended_actions() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "datasetActionHint" in dataset_panel assert "datasetActionHint" in dataset_panel
assert "dataset-action-hint" in dataset_panel assert "dataset-action-hint" in dataset_panel
@@ -17,9 +16,7 @@ def test_dataset_panel_explains_recommended_actions() -> None:
def test_dataset_buttons_have_scan_friendly_action_copy() -> None: def test_dataset_buttons_have_scan_friendly_action_copy() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "dataset-action-grid" in dataset_panel assert "dataset-action-grid" in dataset_panel
assert "Bekijken" in dataset_panel assert "Bekijken" in dataset_panel
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -32,7 +33,7 @@ def test_quality_panel_surfaces_candidate_reference_handoff_summary() -> None:
def test_app_passes_quality_dataset_context() -> None: def test_app_passes_quality_dataset_context() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert "availableVectorDatasets.filter((item) => item.dataset_role !== 'reference')" in app assert "availableVectorDatasets.filter((item) => item.dataset_role !== 'reference')" in app
assert "candidateDatasets={candidateDatasets}" in app assert "candidateDatasets={candidateDatasets}" in app
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_quality_panel_promotes_core_metrics_before_raw_metric_list() -> None: def test_quality_panel_promotes_core_metrics_before_raw_metric_list() -> None:
quality_panel = ( quality_panel = read_feature("quality")
ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx"
).read_text(encoding="utf-8")
assert "CORE_METRIC_ORDER" in quality_panel assert "CORE_METRIC_ORDER" in quality_panel
assert "precision" in quality_panel assert "precision" in quality_panel
@@ -21,9 +20,7 @@ def test_quality_panel_promotes_core_metrics_before_raw_metric_list() -> None:
def test_quality_panel_renders_metric_evidence_cards_and_raw_metrics() -> None: def test_quality_panel_renders_metric_evidence_cards_and_raw_metrics() -> None:
quality_panel = ( quality_panel = read_feature("quality")
ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx"
).read_text(encoding="utf-8")
assert "quality-metric-grid" in quality_panel assert "quality-metric-grid" in quality_panel
assert "quality-metric-card" in quality_panel assert "quality-metric-card" in quality_panel
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_quality_panel_adds_result_filters_and_density_limit() -> None: def test_quality_panel_adds_result_filters_and_density_limit() -> None:
quality_panel = ( quality_panel = read_feature("quality")
ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx"
).read_text(encoding="utf-8")
assert "useState" in quality_panel assert "useState" in quality_panel
assert "qualityStatusFilter" in quality_panel assert "qualityStatusFilter" in quality_panel
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -20,12 +21,8 @@ def test_data_and_map_mobile_polish_css_contracts() -> None:
def test_data_and_map_components_keep_existing_workflow_markup() -> None: def test_data_and_map_components_keep_existing_workflow_markup() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8" map_workspace = read_feature("map_workspace")
)
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(
encoding="utf-8"
)
assert 'className="dataset-upload-form"' in dataset_panel assert 'className="dataset-upload-form"' in dataset_panel
assert 'className="file-input-label"' in dataset_panel assert 'className="file-input-label"' in dataset_panel
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -27,9 +28,7 @@ def test_detection_and_segmentation_keep_ai_lab_workflow_markup() -> None:
(ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"),
) )
) )
segmentation_lab = ( segmentation_lab = read_feature("segmentation")
ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx"
).read_text(encoding="utf-8")
for source in (detection_lab, segmentation_lab): for source in (detection_lab, segmentation_lab):
assert 'className="model-list"' in source assert 'className="model-list"' in source
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -23,9 +24,7 @@ def test_export_and_system_mobile_density_css_contracts() -> None:
def test_export_and_provider_components_keep_workflow_markup() -> None: def test_export_and_provider_components_keep_workflow_markup() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text( provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -24,18 +25,10 @@ def test_inspector_mobile_density_css_contracts() -> None:
def test_inspector_components_keep_mobile_tool_markup() -> None: def test_inspector_components_keep_mobile_tool_markup() -> None:
inspector = (ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx").read_text( inspector = read_feature("shell")
encoding="utf-8" dataset_detail = read_feature("datasets")
) raster_controls = read_feature("datasets")
dataset_detail = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text( vector_controls = read_feature("datasets")
encoding="utf-8"
)
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(
encoding="utf-8"
)
vector_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "VectorControls.tsx").read_text(
encoding="utf-8"
)
assert 'className="workbench-inspector-panel"' in inspector assert 'className="workbench-inspector-panel"' in inspector
assert 'className="inspector-action-bar"' in inspector assert 'className="inspector-action-bar"' in inspector
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -19,15 +20,8 @@ def test_global_focus_visible_contracts_are_defined() -> None:
def test_primary_navigation_has_keyboard_labels() -> None: def test_primary_navigation_has_keyboard_labels() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
navigation = ( navigation = read_feature("shell")
ROOT
/ "frontend"
/ "src"
/ "components"
/ "shell"
/ "WorkbenchNavigation.tsx"
).read_text(encoding="utf-8")
assert 'aria-label={`Open ${item.label}: ${item.description}`}' in navigation assert 'aria-label={`Open ${item.label}: ${item.description}`}' in navigation
assert "title={item.description}" in navigation assert "title={item.description}" in navigation
@@ -39,9 +33,7 @@ def test_primary_navigation_has_keyboard_labels() -> None:
def test_inspector_tabs_are_bound_to_tab_panels() -> None: def test_inspector_tabs_are_bound_to_tab_panels() -> None:
inspector = (ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx").read_text( inspector = read_feature("shell")
encoding="utf-8"
)
assert "const activeTabId = `inspector-tab-${activeTab}`" in inspector assert "const activeTabId = `inspector-tab-${activeTab}`" in inspector
assert "const activePanelId = `inspector-panel-${activeTab}`" in inspector assert "const activePanelId = `inspector-panel-${activeTab}`" in inspector
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -19,9 +20,7 @@ def test_dataset_operation_form_css_contracts() -> None:
def test_raster_controls_expose_readable_operation_groups() -> None: def test_raster_controls_expose_readable_operation_groups() -> None:
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( raster_controls = read_feature("datasets")
encoding="utf-8"
)
assert 'className="dataset-tool-heading"' in raster_controls assert 'className="dataset-tool-heading"' in raster_controls
assert 'className="dataset-tool-helper"' in raster_controls assert 'className="dataset-tool-helper"' in raster_controls
@@ -35,9 +34,7 @@ def test_raster_controls_expose_readable_operation_groups() -> None:
def test_vector_controls_expose_readable_operation_groups() -> None: def test_vector_controls_expose_readable_operation_groups() -> None:
vector_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "VectorControls.tsx").read_text( vector_controls = read_feature("datasets")
encoding="utf-8"
)
assert 'className="dataset-tool-heading"' in vector_controls assert 'className="dataset-tool-heading"' in vector_controls
assert 'className="dataset-tool-helper"' in vector_controls assert 'className="dataset-tool-helper"' in vector_controls
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -18,12 +19,8 @@ def test_result_state_css_contracts() -> None:
def test_quality_and_export_panels_use_result_state_blocks() -> None: def test_quality_and_export_panels_use_result_state_blocks() -> None:
quality_panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( quality_panel = read_feature("quality")
encoding="utf-8" export_center = read_feature("exports")
)
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text(
encoding="utf-8"
)
assert 'className="result-state result-state-error"' in quality_panel assert 'className="result-state result-state-error"' in quality_panel
assert 'className="result-state result-state-empty"' in quality_panel assert 'className="result-state result-state-empty"' in quality_panel
@@ -33,12 +30,8 @@ def test_quality_and_export_panels_use_result_state_blocks() -> None:
def test_ai_labs_use_result_state_blocks() -> None: def test_ai_labs_use_result_state_blocks() -> None:
detection_lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( detection_lab = read_feature("detection")
encoding="utf-8" segmentation_lab = read_feature("segmentation")
)
segmentation_lab = (
ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx"
).read_text(encoding="utf-8")
for content in (detection_lab, segmentation_lab): for content in (detection_lab, segmentation_lab):
assert 'className="result-state result-state-loading"' in content assert 'className="result-state result-state-loading"' in content
@@ -1,21 +1,15 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_workbench_shell_has_skip_link_and_main_focus_target() -> None: def test_workbench_shell_has_skip_link_and_main_focus_target() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
navigation = ( navigation = read_feature("shell")
ROOT
/ "frontend"
/ "src"
/ "components"
/ "shell"
/ "WorkbenchNavigation.tsx"
).read_text(encoding="utf-8")
assert 'className="skip-link"' in app assert 'className="skip-link"' in app
assert 'href="#workspace-main"' in app assert 'href="#workspace-main"' in app
@@ -1,13 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_overview_uses_named_hierarchy_regions() -> None: def test_overview_uses_named_hierarchy_regions() -> None:
app = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") app = read_feature("shell")
assert 'className="overview-action-copy"' in app assert 'className="overview-action-copy"' in app
assert 'className="quick-action-grid overview-quick-actions"' in app assert 'className="quick-action-grid overview-quick-actions"' in app
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -25,9 +26,7 @@ def test_project_and_area_panels_expose_selected_summary_regions() -> None:
def test_dataset_panel_separates_selected_upload_and_catalog_regions() -> None: def test_dataset_panel_separates_selected_upload_and_catalog_regions() -> None:
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( dataset_panel = read_feature("datasets")
encoding="utf-8"
)
assert "const selectedDataset = datasets.find" in dataset_panel assert "const selectedDataset = datasets.find" in dataset_panel
assert 'className="data-selection-summary data-selection-summary-dataset"' in dataset_panel assert 'className="data-selection-summary data-selection-summary-dataset"' in dataset_panel
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_map_workspace_exposes_structured_surfaces() -> None: def test_map_workspace_exposes_structured_surfaces() -> None:
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
assert 'className="map-workspace-shell"' in map_workspace assert 'className="map-workspace-shell"' in map_workspace
assert 'className="map-context-summary"' in map_workspace assert 'className="map-context-summary"' in map_workspace
@@ -21,9 +20,7 @@ def test_map_workspace_exposes_structured_surfaces() -> None:
def test_map_workspace_summary_uses_current_layer_and_selection_state() -> None: def test_map_workspace_summary_uses_current_layer_and_selection_state() -> None:
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( map_workspace = read_feature("map_workspace")
encoding="utf-8"
)
assert "const selectedMapArea = areas.find" in map_workspace assert "const selectedMapArea = areas.find" in map_workspace
assert "selectedMapArea?.name ?? 'Geen gebied geselecteerd'" in map_workspace assert "selectedMapArea?.name ?? 'Geen gebied geselecteerd'" in map_workspace
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_quality_results_panel_exposes_structured_surfaces() -> None: def test_quality_results_panel_exposes_structured_surfaces() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( panel = read_feature("quality")
encoding="utf-8"
)
assert "'quality-results-panel quality-results-panel-empty' : 'quality-results-panel'" in panel assert "'quality-results-panel quality-results-panel-empty' : 'quality-results-panel'" in panel
assert 'className="quality-results-shell"' in panel assert 'className="quality-results-shell"' in panel
@@ -26,9 +25,7 @@ def test_quality_results_panel_exposes_structured_surfaces() -> None:
def test_quality_results_panel_preserves_existing_refresh_filter_and_history_state() -> None: def test_quality_results_panel_preserves_existing_refresh_filter_and_history_state() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( panel = read_feature("quality")
encoding="utf-8"
)
assert "onRefresh" in panel assert "onRefresh" in panel
assert "qualityStatusFilter" in panel assert "qualityStatusFilter" in panel
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -27,9 +28,7 @@ def test_detection_lab_exposes_structured_surfaces() -> None:
def test_segmentation_lab_exposes_structured_surfaces() -> None: def test_segmentation_lab_exposes_structured_surfaces() -> None:
lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( lab = read_feature("segmentation")
encoding="utf-8"
)
assert 'className="workspace-panel ai-lab-shell segmentation-lab-shell"' in lab assert 'className="workspace-panel ai-lab-shell segmentation-lab-shell"' in lab
assert 'className="ai-lab-model-surface"' in lab assert 'className="ai-lab-model-surface"' in lab
@@ -44,12 +43,8 @@ def test_segmentation_lab_exposes_structured_surfaces() -> None:
def test_ai_lab_preserves_existing_detection_and_segmentation_controls() -> None: def test_ai_lab_preserves_existing_detection_and_segmentation_controls() -> None:
detection = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( detection = read_feature("detection")
encoding="utf-8" segmentation = read_feature("segmentation")
)
segmentation = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text(
encoding="utf-8"
)
assert "onRunDetection" in detection assert "onRunDetection" in detection
assert "onLoadResults" in detection assert "onLoadResults" in detection
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_export_center_exposes_final_handoff_surfaces() -> None: def test_export_center_exposes_final_handoff_surfaces() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
assert 'className="export-center"' in export_center assert 'className="export-center"' in export_center
assert 'className="export-center-shell"' in export_center assert 'className="export-center-shell"' in export_center
@@ -40,9 +39,7 @@ def test_provider_panel_exposes_final_system_surfaces() -> None:
def test_export_system_preserves_existing_workflow_controls() -> None: def test_export_system_preserves_existing_workflow_controls() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text( provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text(
encoding="utf-8" encoding="utf-8"
) )
@@ -1,13 +1,14 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_overview_exposes_end_to_end_workflow_guidance() -> None: def test_overview_exposes_end_to_end_workflow_guidance() -> None:
overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") overview = read_feature("shell")
assert 'aria-label="Voortgang van de werkstroom"' in overview assert 'aria-label="Voortgang van de werkstroom"' in overview
assert 'className="workflow-guidance-panel"' in overview assert 'className="workflow-guidance-panel"' in overview
@@ -21,8 +22,8 @@ def test_overview_exposes_end_to_end_workflow_guidance() -> None:
def test_workflow_guidance_routes_to_existing_workspaces() -> None: def test_workflow_guidance_routes_to_existing_workspaces() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") overview = read_feature("shell")
assert "target: 'data'" in overview assert "target: 'data'" in overview
assert "target: 'map'" in overview assert "target: 'map'" in overview
@@ -45,7 +46,7 @@ def test_workflow_guidance_has_responsive_contracts() -> None:
def test_workflow_guidance_complete_state_and_map_copy_are_precise() -> None: def test_workflow_guidance_complete_state_and_map_copy_are_precise() -> None:
overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") overview = read_feature("shell")
assert "workflowComplete" in overview assert "workflowComplete" in overview
assert "Klaar om te delen" in overview assert "Klaar om te delen" in overview
@@ -55,8 +56,8 @@ def test_workflow_guidance_complete_state_and_map_copy_are_precise() -> None:
def test_workflow_guidance_map_and_export_steps_reuse_dataset_context() -> None: def test_workflow_guidance_map_and_export_steps_reuse_dataset_context() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") overview = read_feature("shell")
assert "openWorkflowGuidanceStep" in app assert "openWorkflowGuidanceStep" in app
assert "target === 'map'" in app assert "target === 'map'" in app
@@ -1,14 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import assert_calls, assert_mentions, assert_wired from tests.frontend_contract import assert_calls, assert_mentions, assert_wired, read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_export_center_groups_latest_handoff_artifacts_by_type() -> None: def test_export_center_groups_latest_handoff_artifacts_by_type() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
assert "latestHandoffArtifacts" in export_center assert "latestHandoffArtifacts" in export_center
assert "handoff" in export_center.casefold() assert "handoff" in export_center.casefold()
@@ -23,9 +21,7 @@ def test_export_center_groups_latest_handoff_artifacts_by_type() -> None:
def test_export_center_latest_artifact_cards_keep_existing_actions() -> None: def test_export_center_latest_artifact_cards_keep_existing_actions() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( export_center = read_feature("exports")
encoding="utf-8"
)
assert "getLatestExportByType" in export_center assert "getLatestExportByType" in export_center
assert "const item = artifact.item" in export_center assert "const item = artifact.item" in export_center
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_quality_results_panel_exposes_selected_check_drilldown() -> None: def test_quality_results_panel_exposes_selected_check_drilldown() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( panel = read_feature("quality")
encoding="utf-8"
)
assert "selectedQualityCheckId" in panel assert "selectedQualityCheckId" in panel
assert "selectedQualityCheck" in panel assert "selectedQualityCheck" in panel
@@ -23,9 +22,7 @@ def test_quality_results_panel_exposes_selected_check_drilldown() -> None:
def test_quality_results_panel_surfaces_false_positive_negative_evidence() -> None: def test_quality_results_panel_surfaces_false_positive_negative_evidence() -> None:
panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( panel = read_feature("quality")
encoding="utf-8"
)
assert "Onterecht gevonden" in panel assert "Onterecht gevonden" in panel
assert "Gemiste objecten" in panel assert "Gemiste objecten" in panel
@@ -1,13 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_raster_controls_expose_pipeline_readiness_and_handoff() -> None: def test_raster_controls_expose_pipeline_readiness_and_handoff() -> None:
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( raster_controls = read_feature("datasets")
encoding="utf-8"
)
assert "rasterReadinessItems" in raster_controls assert "rasterReadinessItems" in raster_controls
assert "rasterGuardrailItems" in raster_controls assert "rasterGuardrailItems" in raster_controls
@@ -1,11 +1,12 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_dataset_workflow_selects_useful_default_dataset_after_project_load() -> None: def test_dataset_workflow_selects_useful_default_dataset_after_project_load() -> None:
hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("datasets")
assert "defaultDataset" in hook assert "defaultDataset" in hook
assert "!selectedDatasetId" in hook assert "!selectedDatasetId" in hook
@@ -15,12 +16,8 @@ def test_dataset_workflow_selects_useful_default_dataset_after_project_load() ->
def test_ai_labs_explain_missing_raster_input_before_disabled_runs() -> None: def test_ai_labs_explain_missing_raster_input_before_disabled_runs() -> None:
detection_lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( detection_lab = read_feature("detection")
encoding="utf-8" segmentation_lab = read_feature("segmentation")
)
segmentation_lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text(
encoding="utf-8"
)
assert "Geen luchtbeeld beschikbaar in deze werkruimte." in detection_lab assert "Geen luchtbeeld beschikbaar in deze werkruimte." in detection_lab
assert "Voeg hieronder een gegeorefereerde GeoTIFF toe." in detection_lab assert "Voeg hieronder een gegeorefereerde GeoTIFF toe." in detection_lab
@@ -1,4 +1,5 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
@@ -19,7 +20,7 @@ def test_demo_workflow_contract_includes_raster_fixture_dataset() -> None:
def test_demo_workflow_frontend_uses_raster_fixture_for_ai_labs() -> None: def test_demo_workflow_frontend_uses_raster_fixture_for_ai_labs() -> None:
types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8") types = (ROOT / "frontend" / "src" / "types.ts").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
hook = (ROOT / "frontend" / "src" / "hooks" / "useDemoWorkflow.ts").read_text(encoding="utf-8") hook = (ROOT / "frontend" / "src" / "hooks" / "useDemoWorkflow.ts").read_text(encoding="utf-8")
assert "raster_dataset_id?: string | null" in types assert "raster_dataset_id?: string | null" in types
@@ -1,14 +1,15 @@
from pathlib import Path from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2] ROOT = Path(__file__).resolve().parents[2]
def test_raster_tile_manifest_is_visible_and_can_handoff_to_detection_lab() -> None: def test_raster_tile_manifest_is_visible_and_can_handoff_to_detection_lab() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") app = read_feature("shell")
hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") hook = read_feature("datasets")
detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(encoding="utf-8") detail_panel = read_feature("datasets")
raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(encoding="utf-8") raster_controls = read_feature("datasets")
assert "latestRasterTileManifestPath" in hook assert "latestRasterTileManifestPath" in hook
assert "extractRasterTileManifestPath" in hook assert "extractRasterTileManifestPath" in hook