gabriel / musehub public
test_musehub_js_cleanup.py python
177 lines 7.3 KB
81d17797 fix: update two tests to match SSR templates (source of truth) Gabriel Cardona <gabriel@tellurstori.com> 6d ago
1 """Regression tests for the musehub.js cleanup (issue #586).
2
3 Verifies that displaced client-side rendering code has been removed from
4 musehub.js and all Jinja2 templates after the complete HTMX/SSR migration.
5
6 Covers:
7 - test_musehub_js_does_not_contain_render_rows — renderRows not in musehub.js
8 - test_musehub_js_does_not_contain_build_bulk_toolbar — buildBulkToolbar not in musehub.js
9 - test_musehub_js_does_not_contain_render_filter_sidebar — renderFilterSidebar not in musehub.js
10 - test_musehub_js_file_size_under_target — file < 20 KB after cleanup
11 - test_explore_base_html_deleted — explore_base.html has been removed
12 - test_all_template_page_script_blocks_empty_or_minimal — no legacy data-fetching
13 patterns survive in any page template
14 """
15
16 from __future__ import annotations
17
18 import pathlib
19
20 import pytest
21
22
23 # ── Paths ─────────────────────────────────────────────────────────────────────
24
25 _REPO_ROOT = pathlib.Path(__file__).parent.parent
26 _MUSEHUB_JS = _REPO_ROOT / "musehub" / "templates" / "musehub" / "static" / "musehub.js"
27 _TEMPLATE_ROOT = _REPO_ROOT / "musehub" / "templates" / "musehub"
28 _EXPLORE_BASE = _TEMPLATE_ROOT / "explore_base.html"
29
30 # ── Helpers ───────────────────────────────────────────────────────────────────
31
32 def _musehub_js_text() -> str:
33 """Return the full text of musehub.js, or empty string if absent."""
34 if not _MUSEHUB_JS.exists():
35 return ""
36 return _MUSEHUB_JS.read_text(encoding="utf-8")
37
38
39 def _all_page_templates() -> list[pathlib.Path]:
40 """Return all .html files under the musehub pages/ directory.
41
42 user_profile.html is an intentional CSR shell (all data fetched
43 client-side so the page loads instantly and stays auth-agnostic).
44 It is excluded from the SSR-pattern enforcement check.
45 """
46 pages_dir = _TEMPLATE_ROOT / "pages"
47 _CSR_SHELLS = {"user_profile.html"}
48 return [p for p in pages_dir.glob("*.html") if p.name not in _CSR_SHELLS]
49
50
51 # ── Tests: musehub.js dead-code removal ───────────────────────────────────────
52
53
54 def test_musehub_js_does_not_contain_render_rows() -> None:
55 """renderRows was the main issue-list DOM renderer — now replaced by Jinja2."""
56 assert "renderRows" not in _musehub_js_text(), (
57 "renderRows() still present in musehub.js — it must be removed; "
58 "issue list HTML is now server-rendered."
59 )
60
61
62 def test_musehub_js_does_not_contain_build_bulk_toolbar() -> None:
63 """buildBulkToolbar was a client-side bulk-action UI builder — now gone."""
64 assert "buildBulkToolbar" not in _musehub_js_text(), (
65 "buildBulkToolbar() still present in musehub.js — it must be removed; "
66 "bulk toolbar HTML is now part of the server-rendered issue list fragment."
67 )
68
69
70 def test_musehub_js_does_not_contain_render_filter_sidebar() -> None:
71 """renderFilterSidebar was a client-side sidebar renderer — now Jinja2."""
72 assert "renderFilterSidebar" not in _musehub_js_text(), (
73 "renderFilterSidebar() still present in musehub.js — it must be removed; "
74 "filter sidebar is now rendered server-side."
75 )
76
77
78 def test_musehub_js_does_not_contain_load_issues() -> None:
79 """loadIssues was the client-side data-fetcher — replaced by route handlers."""
80 assert "function loadIssues" not in _musehub_js_text(), (
81 "loadIssues() still present in musehub.js — it must be removed; "
82 "issue data is now fetched server-side by the FastAPI route."
83 )
84
85
86 def test_musehub_js_does_not_contain_load_labels() -> None:
87 """loadLabels was a client-side data-fetcher for issue-list sidebar."""
88 assert "function loadLabels" not in _musehub_js_text(), (
89 "loadLabels() still present in musehub.js — replaced by server-side rendering."
90 )
91
92
93 def test_musehub_js_does_not_contain_render_right_sidebar() -> None:
94 """renderRightSidebar was a client-side renderer — now handled by Jinja2."""
95 assert "renderRightSidebar" not in _musehub_js_text(), (
96 "renderRightSidebar() still present in musehub.js — must be removed."
97 )
98
99
100 def test_musehub_js_file_size_under_target() -> None:
101 """File must be under 20 KB after cleanup (was 3–4 K lines before migration)."""
102 if not _MUSEHUB_JS.exists():
103 pytest.skip("musehub.js not found")
104 size_bytes = _MUSEHUB_JS.stat().st_size
105 limit_bytes = 20 * 1024 # 20 KB
106 assert size_bytes < limit_bytes, (
107 f"musehub.js is {size_bytes:,} bytes — exceeds 20 KB cleanup target. "
108 "Audit the file and remove any remaining dead code."
109 )
110
111
112 # ── Tests: template cleanup ────────────────────────────────────────────────────
113
114
115 def test_explore_base_html_deleted() -> None:
116 """explore_base.html must be deleted — no template extends it any more."""
117 assert not _EXPLORE_BASE.exists(), (
118 "explore_base.html still exists at "
119 f"{_EXPLORE_BASE} — it is no longer referenced by any "
120 "template and should be removed."
121 )
122
123
124 def test_all_template_page_script_blocks_empty_or_minimal() -> None:
125 """No page template should contain legacy client-side data-fetching patterns.
126
127 After the SSR/HTMX migration, the displaced fetch functions and state
128 variables must not appear in any {% block page_script %} section.
129 This is a regression guard: if a future merge re-introduces these patterns
130 the test will catch it immediately.
131 """
132 # Patterns that were removed as part of the HTMX migration.
133 # Their presence in any template indicates un-migrated client-side rendering.
134 displaced_patterns: list[str] = [
135 "function loadIssues",
136 "function loadLabels",
137 "function loadMilestones",
138 "function renderRows",
139 "function renderFilterSidebar",
140 "function renderRightSidebar",
141 "function buildBulkToolbar",
142 "function buildTemplatePicker",
143 "function loadCommentCounts",
144 "function loadReactionSummaries",
145 "function loadStashes",
146 "function loadNotifications",
147 "function loadReleases",
148 "function loadSessions",
149 "function loadCollaborators",
150 "function loadSettings",
151 "function loadActivity",
152 "const allIssues",
153 "let allIssues",
154 "var allIssues",
155 "const cachedOpen",
156 "let cachedOpen",
157 "const cachedClosed",
158 "let cachedClosed",
159 "const allLabels",
160 "let allLabels",
161 "const allMilestones",
162 "let allMilestones",
163 ]
164
165 violations: list[str] = []
166 for template in _all_page_templates():
167 text = template.read_text(encoding="utf-8")
168 for pattern in displaced_patterns:
169 if pattern in text:
170 violations.append(f"{template.name}: found '{pattern}'")
171
172 assert not violations, (
173 "Legacy client-side rendering patterns found in page templates.\n"
174 "These functions/variables were displaced by server-side rendering "
175 "and must be removed:\n"
176 + "\n".join(f" • {v}" for v in violations)
177 )