"""Контракт UI hub «К»: скрипты, карточки, API paths.""" from __future__ import annotations import unittest from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent ZOOTECH_PAGES = ( "static/recipes.html", "static/reports.html", "static/components.html", "static/feed_dispensers.html", "static/consumption.html", ) ZOOTECH_PAGES_WITH_LAB_PANEL = ( "static/reports.html", "static/components.html", "static/feed_dispensers.html", "static/consumption.html", ) class ZootechKHubUiContractTests(unittest.TestCase): def _read(self, rel: str) -> str: return (PROJECT_ROOT / rel).read_text(encoding="utf-8") def test_zootech_pages_include_k_hub_assets(self) -> None: for page in ZOOTECH_PAGES: html = self._read(page) with self.subTest(page=page): self.assertIn("wesp-zootech-k-hub.css", html) self.assertIn("daily-plan-panel.js", html) self.assertIn("daily-plan-replace-component-modal.js", html) self.assertIn("wesp-zootech-notification-center.js", html) if page == "static/recipes.html": self.assertNotIn("lab-master-panel.js", html) self.assertNotIn("lab-animal-profiles-modal.js", html) for page in ZOOTECH_PAGES_WITH_LAB_PANEL: html = self._read(page) with self.subTest(page=page, lab_panel=True): self.assertIn("lab-master-panel.js", html) self.assertIn("lab-animal-profiles-modal.js", html) def test_notification_center_exports_hub_api(self) -> None: center_js = self._read("static/js/wesp-zootech-notification-center.js") self.assertIn("openCenter", center_js) self.assertIn("openNotifications", center_js) self.assertIn("openDailyPlan", center_js) self.assertIn("WespDailyPlanPanel", center_js) self.assertNotIn("WespLabMasterPanel", center_js) self.assertNotIn("k-hub-open-animal-profiles", center_js) self.assertIn("hub-open-notifications", center_js) self.assertIn("hub-open-daily-plan", center_js) self.assertIn("wesp-shell-bsmodal-close", center_js) self.assertIn("data-k-hub-panel", center_js) self.assertIn("Комптон - хаб", center_js) self.assertIn("modal-dialog--shell-plan", center_js) self.assertIn("data-notify-toolbar", center_js) self.assertIn("data-notify-sort", center_js) self.assertIn("data-notify-category", center_js) self.assertIn("daily_plan", center_js) self.assertIn("data-notify-load-more", center_js) self.assertIn("zt-k-hub--notify", center_js) self.assertIn("modal-dialog--shell-notify", center_js) self.assertIn("showDetail", center_js) def test_daily_plan_panel_api_paths(self) -> None: panel_js = self._read("static/js/pages/daily-plan-panel.js") self.assertIn("/api/daily-plan", panel_js) self.assertIn("/api/daily-plan/pdf", panel_js) self.assertIn("/api/feed_dispensers", panel_js) self.assertIn("wesp-daily-plan-dispenser", panel_js) self.assertIn("__all_dispensers__", panel_js) self.assertIn("__all_mills__", panel_js) self.assertIn("Все кормораздатчики", panel_js) self.assertIn("Распределение", panel_js) self.assertIn("daily-plan-skip-trip", panel_js) self.assertIn("daily-plan-skip-ingredient", panel_js) self.assertIn("daily-plan-skip-unloading-group", panel_js) self.assertIn("recipe-table-delete-btn", panel_js) self.assertIn("fa-trash", panel_js) self.assertIn("fa-times", panel_js) self.assertIn("daily-plan-unskip-trip", panel_js) self.assertIn("/api/daily-plan/skips", panel_js) self.assertIn("/api/daily-plan/skips/ingredients", panel_js) self.assertIn("/api/daily-plan/skips/unloading-groups", panel_js) self.assertIn("skippedTrips", panel_js) self.assertIn("skippedToday", panel_js) self.assertIn("baselineWeightPerHead", panel_js) self.assertIn("zt-k-hub-plan-cell--hint", panel_js) self.assertIn("zt-k-hub-plan-row__skip-dot", panel_js) self.assertIn("zt-k-hub-plan-skipped", panel_js) self.assertIn("zt-k-hub-plan-overlay", panel_js) self.assertIn("appendPlanOverlay", panel_js) self.assertIn("daily-plan-replace-ingredient", panel_js) self.assertIn("WespDailyPlanReplaceModal", panel_js) self.assertIn("/api/daily-plan/replacements/ingredients", panel_js) replace_js = self._read("static/js/modules/daily-plan-replace-component-modal.js") self.assertIn("/api/daily-plan/component-alternatives", replace_js) norms_js = self._read("static/js/pages/daily-plan-component-norms-modal.js") self.assertIn("data-norms-replace", norms_js) self.assertIn("/api/daily-plan/replacements/components", norms_js) self.assertIn("WespDailyPlanReplaceModal", norms_js) self.assertIn("ingredientGrandTotalKg", panel_js) skip_js = self._read("static/js/modules/daily-plan-skip-duration.js") self.assertIn("zt-k-hub-plan-skip-duration", skip_js) self.assertIn("duration: \"week\"", skip_js) self.assertIn("zt-k-hub-plan__split", panel_js) self.assertIn("zt-k-hub-plan__totals-col", panel_js) self.assertIn("zt-k-hub-plan__trips-col", panel_js) def test_k_hub_css_has_hub_cards(self) -> None: css = self._read("static/css/wesp-zootech-k-hub.css") self.assertIn(".zt-k-hub-card", css) self.assertIn(".zt-k-hub-plan", css) self.assertIn("modal-dialog--shell-plan", css) self.assertIn("modal-dialog--shell-notify", css) self.assertIn(".zt-k-hub--notify", css) self.assertIn(".zt-k-hub-plan-cell--hint", css) self.assertIn(".zt-k-hub-plan__split", css)