from __future__ import annotations import re import unittest from html.parser import HTMLParser from pathlib import Path STATIC_DIR = Path(__file__).resolve().parents[1] / "static" class IdCollector(HTMLParser): def __init__(self) -> None: super().__init__() self.ids: list[str] = [] def handle_starttag(self, tag, attrs): self.ids.extend(value for key, value in attrs if key == "id" and value) class FrontendContractTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.html = (STATIC_DIR / "index.html").read_text(encoding="utf-8") cls.script = (STATIC_DIR / "app.js").read_text(encoding="utf-8") collector = IdCollector() collector.feed(cls.html) cls.ids = collector.ids def test_html_ids_are_unique(self): duplicates = sorted({item for item in self.ids if self.ids.count(item) > 1}) self.assertEqual(duplicates, []) def test_literal_id_selectors_exist_in_html(self): selectors = set(re.findall(r'querySelector\("#([A-Za-z][A-Za-z0-9_-]*)"\)', self.script)) selectors.update(re.findall(r'getElementById\("([A-Za-z][A-Za-z0-9_-]*)"\)', self.script)) selectors.update(re.findall(r'setText\("([A-Za-z][A-Za-z0-9_-]*)"', self.script)) missing = sorted(selectors - set(self.ids)) self.assertEqual(missing, []) def test_all_primary_views_have_navigation_entries(self): views = set(re.findall(r'id="([A-Za-z][A-Za-z0-9_-]*View|limitPool)" class="workspace-view', self.html)) navigation = set(re.findall(r'data-view="([A-Za-z][A-Za-z0-9_-]*)"', self.html)) self.assertEqual(views, navigation) self.assertEqual(len(views), 13) if __name__ == "__main__": unittest.main()