"""Tests for #822 — session list empty after browser reload / version update. Root cause (from Opus analysis): Chrome's bfcache restores a prior search query into `#sessionSearch` on page restore; `renderSessionListFromCache()` reads that field and applies it as a title filter — hiding every session. Fix: - ``autocomplete="off"`` on the input hints to browsers not to restore the value - Boot-time explicit `sessionSearch.value = ''` before the first render covers fresh loads and hard reloads - ``pageshow`` listener that checks ``event.persisted`` covers the true bfcache restore case (where the async boot IIFE does NOT re-run) """ import pathlib import re REPO = pathlib.Path(__file__).parent.parent def read(rel): return (REPO / rel).read_text(encoding='utf-8') class TestSessionSearchAutocompleteAttribute: """index.html must opt the session search input out of browser autocomplete/restore.""" def test_session_search_has_autocomplete_off(self): src = read('static/index.html') m = re.search(r']*id="sessionSearch"[^>]*>', src) assert m, "#sessionSearch input tag not found in index.html" tag = m.group(0) assert 'autocomplete="off"' in tag or "autocomplete='off'" in tag, ( "#sessionSearch must have autocomplete=\"off\" so browsers do not " "restore a prior search query across reloads or bfcache restores (#822)" ) class TestBootClearsSessionSearch: """Boot sequence must clear any browser-restored search value before the first render, so the initial `renderSessionListFromCache` call sees an empty filter and shows all sessions.""" def test_boot_clears_session_search_value_before_first_render(self): src = read('static/boot.js') # Must find a line that sets sessionSearch.value = '' at boot assert re.search( r"getElementById\(['\"]sessionSearch['\"]\)\s*;\s*if\s*\([^)]+\)\s*[^=]+\.value\s*=\s*['\"]{2}" r"|sessionSearch[^=]+\.value\s*=\s*['\"]{2}", src, ), ( "boot.js must clear #sessionSearch.value to '' before the first render " "(before renderSessionList / loadSession) to avoid stale filter from " "browser form restoration (#822)" ) def test_boot_clear_is_before_first_render_call(self): """The clear must precede the first renderSessionList call path so the initial render shows an unfiltered list.""" src = read('static/boot.js') clear_pos = None m = re.search(r"getElementById\(['\"]sessionSearch['\"]\)", src) if m: clear_pos = m.start() assert clear_pos is not None, "session search clear not found in boot.js" first_render_pos = src.find('renderSessionList()', clear_pos) assert first_render_pos != -1, "renderSessionList() call not found after clear" assert clear_pos < first_render_pos, ( "sessionSearch clear must appear before the first renderSessionList() call" ) class TestPageShowBfcacheHandler: """bfcache restore path: the async boot IIFE does NOT re-run when the browser restores the page from back-forward cache, but the DOM — including any stale value in #sessionSearch — IS restored. A `pageshow` listener with `event.persisted` check is the only reliable way to clear on bfcache.""" def test_pageshow_listener_registered(self): src = read('static/boot.js') assert re.search( r"addEventListener\(\s*['\"]pageshow['\"]", src, ), ( "boot.js must register a `pageshow` event listener to handle " "bfcache-restored page views (#822)" ) def test_pageshow_handler_checks_event_persisted(self): """Only bfcache restores set event.persisted=true; fresh loads have it false and are already handled by the boot IIFE. Guarding prevents clearing the search on every page show (which would wipe an in-progress user filter if any other pageshow triggers happen).""" src = read('static/boot.js') m = re.search( r"addEventListener\(\s*['\"]pageshow['\"].*?\}\s*\)", src, re.DOTALL, ) assert m, "pageshow listener body not found" body = m.group(0) assert 'persisted' in body, ( "pageshow handler must guard on event.persisted so fresh loads " "don't double-clear the field" ) def test_pageshow_handler_clears_session_search(self): src = read('static/boot.js') m = re.search( r"addEventListener\(\s*['\"]pageshow['\"].*?\}\s*\)", src, re.DOTALL, ) assert m body = m.group(0) assert 'sessionSearch' in body, ( "pageshow handler must target #sessionSearch specifically" ) assert re.search(r"\.value\s*=\s*['\"]{2}", body), ( "pageshow handler must set sessionSearch.value = ''" ) def test_pageshow_handler_triggers_rerender(self): """After clearing on bfcache restore, the cached DOM still shows the filtered view. Re-rendering from cache with the now-empty filter repopulates the list.""" src = read('static/boot.js') m = re.search( r"addEventListener\(\s*['\"]pageshow['\"].*?\}\s*\)", src, re.DOTALL, ) assert m body = m.group(0) assert 'renderSessionListFromCache' in body or 'renderSessionList' in body, ( "pageshow handler must re-render the list after clearing the filter " "so the stale filtered DOM is replaced with the full list" )