import json import subprocess from pathlib import Path WEEK_CALENDAR = Path(__file__).parents[1] / "frontend" / "week-calendar.js" INDEX = Path(__file__).parents[1] / "frontend" / "index.html" DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js" BUNDLE = Path(__file__).parents[1] / "src" / "frontend_bundle.py" def run_node(source: str): completed = subprocess.run(["node", "-e", source], capture_output=True, text=True) assert completed.returncode == 0, completed.stderr return json.loads(completed.stdout) def test_week_calendar_builds_ordered_local_time_blocks_and_serializes_opaque_events(): plan = { "timezone": "America/New_York", "revision": 7, "days": [{ "plan_date": "2026-08-21", "ids": ["issue:stackchain/dashboard:12:", "pull:stackchain/api:7:"], "capacity_minutes": 120, "estimates": { "issue:stackchain/dashboard:12:": 45, "pull:stackchain/api:7:": 30, }, }], } items = { "issue:stackchain/dashboard:12:": { "id": 401, "title": "Plan, ship; verify", "repository": "stackchain/dashboard", "number": 12, "url": "https://forge.alexanderwhitestone.com/git/stackchain/stackchain-dashboard/issues/12", }, "pull:stackchain/api:7:": { "title": "API release", "repository": "stackchain/api", "number": 7, "url": "https://forge.alexanderwhitestone.com/git/stackchain/api/pulls/7", }, } source = f""" const calendar=require({json.dumps(str(WEEK_CALENDAR))}); const plan={json.dumps(plan)}, items={json.dumps(items)}; const blocks=calendar.buildWeekBlocks(plan, {{'2026-08-21':'09:00'}}, id=>items[id]); const text=calendar.serializeWeekCalendar(blocks, {{timezone:plan.timezone,revision:plan.revision,generatedAt:'20260820T120000Z'}}); console.log(JSON.stringify({{blocks,text}})); """ result = run_node(source) assert [(block["start_time"], block["end_time"]) for block in result["blocks"]] == [ ("09:00", "09:45"), ("09:45", "10:15") ] text = result["text"] assert "TZID:America/New_York" in text assert "DTSTART;TZID=America/New_York:20260821T090000" in text assert "DTEND;TZID=America/New_York:20260821T094500" in text assert "DTSTART;TZID=America/New_York:20260821T094500" in text assert "DTEND;TZID=America/New_York:20260821T101500" in text assert "TRANSP:OPAQUE" in text assert "UID:week-7-20260821-issue-stackchain-dashboard-12@stackchain" in text assert "SUMMARY:Plan\\, ship\\; verify" in text unfolded = text.replace("\r\n ", "") assert "URL:https://forge.alexanderwhitestone.com/git/stackchain/stackchain-dashboard/issues/12" in unfolded def test_week_calendar_excludes_unselected_items_and_downloads_when_native_share_is_unavailable(): source = f""" const calendar=require({json.dumps(str(WEEK_CALENDAR))}); const plan={{timezone:'UTC',revision:2,days:[{{plan_date:'2026-08-21',ids:['one','two'],capacity_minutes:60,estimates:{{one:20,two:25}}}}]}}; const items={{one:{{title:'Private',repository:'r',number:1,url:'https://forge/r/1'}},two:{{title:'Public',repository:'r',number:2,url:'https://forge/r/2'}}}}; const blocks=calendar.buildWeekBlocks(plan,{{'2026-08-21':'13:00'}},id=>items[id],new Set(['two'])); class FakeFile {{constructor(parts,name,options){{this.parts=parts;this.name=name;this.type=options.type;}}}} const clicks=[],revoked=[]; (async()=>{{ const result=await calendar.deliverWeekCalendar({{text:'calendar',filename:'week.ics',navigator:{{}},FileCtor:FakeFile, document:{{createElement:()=>({{click(){{clicks.push([this.download,this.href]);}}}})}}, urlApi:{{createObjectURL:()=> 'blob:week',revokeObjectURL:value=>revoked.push(value)}}}}); console.log(JSON.stringify({{blocks,result,clicks,revoked}})); }})(); """ result = run_node(source) assert [block["id"] for block in result["blocks"]] == ["two"] assert result["blocks"][0]["start_time"] == "13:00" assert result["result"] == "downloaded" assert result["clicks"] == [["week.ics", "blob:week"]] assert result["revoked"] == ["blob:week"] def test_week_calendar_handoff_is_wired_into_confirmation_with_mobile_privacy_controls(): index = INDEX.read_text() dashboard = DASHBOARD.read_text() bundle = BUNDLE.read_text() assert '' in index assert 'id="week-calendar-handoff"' in index assert 'id="week-calendar-days"' in index assert 'id="share-week-calendar"' in index assert 'id="back-to-week-review-from-calendar"' in index assert "Titles, references, dates, and times are included" in index assert "mountWeekCalendarHandoff" in WEEK_CALENDAR.read_text() assert "weekCalendar.open(weekPlan.state())" in dashboard assert "weekCalendar.close()" in dashboard assert '"static/week-calendar.js", "static/week-plan.js"' in bundle