import json import subprocess from pathlib import Path import pytest from src.views import dashboard OFFLINE_WORK = Path(__file__).parents[1] / "frontend" / "offline-work.js" def run_scenario(scenario: str) -> dict: script = f""" const createOfflineWorkStore = require({json.dumps(str(OFFLINE_WORK))}); const values = new Map(); const storage = {{ getItem: key => values.has(key) ? values.get(key) : null, setItem: (key, value) => values.set(key, value), removeItem: key => values.delete(key), }}; {scenario} """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) return json.loads(result.stdout) def test_opted_in_snapshot_survives_restart_with_only_queue_card_fields(): result = run_scenario(""" const store = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:00:00Z')}); store.setEnabled(true); store.save({ user:{login:'timmy', full_name:'Timmy', token:'secret'}, issues:[{id:1, number:8, title:'Ship offline work', body:'private body', state:'open', repository:'stackchain/dashboard', labels:['P0'], assignees:['timmy'], updated_at:'2026-08-07T11:00:00Z', url:'https://forge.example/issues/8'}], pull_requests:[], notifications:[{id:9, number:8, title:'Updated', unread:true, subject_body:'private comment', repository:'stackchain/dashboard', subject_type:'Issue', updated_at:'2026-08-07T11:30:00Z', url:'https://forge.example/issues/8'}], work_pagination:{issue:{page:1,total:1,has_more:false}}, repos:[{private:true}], events:[{body:'private event'}], }); const restarted = createOfflineWorkStore({storage, now:() => new Date('2026-08-07T12:01:00Z')}); process.stdout.write(JSON.stringify({loaded:restarted.load('timmy'), raw:[...values.values()].join(' ')})); """) assert result["loaded"]["user"] == {"login": "timmy", "full_name": "Timmy"} assert result["loaded"]["issues"][0]["title"] == "Ship offline work" assert result["loaded"]["notifications"][0]["unread"] is True assert result["loaded"]["saved_at"] == "2026-08-07T12:00:00.000Z" assert "private body" not in result["raw"] assert "private comment" not in result["raw"] assert "private event" not in result["raw"] assert "secret" not in result["raw"] def test_expired_snapshot_is_deleted_and_wrong_user_cannot_load_it(): result = run_scenario(""" const writer = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:00Z'), maxAgeMs:1000}); writer.setEnabled(true); writer.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]}); const wrongUser = writer.load('alexander'); const expired = createOfflineWorkStore({storage, now:() => new Date('2026-08-01T12:00:02Z'), maxAgeMs:1000}); const expiredValue = expired.load('timmy'); process.stdout.write(JSON.stringify({wrongUser, expiredValue, snapshotStillStored:values.has('stackchain.offline-work.snapshot.v1')})); """) assert result == { "wrongUser": None, "expiredValue": None, "snapshotStillStored": False, } def test_opting_out_deletes_the_persisted_snapshot(): result = run_scenario(""" const store = createOfflineWorkStore({storage}); store.setEnabled(true); store.save({user:{login:'timmy'}, issues:[], pull_requests:[], notifications:[]}); store.setEnabled(false); process.stdout.write(JSON.stringify({enabled:store.enabled(), loaded:store.load()})); """) assert result == {"enabled": False, "loaded": None} @pytest.mark.anyio async def test_dashboard_offers_private_offline_work_controls_and_read_only_hydration(): html = await dashboard() assert '' in html assert 'id="keep-work-offline"' in html assert 'Keep My Work available offline' in html assert 'id="clear-offline-work"' in html assert 'id="offline-work-status"' in html assert 'offlineWorkStore.save({' in html assert 'offlineWorkStore.load()' in html assert 'Offline · saved ' in html assert "setOfflineWorkMode(true)" in html assert "if (offlineWorkMode)" in html assert '.offline-work-controls button { min-height:44px;' in html @pytest.mark.anyio async def test_initial_http_outage_hydrates_saved_work_and_recovers_on_live_snapshot(): html = await dashboard() assert "if (!hasContextSnapshot && hydrateOfflineWork('outage')) return;" in html assert "Outage · saved " in html assert "Server unavailable · showing private My Work saved " in html assert "Live details and actions will return automatically." in html assert "function renderLiveSnapshot(snapshot, changedSections" in html assert "setOfflineWorkMode(false);\n offlineStatus.hidden = true;" in html