import json import subprocess from pathlib import Path import pytest from tests.dashboard_bundle import dashboard START_DAY = Path(__file__).resolve().parents[1] / "frontend" / "mobile-start-day.js" def run_node(script: str) -> dict: result = subprocess.run(["node", "-e", script], capture_output=True, text=True) assert result.returncode == 0, result.stderr return json.loads(result.stdout) def test_start_day_briefing_guides_urgent_review_before_today(): script = f""" const createStartDay = require({json.dumps(str(START_DAY))}); let counts = {{agenda:2, attention:3, update:4, filed:1, today:5}}; const opened = []; const controller = createStartDay({{ getCounts: () => counts, openQueue: name => opened.push(name), }}); const first = controller.briefing(); controller.startNext(); counts = {{agenda:0, attention:3, update:4, filed:1, today:5}}; const second = controller.briefing(); controller.startNext(); counts = {{agenda:0, attention:0, update:0, filed:0, today:5}}; const ready = controller.briefing(); controller.startNext(); process.stdout.write(JSON.stringify({{first, second, ready, opened}})); """ result = run_node(script) assert result == { "first": { "total": 10, "next": "agenda", "label": "Review Agenda", "summary": "10 items before Today · 5 planned", "phases": [ {"name": "agenda", "label": "Agenda", "count": 2}, {"name": "attention", "label": "Attention", "count": 3}, {"name": "update", "label": "Updates", "count": 4}, {"name": "filed", "label": "Filed", "count": 1}, ], }, "second": { "total": 8, "next": "attention", "label": "Review Attention", "summary": "8 items before Today · 5 planned", "phases": [ {"name": "attention", "label": "Attention", "count": 3}, {"name": "update", "label": "Updates", "count": 4}, {"name": "filed", "label": "Filed", "count": 1}, ], }, "ready": { "total": 0, "next": "today", "label": "Continue Today", "summary": "Review clear · 5 planned", "phases": [], }, "opened": ["agenda", "attention", "today"], } def test_start_day_view_renders_refreshed_phases_and_launches_primary_action(): script = f""" const createStartDay = require({json.dumps(str(START_DAY))}); const elements = {{ summary: {{textContent:''}}, phases: {{textContent:''}}, action: {{textContent:'', listeners:{{}}, addEventListener(name, fn) {{ this.listeners[name] = fn; }}}}, }}; const opened = []; let counts = {{agenda:0, attention:2, update:1, filed:0, today:3}}; const controller = createStartDay({{ getCounts: () => counts, openQueue: name => opened.push(name), elements, }}); controller.start(); const first = {{summary:elements.summary.textContent, phases:elements.phases.textContent, action:elements.action.textContent}}; elements.action.listeners.click(); counts = {{agenda:0, attention:0, update:0, filed:0, today:3}}; controller.render(); const ready = {{summary:elements.summary.textContent, phases:elements.phases.textContent, action:elements.action.textContent}}; process.stdout.write(JSON.stringify({{first, ready, opened}})); """ assert run_node(script) == { "first": { "summary": "3 items before Today · 3 planned", "phases": "Attention 2 · Updates 1", "action": "Review Attention", }, "ready": { "summary": "Review clear · 3 planned", "phases": "All urgent queues reviewed", "action": "Continue Today", }, "opened": ["attention"], } @pytest.mark.anyio async def test_dashboard_wires_thumb_safe_start_day_briefing_into_offline_mobile_bundle(): html = await dashboard() service_worker = (START_DAY.parent / "service-worker.js").read_text() assert 'class="mobile-start-day"' in html assert 'id="mobile-start-day-summary"' in html assert 'id="mobile-start-day-phases"' in html assert 'id="mobile-start-day-action"' in html assert '' in html assert "const mobileStartDay = createMobileStartDay({" in html assert "openQueue: name => name === 'find'" in html assert "mobileStartDay.render();" in html assert ".mobile-start-day-action { width:100%; min-height:48px;" in html assert "max-width:100%; overflow-wrap:anywhere;" in html assert "BASE + 'static/mobile-start-day.js'" in service_worker