diff --git a/backend/tests/frontend_contract.py b/backend/tests/frontend_contract.py index 2fa9a7d7..a13d893b 100644 --- a/backend/tests/frontend_contract.py +++ b/backend/tests/frontend_contract.py @@ -22,20 +22,77 @@ ROOT = Path(__file__).resolve().parents[2] FRONTEND_SRC = ROOT / "frontend" / "src" -# The map workspace is one feature split across several modules: the container -# component, its domain layer and its pure helpers. A contract belongs to the -# feature, not to whichever file currently holds it, so splitting a 4.000-line -# component must not red the suite. -MAP_WORKSPACE_SOURCES = ( - "components/map/MapWorkspace.tsx", - "components/map/mapWorkspaceThemes.ts", - "components/map/mapWorkspaceUtils.ts", - "hooks/useMapImageOverlays.ts", - "hooks/useMapRectangleSelection.ts", - "hooks/useFullGisWorkflow.ts", - "components/map/MapExplorerView.tsx", - "components/map/MapAdvancedWorkbench.tsx", -) +# A feature is one behaviour spread over several modules: a container, its +# hooks, its domain layer, its pure helpers. A contract belongs to the feature, +# not to whichever file currently holds it, so moving code between siblings +# must not red the suite. Missing entries are skipped, so a group survives a +# module being split further, renamed or merged back. +# +# Use these for *positive* contracts ("this is wired"). A negative contract +# ("this component performs no transport") is a statement about one file and +# must keep reading that file, or widening it would quietly weaken the check. +FEATURE_SOURCES: dict[str, tuple[str, ...]] = { + "map_workspace": ( + "components/map/MapWorkspace.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: @@ -59,10 +116,22 @@ def read_frontend_area(*relative_paths: str) -> str: 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: """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: diff --git a/backend/tests/test_frontend_contract_test_style.py b/backend/tests/test_frontend_contract_test_style.py index c84e1010..92cb754a 100644 --- a/backend/tests/test_frontend_contract_test_style.py +++ b/backend/tests/test_frontend_contract_test_style.py @@ -73,3 +73,48 @@ def test_frontend_contract_helpers_are_available() -> None: assert_wired(source, "analyzeSelection") assert_calls(source, "analyzeSelection", first_argument="bbox") assert_mentions(source, "AREAIDFORSELECTION") + + +FRONTEND_READ = re.compile( + r'(?P\w+)\s*=\s*\(?\s*(?:ROOT|root|REPO_ROOT)\s*/\s*' + r'(?P"[^"]+"(?:\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)}" + ) diff --git a/backend/tests/test_rc4_national_coverage.py b/backend/tests/test_rc4_national_coverage.py index 6ed903eb..bce1fcd2 100644 --- a/backend/tests/test_rc4_national_coverage.py +++ b/backend/tests/test_rc4_national_coverage.py @@ -13,7 +13,7 @@ from app.models import Area, Dataset, Project from app.schemas.coverage import CoverageBBox from app.services.coverage_registry_service import CoverageRegistryService, THEMES, ZONES 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: @@ -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: root = Path(__file__).parents[2] 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") - coverage_hook = (root / "frontend" / "src" / "hooks" / "useCoverageResolver.ts").read_text(encoding="utf-8") + workspace_hook = read_feature("shell") + coverage_hook = read_feature("map_workspace") map_workspace = read_map_workspace() assert "Belgium and North Sea Workbench" in focus diff --git a/backend/tests/test_rc9_ux_release_contract.py b/backend/tests/test_rc9_ux_release_contract.py index f269adf7..112e0e1c 100644 --- a/backend/tests/test_rc9_ux_release_contract.py +++ b/backend/tests/test_rc9_ux_release_contract.py @@ -2,7 +2,7 @@ from __future__ import annotations import json 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] @@ -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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + app = read_feature("shell") map_workspace = read_map_workspace() geo_map = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text( encoding="utf-8" diff --git a/backend/tests/test_sprint100_segmentation_manifest_handoff.py b/backend/tests/test_sprint100_segmentation_manifest_handoff.py index 0a4d6eb1..a3d03314 100644 --- a/backend/tests/test_sprint100_segmentation_manifest_handoff.py +++ b/backend/tests/test_sprint100_segmentation_manifest_handoff.py @@ -1,17 +1,16 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_raster_tile_manifest_can_handoff_to_segmentation_lab() -> None: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - hook = (ROOT / "frontend" / "src" / "hooks" / "useSegmentationWorkflow.ts").read_text(encoding="utf-8") - detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(encoding="utf-8") - raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(encoding="utf-8") - segmentation_lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( - encoding="utf-8", - ) + app = read_feature("shell") + hook = read_feature("segmentation") + detail_panel = read_feature("datasets") + raster_controls = read_feature("datasets") + segmentation_lab = read_feature("segmentation") assert "segmentationTileManifestPath" in hook assert "setSegmentationTileManifestPath" in hook diff --git a/backend/tests/test_sprint103_ai_lab_run_readiness.py b/backend/tests/test_sprint103_ai_lab_run_readiness.py index cc60134d..9fd45f64 100644 --- a/backend/tests/test_sprint103_ai_lab_run_readiness.py +++ b/backend/tests/test_sprint103_ai_lab_run_readiness.py @@ -1,15 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_detection_lab_exposes_run_readiness_contract() -> None: - lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( - encoding="utf-8" - ) + lab = read_feature("detection") assert "selectedDetectionModel = detectionModels.find" 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: - lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( - encoding="utf-8" - ) + lab = read_feature("segmentation") assert "segmentationHasDataset" in lab assert "segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0" in lab diff --git a/backend/tests/test_sprint104_ai_lab_action_guardrails.py b/backend/tests/test_sprint104_ai_lab_action_guardrails.py index e8c37664..264d67c0 100644 --- a/backend/tests/test_sprint104_ai_lab_action_guardrails.py +++ b/backend/tests/test_sprint104_ai_lab_action_guardrails.py @@ -1,15 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_detection_lab_distinguishes_configured_model_from_ui_runnable_action() -> None: - lab = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( - encoding="utf-8" - ) + lab = read_feature("detection") assert "detectionModelUiRunnable" 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: - lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( - encoding="utf-8" - ) + lab = read_feature("segmentation") assert "segmentationModelUiRunnable" in lab assert "selectedSegmentationModelId !== 'fixture-segmenter'" in lab diff --git a/backend/tests/test_sprint105_map_feature_extract.py b/backend/tests/test_sprint105_map_feature_extract.py index c24fc2e8..5428208e 100644 --- a/backend/tests/test_sprint105_map_feature_extract.py +++ b/backend/tests/test_sprint105_map_feature_extract.py @@ -1,7 +1,7 @@ from __future__ import annotations 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] @@ -23,7 +23,7 @@ def test_map_workspace_exposes_feature_extract_actions() -> None: def test_geomap_highlights_selected_feature_layer() -> None: 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 "selected-feature" in geomap diff --git a/backend/tests/test_sprint106_map_bbox_extract.py b/backend/tests/test_sprint106_map_bbox_extract.py index b748c5aa..691c1652 100644 --- a/backend/tests/test_sprint106_map_bbox_extract.py +++ b/backend/tests/test_sprint106_map_bbox_extract.py @@ -11,7 +11,7 @@ from shapely.geometry import Polygon, box from app.core.errors import AppError from app.models import Dataset, VectorFeature 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] @@ -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: - api_client = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") + api_client = read_feature("datasets") map_workspace = read_map_workspace() geomap = (ROOT / "frontend" / "src" / "components" / "GeoMap.tsx").read_text(encoding="utf-8") - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - extract_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapSelectionExtract.ts").read_text(encoding="utf-8") - theme_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") + app = read_feature("shell") + extract_hook = read_feature("map_workspace") + theme_hook = read_feature("map_workspace") assert "selectVectorFeatures" in api_client assert "Area selection" in map_workspace diff --git a/backend/tests/test_sprint107_map_selection_export.py b/backend/tests/test_sprint107_map_selection_export.py index 3f32c96a..c19c93f2 100644 --- a/backend/tests/test_sprint107_map_selection_export.py +++ b/backend/tests/test_sprint107_map_selection_export.py @@ -16,6 +16,7 @@ from app.schemas.export import ExportCreateResponse from app.services.export_service import ExportService from app.services.storage_service import StorageService from app.services.vector_feature_service import VectorFeatureService +from tests.frontend_contract import read_feature 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: 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") - export_hook = (ROOT / "frontend" / "src" / "hooks" / "useExportWorkflow.ts").read_text(encoding="utf-8") - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + export_hook = read_feature("exports") + map_workspace = read_feature("map_workspace") + app = read_feature("shell") assert "'vector_selection'" in types assert "bbox?: VectorSelectionBBox" in exports_api diff --git a/backend/tests/test_sprint108_map_selection_derived_dataset.py b/backend/tests/test_sprint108_map_selection_derived_dataset.py index a4a54c59..72f6326a 100644 --- a/backend/tests/test_sprint108_map_selection_derived_dataset.py +++ b/backend/tests/test_sprint108_map_selection_derived_dataset.py @@ -12,6 +12,7 @@ from app.schemas.dataset import DatasetCreateResponse from app.services.storage_service import StorageService from app.services.vector_feature_service import VectorFeatureService from app.services.vector_operations_service import VectorOperationsService +from tests.frontend_contract import read_feature 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: 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") - 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") + datasets_api = read_feature("datasets") + app = read_feature("shell") + map_workspace = read_feature("map_workspace") assert "VectorSelectionDeriveRequest" in types assert "deriveVectorSelection" in datasets_api diff --git a/backend/tests/test_sprint109_map_selection_qa_shortcut.py b/backend/tests/test_sprint109_map_selection_qa_shortcut.py index 22a17513..7cd18e80 100644 --- a/backend/tests/test_sprint109_map_selection_qa_shortcut.py +++ b/backend/tests/test_sprint109_map_selection_qa_shortcut.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts" 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() hook = hook_path.read_text(encoding="utf-8") diff --git a/backend/tests/test_sprint112_qa_evidence_overlay.py b/backend/tests/test_sprint112_qa_evidence_overlay.py index 8361fa2c..d86c7851 100644 --- a/backend/tests/test_sprint112_qa_evidence_overlay.py +++ b/backend/tests/test_sprint112_qa_evidence_overlay.py @@ -12,6 +12,7 @@ from app.db.session import get_db from app.main import app from app.models import QualityCheck, VectorFeature from app.services.quality_evidence_service import QualityEvidenceService +from tests.frontend_contract import read_feature class FakeQuery: @@ -169,7 +170,7 @@ def test_frontend_quality_evidence_overlay_contract_is_wired() -> None: root = Path(__file__).resolve().parents[2] 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") assert "qaEvidenceData" in geo_map diff --git a/backend/tests/test_sprint116_operational_gis_map_workflow.py b/backend/tests/test_sprint116_operational_gis_map_workflow.py index ac86e118..65ecee52 100644 --- a/backend/tests/test_sprint116_operational_gis_map_workflow.py +++ b/backend/tests/test_sprint116_operational_gis_map_workflow.py @@ -1,5 +1,5 @@ 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] @@ -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: 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") assert "map-database-layer-select" in map_workspace diff --git a/backend/tests/test_sprint118_yolo_preflight_ui.py b/backend/tests/test_sprint118_yolo_preflight_ui.py index d1994cfc..07049ed9 100644 --- a/backend/tests/test_sprint118_yolo_preflight_ui.py +++ b/backend/tests/test_sprint118_yolo_preflight_ui.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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"), ) ) - 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") 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 "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"), ) ) - 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") 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( encoding="utf-8" ) diff --git a/backend/tests/test_sprint123_raster_detection_handoff_operational.py b/backend/tests/test_sprint123_raster_detection_handoff_operational.py index 45d1c8d7..cfaf7140 100644 --- a/backend/tests/test_sprint123_raster_detection_handoff_operational.py +++ b/backend/tests/test_sprint123_raster_detection_handoff_operational.py @@ -1,11 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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") 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: - controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( - encoding="utf-8" - ) + controls = read_feature("datasets") assert "latestRasterTileManifest" in controls assert "Aantal tegels" in controls diff --git a/backend/tests/test_sprint155_detection_operator_profiles.py b/backend/tests/test_sprint155_detection_operator_profiles.py index 260a3194..9bdf1913 100644 --- a/backend/tests/test_sprint155_detection_operator_profiles.py +++ b/backend/tests/test_sprint155_detection_operator_profiles.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: 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 "setSelectedDetectionModelId('yolo-configured')" in hook diff --git a/backend/tests/test_sprint169_long_context_name_readability.py b/backend/tests/test_sprint169_long_context_name_readability.py index 87f927fb..4c1faabf 100644 --- a/backend/tests/test_sprint169_long_context_name_readability.py +++ b/backend/tests/test_sprint169_long_context_name_readability.py @@ -1,11 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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 = ( ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx" ).read_text(encoding="utf-8") diff --git a/backend/tests/test_sprint177_mol_primary_focus.py b/backend/tests/test_sprint177_mol_primary_focus.py index ef10d034..72465d37 100644 --- a/backend/tests/test_sprint177_mol_primary_focus.py +++ b/backend/tests/test_sprint177_mol_primary_focus.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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( encoding="utf-8" ) - navigation = ( - ROOT - / "frontend" - / "src" - / "components" - / "shell" - / "WorkbenchNavigation.tsx" - ).read_text(encoding="utf-8") + navigation = read_feature("shell") assert "NATIONAL_WORKSPACE_PROJECT_NAME = 'Belgium and North Sea Workbench'" in focus assert "NATIONAL_WORKSPACE_REGION = 'Belgie en Belgische Noordzee'" in focus diff --git a/backend/tests/test_sprint180_premium_workbench.py b/backend/tests/test_sprint180_premium_workbench.py index 5c97fc99..6071acb4 100644 --- a/backend/tests/test_sprint180_premium_workbench.py +++ b/backend/tests/test_sprint180_premium_workbench.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - app = read("frontend/src/App.tsx") - inspector = read("frontend/src/components/inspector/WorkbenchInspector.tsx") + app = read_feature("shell") + inspector = read_feature("shell") assert "import './styles/premium.css'" 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: project = read("frontend/src/components/project/ProjectPanel.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") assert '
' 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: - map_workspace = read("frontend/src/components/map/MapWorkspace.tsx") + map_workspace = read_feature("map_workspace") css = read("frontend/src/styles/premium.css") 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"), ) ) - segmentation = read("frontend/src/components/segmentation/SegmentationLab.tsx") + segmentation = read_feature("segmentation") css = read("frontend/src/styles/premium.css") assert '
' in detection diff --git a/backend/tests/test_sprint181_mol_municipality_workspace.py b/backend/tests/test_sprint181_mol_municipality_workspace.py index c87cee37..83b7f6a5 100644 --- a/backend/tests/test_sprint181_mol_municipality_workspace.py +++ b/backend/tests/test_sprint181_mol_municipality_workspace.py @@ -10,6 +10,7 @@ from shapely.geometry import Polygon, shape from app.models import VectorFeature from app.services.vector_feature_service import VectorFeatureService +from tests.frontend_contract import read_feature 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") 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") - 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_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 "COPY scripts/provision_mol_municipality_workspace.py" in dockerfile diff --git a/backend/tests/test_sprint182_viewport_vector_delivery.py b/backend/tests/test_sprint182_viewport_vector_delivery.py index 7311346d..d985e910 100644 --- a/backend/tests/test_sprint182_viewport_vector_delivery.py +++ b/backend/tests/test_sprint182_viewport_vector_delivery.py @@ -4,6 +4,7 @@ import pytest from pydantic import ValidationError from app.schemas.operations import VectorSelectionBBox, VectorSelectionRequest +from tests.frontend_contract import read_feature 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: 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") - 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_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: - 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") - workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") + workspace = read_feature("map_workspace") assert "useViewportVectorLayer" in app assert "fitMapDataOnChange={!viewportVectorLayerActive}" in app diff --git a/backend/tests/test_sprint183_map_layer_source_mode.py b/backend/tests/test_sprint183_map_layer_source_mode.py index 295211a3..45f2e0d8 100644 --- a/backend/tests/test_sprint183_map_layer_source_mode.py +++ b/backend/tests/test_sprint183_map_layer_source_mode.py @@ -1,12 +1,13 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_map_source_mode_keeps_database_layers_distinct_from_analysis_results() -> None: - app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") - workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + workspace = read_feature("map_workspace") assert "const [mapContentMode, setMapContentMode]" in app assert "mapContentMode === 'analysis' && analysisMapLayerAvailable" in app diff --git a/backend/tests/test_sprint186_map_first_geographic_explorer.py b/backend/tests/test_sprint186_map_first_geographic_explorer.py index 2a7bee86..f6524800 100644 --- a/backend/tests/test_sprint186_map_first_geographic_explorer.py +++ b/backend/tests/test_sprint186_map_first_geographic_explorer.py @@ -3,7 +3,7 @@ from __future__ import annotations import re 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] @@ -14,7 +14,7 @@ def read(path: str) -> str: 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() assert "useState('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: - workflow = read("frontend/src/hooks/useDatasetWorkflow.ts") + workflow = read_feature("datasets") assert "const datasetDetailRequestSequence = useRef(0)" in workflow assert "const detailRequestId = ++datasetDetailRequestSequence.current" in workflow diff --git a/backend/tests/test_sprint189_kempen_scope.py b/backend/tests/test_sprint189_kempen_scope.py index 36e3a7dc..116af173 100644 --- a/backend/tests/test_sprint189_kempen_scope.py +++ b/backend/tests/test_sprint189_kempen_scope.py @@ -5,6 +5,7 @@ from pathlib import Path import sys from shapely.geometry import Polygon, shape +from tests.frontend_contract import read_feature 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"), ) ) - 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/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: - bootstrap = (ROOT / "frontend/src/hooks/useWorkbenchBootstrap.ts").read_text(encoding="utf-8") - project_workspace = (ROOT / "frontend/src/hooks/useProjectWorkspace.ts").read_text(encoding="utf-8") - map_state = (ROOT / "frontend/src/hooks/useMapWorkspaceState.ts").read_text(encoding="utf-8") - dataset_workflow = (ROOT / "frontend/src/hooks/useDatasetWorkflow.ts").read_text(encoding="utf-8") + bootstrap = read_feature("shell") + project_workspace = read_feature("shell") + map_state = read_feature("map_workspace") + dataset_workflow = read_feature("datasets") selected_project_branch = bootstrap.split("if (!selectedProjectId)", maxsplit=1)[1] assert "resetProjectData()" in selected_project_branch diff --git a/backend/tests/test_sprint192_regional_map_state.py b/backend/tests/test_sprint192_regional_map_state.py index ca6b3ca9..33dd6759 100644 --- a/backend/tests/test_sprint192_regional_map_state.py +++ b/backend/tests/test_sprint192_regional_map_state.py @@ -1,5 +1,5 @@ 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] @@ -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: - selection_hook = read("frontend/src/hooks/useMapSelectionExtract.ts") - themes_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") + selection_hook = read_feature("map_workspace") + themes_hook = read_feature("map_workspace") assert "const requestSequence = useRef(0)" in selection_hook assert "requestSequence.current += 1\n setMapSelectionBbox(null)" in selection_hook diff --git a/backend/tests/test_sprint193_end_user_workbench.py b/backend/tests/test_sprint193_end_user_workbench.py index ee37c816..40b208c6 100644 --- a/backend/tests/test_sprint193_end_user_workbench.py +++ b/backend/tests/test_sprint193_end_user_workbench.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - app = read("frontend/src/App.tsx") + app = read_feature("shell") for label in ("Kaart", "Bronnen", "Kwaliteit", "Beeldanalyse", "Downloads"): 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: projects = read("frontend/src/components/project/ProjectPanel.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") 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: - hook = read("frontend/src/hooks/useDetectionWorkflow.ts") - lab = read("frontend/src/components/detection/DetectionLab.tsx") + hook = read_feature("detection") + lab = read_feature("detection") assert "useState('yolo-configured')" 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: - profiles = read("frontend/src/components/detection/detectionProfiles.ts") - quality = read("frontend/src/components/quality/QualityResultsPanel.tsx") + profiles = read_feature("detection") + quality = read_feature("quality") export_preview = read("frontend/src/components/exports/ExportPreview.tsx") providers = read("frontend/src/components/providers/ProviderPanel.tsx") diff --git a/backend/tests/test_sprint194_regional_timeseries.py b/backend/tests/test_sprint194_regional_timeseries.py index 3bcf145d..7bd4a21e 100644 --- a/backend/tests/test_sprint194_regional_timeseries.py +++ b/backend/tests/test_sprint194_regional_timeseries.py @@ -16,6 +16,7 @@ from rasterio.transform import from_origin from app.models import Dataset from app.services.vector_feature_service import VectorFeatureService +from tests.frontend_contract import read_feature 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: 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") - catalog = (ROOT / "frontend/src/components/datasets/DatasetPanel.tsx").read_text(encoding="utf-8") + workspace = read_feature("map_workspace") + catalog = read_feature("datasets") 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") - exports = (ROOT / "frontend/src/components/exports/ExportCenter.tsx").read_text(encoding="utf-8") + detection = read_feature("detection") + exports = read_feature("exports") assert "department_omgeving_land_use: 'Departement Omgeving'" in display assert "statbel: 'Statbel'" in display diff --git a/backend/tests/test_sprint195_guided_detection_workflow.py b/backend/tests/test_sprint195_guided_detection_workflow.py index c2360257..0f8b4221 100644 --- a/backend/tests/test_sprint195_guided_detection_workflow.py +++ b/backend/tests/test_sprint195_guided_detection_workflow.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - hook = read("frontend/src/hooks/useDetectionWorkflow.ts") + hook = read_feature("detection") assert "prepareAndRunDetection" 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: - hook = read("frontend/src/hooks/useDetectionWorkflow.ts") + hook = read_feature("detection") assert "uploadDetectionRaster" 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: - lab = read("frontend/src/components/detection/DetectionLab.tsx") - app = read("frontend/src/App.tsx") + lab = read_feature("detection") + app = read_feature("shell") assert "Gebouwen zoeken en op kaart tonen" 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: - lab = read("frontend/src/components/detection/DetectionLab.tsx") - hook = read("frontend/src/hooks/useDetectionWorkflow.ts") + lab = read_feature("detection") + hook = read_feature("detection") assert 'aria-label="Kwaliteitscontrole gebouwdetectie"' 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: - app = read("frontend/src/App.tsx") + app = read_feature("shell") assert "analysisMapLayerActive && mapFeatureCollection" in app assert "`${mapLayerLabel} · controle vereist`" in app diff --git a/backend/tests/test_sprint196_map_orthophoto_analysis.py b/backend/tests/test_sprint196_map_orthophoto_analysis.py index 0ffafa7a..f96b4ea6 100644 --- a/backend/tests/test_sprint196_map_orthophoto_analysis.py +++ b/backend/tests/test_sprint196_map_orthophoto_analysis.py @@ -21,6 +21,7 @@ from app.main import app from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegistry, SourceSnapshot from app.schemas.orthophoto import OrthophotoAcquireRequest from app.services.orthophoto_acquisition_service import OrthophotoAcquisitionService +from tests.frontend_contract import read_feature 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: - 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") - 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 "onRunOrthophotoAnalysis={mapOrthophotoAnalysis.run}" in app_source diff --git a/backend/tests/test_sprint197_accuracy_review_loop.py b/backend/tests/test_sprint197_accuracy_review_loop.py index df0e68de..b85a4a8d 100644 --- a/backend/tests/test_sprint197_accuracy_review_loop.py +++ b/backend/tests/test_sprint197_accuracy_review_loop.py @@ -19,6 +19,7 @@ from app.schemas.detection_review import ( DetectionReviewUpsert, ) from app.services.detection_review_service import DetectionReviewService +from tests.frontend_contract import read_feature 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: 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") assert "MAP_BUILDING_QA_IOU_THRESHOLD = 0.25" in hook diff --git a/backend/tests/test_sprint19_map_workbench.py b/backend/tests/test_sprint19_map_workbench.py index e127457a..3606d702 100644 --- a/backend/tests/test_sprint19_map_workbench.py +++ b/backend/tests/test_sprint19_map_workbench.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - component = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + component = read_feature("map_workspace") assert "mapLayerVisible" in app assert "mapLayerOpacity" in app diff --git a/backend/tests/test_sprint201_semantic_selection_metrics.py b/backend/tests/test_sprint201_semantic_selection_metrics.py index 3b9024f0..e5adb31c 100644 --- a/backend/tests/test_sprint201_semantic_selection_metrics.py +++ b/backend/tests/test_sprint201_semantic_selection_metrics.py @@ -6,6 +6,7 @@ from uuid import uuid4 from app.models import Dataset from app.schemas.operations import VectorSelectionSummary from app.services.vector_feature_service import VectorFeatureService +from tests.frontend_contract import read_feature 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: 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") - 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 '"label": "Bebouwde grondoppervlakte"' in buildings diff --git a/backend/tests/test_sprint202_temporal_metrics_and_ollama.py b/backend/tests/test_sprint202_temporal_metrics_and_ollama.py index 8e10a44b..f05d13b3 100644 --- a/backend/tests/test_sprint202_temporal_metrics_and_ollama.py +++ b/backend/tests/test_sprint202_temporal_metrics_and_ollama.py @@ -12,6 +12,7 @@ from app.main import app from app.schemas.assistant import AssistantContextMetric, AssistantModelRead, AssistantQueryRequest, AssistantStatus, AssistantTemporalSeries from app.services.geo_assistant_service import GeoAssistantService from app.services.temporal_analysis_service import TemporalAnalysisService +from tests.frontend_contract import read_feature 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: - app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") - workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") - catalog = (ROOT / "frontend/src/components/datasets/SourceCatalogPanel.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + workspace = read_feature("map_workspace") + catalog = read_feature("datasets") assistant_hook = (ROOT / "frontend/src/hooks/useGeoAssistant.ts").read_text(encoding="utf-8") assert "SourceCatalogPanel" in app diff --git a/backend/tests/test_sprint203_waterinfo_history.py b/backend/tests/test_sprint203_waterinfo_history.py index 6ce43c66..f53ca1e8 100644 --- a/backend/tests/test_sprint203_waterinfo_history.py +++ b/backend/tests/test_sprint203_waterinfo_history.py @@ -5,6 +5,7 @@ import sys from pathlib import Path from shapely.geometry import box +from tests.frontend_contract import read_feature 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: - 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 "offset < (total ?? 0)" in client diff --git a/backend/tests/test_sprint204_bwk_natura2000.py b/backend/tests/test_sprint204_bwk_natura2000.py index 71277bc8..5953e551 100644 --- a/backend/tests/test_sprint204_bwk_natura2000.py +++ b/backend/tests/test_sprint204_bwk_natura2000.py @@ -13,7 +13,7 @@ from shapely.ops import transform as transform_geometry from app.models import Dataset from app.schemas.operations import VectorSelectionSummary 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] @@ -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") service = (ROOT / "backend" / "app" / "services" / "vector_feature_service.py").read_text(encoding="utf-8") 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 "py_compile scripts/provision_mol_bwk_natura2000.py" in readiness diff --git a/backend/tests/test_sprint205_agricultural_parcel_history.py b/backend/tests/test_sprint205_agricultural_parcel_history.py index c7c73d5a..1a64f0db 100644 --- a/backend/tests/test_sprint205_agricultural_parcel_history.py +++ b/backend/tests/test_sprint205_agricultural_parcel_history.py @@ -14,7 +14,7 @@ from shapely.ops import transform as transform_geometry from app.models import Dataset from app.schemas.operations import VectorSelectionSummary 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] @@ -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") readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(encoding="utf-8") 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") assert "/datasets/upload" in operator diff --git a/backend/tests/test_sprint205_dhmv_terrain.py b/backend/tests/test_sprint205_dhmv_terrain.py index 3c714b79..a8c483b4 100644 --- a/backend/tests/test_sprint205_dhmv_terrain.py +++ b/backend/tests/test_sprint205_dhmv_terrain.py @@ -21,7 +21,7 @@ from app.models import Area, Dataset, DatasetVersion, Job, Project, SourceRegist from app.schemas.dhmv import DhmvAcquireRequest, TerrainPartitionSelectionRequest, TerrainSelectionRequest from app.services.dhmv_acquisition_service import DhmvAcquisitionService 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] @@ -618,8 +618,8 @@ def test_frontend_and_runtime_expose_dhmv_workflow() -> None: ROOT / "frontend" / "src" / "lib" / "datasetCapabilities.ts" ).read_text(encoding="utf-8") map_source = read_map_workspace() - hook_source = (ROOT / "frontend" / "src" / "hooks" / "useMapSelectionExtract.ts").read_text(encoding="utf-8") - service_source = (ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") + hook_source = read_feature("map_workspace") + service_source = read_feature("datasets") assert "digitaal_vlaanderen_dhmv" in capabilities_source assert "isMapRasterDataset" in capabilities_source diff --git a/backend/tests/test_sprint206_buildings_addresses_register.py b/backend/tests/test_sprint206_buildings_addresses_register.py index 905036a2..a6c021e7 100644 --- a/backend/tests/test_sprint206_buildings_addresses_register.py +++ b/backend/tests/test_sprint206_buildings_addresses_register.py @@ -12,7 +12,7 @@ from shapely.ops import transform as transform_geometry from app.models import Dataset from app.schemas.operations import VectorSelectionSummary 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] @@ -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") readiness = (ROOT / "scripts/run_readiness_check.sh").read_text(encoding="utf-8") 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") assert "/datasets/upload" in operator diff --git a/backend/tests/test_sprint209_regional_historical_landuse.py b/backend/tests/test_sprint209_regional_historical_landuse.py index d947d4f9..1b86d291 100644 --- a/backend/tests/test_sprint209_regional_historical_landuse.py +++ b/backend/tests/test_sprint209_regional_historical_landuse.py @@ -7,6 +7,7 @@ from pathlib import Path import sys from shapely.geometry import box, mapping, shape +from tests.frontend_contract import read_feature 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: 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") - app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") - workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + workspace = read_feature("map_workspace") assert "COPY scripts/provision_regional_historical_landuse.py" in dockerfile assert "py_compile scripts/provision_regional_historical_landuse.py" in readiness diff --git a/backend/tests/test_sprint20_area_map_overlay.py b/backend/tests/test_sprint20_area_map_overlay.py index 0301d848..57bd67b8 100644 --- a/backend/tests/test_sprint20_area_map_overlay.py +++ b/backend/tests/test_sprint20_area_map_overlay.py @@ -7,6 +7,7 @@ from shapely.geometry import MultiPolygon, Polygon from app.models import Area 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: @@ -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: 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") - 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") assert "selectedMapAreaId" in app diff --git a/backend/tests/test_sprint210_regional_bwk_natura2000.py b/backend/tests/test_sprint210_regional_bwk_natura2000.py index d4a13838..aaafe13b 100644 --- a/backend/tests/test_sprint210_regional_bwk_natura2000.py +++ b/backend/tests/test_sprint210_regional_bwk_natura2000.py @@ -11,7 +11,7 @@ from shapely.geometry import box, mapping, shape from app.models import Dataset 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] @@ -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") service = (ROOT / "backend/app/services/vector_feature_service.py").read_text(encoding="utf-8") 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") migration = ( ROOT / "backend/alembic/versions/202607160001_vector_feature_municipality_index.py" diff --git a/backend/tests/test_sprint212_platform_source_portfolio.py b/backend/tests/test_sprint212_platform_source_portfolio.py index bfb27be1..7be1beb3 100644 --- a/backend/tests/test_sprint212_platform_source_portfolio.py +++ b/backend/tests/test_sprint212_platform_source_portfolio.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - 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") assert "Welke vragen kan GeoIntel beantwoorden?" in catalog diff --git a/backend/tests/test_sprint219_regional_raster_explorer.py b/backend/tests/test_sprint219_regional_raster_explorer.py index cfabf6fe..521c4a1d 100644 --- a/backend/tests/test_sprint219_regional_raster_explorer.py +++ b/backend/tests/test_sprint219_regional_raster_explorer.py @@ -1,5 +1,5 @@ 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] @@ -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: - app = (ROOT / "frontend/src/App.tsx").read_text(encoding="utf-8") + app = read_feature("shell") workspace = read_map_workspace() - hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") - api = (ROOT / "frontend/src/services/api/datasets.ts").read_text(encoding="utf-8") + hook = read_feature("map_workspace") + api = read_feature("datasets") assert "regionalScopeSelected" in workspace assert "rasterPartitionsForDataset" in workspace diff --git a/backend/tests/test_sprint221_source_freshness_audit.py b/backend/tests/test_sprint221_source_freshness_audit.py index caff5ed8..29c29ab0 100644 --- a/backend/tests/test_sprint221_source_freshness_audit.py +++ b/backend/tests/test_sprint221_source_freshness_audit.py @@ -7,6 +7,7 @@ from types import SimpleNamespace from app.models import Dataset, DatasetVersion 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) @@ -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") 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") - app = (root / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") - api = (root / "frontend" / "src" / "services" / "api" / "datasets.ts").read_text(encoding="utf-8") + app = read_feature("shell") + api = read_feature("datasets") assert "Request(endpoint" in script assert "method=\"POST\"" not in script diff --git a/backend/tests/test_sprint22_workbench_status_strip.py b/backend/tests/test_sprint22_workbench_status_strip.py index 99fd8a65..6d4e57ef 100644 --- a/backend/tests/test_sprint22_workbench_status_strip.py +++ b/backend/tests/test_sprint22_workbench_status_strip.py @@ -1,14 +1,15 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_frontend_wires_v1_workbench_status_strip() -> None: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + overview = read_feature("shell") component = (ROOT / "frontend" / "src" / "components" / "WorkbenchStatusStrip.tsx").read_text(encoding="utf-8") css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") diff --git a/backend/tests/test_sprint232_v1_completion_flow.py b/backend/tests/test_sprint232_v1_completion_flow.py index dd7b1b7f..ca3d679d 100644 --- a/backend/tests/test_sprint232_v1_completion_flow.py +++ b/backend/tests/test_sprint232_v1_completion_flow.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - workspace = read("frontend/src/components/map/MapWorkspace.tsx") + workspace = read_feature("map_workspace") styles = read("frontend/src/styles/app.css") 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: - workspace = read("frontend/src/components/map/MapWorkspace.tsx") + workspace = read_feature("map_workspace") assert "activeTheme.id}-analysis.json" 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: 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") assert "onOpenAssistant: () => void" in workspace diff --git a/backend/tests/test_sprint233_operational_completion.py b/backend/tests/test_sprint233_operational_completion.py index f8f2d11f..8e575f2a 100644 --- a/backend/tests/test_sprint233_operational_completion.py +++ b/backend/tests/test_sprint233_operational_completion.py @@ -20,7 +20,7 @@ from app.services.storage_service import StorageService from app.services.source_registry_service import SourceRegistryService from app.services.temporal_analysis_service import TemporalAnalysisService 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: @@ -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: root = Path(__file__).resolve().parents[2] 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") 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: 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") 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: 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]?.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: 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(null)" 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: root = Path(__file__).resolve().parents[2] - quality = (root / "frontend/src/components/quality/QualityResultsPanel.tsx").read_text(encoding="utf-8") - detection = (root / "frontend/src/components/detection/DetectionLab.tsx").read_text(encoding="utf-8") + quality = read_feature("quality") + detection = read_feature("detection") assert "Laatste score (0-1)" 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: 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") 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: 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 "Historische vergelijking (JSON)" in exports diff --git a/backend/tests/test_sprint234_project_lifecycle_cleanup.py b/backend/tests/test_sprint234_project_lifecycle_cleanup.py index 8bab74da..bd6bd0b0 100644 --- a/backend/tests/test_sprint234_project_lifecycle_cleanup.py +++ b/backend/tests/test_sprint234_project_lifecycle_cleanup.py @@ -15,6 +15,7 @@ from app.api.routes import projects as project_routes from app.main import app from app.schemas.project import ProjectRead, ProjectUpdate from app.services.project_service import ProjectService +from tests.frontend_contract import read_feature 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: - 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") - 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") - map_workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") + detection_lab = read_feature("detection") + segmentation_lab = read_feature("segmentation") + map_workspace = read_feature("map_workspace") premium_css = (ROOT / "frontend/src/styles/premium.css").read_text(encoding="utf-8") assert "OverviewWorkspace" in app_source diff --git a/backend/tests/test_sprint236_bathymetry_expansion.py b/backend/tests/test_sprint236_bathymetry_expansion.py index 1967abd1..7e8e8369 100644 --- a/backend/tests/test_sprint236_bathymetry_expansion.py +++ b/backend/tests/test_sprint236_bathymetry_expansion.py @@ -31,7 +31,7 @@ if str(SCRIPTS) not in sys.path: sys.path.insert(0, str(SCRIPTS)) 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: @@ -433,9 +433,7 @@ def test_frontend_bounds_large_area_and_dataset_catalogs() -> None: area_panel = ( ROOT / "frontend" / "src" / "components" / "project" / "AreaPanel.tsx" ).read_text(encoding="utf-8") - dataset_panel = ( - ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx" - ).read_text(encoding="utf-8") + dataset_panel = read_feature("datasets") assert "const AREA_CATALOG_PAGE_SIZE = 12" 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: - 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") focus = ( ROOT / "frontend" / "src" / "config" / "primaryFocus.ts" ).read_text(encoding="utf-8") map_workspace = read_map_workspace() - theme_hook = ( - ROOT / "frontend" / "src" / "hooks" / "useMapThemeSelectionInsights.ts" - ).read_text(encoding="utf-8") - dataset_api = ( - ROOT / "frontend" / "src" / "services" / "api" / "datasets.ts" - ).read_text(encoding="utf-8") + theme_hook = read_feature("map_workspace") + dataset_api = read_feature("datasets") assert "isPartitionedBathymetry" in map_workspace assert "regionalPartitionedThemeActive" in map_workspace diff --git a/backend/tests/test_sprint237_flanders_thematic_on_demand.py b/backend/tests/test_sprint237_flanders_thematic_on_demand.py index f9afc37f..03965a09 100644 --- a/backend/tests/test_sprint237_flanders_thematic_on_demand.py +++ b/backend/tests/test_sprint237_flanders_thematic_on_demand.py @@ -1,5 +1,5 @@ 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] @@ -11,9 +11,9 @@ def read(path: str) -> str: def test_flanders_workspace_exposes_governed_thematic_products_on_demand() -> None: workspace = read_map_workspace() - product_hook = read("frontend/src/hooks/useOfficialMapProducts.ts") - selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") - api = read("frontend/src/services/api/datasets.ts") + product_hook = read_feature("map_workspace") + selection_hook = read_feature("map_workspace") + api = read_feature("datasets") assert "activeScopeProject?.name === FLANDERS_WORKSPACE_PROJECT_NAME" in workspace assert "new Map" 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: workspace = read_map_workspace() - app = read("frontend/src/App.tsx") - selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") + app = read_feature("shell") + selection_hook = read_feature("map_workspace") # Selection reads the active theme; the loop form is incidental. assert_wired(workspace, "activeTheme") diff --git a/backend/tests/test_sprint238_flanders_raster_catalogs.py b/backend/tests/test_sprint238_flanders_raster_catalogs.py index d37b7824..ff1adea0 100644 --- a/backend/tests/test_sprint238_flanders_raster_catalogs.py +++ b/backend/tests/test_sprint238_flanders_raster_catalogs.py @@ -2,6 +2,7 @@ from pathlib import Path from app.schemas.flood_hazard import FloodHazardAcquireRequest from app.schemas.operations import VectorSelectionBBox +from tests.frontend_contract import read_feature 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: - hook = read("frontend/src/hooks/useOfficialMapProducts.ts") + hook = read_feature("map_workspace") assert "datasetsApi.listThematicRasterProducts" 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: - selection_hook = read("frontend/src/hooks/useMapThemeSelectionInsights.ts") - workspace = read("frontend/src/components/map/MapWorkspace.tsx") + selection_hook = read_feature("map_workspace") + workspace = read_feature("map_workspace") for acquisition_kind in ( "'thematic_raster'", diff --git a/backend/tests/test_sprint239_bounded_grb_acquisition.py b/backend/tests/test_sprint239_bounded_grb_acquisition.py index 76a27b86..f7e4e719 100644 --- a/backend/tests/test_sprint239_bounded_grb_acquisition.py +++ b/backend/tests/test_sprint239_bounded_grb_acquisition.py @@ -19,7 +19,7 @@ from app.models import Area, Dataset, Job, Project from app.schemas.grb import GrbAcquireRequest from app.services.dataset_service import DatasetService 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] @@ -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: - selection_hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") - catalog_hook = (ROOT / "frontend/src/hooks/useOfficialMapProducts.ts").read_text(encoding="utf-8") + selection_hook = read_feature("map_workspace") + catalog_hook = read_feature("map_workspace") workspace = read_map_workspace() contracts = (ROOT / "docs/API_CONTRACTS.md").read_text(encoding="utf-8") diff --git a/backend/tests/test_sprint240_official_flemish_themes.py b/backend/tests/test_sprint240_official_flemish_themes.py index 8e6eb22e..d195acdb 100644 --- a/backend/tests/test_sprint240_official_flemish_themes.py +++ b/backend/tests/test_sprint240_official_flemish_themes.py @@ -23,6 +23,7 @@ from app.schemas.official_vector import OfficialVectorAcquireRequest from app.services.dataset_service import DatasetService from app.services.official_vector_acquisition_service import OfficialVectorAcquisitionService from app.services.thematic_raster_acquisition_service import ThematicRasterAcquisitionService +from tests.frontend_contract import read_feature 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 any(isinstance(item, Job) for item in db.added) - selection_hook = (ROOT / "frontend/src/hooks/useMapThemeSelectionInsights.ts").read_text(encoding="utf-8") - catalog_hook = (ROOT / "frontend/src/hooks/useOfficialMapProducts.ts").read_text(encoding="utf-8") + selection_hook = read_feature("map_workspace") + catalog_hook = read_feature("map_workspace") workspace = (ROOT / "frontend/src/components/map/MapWorkspace.tsx").read_text(encoding="utf-8") assert "datasetsApi.acquireOfficialVector" in selection_hook assert "datasetsApi.listOfficialVectorProducts" in catalog_hook diff --git a/backend/tests/test_sprint26_frontend_workflow_hooks.py b/backend/tests/test_sprint26_frontend_workflow_hooks.py index 731846e6..2e47b9d5 100644 --- a/backend/tests/test_sprint26_frontend_workflow_hooks.py +++ b/backend/tests/test_sprint26_frontend_workflow_hooks.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts").read_text(encoding="utf-8") + hook = read_feature("detection") assert "detectionApi.listModels" 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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useSegmentationWorkflow.ts").read_text(encoding="utf-8") + hook = read_feature("segmentation") assert "segmentationApi.listModels" in hook assert "segmentationApi.listRuns" in hook diff --git a/backend/tests/test_sprint27_frontend_workflow_hooks.py b/backend/tests/test_sprint27_frontend_workflow_hooks.py index f0666aae..00d19b97 100644 --- a/backend/tests/test_sprint27_frontend_workflow_hooks.py +++ b/backend/tests/test_sprint27_frontend_workflow_hooks.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useExportWorkflow.ts").read_text(encoding="utf-8") + hook = read_feature("exports") assert "exportsApi.listProjectExports" 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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useQualityWorkflow.ts").read_text(encoding="utf-8") + hook = read_feature("quality") assert "qaApi.listQualityChecks" 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: app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - quality_panel = ( - ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" - ).read_text(encoding="utf-8") + quality_panel = read_feature("quality") assert " loadQualityChecks()}" in app diff --git a/backend/tests/test_sprint28_dataset_workflow_hook.py b/backend/tests/test_sprint28_dataset_workflow_hook.py index 58104bd6..9a8b6d32 100644 --- a/backend/tests/test_sprint28_dataset_workflow_hook.py +++ b/backend/tests/test_sprint28_dataset_workflow_hook.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") + hook = read_feature("datasets") assert "datasetsApi.upload" 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: app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) - detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") + detail_panel = read_feature("datasets") assert " None: app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - inspector = ( - ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx" - ).read_text(encoding="utf-8") + inspector = read_feature("shell") assert "from './components/datasets/DatasetPanel'" 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: - panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text(encoding="utf-8") + panel = read_feature("datasets") assert "Eigen bronbestand toevoegen" 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: - detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text( - 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" - ) + detail_panel = read_feature("datasets") + raster_controls = read_feature("datasets") + vector_controls = read_feature("datasets") assert " None: def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None: 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 "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: - 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 "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: - hook = (ROOT / "frontend" / "src" / "hooks" / "useMapWorkspaceState.ts").read_text(encoding="utf-8") + hook = read_feature("map_workspace") assert "areaFeatureCollection" 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: - dataset_hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") - map_hook = (ROOT / "frontend" / "src" / "hooks" / "useMapWorkspaceState.ts").read_text(encoding="utf-8") + dataset_hook = read_feature("datasets") + map_hook = read_feature("map_workspace") assert "setSelectedClipAreaId(areas[0].id)" in dataset_hook assert "setSelectedMapAreaId(areas[0].id)" in map_hook diff --git a/backend/tests/test_sprint47_workbench_interaction_smoke.py b/backend/tests/test_sprint47_workbench_interaction_smoke.py index 8596bf2a..7ec9bd29 100644 --- a/backend/tests/test_sprint47_workbench_interaction_smoke.py +++ b/backend/tests/test_sprint47_workbench_interaction_smoke.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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( encoding="utf-8" ) - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - 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" - ) + map_workspace = read_feature("map_workspace") + dataset_panel = read_feature("datasets") + quality_panel = read_feature("quality") + export_center = read_feature("exports") assert 'data-testid="project-panel"' in project_panel assert 'data-testid={`project-select-${project.id}`}' in project_panel diff --git a/backend/tests/test_sprint49_workbench_shell_refactor.py b/backend/tests/test_sprint49_workbench_shell_refactor.py index cf41339a..6548ecf1 100644 --- a/backend/tests/test_sprint49_workbench_shell_refactor.py +++ b/backend/tests/test_sprint49_workbench_shell_refactor.py @@ -1,21 +1,15 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_app_uses_task_based_workbench_shell() -> None: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - navigation = ( - ROOT - / "frontend" - / "src" - / "components" - / "shell" - / "WorkbenchNavigation.tsx" - ).read_text(encoding="utf-8") + app = read_feature("shell") + navigation = read_feature("shell") assert "type WorkspaceKey" in app assert "workspaceNavItems" in app diff --git a/backend/tests/test_sprint50_workspace_usability_polish.py b/backend/tests/test_sprint50_workspace_usability_polish.py index edf4c7d9..f0f29ac6 100644 --- a/backend/tests/test_sprint50_workspace_usability_polish.py +++ b/backend/tests/test_sprint50_workspace_usability_polish.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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( encoding="utf-8" ) - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "compact-form" 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: - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + map_workspace = read_feature("map_workspace") detection_lab = "\n".join( ( (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text(encoding="utf-8"), (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionModelManagement.tsx").read_text(encoding="utf-8"), ) ) - segmentation_lab = ( - ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx" - ).read_text(encoding="utf-8") + segmentation_lab = read_feature("segmentation") styles = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") assert "map-toolbar" in map_workspace diff --git a/backend/tests/test_sprint51_quality_export_polish.py b/backend/tests/test_sprint51_quality_export_polish.py index 77001efb..6b7b724d 100644 --- a/backend/tests/test_sprint51_quality_export_polish.py +++ b/backend/tests/test_sprint51_quality_export_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") export_preview = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportPreview.tsx").read_text( encoding="utf-8" ) diff --git a/backend/tests/test_sprint52_workbench_inspector_tabs.py b/backend/tests/test_sprint52_workbench_inspector_tabs.py index 9410ace9..54b4f2f9 100644 --- a/backend/tests/test_sprint52_workbench_inspector_tabs.py +++ b/backend/tests/test_sprint52_workbench_inspector_tabs.py @@ -1,11 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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 " No def test_dataset_detail_props_remain_exported_for_inspector_reuse() -> None: - dataset_panel = ( - ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx" - ).read_text(encoding="utf-8") + dataset_panel = read_feature("datasets") assert "export interface DatasetDetailPanelProps" in dataset_panel assert 'className="dataset-detail-panel"' in dataset_panel diff --git a/backend/tests/test_sprint53_selection_ergonomics.py b/backend/tests/test_sprint53_selection_ergonomics.py index 4569adc3..6bd9993f 100644 --- a/backend/tests/test_sprint53_selection_ergonomics.py +++ b/backend/tests/test_sprint53_selection_ergonomics.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_dataset_panel_exposes_map_and_export_quick_actions() -> None: - panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + panel = read_feature("datasets") assert "selectedDatasetId" 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: 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 "@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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + app = read_feature("shell") assert "const openDatasetInMap = (dataset: DatasetCreateResponse) => {" in app assert "loadDatasetDetails(selectedProjectId, dataset)" in app diff --git a/backend/tests/test_sprint62_frontend_visual_polish.py b/backend/tests/test_sprint62_frontend_visual_polish.py index ea28ba64..4d844a88 100644 --- a/backend/tests/test_sprint62_frontend_visual_polish.py +++ b/backend/tests/test_sprint62_frontend_visual_polish.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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( encoding="utf-8" ) - navigation = ( - ROOT - / "frontend" - / "src" - / "components" - / "shell" - / "WorkbenchNavigation.tsx" - ).read_text(encoding="utf-8") + navigation = read_feature("shell") assert "workspace-command-bar" 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( encoding="utf-8" ) - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) - 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") + dataset_panel = read_feature("datasets") + detection_lab = read_feature("detection") + segmentation_lab = read_feature("segmentation") assert "empty-state" in project_panel assert "empty-state" in dataset_panel diff --git a/backend/tests/test_sprint63_map_overlay_ergonomics.py b/backend/tests/test_sprint63_map_overlay_ergonomics.py index 702b9adb..a0c1d6da 100644 --- a/backend/tests/test_sprint63_map_overlay_ergonomics.py +++ b/backend/tests/test_sprint63_map_overlay_ergonomics.py @@ -1,16 +1,15 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_map_workspace_exposes_layer_provenance_and_feature_summary() -> None: - 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" - ) + app = read_feature("shell") + map_workspace = read_feature("map_workspace") css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8") 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: - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + map_workspace = read_feature("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 diff --git a/backend/tests/test_sprint64_export_handoff_polish.py b/backend/tests/test_sprint64_export_handoff_polish.py index f3ac9375..f9b8dcc3 100644 --- a/backend/tests/test_sprint64_export_handoff_polish.py +++ b/backend/tests/test_sprint64_export_handoff_polish.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_export_center_surfaces_handoff_readiness_context() -> None: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") assert 'className="handoff-summary-card"' in export_center assert 'className="handoff-readiness-grid"' in export_center diff --git a/backend/tests/test_sprint67_map_empty_state_quick_actions.py b/backend/tests/test_sprint67_map_empty_state_quick_actions.py index 94ae6442..accc8473 100644 --- a/backend/tests/test_sprint67_map_empty_state_quick_actions.py +++ b/backend/tests/test_sprint67_map_empty_state_quick_actions.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_map_empty_state_surfaces_ready_vector_dataset_actions() -> None: - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + map_workspace = read_feature("map_workspace") assert "availableMapDatasets" 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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") + app = read_feature("shell") assert "availableMapDatasets" in app assert "datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready')" in app diff --git a/backend/tests/test_sprint68_dataset_catalog_density.py b/backend/tests/test_sprint68_dataset_catalog_density.py index f7fc10d5..61386afd 100644 --- a/backend/tests/test_sprint68_dataset_catalog_density.py +++ b/backend/tests/test_sprint68_dataset_catalog_density.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_dataset_panel_surfaces_role_summary_and_badges() -> None: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "roleSummaries" 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: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "dataset-card-kicker" in dataset_panel assert "Bron: {dataset.source_name ?? dataset.source}" in dataset_panel diff --git a/backend/tests/test_sprint69_dataset_action_polish.py b/backend/tests/test_sprint69_dataset_action_polish.py index fefdf0ac..d177ed1f 100644 --- a/backend/tests/test_sprint69_dataset_action_polish.py +++ b/backend/tests/test_sprint69_dataset_action_polish.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_dataset_panel_explains_recommended_actions() -> None: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "datasetActionHint" 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: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "dataset-action-grid" in dataset_panel assert "Bekijken" in dataset_panel diff --git a/backend/tests/test_sprint70_quality_handoff_polish.py b/backend/tests/test_sprint70_quality_handoff_polish.py index d519a250..70b0259f 100644 --- a/backend/tests/test_sprint70_quality_handoff_polish.py +++ b/backend/tests/test_sprint70_quality_handoff_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - 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 "candidateDatasets={candidateDatasets}" in app diff --git a/backend/tests/test_sprint71_quality_metric_polish.py b/backend/tests/test_sprint71_quality_metric_polish.py index 92802f6b..0517a7b0 100644 --- a/backend/tests/test_sprint71_quality_metric_polish.py +++ b/backend/tests/test_sprint71_quality_metric_polish.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_quality_panel_promotes_core_metrics_before_raw_metric_list() -> None: - quality_panel = ( - ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" - ).read_text(encoding="utf-8") + quality_panel = read_feature("quality") assert "CORE_METRIC_ORDER" 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: - quality_panel = ( - ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" - ).read_text(encoding="utf-8") + quality_panel = read_feature("quality") assert "quality-metric-grid" in quality_panel assert "quality-metric-card" in quality_panel diff --git a/backend/tests/test_sprint73_quality_result_filtering.py b/backend/tests/test_sprint73_quality_result_filtering.py index ace1af78..e8fbad34 100644 --- a/backend/tests/test_sprint73_quality_result_filtering.py +++ b/backend/tests/test_sprint73_quality_result_filtering.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_quality_panel_adds_result_filters_and_density_limit() -> None: - quality_panel = ( - ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx" - ).read_text(encoding="utf-8") + quality_panel = read_feature("quality") assert "useState" in quality_panel assert "qualityStatusFilter" in quality_panel diff --git a/backend/tests/test_sprint74_data_map_mobile_polish.py b/backend/tests/test_sprint74_data_map_mobile_polish.py index c779838b..2dd52fad 100644 --- a/backend/tests/test_sprint74_data_map_mobile_polish.py +++ b/backend/tests/test_sprint74_data_map_mobile_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") + map_workspace = read_feature("map_workspace") assert 'className="dataset-upload-form"' in dataset_panel assert 'className="file-input-label"' in dataset_panel diff --git a/backend/tests/test_sprint75_ai_labs_mobile_polish.py b/backend/tests/test_sprint75_ai_labs_mobile_polish.py index 223bb151..8b286afb 100644 --- a/backend/tests/test_sprint75_ai_labs_mobile_polish.py +++ b/backend/tests/test_sprint75_ai_labs_mobile_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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"), ) ) - segmentation_lab = ( - ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx" - ).read_text(encoding="utf-8") + segmentation_lab = read_feature("segmentation") for source in (detection_lab, segmentation_lab): assert 'className="model-list"' in source diff --git a/backend/tests/test_sprint76_export_system_mobile_polish.py b/backend/tests/test_sprint76_export_system_mobile_polish.py index 58e4bf25..0ca1306d 100644 --- a/backend/tests/test_sprint76_export_system_mobile_polish.py +++ b/backend/tests/test_sprint76_export_system_mobile_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text( encoding="utf-8" ) diff --git a/backend/tests/test_sprint77_inspector_mobile_polish.py b/backend/tests/test_sprint77_inspector_mobile_polish.py index 8da3dbbd..1367a84e 100644 --- a/backend/tests/test_sprint77_inspector_mobile_polish.py +++ b/backend/tests/test_sprint77_inspector_mobile_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - inspector = (ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx").read_text( - encoding="utf-8" - ) - dataset_detail = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text( - 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" - ) + inspector = read_feature("shell") + dataset_detail = read_feature("datasets") + raster_controls = read_feature("datasets") + vector_controls = read_feature("datasets") assert 'className="workbench-inspector-panel"' in inspector assert 'className="inspector-action-bar"' in inspector diff --git a/backend/tests/test_sprint79_accessibility_focus_polish.py b/backend/tests/test_sprint79_accessibility_focus_polish.py index c7c4e12e..8d3f4ba4 100644 --- a/backend/tests/test_sprint79_accessibility_focus_polish.py +++ b/backend/tests/test_sprint79_accessibility_focus_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - navigation = ( - ROOT - / "frontend" - / "src" - / "components" - / "shell" - / "WorkbenchNavigation.tsx" - ).read_text(encoding="utf-8") + app = read_feature("shell") + navigation = read_feature("shell") assert 'aria-label={`Open ${item.label}: ${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: - inspector = (ROOT / "frontend" / "src" / "components" / "inspector" / "WorkbenchInspector.tsx").read_text( - encoding="utf-8" - ) + inspector = read_feature("shell") assert "const activeTabId = `inspector-tab-${activeTab}`" in inspector assert "const activePanelId = `inspector-panel-${activeTab}`" in inspector diff --git a/backend/tests/test_sprint80_operation_form_readability.py b/backend/tests/test_sprint80_operation_form_readability.py index 823179fc..db3ddac1 100644 --- a/backend/tests/test_sprint80_operation_form_readability.py +++ b/backend/tests/test_sprint80_operation_form_readability.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( - encoding="utf-8" - ) + raster_controls = read_feature("datasets") assert 'className="dataset-tool-heading"' 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: - vector_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "VectorControls.tsx").read_text( - encoding="utf-8" - ) + vector_controls = read_feature("datasets") assert 'className="dataset-tool-heading"' in vector_controls assert 'className="dataset-tool-helper"' in vector_controls diff --git a/backend/tests/test_sprint81_result_state_polish.py b/backend/tests/test_sprint81_result_state_polish.py index 743a0a31..fc56da2c 100644 --- a/backend/tests/test_sprint81_result_state_polish.py +++ b/backend/tests/test_sprint81_result_state_polish.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: - 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" - ) + quality_panel = read_feature("quality") + export_center = read_feature("exports") assert 'className="result-state result-state-error"' 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: - 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") + detection_lab = read_feature("detection") + segmentation_lab = read_feature("segmentation") for content in (detection_lab, segmentation_lab): assert 'className="result-state result-state-loading"' in content diff --git a/backend/tests/test_sprint82_shell_density_polish.py b/backend/tests/test_sprint82_shell_density_polish.py index 66a1ce47..7bdf80a6 100644 --- a/backend/tests/test_sprint82_shell_density_polish.py +++ b/backend/tests/test_sprint82_shell_density_polish.py @@ -1,21 +1,15 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_workbench_shell_has_skip_link_and_main_focus_target() -> None: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - navigation = ( - ROOT - / "frontend" - / "src" - / "components" - / "shell" - / "WorkbenchNavigation.tsx" - ).read_text(encoding="utf-8") + app = read_feature("shell") + navigation = read_feature("shell") assert 'className="skip-link"' in app assert 'href="#workspace-main"' in app diff --git a/backend/tests/test_sprint83_workspace_panel_hierarchy.py b/backend/tests/test_sprint83_workspace_panel_hierarchy.py index 23c042ab..779974cd 100644 --- a/backend/tests/test_sprint83_workspace_panel_hierarchy.py +++ b/backend/tests/test_sprint83_workspace_panel_hierarchy.py @@ -1,13 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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="quick-action-grid overview-quick-actions"' in app diff --git a/backend/tests/test_sprint84_data_workspace_density.py b/backend/tests/test_sprint84_data_workspace_density.py index b6951a8e..88b295df 100644 --- a/backend/tests/test_sprint84_data_workspace_density.py +++ b/backend/tests/test_sprint84_data_workspace_density.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text( - encoding="utf-8" - ) + dataset_panel = read_feature("datasets") assert "const selectedDataset = datasets.find" in dataset_panel assert 'className="data-selection-summary data-selection-summary-dataset"' in dataset_panel diff --git a/backend/tests/test_sprint85_map_workspace_density.py b/backend/tests/test_sprint85_map_workspace_density.py index 7e1c5d11..9fbe0c00 100644 --- a/backend/tests/test_sprint85_map_workspace_density.py +++ b/backend/tests/test_sprint85_map_workspace_density.py @@ -1,15 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_map_workspace_exposes_structured_surfaces() -> None: - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + map_workspace = read_feature("map_workspace") assert 'className="map-workspace-shell"' 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: - map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text( - encoding="utf-8" - ) + map_workspace = read_feature("map_workspace") assert "const selectedMapArea = areas.find" in map_workspace assert "selectedMapArea?.name ?? 'Geen gebied geselecteerd'" in map_workspace diff --git a/backend/tests/test_sprint86_quality_workspace_density.py b/backend/tests/test_sprint86_quality_workspace_density.py index 925802f3..cd8baf6f 100644 --- a/backend/tests/test_sprint86_quality_workspace_density.py +++ b/backend/tests/test_sprint86_quality_workspace_density.py @@ -1,15 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_quality_results_panel_exposes_structured_surfaces() -> None: - panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( - encoding="utf-8" - ) + panel = read_feature("quality") assert "'quality-results-panel quality-results-panel-empty' : 'quality-results-panel'" 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: - panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( - encoding="utf-8" - ) + panel = read_feature("quality") assert "onRefresh" in panel assert "qualityStatusFilter" in panel diff --git a/backend/tests/test_sprint88_ai_lab_density.py b/backend/tests/test_sprint88_ai_lab_density.py index 3b2aa766..cb8b58ca 100644 --- a/backend/tests/test_sprint88_ai_lab_density.py +++ b/backend/tests/test_sprint88_ai_lab_density.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature 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: - lab = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( - encoding="utf-8" - ) + lab = read_feature("segmentation") assert 'className="workspace-panel ai-lab-shell segmentation-lab-shell"' 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: - detection = (ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx").read_text( - encoding="utf-8" - ) - segmentation = (ROOT / "frontend" / "src" / "components" / "segmentation" / "SegmentationLab.tsx").read_text( - encoding="utf-8" - ) + detection = read_feature("detection") + segmentation = read_feature("segmentation") assert "onRunDetection" in detection assert "onLoadResults" in detection diff --git a/backend/tests/test_sprint89_export_system_density.py b/backend/tests/test_sprint89_export_system_density.py index cc6201eb..99703372 100644 --- a/backend/tests/test_sprint89_export_system_density.py +++ b/backend/tests/test_sprint89_export_system_density.py @@ -1,15 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_export_center_exposes_final_handoff_surfaces() -> None: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") assert 'className="export-center"' 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: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text( encoding="utf-8" ) diff --git a/backend/tests/test_sprint90_workflow_guidance.py b/backend/tests/test_sprint90_workflow_guidance.py index 9d6b7858..9305ee59 100644 --- a/backend/tests/test_sprint90_workflow_guidance.py +++ b/backend/tests/test_sprint90_workflow_guidance.py @@ -1,13 +1,14 @@ from __future__ import annotations from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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 '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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + overview = read_feature("shell") assert "target: 'data'" 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: - overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") + overview = read_feature("shell") assert "workflowComplete" 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: - app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") - overview = (ROOT / "frontend" / "src" / "components" / "overview" / "OverviewWorkspace.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + overview = read_feature("shell") assert "openWorkflowGuidanceStep" in app assert "target === 'map'" in app diff --git a/backend/tests/test_sprint93_export_handoff_completion.py b/backend/tests/test_sprint93_export_handoff_completion.py index 300ae833..8d210ba7 100644 --- a/backend/tests/test_sprint93_export_handoff_completion.py +++ b/backend/tests/test_sprint93_export_handoff_completion.py @@ -1,14 +1,12 @@ 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] def test_export_center_groups_latest_handoff_artifacts_by_type() -> None: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") assert "latestHandoffArtifacts" in export_center 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: - export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text( - encoding="utf-8" - ) + export_center = read_feature("exports") assert "getLatestExportByType" in export_center assert "const item = artifact.item" in export_center diff --git a/backend/tests/test_sprint94_quality_drilldown.py b/backend/tests/test_sprint94_quality_drilldown.py index fc32b1fb..d80b5b07 100644 --- a/backend/tests/test_sprint94_quality_drilldown.py +++ b/backend/tests/test_sprint94_quality_drilldown.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_quality_results_panel_exposes_selected_check_drilldown() -> None: - panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( - encoding="utf-8" - ) + panel = read_feature("quality") assert "selectedQualityCheckId" 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: - panel = (ROOT / "frontend" / "src" / "components" / "quality" / "QualityResultsPanel.tsx").read_text( - encoding="utf-8" - ) + panel = read_feature("quality") assert "Onterecht gevonden" in panel assert "Gemiste objecten" in panel diff --git a/backend/tests/test_sprint95_raster_pipeline_hardening.py b/backend/tests/test_sprint95_raster_pipeline_hardening.py index 602d72e9..55fbffcd 100644 --- a/backend/tests/test_sprint95_raster_pipeline_hardening.py +++ b/backend/tests/test_sprint95_raster_pipeline_hardening.py @@ -1,13 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] def test_raster_controls_expose_pipeline_readiness_and_handoff() -> None: - raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text( - encoding="utf-8" - ) + raster_controls = read_feature("datasets") assert "rasterReadinessItems" in raster_controls assert "rasterGuardrailItems" in raster_controls diff --git a/backend/tests/test_sprint96_useful_default_context.py b/backend/tests/test_sprint96_useful_default_context.py index 3fccd079..b912aac8 100644 --- a/backend/tests/test_sprint96_useful_default_context.py +++ b/backend/tests/test_sprint96_useful_default_context.py @@ -1,11 +1,12 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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 "!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: - 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" - ) + detection_lab = read_feature("detection") + segmentation_lab = read_feature("segmentation") assert "Geen luchtbeeld beschikbaar in deze werkruimte." in detection_lab assert "Voeg hieronder een gegeorefereerde GeoTIFF toe." in detection_lab diff --git a/backend/tests/test_sprint97_demo_raster_fixture.py b/backend/tests/test_sprint97_demo_raster_fixture.py index 72a1fca5..dbf27040 100644 --- a/backend/tests/test_sprint97_demo_raster_fixture.py +++ b/backend/tests/test_sprint97_demo_raster_fixture.py @@ -1,4 +1,5 @@ from pathlib import Path +from tests.frontend_contract import read_feature 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: 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") assert "raster_dataset_id?: string | null" in types diff --git a/backend/tests/test_sprint99_raster_ui_handoff.py b/backend/tests/test_sprint99_raster_ui_handoff.py index 1b0dea4f..59be107e 100644 --- a/backend/tests/test_sprint99_raster_ui_handoff.py +++ b/backend/tests/test_sprint99_raster_ui_handoff.py @@ -1,14 +1,15 @@ from pathlib import Path +from tests.frontend_contract import read_feature ROOT = Path(__file__).resolve().parents[2] 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") - hook = (ROOT / "frontend" / "src" / "hooks" / "useDatasetWorkflow.ts").read_text(encoding="utf-8") - detail_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetDetailPanel.tsx").read_text(encoding="utf-8") - raster_controls = (ROOT / "frontend" / "src" / "components" / "datasets" / "RasterControls.tsx").read_text(encoding="utf-8") + app = read_feature("shell") + hook = read_feature("datasets") + detail_panel = read_feature("datasets") + raster_controls = read_feature("datasets") assert "latestRasterTileManifestPath" in hook assert "extractRasterTileManifestPath" in hook