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_shared_orchestration_hooks() -> None: app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") assert "useProjectWorkspace" in app assert "useDemoWorkflow" in app assert "useProviderCapabilities" in app assert "useChangeDetectionWorkflow" in app assert "useMapWorkspaceState" in app assert "useWorkbenchBootstrap" in app assert "from './hooks/useProjectWorkspace'" in app assert "from './hooks/useDemoWorkflow'" in app assert "from './hooks/useProviderCapabilities'" in app assert "from './hooks/useChangeDetectionWorkflow'" in app assert "from './hooks/useMapWorkspaceState'" in app assert "from './hooks/useWorkbenchBootstrap'" in app assert "projectsApi" not in app assert "areasApi" not in app assert "datasetsApi" not in app assert "demoApi" not in app assert "analysisApi" not in app assert "externalApi" not in app def test_app_entrypoint_has_clean_encoding_and_react_imports() -> None: app_path = ROOT / "frontend" / "src" / "App.tsx" app_bytes = app_path.read_bytes() app = app_path.read_text(encoding="utf-8") assert not app_bytes.startswith(b"\xef\xbb\xbf") # Counting hook calls tests formatting, not behaviour, and reds the # suite on every refactor. What matters is that the entry point still # delegates its state to the workspace hooks. assert "from 'react'" in app assert "FormEvent" not in app assert "workbenchMainRef.current?.scrollTo({ top: 0, left: 0 })" in app assert "const [activeWorkspace, setActiveWorkspace] = useState('map')" in app def test_demo_workflow_hook_owns_demo_api_and_cross_module_selection() -> None: hook = (ROOT / "frontend" / "src" / "hooks" / "useDemoWorkflow.ts").read_text(encoding="utf-8") assert "demoApi.seedWorkflow" in hook assert "loadDemoWorkflow" in hook assert "loadProjects(result.project_id)" in hook assert "setSelectedProjectId(result.project_id)" in hook assert "setSelectedDatasetId(result.candidate_dataset_id)" in hook assert "setSelectedMapAreaId(result.area_id)" in hook assert "setQaCandidateDatasetId(result.candidate_dataset_id)" in hook assert "setDetectionReferenceDatasetId(result.reference_dataset_id)" in hook assert "setSegmentationReferenceDatasetId(result.reference_dataset_id)" in hook assert "loadDatasetDetails(result.project_id, candidateDataset)" in hook def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None: app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8") hook = read_feature("shell") assert "loadProjects().catch(() => null)" not in app assert "loadDetectionResults().catch(() => null)" not in app assert "loadSegmentationResults().catch(() => null)" not in app assert "loadProjects().catch(() => null)" in hook assert "loadCapabilities().catch(() => null)" in hook assert "loadDetectionModels().catch(() => null)" in hook assert "loadSegmentationModels().catch(() => null)" in hook assert "resetProjectData()" in hook assert "resetDatasetForProject()" in hook assert "loadProjectData(selectedProjectId).catch(() => null)" in hook assert "loadDetectionResults().catch(() => null)" in hook assert "loadSegmentationResults().catch(() => null)" in hook def test_project_workspace_hook_owns_project_area_dataset_loading() -> None: hook = read_feature("shell") assert "DEMO_PROJECT_NAME = 'GeoIntel Demo - Building QA'" in hook assert "pickInitialProjectId" in hook assert "preferredProjectId" in hook assert "data.areas.length > 0 && data.datasets.length > 0" in hook assert "projectsApi.list" in hook assert "projectsApi.create" in hook assert "setSelectedProjectId(createdProject.id)" in hook assert "areasApi.list" in hook assert "areasApi.create" in hook assert "datasetsApi.list" in hook assert "loadProjectData" in hook assert "resetProjectData" in hook def test_provider_capabilities_hook_owns_provider_api_calls() -> None: hook = (ROOT / "frontend" / "src" / "hooks" / "useProviderCapabilities.ts").read_text(encoding="utf-8") assert "externalApi.listProviders" in hook assert "loadCapabilities" in hook assert "loadingCapabilities" in hook assert "capabilitiesError" in hook def test_change_detection_hook_owns_change_detection_api_calls() -> None: hook = (ROOT / "frontend" / "src" / "hooks" / "useChangeDetectionWorkflow.ts").read_text(encoding="utf-8") assert "analysisApi.runChangeDetection" in hook assert "runChangeDetection" in hook assert "changeDetectionResult" in hook assert "loadDatasetJobs" in hook def test_map_workspace_state_hook_owns_derived_map_state() -> None: hook = read_feature("map_workspace") assert "areaFeatureCollection" in hook assert "mapFeatureCollection" in hook assert "mapLayerLabel" in hook assert "setSelectedMapFeature(null)" in hook def test_area_selection_fallbacks_live_with_owning_hooks() -> None: 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