import json import subprocess from pathlib import Path BOOTSTRAP = Path(__file__).parents[1] / "frontend" / "workspace-bootstrap.js" INDEX = Path(__file__).parents[1] / "frontend" / "index.html" CSS = Path(__file__).parents[1] / "frontend" / "dashboard.css" def run_bootstrap(scenario: str) -> dict: harness = f""" const loadWorkspace = require({json.dumps(str(BOOTSTRAP))}); (async()=>{{ {scenario} }})().catch(error=>{{console.error(error);process.exit(1);}}); """ completed = subprocess.run( ["node", "-e", harness], check=True, capture_output=True, text=True ) return json.loads(completed.stdout) def test_workspace_bootstrap_demand_loads_optional_features_after_work_core(): result = run_bootstrap(""" const status={textContent:''}; const document={ querySelector(selector) { if (selector === 'meta[name="stackchain-feature-work-core"]') return {content:'feature-work-core-123.js'}; if (selector === 'meta[name="stackchain-feature-today-timer"]') return {content:'feature-workspace-abc.js'}; if (selector === 'meta[name="stackchain-feature-planning"]') return {content:'feature-planning-def.js'}; if (selector === '#my-work-action-status') return status; return null; } }; const requested=[]; const createLoader=options=>({load:async name=>{requested.push(name + ':' + options.urls[name]);}}); const lifecycle=await loadWorkspace({document,createLoader}); const before=requested.slice(); await lifecycle.hydrateWorkspace?.(); console.log(JSON.stringify({before,after:requested,status:status.textContent})); """) assert result == { "before": ["work-core:feature-work-core-123.js"], "after": [ "work-core:feature-work-core-123.js", "today-timer:feature-workspace-abc.js", "planning:feature-planning-def.js", ], "status": "", } def test_workspace_bootstrap_hydrates_once_and_replays_dependent_mobile_action(): result = run_bootstrap(""" const listeners={}; const requested=[]; const document={ querySelector(selector) { const match=selector.match(/stackchain-feature-([^\"]+)/); return match ? {content:'feature-' + match[1] + '.js'} : null; }, addEventListener(name,callback,capture){listeners[name]={callback,capture};}, removeEventListener(name,callback,capture){ if(listeners[name]?.callback===callback && listeners[name]?.capture===capture) delete listeners[name]; }, }; const createLoader=()=>({load:async name=>{ requested.push(name); if(name==='today-timer') setImmediate(()=>{ actionReady=true; lifecycle.markWorkspaceReady?.(); }); }}); let lifecycle=await loadWorkspace({document,createLoader}); let prevented=0,stopped=0,replayed=0,actionReady=false,replayedReady=false; const target={ closest(selector){return selector.includes(':not([data-mobile-task="work"])') ? null : this;}, click(){replayed++; replayedReady=actionReady;}, }; await listeners.click?.callback({target,preventDefault(){prevented++;},stopImmediatePropagation(){stopped++;}}); console.log(JSON.stringify({requested,prevented,stopped,replayed,replayedReady,listening:Boolean(listeners.click)})); """) assert result == { "requested": ["work-core", "today-timer", "planning"], "prevented": 1, "stopped": 1, "replayed": 1, "replayedReady": True, "listening": False, } def test_workspace_bootstrap_hydrates_immediately_for_deep_link(): result = run_bootstrap(""" const requested=[]; const document={ querySelector(selector) { const match=selector.match(/stackchain-feature-([^\"]+)/); return match ? {content:'feature-' + match[1] + '.js'} : null; }, addEventListener(){}, }; const window={location:{hash:'#/my-work/today'},addEventListener(){},removeEventListener(){}}; const createLoader=()=>({load:async name=>{requested.push(name);}}); const lifecycle=await loadWorkspace({document,window,createLoader}); await lifecycle.deepLinkReady; console.log(JSON.stringify({requested})); """) assert result == {"requested": ["work-core", "today-timer", "planning"]} def test_workspace_bootstrap_returns_after_work_core_before_optional_features_hydrate(): result = run_bootstrap(""" const document={querySelector(selector) { const match=selector.match(/stackchain-feature-([^\"]+)/); return match ? {content:'feature-' + match[1] + '.js'} : null; }}; const requested=[]; const releases={}; const createLoader=()=>({load:name=>{ requested.push(name); if (name === 'work-core') return Promise.resolve(); return new Promise(resolve=>{releases[name]=resolve;}); }}); const lifecycle=await loadWorkspace({document,createLoader}); const returned=requested.slice(); const hydration=lifecycle.hydrateWorkspace(); releases['today-timer'](); releases.planning(); await hydration; console.log(JSON.stringify({returned,settled:requested})); """) assert result == { "returned": ["work-core"], "settled": ["work-core", "today-timer", "planning"], } def test_workspace_bootstrap_recovers_one_transient_failure_in_place(): result = run_bootstrap(""" const status={textContent:''}; const retry={hidden:true,disabled:false}; let attempts=0; const document={querySelector(selector) { if (selector.startsWith('meta[')) return {content:'feature-workspace-abc.js'}; if (selector === '#my-work-action-status') return status; if (selector === '#retry-workspace') return retry; return null; }}; const createLoader=()=>({load:async()=>{attempts++; if (attempts === 1) throw new Error('brief outage');}}); const schedule=callback=>{callback();}; const lifecycle=await loadWorkspace({document,createLoader,schedule}); await lifecycle.hydrateWorkspace(); console.log(JSON.stringify({attempts,status:status.textContent,retryHidden:retry.hidden})); """) assert result == {"attempts": 4, "status": "", "retryHidden": True} def test_workspace_bootstrap_offers_single_flight_manual_retry_without_reloading(): result = run_bootstrap(""" const status={textContent:''}; const listeners={}; let reloads=0; let attempts=0; const retry={hidden:true,disabled:false,addEventListener(name,callback){listeners[name]=callback;}}; const document={querySelector(selector) { if (selector.startsWith('meta[')) return {content:'feature-workspace-abc.js'}; if (selector === '#my-work-action-status') return status; if (selector === '#retry-workspace') return retry; return null; }}; const window={location:{reload(){reloads++;}},addEventListener(){},removeEventListener(){}}; const createLoader=()=>({load:async()=>{attempts++; if (attempts < 3) throw new Error('offline');}}); const loading=loadWorkspace({document,window,createLoader,schedule:callback=>callback()}); await new Promise(resolve=>setImmediate(resolve)); const offered={hidden:retry.hidden,disabled:retry.disabled,status:status.textContent}; const first=listeners.click(); const second=listeners.click(); await Promise.all([first,second,loading]); console.log(JSON.stringify({attempts,reloads,offered,status:status.textContent,retryHidden:retry.hidden})); """) assert result == { "attempts": 3, "reloads": 0, "offered": { "hidden": False, "disabled": False, "status": "Workspace unavailable. Reconnect or retry.", }, "status": "", "retryHidden": True, } def test_workspace_retry_control_is_accessible_and_touch_safe(): index = INDEX.read_text() css = CSS.read_text() assert '' in index assert ".retry-work-route { min-height:44px;" in css def test_workspace_bootstrap_reconnect_retries_in_place_without_reloading(): result = run_bootstrap(""" const status={textContent:''}; const listeners={}; let reloads=0; let attempts=0; const retry={hidden:true,disabled:false,addEventListener(){},removeEventListener(){}}; const document={querySelector(selector) { if (selector.startsWith('meta[')) return {content:'feature-workspace-abc.js'}; if (selector === '#my-work-action-status') return status; if (selector === '#retry-workspace') return retry; return null; }}; const window={ location:{reload(){reloads++;}}, addEventListener(name,callback) { listeners[name]=callback; }, removeEventListener(name,callback) { if (listeners[name] === callback) delete listeners[name]; }, }; const createLoader=()=>({load:async()=>{attempts++; if (attempts < 3) throw new Error('offline');}}); const loading=loadWorkspace({document,window,createLoader,schedule:callback=>callback()}); await new Promise(resolve=>setImmediate(resolve)); const waiting=Boolean(listeners.online); if (listeners.online) listeners.online(); await loading; console.log(JSON.stringify({attempts,waiting,reloads,status:status.textContent})); """) assert result == { "attempts": 3, "waiting": True, "reloads": 0, "status": "", } def test_workspace_bootstrap_replays_online_event_after_dashboard_registers_handlers(): result = run_bootstrap(""" const status={textContent:''}; const listeners={}; const document={querySelector(selector) { if (selector.startsWith('meta[')) return {content:'feature-workspace-abc.js'}; if (selector === '#my-work-action-status') return status; return null; }}; const window={ addEventListener(name,callback) { listeners[name]=callback; }, removeEventListener(name,callback) { if (listeners[name] === callback) delete listeners[name]; }, }; const createLoader=()=>({load:async()=>{listeners.online();}}); const lifecycle=await loadWorkspace({document,window,createLoader}); let reconnects=0; lifecycle.replayOnline(()=>{reconnects++;}); lifecycle.replayOnline(()=>{reconnects++;}); console.log(JSON.stringify({reconnects,listening:Boolean(listeners.online)})); """) assert result == {"reconnects": 1, "listening": False}