diff --git a/services/travel-memories/tests/test_crosscutting.py b/services/travel-memories/tests/test_crosscutting.py new file mode 100644 index 0000000..a9aa6c7 --- /dev/null +++ b/services/travel-memories/tests/test_crosscutting.py @@ -0,0 +1,87 @@ +import json + + +def test_hard_refresh_preserves_triage_state(base_url, page, seed_state, flask_app): + """State is server-side — hard refresh must not reset it.""" + album_id = seed_state("phase2_state") + page.goto(f"{base_url}/triage?album_id={album_id}") + page.locator(".photo-card").first.click() + page.keyboard.press("j") + page.wait_for_timeout(400) + page.reload() + first_card = page.locator(".photo-card").first + assert "border-success" in first_card.get_attribute("class") + + +def test_back_nav_from_group_to_triage_marks_curate_group_stale(base_url, page, seed_state): + album_id = seed_state("phase4_state") # completed=[triage, curate], phase=group + page.request.post( + f"{base_url}/nav/phase", + data=json.dumps({"album_id": album_id, "target_phase": "triage"}), + headers={"Content-Type": "application/json"}, + ) + resp = page.request.get(f"{base_url}/state/{album_id}") + state = resp.json() + assert "curate" in state["phase_stale"] + assert "group" in state["phase_stale"] + + +def test_stale_banner_visible_on_stale_phase(base_url, page, seed_state): + album_id = seed_state("phase4_state") + page.request.post( + f"{base_url}/nav/phase", + data=json.dumps({"album_id": album_id, "target_phase": "triage"}), + headers={"Content-Type": "application/json"}, + ) + # Now visit curate (which is stale) + page.goto(f"{base_url}/curate?album_id={album_id}") + assert page.locator("#stale-banner").is_visible() + + +def test_dismiss_stale_clears_flag(base_url, page, seed_state, flask_app): + album_id = seed_state("phase4_state") + page.request.post( + f"{base_url}/nav/phase", + data=json.dumps({"album_id": album_id, "target_phase": "triage"}), + headers={"Content-Type": "application/json"}, + ) + page.goto(f"{base_url}/curate?album_id={album_id}") + page.locator("#stale-banner button").click() + page.wait_for_url("**/curate**") + with flask_app.app_context(): + from app.state import load_state + state = load_state(album_id, flask_app) + assert "curate" not in state.phase_stale + + +def test_exported_group_not_affected_by_back_nav(base_url, page, seed_state, flask_app): + """Exporting then going back to triage must not touch the exported group.""" + album_id = seed_state("phase6_state") + # Manually set one group to exported + with flask_app.app_context(): + from app.state import load_state, save_state + state = load_state(album_id, flask_app) + state.groups[0].status = "exported" + save_state(state, flask_app) + # Navigate back + page.request.post( + f"{base_url}/nav/phase", + data=json.dumps({"album_id": album_id, "target_phase": "triage"}), + headers={"Content-Type": "application/json"}, + ) + with flask_app.app_context(): + from app.state import load_state + state = load_state(album_id, flask_app) + assert state.groups[0].status == "exported" + + +def test_notes_autosave_survives_phase_navigation(base_url, page, seed_state, flask_app): + album_id = seed_state("phase2_state") + page.request.post( + f"{base_url}/notes/save", + data=json.dumps({"album_id": album_id, "notes": "survives navigation"}), + headers={"Content-Type": "application/json"}, + ) + page.goto(f"{base_url}/curate?album_id={album_id}") + resp = page.request.get(f"{base_url}/notes/{album_id}") + assert resp.json()["notes"] == "survives navigation"