Navigation

Test source at commit 5274a75a · v0.2.44

UC-11: Route handling + mode switching (P1).

Test flow:

View source on GitHubtests/qa/v030/p1__navigation/test__navigation.py


Test Methods

Method Description Screenshots
gallery_route GET /en-gb/gallery/#hash loads gallery view. 0
browse_route GET /en-gb/browse/#hash loads browse view. 0
view_route GET /en-gb/view/#hash loads single-file viewer. 0
short_v_route /en-gb/v/#hash is equivalent to /en-gb/view/#hash. 0
download_route_auto_detect /en-gb/download/#hash auto-detects mode and decrypts. 0
gallery_to_browse_hash_preserved Gallery ‘Folder view’ link navigates to /browse/ (hash preserved if supported). 0
browse_to_gallery_hash_preserved Browse ‘Gallery view’ link navigates to /gallery/ (hash preserved if supported). 0
copy_link_includes_key Copy Link button in gallery/browse includes the key in the URL (P1). 0

Screenshots

Gallery route loaded

01 Gallery Route

Deterministic view (non-dynamic areas only) ![01 Gallery Route — masked](screenshots/01_gallery_route__deterministic.png)

02 Browse Route

Browse route loaded

02 Browse Route

Deterministic view (non-dynamic areas only) ![02 Browse Route — masked](screenshots/02_browse_route__deterministic.png)

03 View Route

View route loaded

03 View Route

Deterministic view (non-dynamic areas only) ![03 View Route — masked](screenshots/03_view_route__deterministic.png)

04 Short V Route

Short /v/ route loaded

04 Short V Route

Deterministic view (non-dynamic areas only) ![04 Short V Route — masked](screenshots/04_short_v_route__deterministic.png)

05 Download Auto

Download route auto-detect

05 Download Auto

Deterministic view (non-dynamic areas only) ![05 Download Auto — masked](screenshots/05_download_auto__deterministic.png)

Gallery → browse

06 Gallery To Browse

Deterministic view (non-dynamic areas only) ![06 Gallery To Browse — masked](screenshots/06_gallery_to_browse__deterministic.png)

Browse → gallery

07 Browse To Gallery

Deterministic view (non-dynamic areas only) ![07 Browse To Gallery — masked](screenshots/07_browse_to_gallery__deterministic.png)

Copy Link button clicked

08 Copy Link

Deterministic view (non-dynamic areas only) ![08 Copy Link — masked](screenshots/08_copy_link__deterministic.png)

