import json import subprocess from pathlib import Path import pytest from src.views import dashboard DOCK = Path(__file__).resolve().parents[1] / "frontend" / "mobile-task-dock.js" def test_mobile_task_dock_routes_actions_hides_for_overlays_and_restores_focus(): script = f""" const createDock = require({json.dumps(str(DOCK))}); class FakeElement {{ constructor() {{ this.listeners = {{}}; this.attributes = {{}}; this.classList = {{ values:new Set(), contains:value => this.classList.values.has(value) }}; this.hidden = false; this.focuses = 0; }} addEventListener(name, callback) {{ this.listeners[name] = callback; }} click() {{ this.listeners.click({{currentTarget:this}}); }} setAttribute(name, value) {{ this.attributes[name] = value; }} removeAttribute(name) {{ delete this.attributes[name]; }} focus() {{ this.focuses += 1; }} }} const nav = new FakeElement(); const buttons = Object.fromEntries(['work','find','new','search','drafts'].map(name => [name, new FakeElement()])); const overlay = new FakeElement(); const calls = []; let observerCallback; const dock = createDock({{ nav, buttons, overlays:[overlay], actions: Object.fromEntries(Object.keys(buttons).map(name => [name, () => calls.push(name)])), observe(callback) {{ observerCallback = callback; return {{disconnect() {{}}}}; }}, }}); dock.start(); buttons.work.click(); buttons.find.click(); overlay.classList.values.add('open'); observerCallback(); const hiddenWhileOpen = nav.hidden; overlay.classList.values.delete('open'); observerCallback(); buttons.drafts.click(); process.stdout.write(JSON.stringify({{ calls, hiddenWhileOpen, hiddenAfterClose:nav.hidden, findFocuses:buttons.find.focuses, current:Object.fromEntries(Object.entries(buttons).map(([name, button]) => [name, button.attributes['aria-current'] || null])), }})); """ result = subprocess.run( ["node", "-e", script], capture_output=True, text=True ) assert result.returncode == 0, result.stderr assert json.loads(result.stdout) == { "calls": ["work", "find", "drafts"], "hiddenWhileOpen": True, "hiddenAfterClose": False, "findFocuses": 1, "current": { "work": None, "find": None, "new": None, "search": None, "drafts": "page", }, } @pytest.mark.anyio async def test_dashboard_renders_and_wires_phone_safe_task_dock(): html = await dashboard() assert '