在 376–900px 五列目录把方向 chip 与期末金额分行,并加 Chromium 盒模型回归,避免 15 位金额盖住未决状态。 Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
5.1 KiB
Python
110 lines
5.1 KiB
Python
"""Static + Node checks for the B-44 visual rework.
|
|
|
|
Covers unique ids, nav copy, drawer/directory breakpoints, eight-column
|
|
event table, and the exported keyboard/amount helpers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
WEB = ROOT / "web"
|
|
|
|
|
|
class B44FrontendContractTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls.admin = (WEB / "admin.html").read_text(encoding="utf-8")
|
|
cls.company = (WEB / "company.html").read_text(encoding="utf-8")
|
|
cls.css = (WEB / "styles.css").read_text(encoding="utf-8")
|
|
cls.js = (WEB / "app.js").read_text(encoding="utf-8")
|
|
|
|
def test_admin_ids_are_unique(self) -> None:
|
|
self.assertEqual(1, self.admin.count('id="companyLedgers"'))
|
|
self.assertEqual(1, self.admin.count('id="balanceLedgers"'))
|
|
self.assertIn('data-view="pair"', self.admin)
|
|
self.assertRegex(self.admin, r'data-view="pair"[^>]*>[\s\S]*?<span>往来查询</span>')
|
|
self.assertIn("<h1>往来查询</h1>", self.admin)
|
|
self.assertIn("转为异常后,该记录暂不纳入余额计算", self.admin)
|
|
self.assertIn('id="auditExceptionNote"', self.admin)
|
|
|
|
def test_company_balance_groups_and_nav(self) -> None:
|
|
self.assertIn('id="companyBalanceGroups"', self.company)
|
|
self.assertNotIn('id="companyBalanceLine"', self.company)
|
|
self.assertRegex(self.company, r'data-view="balances"[^>]*>[\s\S]*?<span>往来余额</span>')
|
|
self.assertIn("<h1>往来余额</h1>", self.company)
|
|
|
|
def test_css_directory_and_drawer_breakpoints(self) -> None:
|
|
self.assertIn("@media (max-width: 375px)", self.css)
|
|
self.assertIn("@media (max-width: 1179px)", self.css)
|
|
self.assertIn("@media (min-width: 376px) and (max-width: 900px)", self.css)
|
|
self.assertIn(
|
|
"minmax(80px, 0.85fr) minmax(152px, 1.2fr) minmax(96px, 0.9fr) minmax(84px, 0.5fr) 24px",
|
|
self.css,
|
|
)
|
|
self.assertIn("@media (max-width: 767px)", self.css)
|
|
self.assertIn(".drawer {", self.css)
|
|
self.assertIn("width: 640px", self.css)
|
|
self.assertIn(".drawer { width: 480px; }", self.css)
|
|
self.assertIn(".drawer { width: 100%; }", self.css)
|
|
self.assertIn(".drawer .pair-balance-line.is-six", self.css)
|
|
self.assertIn("repeat(3, 1fr)", self.css)
|
|
self.assertIn(".drawer .event-table { min-width: 860px; }", self.css)
|
|
self.assertIn(".company-row.is-balance-counterparty", self.css)
|
|
self.assertIn(".ledger-hide-md", self.css)
|
|
self.assertIn(".company-name b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }", self.css)
|
|
self.assertIn(".amount-with-currency.is-compact { font-size: 11px; }", self.css)
|
|
self.assertIn("minmax(88px, 1fr) minmax(0, max-content) 24px", self.css)
|
|
self.assertIn("grid-row: 1 / span 2", self.css)
|
|
self.assertIn("flex-direction: column; align-items: flex-end; gap: 2px;", self.css)
|
|
|
|
def test_js_selectors_and_event_table(self) -> None:
|
|
self.assertIn('loadAdminBalances($("#balanceLedgers"))', self.js)
|
|
self.assertNotIn('loadAdminBalances($("#companyLedgers"))', self.js)
|
|
self.assertIn("交易日期", self.js)
|
|
self.assertIn("本方账户", self.js)
|
|
self.assertIn("对方账户", self.js)
|
|
self.assertIn("摘要", self.js)
|
|
self.assertIn("匹配状态", self.js)
|
|
self.assertIn("function eventTableHead()", self.js)
|
|
self.assertIn("colspan=\"8\"", self.js)
|
|
self.assertIn("function cycleTab(", self.js)
|
|
self.assertIn("function drawerEscAction(", self.js)
|
|
self.assertIn("function eventIsNegative(", self.js)
|
|
self.assertIn("fmtAbsMoney", self.js)
|
|
self.assertIn("is-balance-counterparty", self.js)
|
|
self.assertIn("function isCompactAmount(", self.js)
|
|
self.assertIn("function auditEvidenceFields(", self.js)
|
|
self.assertIn("无关联流水", self.js)
|
|
self.assertIn('row.dataset.direction = "outgoing"', self.js)
|
|
self.assertIn("row.dataset.counterpartyName", self.js)
|
|
self.assertIn("row.dataset.relatedSourceRowId", self.js)
|
|
heads = re.search(
|
|
r"function eventTableHead\(\) \{\s*return `([^`]+)`",
|
|
self.js,
|
|
)
|
|
self.assertIsNotNone(heads)
|
|
markup = heads.group(1)
|
|
self.assertNotIn("来源", markup)
|
|
for label in ("交易日期", "方向", "科目", "本方账户", "对方账户", "摘要", "匹配状态", "金额"):
|
|
self.assertIn(label, markup)
|
|
self.assertEqual(8, len(re.findall(r"<th\b", markup)))
|
|
|
|
def test_node_keyboard_and_amount_helpers(self) -> None:
|
|
result = subprocess.run(
|
|
["node", str(ROOT / "tests" / "b44_ui_check.js")],
|
|
capture_output=True,
|
|
text=True,
|
|
cwd=str(ROOT),
|
|
)
|
|
self.assertEqual(0, result.returncode, result.stdout + result.stderr)
|
|
self.assertIn("b44_ui_check ok", result.stdout)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|