View test source — tests/qa/v030/p1__navigation/test__navigation.py ```python """UC-11: Route handling + mode switching (P1). Test flow: - Upload a zip with images via API - Navigate to /gallery/#hash → verify gallery view - Navigate to /browse/#hash → verify browse view - Navigate to /view/#hash → verify single file view - Navigate to /v/#hash → verify same as /view/ (short URL) - Navigate to /download/#hash → verify auto-detect mode - Verify gallery "Folder view" link → navigates to /browse/ - Verify browse "Gallery view" link → navigates to /gallery/ """ import pytest import zipfile import io pytestmark = pytest.mark.p1 _PNG_HEADER = ( b'\x89PNG\r\n\x1a\n' b'\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01' b'\x08\x02\x00\x00\x00\x90wS\xde' b'\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f\x00\x00\x01\x01\x00\x05\x18\xd8N' b'\x00\x00\x00\x00IEND\xaeB`\x82' ) def _make_zip(): buf = io.BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr("archive/img1.png", _PNG_HEADER) zf.writestr("archive/img2.png", _PNG_HEADER) zf.writestr("archive/img3.png", _PNG_HEADER) buf.seek(0) return buf.read() def _open_route(page, url: str) -> None: """Navigate to a download/gallery/browse route and wait for it to settle. CR-001: Uses body[data-ready] (set by SG/Send bootstrap) instead of networkidle — networkidle never resolves on routes with background API calls (e.g. checkToken). After the ready signal, wait_for_timeout(1500) lets JS render shadow DOM content before screenshot/assertion. SG/Send renders all visible content in shadow DOM, so body.textContent is always empty — do NOT use expect(body).not_to_be_empty() here. """ page.goto(url, wait_until="commit") page.wait_for_selector("body[data-ready]", timeout=10_000) page.wait_for_timeout(1_500) class TestRouteHandling: """Verify that all download routes load correctly and preserve hash.""" def _make_transfer(self, transfer_helper): zip_bytes = _make_zip() return transfer_helper.upload_encrypted(zip_bytes, "archive.zip") def test_gallery_route(self, page, ui_url, transfer_helper, screenshots): """GET /en-gb/gallery/#hash loads gallery view.""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/gallery/#{tid}/{key_b64}") screenshots.capture(page, "01_gallery_route", "Gallery route loaded") # inner_text avoids false positive from inline scripts containing "error" page_text = page.inner_text("body") or "" assert "error" not in page_text.lower() or len(page_text) > 100 def test_browse_route(self, page, ui_url, transfer_helper, screenshots): """GET /en-gb/browse/#hash loads browse view.""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/browse/#{tid}/{key_b64}") screenshots.capture(page, "02_browse_route", "Browse route loaded") page_text = page.inner_text("body") or "" assert "error" not in page_text.lower() or len(page_text) > 100 def test_view_route(self, page, ui_url, transfer_helper, screenshots): """GET /en-gb/view/#hash loads single-file viewer.""" tid, key_b64 = transfer_helper.upload_encrypted(b"plain text content", "note.txt") _open_route(page, f"{ui_url}/en-gb/view/#{tid}/{key_b64}") screenshots.capture(page, "03_view_route", "View route loaded") page_text = page.inner_text("body") or "" assert "error" not in page_text.lower() or len(page_text) > 100 def test_short_v_route(self, page, ui_url, transfer_helper, screenshots): """/en-gb/v/#hash is equivalent to /en-gb/view/#hash.""" tid, key_b64 = transfer_helper.upload_encrypted(b"short url test content", "note.txt") _open_route(page, f"{ui_url}/en-gb/v/#{tid}/{key_b64}") screenshots.capture(page, "04_short_v_route", "Short /v/ route loaded") page_text = page.inner_text("body") or "" assert "error" not in page_text.lower() or len(page_text) > 100 def test_download_route_auto_detect(self, page, ui_url, transfer_helper, screenshots): """/en-gb/download/#hash auto-detects mode and decrypts.""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/download/#{tid}/{key_b64}") screenshots.capture(page, "05_download_auto", "Download route auto-detect") page_text = page.inner_text("body") or "" assert "error" not in page_text.lower() or len(page_text) > 100 def test_gallery_to_browse_hash_preserved(self, page, ui_url, transfer_helper, screenshots): """Gallery 'Folder view' link navigates to /browse/ (hash preserved if supported).""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/gallery/#{tid}/{key_b64}") browse_link = page.locator( "a:has-text('Folder view'), a:has-text('Browse'), a[href*='browse']" ).first if browse_link.is_visible(timeout=5000): browse_link.click() page.wait_for_selector("body[data-ready]", timeout=10_000) page.wait_for_timeout(800) screenshots.capture(page, "06_gallery_to_browse", "Gallery → browse") current_url = page.url assert "/browse/" in current_url, \ f"Gallery→browse navigation failed: {current_url}" # Hash preservation depends on UI version — not asserted here def test_browse_to_gallery_hash_preserved(self, page, ui_url, transfer_helper, screenshots): """Browse 'Gallery view' link navigates to /gallery/ (hash preserved if supported).""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/browse/#{tid}/{key_b64}") gallery_link = page.locator( "a:has-text('Gallery view'), a:has-text('Gallery'), a[href*='gallery']" ).first if gallery_link.is_visible(timeout=5000): gallery_link.click() page.wait_for_selector("body[data-ready]", timeout=10_000) page.wait_for_timeout(800) screenshots.capture(page, "07_browse_to_gallery", "Browse → gallery") current_url = page.url assert "/gallery/" in current_url, \ f"Browse→gallery navigation failed: {current_url}" # Hash preservation depends on UI version — not asserted here def test_copy_link_includes_key(self, page, ui_url, transfer_helper, screenshots): """Copy Link button in gallery/browse includes the key in the URL (P1).""" tid, key_b64 = self._make_transfer(transfer_helper) _open_route(page, f"{ui_url}/en-gb/gallery/#{tid}/{key_b64}") # Look for Copy Link button or URL input copy_btn = page.locator( "button:has-text('Copy'), button:has-text('Copy Link'), [class*='copy']" ).first if copy_btn.is_visible(timeout=3000): copy_btn.click() page.wait_for_timeout(500) screenshots.capture(page, "08_copy_link", "Copy Link button clicked") # Check that any URL input shown contains the hash url_input = page.locator("input[readonly], input[value*='#']").first if url_input.is_visible(timeout=3000): url_value = url_input.get_attribute("value") or "" assert "#" in url_value, \ f"Copy Link URL missing hash fragment (key not included): {url_value}" ```