diff --git a/tests/test_pwa_manifest_sw.py b/tests/test_pwa_manifest_sw.py index 8d1769f37d..e1f84b3c9b 100644 --- a/tests/test_pwa_manifest_sw.py +++ b/tests/test_pwa_manifest_sw.py @@ -262,6 +262,21 @@ def test_sw_shell_assets_match_versioned_asset_urls(self): "?v=__WEBUI_VERSION__ to match the URL the page requests" ) + def test_sw_shell_assets_are_network_first(self): + """Shell JS/CSS must prefer the network, then fall back to CacheStorage. + + Cache-first with an unchanged local dev version can keep stale boot.js + loaded after a hotfix, which is exactly how browser chrome/theme-color + regressions survive a patch until someone performs cache exorcism. + """ + src = SW.read_text(encoding="utf-8") + marker = "// Shell assets: network-first with cache fallback" + assert marker in src + block = src[src.find(marker):src.find(marker) + 900] + assert "fetch(event.request).then" in block + assert "caches.match(event.request)" in block + assert "caches.match(event.request).then((cached)" not in block[:250] + def test_index_route_url_encodes_asset_version(self): src = ROUTES.read_text(encoding="utf-8") idx = src.find('parsed.path in ("/", "/index.html")') diff --git a/tests/test_theme_color_meta_bridge.py b/tests/test_theme_color_meta_bridge.py index 4faaeed86e..29041cbe46 100644 --- a/tests/test_theme_color_meta_bridge.py +++ b/tests/test_theme_color_meta_bridge.py @@ -9,6 +9,8 @@ (covering both prism-loaded and prism-absent paths) and from `_applySkin()`. - The helper reads `getComputedStyle(html).getPropertyValue('--bg')`, which means every skin (Default, Sienna, Sisyphus, Charizard, etc.) reaches the meta tag. +- Both the pre-paint script and boot sync update all theme-color tags and remove + stale media attributes so OS light/dark preference cannot override the user theme. This bridge is the source of truth that native WKWebView wrappers (hermes-webui/hermes-swift-mac) read instead of pixel-sampling the page — @@ -43,17 +45,20 @@ def test_runtime_theme_color_meta_has_stable_id(self): # Must be on a meta tag (not some other element) assert ' seeds the runtime meta tag from localStorage + def test_inline_pre_paint_script_seeds_all_theme_color_metas(self): + """An inline script in seeds all theme-color tags from localStorage before any external JS loads. This prevents a single-frame flash of the - OS-default theme-color when the user has explicitly chosen the opposite. + OS-default theme-color when the user has explicitly chosen the opposite, + and prevents media-query fallbacks from overriding the runtime tag. """ src = INDEX.read_text(encoding="utf-8") - assert "hermes-theme-color" in src + assert "hermes-theme" in src # The seeder must read from the same localStorage key the theme bootstrap uses. assert "localStorage.getItem('hermes-theme')" in src - # And must call setAttribute('content', ...) on the meta tag. + # It must update every theme-color tag and neutralize stale light/dark media hints. + assert "querySelectorAll('meta[name=\"theme-color\"]')" in src assert "setAttribute('content'" in src or 'setAttribute("content"' in src + assert "removeAttribute('media')" in src class TestBootJsThemeColorSync: @@ -70,13 +75,17 @@ def test_sync_helper_reads_computed_bg_var(self): # The helper reads getComputedStyle on documentElement and extracts --bg. assert "getComputedStyle(document.documentElement).getPropertyValue('--bg')" in src - def test_sync_helper_targets_known_meta_id(self): - """The helper must target the same id declared in index.html. Drift here - is the most common way a one-line frontend change silently breaks the - Swift app's theme-color reader. + def test_sync_helper_updates_all_theme_color_tags(self): + """The helper must update the canonical id tag and the static fallback tags. + Desktop/native chrome can prefer a matching media tag over the id tag; if + stale media variants remain light while the app is dark, the title bar goes beige. + Civilization trembles, but mostly the window looks wrong. """ src = BOOT.read_text(encoding="utf-8") assert "getElementById('hermes-theme-color')" in src + assert "querySelectorAll('meta[name=\"theme-color\"]')" in src + assert "setAttribute('content',bg)" in src + assert "removeAttribute('media')" in src def test_set_resolved_theme_calls_sync_in_both_branches(self): """_setResolvedTheme has two exit paths: