shopstack / tests /test_app_architecture_fixes.py
pranaysuyash's picture
Sync ShopStack 2026-06-15: corrections panel, empty-state rewrite, market-source suppression
8294cde verified
Raw
History Blame Contribute Delete
4.59 kB
"""Regression tests for app.py architecture fixes (2026-06-14).
These tests verify:
1. Required-handle guards exist in app.py (P0 from audit)
2. Household-switch triggers a full page reload (so all tabs
show fresh data, not just Today)
3. The JS reload helper is properly wired
4. The window.toggleTheme binding exists in the header script
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parents[1]
class TestAppPyHandleGuards:
"""app.py must fail fast if today/reconcile builders return None."""
def test_today_handle_guarded(self):
app_source = (REPO_ROOT / "app.py").read_text(encoding="utf-8")
# Look for a guard pattern: handles.get("today") with a check
assert re.search(
r'handles\.get\(["\']today["\']\)',
app_source,
), "app.py must extract today handles with handles.get('today')"
# And a RuntimeError check
assert re.search(
r"today_handles\s+is\s+None.*?RuntimeError",
app_source,
re.DOTALL,
), "app.py must check 'today_handles is None' and raise RuntimeError"
def test_reconcile_handle_guarded(self):
app_source = (REPO_ROOT / "app.py").read_text(encoding="utf-8")
assert re.search(
r'handles\.get\(["\']reconcile["\']\)',
app_source,
), "app.py must extract reconcile handles with handles.get('reconcile')"
assert re.search(
r"reconcile_handles\s+is\s+None.*?RuntimeError",
app_source,
re.DOTALL,
), "app.py must check 'reconcile_handles is None' and raise RuntimeError"
class TestHouseholdSwitchReload:
"""Switching households must reload the page so all tabs refresh.
The reload JS was extracted from app.py to
``shopstack/ui/state/household_wiring.py`` during the household-wiring
refactor (motto §7 supersession). These tests check the canonical
location. app.py delegates via ``wire_household_handlers()``.
"""
WIRING_PATH = REPO_ROOT / "shopstack" / "ui" / "state" / "household_wiring.py"
def test_reload_js_function_exists(self):
"""The household-switch reload JS must exist in household_wiring.py."""
wiring_source = self.WIRING_PATH.read_text(encoding="utf-8")
assert "_HOUSEHOLD_SWITCH_RELOAD_JS" in wiring_source, (
"household_wiring.py must define _HOUSEHOLD_SWITCH_RELOAD_JS "
"so all tabs show fresh data after a household switch"
)
def test_reload_js_uses_location_href_not_replace(self):
"""The reload JS should use window.location.href (preserves hash)
not location.replace (loses hash)."""
wiring_source = self.WIRING_PATH.read_text(encoding="utf-8")
assert "window.location.href" in wiring_source, (
"Reload should use window.location.href to preserve the hash"
)
def test_reload_wired_to_household_change(self):
"""The reload JS must be wired to the household dropdown change."""
wiring_source = self.WIRING_PATH.read_text(encoding="utf-8")
assert "household_dropdown.change" in wiring_source or ".change" in wiring_source, (
"The reload must be invoked after the household dropdown change"
)
def test_reload_preserves_url_hash(self):
"""The reload should preserve the URL hash so the user stays
on the same tab (Today, Basket, etc.) after switching."""
wiring_source = self.WIRING_PATH.read_text(encoding="utf-8")
assert "window.location.hash" in wiring_source, (
"Reload should read window.location.hash to preserve the tab"
)
class TestToggleThemeWindowBinding:
"""toggleTheme must be explicitly bound to window for P0 browser
hydration robustness (in case script_bootstrap_js doesn't fire)."""
def test_header_script_binds_toggle_theme_to_window(self):
header_source = (REPO_ROOT / "shopstack" / "ui" / "header.py").read_text(
encoding="utf-8",
)
# The header script should explicitly bind toggleTheme to window
# so it's accessible regardless of how the script is executed
assert re.search(
r"window\.toggleTheme\s*=\s*toggleTheme",
header_source,
), (
"header.py must explicitly bind toggleTheme to window "
"(window.toggleTheme = toggleTheme) so the theme toggle "
"button works even if script_bootstrap_js doesn't fire."
)