"""UI-контракт модуля lab: скрипты, API paths, K-hub.""" from __future__ import annotations import unittest from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent ZOOTECH_PAGES_WITH_LAB_PANEL = ( "static/reports.html", "static/components.html", "static/feed_dispensers.html", "static/consumption.html", ) class LabUiContractTests(unittest.TestCase): def _read(self, rel: str) -> str: return (PROJECT_ROOT / rel).read_text(encoding="utf-8") def test_zootech_pages_include_lab_master_panel(self) -> None: for page in ZOOTECH_PAGES_WITH_LAB_PANEL: with self.subTest(page=page): html = self._read(page) self.assertIn("lab-master-panel.js", html) self.assertIn("lab-animal-profiles-modal.js", html) def test_recipes_page_without_lab_integration(self) -> None: """Рецепты — отдельно от /lab; зоотехника только на lab.html.""" html = self._read("static/recipes.html") self.assertNotIn("lab-recipes-overlay.js", html) self.assertNotIn("lab-recipes-plan-overlay.js", html) self.assertNotIn("lab-master-panel.js", html) def test_components_page_lab_nutrients_ui(self) -> None: html = self._read("static/components.html") self.assertIn("lab-components-nutrients.js", html) self.assertIn("labNutrientsCreateMount", html) self.assertIn("labAgrostarDrop", html) self.assertIn("component-add-agrostar__head", html) self.assertIn("lab-agrostar-import.js", html) self.assertIn("component-add-save-btn", html) self.assertIn("componentAddSaveBtnTop", html) self.assertNotIn('id="protein"', html) self.assertNotIn('id="energy"', html) self.assertIn("component-form-row--primary", html) js = self._read("static/js/pages/lab-components-nutrients.js") self.assertIn("renderCardInfoHtml", js) self.assertIn("NUTRIENT_ROWS", js) self.assertIn("LAB_INPUT_ROWS", js) self.assertIn("EXTRA_LAB_ROWS", js) self.assertIn("lab-nutrients-row--extra", js) self.assertIn("externalNo", html) self.assertNotIn("toggle-nutrients", js) self.assertNotIn(".includes(target)", js) self.assertNotIn("FUTTERPLUS", js) self.assertNotIn('"СВ"', js.split("LAB_INPUT_ROWS")[1].split("];")[0]) def test_lab_sandbox_page(self) -> None: html = self._read("static/lab.html") js = self._read("static/js/pages/lab-sandbox.js") prime_js = self._read("static/js/pages/lab-heisenberg-prime.js") self.assertIn("lab-sandbox.js", html) self.assertIn("lab-heisenberg-prime.js", html) self.assertIn("labSandboxRecipe", html) self.assertIn("/api/lab/rations/", js) self.assertIn("/api/lab/recipes", js) self.assertIn("recalculate", js) self.assertIn("WespLabHeisenbergPrime", prime_js) self.assertIn("function error", prime_js) self.assertIn("lab-heisenberg-prime-active", self._read("static/css/wesp-lab-sandbox.css")) lab_css = self._read("static/css/wesp-lab-sandbox.css") self.assertIn("body.zootech-app-body.lab-sandbox-page", lab_css) self.assertIn("lab-heisenberg-bg.jpg", lab_css) def test_lab_formulate_page(self) -> None: html = self._read("static/lab.html") js = self._read("static/js/pages/lab-formulate.js") self.assertIn("lab-formulate.js", html) self.assertIn("labFormulateRun", html) self.assertIn("labFormulateSteps", html) self.assertIn("labFormulatePanes", html) self.assertIn("/api/lab/formulate", js) self.assertIn("/api/lab/norms-preview", js) self.assertIn("labFormulateNormsMethod", html) self.assertIn("groupSelections", js) self.assertIn("/api/lab/formulate/components", js) self.assertIn("labFormulateRun", js) def test_components_main_feed_field(self) -> None: js = self._read("static/js/pages/lab-components-nutrients.js") self.assertIn("Осн.Корм", js) self.assertIn("Осн. корм", js) def test_lab_master_panel_api_paths(self) -> None: js = self._read("static/js/pages/lab-master-panel.js") for path in ( "/api/lab/rations/", "/api/lab/recipes", "/api/lab/animal-profiles", "/api/components", "lab-recalculate", "lab-save", "lab-add-line", "lab-print-ration", ): self.assertIn(path, js) def test_lab_recipes_overlay_api_paths(self) -> None: js = self._read("static/js/pages/lab-recipes-overlay.js") self.assertIn("/api/lab/rations/", js) self.assertIn("apply-from-master", js) self.assertIn("sync-to-master", js) self.assertIn("open-lab-k-hub", js) self.assertIn("rationType", js) self.assertIn("labRecipeQuality", js) def test_lab_plan_overlay_daily_plan_api(self) -> None: js = self._read("static/js/pages/lab-recipes-plan-overlay.js") self.assertIn("/api/daily-plan/skips/ingredients", js) self.assertIn("/api/daily-plan/replacements/ingredients", js) self.assertIn("recipe-plan-skip-ingredient", js) def test_k_hub_uses_daily_plan_not_lab_master(self) -> None: center = self._read("static/js/wesp-zootech-notification-center.js") self.assertIn("WespDailyPlanPanel", center) self.assertNotIn("WespLabMasterPanel", center) self.assertNotIn("k-hub-open-animal-profiles", center) def test_lab_profiles_page_exists(self) -> None: html = self._read("static/lab-profiles.html") self.assertIn("lab-profiles.js", html) js = self._read("static/js/pages/lab-profiles.js") self.assertIn("/api/lab/animal-profiles", js) self.assertIn("labProfilesDelete", html) self.assertIn('method: "DELETE"', js) self.assertIn("labProfilesSourceFilter", html) self.assertIn("labProfilesNormsSummary", html) self.assertIn("/api/lab/norms-preview", js) self.assertIn("buildNormSourceMap", js)