from __future__ import annotations import unittest from pathlib import Path class FrontendStaticContractTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.app_js = (Path(__file__).resolve().parent.parent / "frontend" / "app.js").read_text( encoding="utf-8", ) cls.index_html = (Path(__file__).resolve().parent.parent / "frontend" / "index.html").read_text( encoding="utf-8", ) cls.workspace_js = (Path(__file__).resolve().parent.parent / "frontend" / "workspace.js").read_text( encoding="utf-8", ) def test_app_js_does_not_use_inline_onclick_handlers(self) -> None: self.assertNotIn('onclick="', self.app_js) def test_explorer_interactions_use_data_attributes_and_global_compat_exports(self) -> None: self.assertIn('data-explorer-cat="', self.app_js) self.assertIn('data-symbol="${escapeHtml(s.symbol)}"', self.app_js) self.assertIn("window.selectExplorerCat = selectExplorerCat;", self.app_js) self.assertIn("window.explorerSelectSymbol = explorerSelectSymbol;", self.app_js) def test_market_toggle_dom_ref_is_explicit(self) -> None: self.assertIn("const toggleMarketBtn = document.getElementById('toggleMarketBtn');", self.app_js) def test_layout_switcher_reconciles_actual_workspace_state_before_noop(self) -> None: self.assertIn("function inferWorkspaceLayoutPreset()", self.app_js) self.assertIn("const currentPreset = syncWorkspaceLayoutPreset();", self.app_js) self.assertIn("const isPresetFullyApplied = (", self.app_js) def test_progressive_ai_forecast_contract_is_exposed(self) -> None: self.assertIn( "window.buildCombinedForecastPayloadForPane = buildCombinedForecastPayloadForPane;", self.app_js, ) self.assertIn("async fetchForecastModel(symbol, interval, horizon, modelKey, signal)", self.workspace_js) self.assertIn("complete: false", self.workspace_js) def test_shared_chart_viewport_contract_is_centralized(self) -> None: self.assertIn("const CHART_RIGHT_OFFSET = 50;", self.app_js) self.assertIn("function buildSharedTimeScaleOptions(overrides = {})", self.app_js) self.assertIn("function getAllOpenChartInstances()", self.app_js) self.assertIn("function zoomAllCharts(direction)", self.app_js) self.assertIn("function panAllCharts(direction)", self.app_js) self.assertIn("function restoreOrFitChartViewport(chartKey, chartInstance, context)", self.app_js) self.assertIn("function registerChartViewportTracking(chartInstance, chartKey, getContext)", self.app_js) self.assertIn("function alignChartViewportToReservedSpace(chartKey, chartInstance, context, options = {})", self.app_js) self.assertIn("function resolveChartRightEdgeAnchor(chartKey)", self.app_js) self.assertIn("const zoomOutBtn = document.getElementById('zoomOutBtn');", self.app_js) self.assertIn("const zoomInBtn = document.getElementById('zoomInBtn');", self.app_js) self.assertIn("const panLeftBtn = document.getElementById('panLeftBtn');", self.app_js) self.assertIn("const panRightBtn = document.getElementById('panRightBtn');", self.app_js) self.assertIn("function setupChartNavigationCluster()", self.app_js) self.assertIn('id="zoomOutBtn"', self.index_html) self.assertIn('id="zoomInBtn"', self.index_html) self.assertIn('id="panLeftBtn"', self.index_html) self.assertIn('id="panRightBtn"', self.index_html) self.assertIn('.chart-nav-cluster', self.index_html) if __name__ == "__main__": unittest.main()