from __future__ import annotations import unittest from html.parser import HTMLParser from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[1] STATIC_ROOT = PROJECT_ROOT / "arr_web" / "static" class DailyVisualStaticContractTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.html = (STATIC_ROOT / "index.html").read_text(encoding="utf-8") cls.styles = (STATIC_ROOT / "styles.css").read_text(encoding="utf-8") def test_daily_workspace_uses_one_clear_history_title(self) -> None: self.assertIn('ARR Report', self.html) self.assertIn('

Daily Report

', self.html) self.assertNotIn('

Daily Report

', self.html) self.assertNotIn('class="daily-section-heading"', self.html) self.assertNotIn("THIS MONTH", self.html) self.assertNotIn("日报处理记录", self.html) self.assertIn("font-size: clamp(20px, 1.75vw, 24px)", self.styles) def test_task_log_utility_is_a_low_emphasis_link(self) -> None: self.assertIn(' None: overview_start = self.html.index('
\n\n
.metric-card", self.styles) self.assertIn( ".daily-overview > .metric-card { align-self: stretch; display: flex; flex-direction: column; justify-content: center; height: auto; min-height: 0;", self.styles, ) self.assertIn(".daily-overview > .metric-card::after", self.styles) self.assertNotIn("width: max-content", self.styles) self.assertIn("height: auto; min-height: 0", self.styles) self.assertIn(".daily-overview { grid-template-columns: 1fr;", self.styles) self.assertIn("@media (min-width: 701px)", self.styles) self.assertIn(".daily-overview .dropzone {", self.styles) self.assertIn("grid-template-columns: 28px minmax(0, 1fr)", self.styles) self.assertIn("min-height: 72px", self.styles) self.assertIn(".daily-overview .upload-footer", self.styles) self.assertIn("min-height: 44px", self.styles) self.assertIn("min-width: 96px", self.styles) def test_upload_panel_has_compact_action_footer(self) -> None: self.assertIn('class="panel-heading upload-heading"', self.html) self.assertIn('class="upload-footer"', self.html) self.assertIn(".upload-panel .primary-button", self.styles) self.assertIn("min-width: 118px", self.styles) self.assertNotIn("width: calc(100% - 48px)", self.styles) def test_download_card_exposes_accessible_retrieval_progress_with_reduced_motion(self) -> None: start = self.html.index('id="arr-acquisition-progress"') end = self.html.index('
', start) progress = self.html[start:end] self.assertLess(self.html.index('id="arr-download-status"'), start) self.assertIn('role="progressbar"', progress) self.assertIn('aria-labelledby="arr-acquisition-label"', progress) self.assertIn('aria-describedby="arr-acquisition-detail"', progress) self.assertIn('aria-valuemin="0" aria-valuemax="100"', progress) self.assertNotIn('aria-valuenow=', progress) self.assertIn('@media (prefers-reduced-motion: reduce)', self.styles) self.assertIn('.arr-acquisition-track.is-indeterminate > span { animation: none; }', self.styles) def test_review_dates_are_independent_and_both_review_bodies_share_one_workspace(self) -> None: class Regions(HTMLParser): def __init__(self) -> None: super().__init__() self.stack = [] self.parents = {} def handle_starttag(self, tag, attrs): element_id = dict(attrs).get("id") if element_id: self.parents[element_id] = [item[1] for item in self.stack if item[1]] if tag not in {"area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"}: self.stack.append((tag, element_id)) def handle_endtag(self, tag): for index in range(len(self.stack) - 1, -1, -1): if self.stack[index][0] == tag: del self.stack[index:] break regions = Regions() regions.feed(self.html) dates = regions.parents["arr-download-pending"] self.assertNotIn("arr-download-form", dates) self.assertNotIn("arr-data-review-panel", dates) self.assertNotIn("manual-review-workspace", dates) for body_id in ("arr-data-review-panel", "daily-price-review-panel"): self.assertIn("manual-review-workspace", regions.parents[body_id]) self.assertLess(self.html.index('id="arr-download-pending"'), self.html.index('id="manual-review-workspace"')) self.assertEqual(self.html.count('

人工核对

'), 1) self.assertIn('

需处理日期

', self.html) self.assertNotIn('

', self.html) self.assertNotIn('

', self.html) def test_removed_upload_labels_do_not_return(self) -> None: upload_start = self.html.index('class="panel upload-panel"') upload_end = self.html.index("

", upload_start) upload_markup = self.html[upload_start:upload_end] self.assertNotIn("本月留痕", upload_markup) self.assertNotIn("01 / UPLOAD", upload_markup) if __name__ == "__main__": unittest.main(verbosity=2)