feat: plan Week Ahead in one mobile pass (Closes #1186)
CI / lint (pull_request) Successful in 4m5s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 4m39s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-20 16:30:45 +00:00
1 parent 6718160381
commit 55e1c3d6dd
6 files changed
+106 -10

No files matched your search

+12 -4
View File
@@ -67,11 +67,21 @@ def test_release_artifact_plans_seven_touch_safe_mobile_dates(
assert bounds and bounds["height"] >= 44
dates.nth(2).click()
expect(dates.nth(2)).to_have_attribute("aria-current", "date")
expect(page.locator("#week-plan-progress")).to_have_text("Day 3 of 7 · 0 planned")
expect(page.locator("#save-today-plan")).to_have_text("Save & next")
page.locator("#save-today-plan").click()
expect(page.locator("#plan-today-sheet")).to_be_visible()
expect(dates.nth(3)).to_have_attribute("aria-current", "date")
expect(page.locator("#week-plan-progress")).to_have_text("Day 4 of 7 · 0 planned")
dates.nth(6).click()
expect(page.locator("#save-today-plan")).to_have_text("Save week")
page.locator("#save-today-plan").click()
expect(page.locator("#plan-today-sheet")).to_be_hidden()
page.wait_for_function("() => document.querySelector('#mobile-week-summary').textContent !== 'Loading Week Ahead…'")
assert saved and len(saved[-1]["days"]) == 1
assert saved[-1]["days"][0]["plan_date"]
assert saved and len(saved[-1]["days"]) == 2
assert [day["plan_date"] for day in saved[-1]["days"]] == sorted(
day["plan_date"] for day in saved[-1]["days"]
)
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
browser.close()
finally:
@@ -134,8 +144,6 @@ def test_release_artifact_reconciles_only_the_week_day_changed_on_both_devices(
page.locator("#save-today-plan").click()
page.wait_for_function("() => document.querySelector('#mobile-week-summary').textContent.includes('Conflict')")
page.locator('[data-mobile-task="queues"]').click()
page.locator('[data-mobile-queue="week"]').click()
choices = page.locator("[data-week-conflict-choice]")
expect(choices).to_have_count(2)
for index in range(2):
+57
View File
@@ -47,6 +47,63 @@ console.log(JSON.stringify({dates,saved,requests}));
assert result["requests"][1]["body"]["days"][2]["ids"] == ["issue:r:3:"]
def test_week_controller_advances_through_one_continuous_seven_day_pass():
result = run_controller("""
const week=createWeekPlan({fetchJson:async()=>({}),localDate:()=> '2026-08-20',timeZone:()=> 'UTC'});
week.adopt({revision:4,timezone:'UTC',days:[
{plan_date:'2026-08-21',ids:['planned'],capacity_minutes:60,estimates:{planned:30}},
{plan_date:'2026-08-23',ids:['also-planned'],capacity_minutes:90,estimates:{'also-planned':45}}
]});
const first=week.pass('2026-08-21');
const middle=week.pass('2026-08-24');
const last=week.pass('2026-08-27');
console.log(JSON.stringify({first,middle,last}));
""")
assert result == {
"first": {"position": 1, "total": 7, "planned": 2, "next_date": "2026-08-22", "last": False},
"middle": {"position": 4, "total": 7, "planned": 2, "next_date": "2026-08-25", "last": False},
"last": {"position": 7, "total": 7, "planned": 2, "next_date": None, "last": True},
}
def test_week_workflow_saves_and_advances_without_closing_the_planner():
result = run_controller("""
const createWorkflow=createWeekPlan.Workflow;
const dates=['2026-08-21','2026-08-22','2026-08-23','2026-08-24','2026-08-25','2026-08-26','2026-08-27'];
const elements=new Map();
const makeElement=()=>({hidden:false,textContent:'',disabled:false,innerHTML:'',addEventListener:()=>{},focus:()=>{},
querySelectorAll:()=>[],querySelector:()=>null,scrollIntoView:()=>{}});
const root=makeElement();
Object.defineProperty(root,'innerHTML',{set(value){this.value=value;this.buttons=[...value.matchAll(/data-week-plan-date=\"([^\"]+)/g)].map(match=>({
dataset:{weekPlanDate:match[1]},addEventListener:()=>{},scrollIntoView(){this.scrolled=true;}
}));},get(){return this.value||'';}});
root.querySelectorAll=()=>root.buttons||[];
root.querySelector=selector=>(root.buttons||[]).find(button=>selector.includes(button.dataset.weekPlanDate))||null;
elements.set('#week-plan-dates',root);
for(const selector of ['#mobile-week-summary','#my-work-action-status','#week-plan-progress','#save-today-plan']) elements.set(selector,makeElement());
let opened=0,staged=[];
const controller={dates:()=>dates.map((date,index)=>({date,label:'Day '+(index+1)})),day:date=>({plan_date:date,ids:[]}),
pass:date=>{const index=dates.indexOf(date);return {position:index+1,total:7,planned:staged.length,next_date:dates[index+1]||null,last:index===6};},
load:async()=>({}),summary:()=> 'Nothing planned',stageDay:(date,plan)=>{staged.push({date,plan});return true;},flush:async()=>({})};
const workflow=createWorkflow({controller,qs:selector=>elements.get(selector),getLogin:()=> 'timmy',
openPlanner:()=>{opened+=1;},escapeHtml:value=>value,escapeAttribute:value=>value,
todayWork:{replace:()=>{},replacePlanning:()=>{}},refresh:()=>{},warm:()=>{}});
await workflow.open({disabled:false});
workflow.save({ids:['issue:r:1:'],capacity_minutes:60,estimates:{'issue:r:1:':30}});
const continued=workflow.advance();
console.log(JSON.stringify({continued,selected:workflow.selectedDate(),opened,staged,
progress:elements.get('#week-plan-progress').textContent,label:elements.get('#save-today-plan').textContent}));
""")
assert result["continued"] is True
assert result["selected"] == "2026-08-22"
assert result["opened"] == 3
assert result["staged"][0]["date"] == "2026-08-21"
assert result["progress"] == "Day 2 of 7 · 1 planned"
assert result["label"] == "Save & next"
def test_week_controller_preserves_both_versions_when_another_device_changes_the_week():
result = run_controller("""
let calls=0;