@@ -0,0 +1,125 @@
|
||||
"""UI-контракты кормоцеха: HTML/JS и страницы (без браузерного раннера)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from app import create_app, db
|
||||
from app.services.setup_state import mark_setup_complete
|
||||
from config import TestingConfig
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
STATIC = PROJECT_ROOT / "static"
|
||||
|
||||
|
||||
class FeedMillUiConfig(TestingConfig):
|
||||
_TMP_DIR = tempfile.mkdtemp(prefix="wesp-feed-mill-ui-")
|
||||
SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(_TMP_DIR, 'recipes_test.db')}"
|
||||
SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(_TMP_DIR, 'reports_test.db')}"}
|
||||
AUTH_LOGIN = "mill-ui-admin"
|
||||
AUTH_PASSWORD = "mill-ui-secret"
|
||||
|
||||
|
||||
def _drain_response(resp) -> None:
|
||||
try:
|
||||
resp.get_data()
|
||||
finally:
|
||||
close = getattr(resp, "close", None)
|
||||
if callable(close):
|
||||
close()
|
||||
|
||||
|
||||
class FeedMillUiTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.app = create_app(FeedMillUiConfig)
|
||||
self.client = self.app.test_client()
|
||||
self.ctx = self.app.app_context()
|
||||
self.ctx.push()
|
||||
db.create_all()
|
||||
mark_setup_complete(self.app)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
self.ctx.pop()
|
||||
|
||||
def _login(self) -> None:
|
||||
resp = self.client.post(
|
||||
"/api/auth/login", json={"login": "mill-ui-admin", "password": "mill-ui-secret"}
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_feed_dispensers_page_requires_auth(self) -> None:
|
||||
resp = self.client.get("/feed_dispensers", follow_redirects=False)
|
||||
self.assertIn(resp.status_code, (302, 401, 403))
|
||||
|
||||
def test_feed_dispensers_and_recipes_pages_ok_when_authed(self) -> None:
|
||||
self._login()
|
||||
for path in ("/feed_dispensers", "/recipes"):
|
||||
resp = self.client.get(path)
|
||||
_drain_response(resp)
|
||||
self.assertEqual(resp.status_code, 200, path)
|
||||
self.assertIn(b"<!DOCTYPE html>", resp.data or b"")
|
||||
|
||||
def test_feed_dispensers_html_mill_modal_and_create_payload(self) -> None:
|
||||
html = (STATIC / "feed_dispensers.html").read_text(encoding="utf-8")
|
||||
self.assertIn("Добавить кормоцех", html)
|
||||
self.assertIn('id="addMillModalLabel"', html)
|
||||
self.assertIn("type: 'mill'", html)
|
||||
self.assertIn("dispenser-card--mill", html)
|
||||
self.assertIn("!isMill", html)
|
||||
self.assertIn("managePeriods", html)
|
||||
|
||||
def test_recipes_data_controller_mill_branch(self) -> None:
|
||||
js = (STATIC / "js" / "pages" / "recipes-data-controller.js").read_text(encoding="utf-8")
|
||||
self.assertIn('getCurrentDispenserType() === "mill"', js)
|
||||
self.assertIn("loadMillRecipes", js)
|
||||
self.assertIn('millRecipesList', js)
|
||||
self.assertIn('dispenser.type === "mill"', js)
|
||||
self.assertIn('? "recipes" : "periods"', js)
|
||||
|
||||
def test_recipes_operations_mill_uses_global_delete(self) -> None:
|
||||
js = (STATIC / "js" / "pages" / "recipes-operations-controller.js").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn('getCurrentDispenserType() === "mill"', js)
|
||||
self.assertIn("unlinkFromPeriodOnly", js)
|
||||
self.assertIn("/api/recipes/", js)
|
||||
# mill: DELETE глобальный, не unlink из периода
|
||||
self.assertRegex(
|
||||
js,
|
||||
r"unlinkFromPeriodOnly\s*=\s*Boolean\(periodId\s*&&\s*dispenserId\s*&&\s*!isMill\)",
|
||||
)
|
||||
|
||||
def test_recipes_editor_mill_target_component(self) -> None:
|
||||
js = (STATIC / "js" / "pages" / "recipes-editor-controller.js").read_text(encoding="utf-8")
|
||||
self.assertIn('getCurrentDispenserType() === "mill"', js)
|
||||
self.assertIn("target_component", js)
|
||||
|
||||
def test_recipe_period_transfer_excludes_mill(self) -> None:
|
||||
js = (STATIC / "js" / "modules" / "recipes" / "recipe-period-transfer.js").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertIn('dataset.dispenserType === "mill"', js)
|
||||
mill_skips = len(re.findall(r'dispenserType\s*===\s*["\']mill["\']', js))
|
||||
self.assertGreaterEqual(mill_skips, 2)
|
||||
|
||||
def test_recipes_html_legacy_mill_flow_present(self) -> None:
|
||||
html = (STATIC / "recipes.html").read_text(encoding="utf-8")
|
||||
self.assertIn("currentDispenserType = 'dispenser'", html)
|
||||
self.assertIn("currentDispenserType === 'mill'", html)
|
||||
self.assertIn("target_component_id", html)
|
||||
self.assertIn("Загрузка рецептов кормоцеха", html)
|
||||
|
||||
def test_recipe_skeleton_mill_label(self) -> None:
|
||||
js = (STATIC / "js" / "modules" / "recipes" / "recipe-skeleton.js").read_text(encoding="utf-8")
|
||||
self.assertIn("mill:", js)
|
||||
self.assertIn("Загрузка рецептов", js)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user