feat: keep progressive My Work live (Closes #1411)
CI / lint (pull_request) Successful in 3m40s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 6m38s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-25 22:30:14 +00:00
1 parent e7e8c24d05
commit f9b29d8d14
2 files changed
+186 -17

No files matched your search

+122 -1
View File
@@ -243,4 +243,125 @@ const flow=context.module.exports({{document,fetchSnapshot:async()=>({{
"""
result = subprocess.run(["node", "-e", harness], check=True, capture_output=True, text=True)
assert json.loads(result.stdout) == {"before": "", "after": "timmy", "stopped": "timmy"}
assert json.loads(result.stdout) == {"before": "", "after": "timmy", "stopped": "timmy"}
def test_progressive_my_work_retries_a_failed_snapshot_without_reloading():
harness = f"""
const fs=require('fs'); const vm=require('vm');
const list={{innerHTML:''}}; const status={{textContent:''}};
const document={{
querySelector:selector=>selector==='#my-work-list'?list:selector==='#my-work-status'?status:null,
querySelectorAll:()=>[],
}};
let timer=null; let attempts=0;
const context={{module:{{exports:{{}}}},exports:{{}},console,URL,URLSearchParams,AbortController,document}};
vm.createContext(context);
vm.runInContext(fs.readFileSync({json.dumps(str(MY_WORK))},'utf8'),context);
context.buildMyWork=context.module.exports; context.module={{exports:{{}}}};
vm.runInContext(fs.readFileSync({json.dumps(str(Path(__file__).parents[1] / 'frontend' / 'context-poller.js'))},'utf8'),context);
context.createContextPoller=context.module.exports; context.module={{exports:{{}}}};
vm.runInContext(fs.readFileSync({json.dumps(str(MODULE))},'utf8'),context);
const flow=context.module.exports({{
document,
fetchSnapshot:async()=>{{
attempts += 1;
if(attempts===1) throw new Error('temporary outage');
return {{context:{{user:{{login:'timmy'}},issues:[{{
number:9,title:'Recovered assignment',repository:'stackchain/dashboard',
assignees:['timmy'],url:'https://forge.example/issues/9',
}}],pull_requests:[]}},events:[],notifications:[]}};
}},
pollerOptions:{{
setTimer:callback=>{{timer=callback; return 1;}}, clearTimer:()=>{{timer=null;}},
setDeadlineTimer:()=>2, clearDeadlineTimer:()=>{{}}, intervalMs:5,
}},
}});
(async()=>{{
const first=await flow.start();
const reconnecting=status.textContent;
if(!timer) throw new Error('retry was not scheduled');
timer(); await Promise.resolve(); await Promise.resolve(); await new Promise(resolve=>setImmediate(resolve));
console.log(JSON.stringify({{first,reconnecting,attempts,status:status.textContent,html:list.innerHTML}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
result = subprocess.run(["node", "-e", harness], check=True, capture_output=True, text=True)
assert json.loads(result.stdout) == {
"first": False,
"reconnecting": "Assigned work is reconnecting…",
"attempts": 2,
"status": "1 assigned work item ready.",
"html": '<article class="my-work-card progressive-my-work-card"><a class="my-work-card-main" href="https://forge.example/issues/9"><strong>Recovered assignment</strong><span class="small">stackchain/dashboard#9 · Assigned to you</span></a></article>',
}
def test_progressive_my_work_refreshes_with_revisions_and_coalesces_lifecycle_recovery():
harness = f"""
const fs=require('fs'); const vm=require('vm');
const list={{innerHTML:''}}; const status={{textContent:''}};
const document={{
hidden:false,
querySelector:selector=>selector==='#my-work-list'?list:selector==='#my-work-status'?status:null,
querySelectorAll:()=>[],
}};
const handlers={{}}; const removed=[];
const lifecycleTarget={{
addEventListener:(name,callback)=>{{handlers[name]=callback;}},
removeEventListener:(name,callback)=>{{if(handlers[name]===callback){{removed.push(name);delete handlers[name];}}}},
}};
let timer=null; let calls=[]; let resolveRefresh;
const initial={{
context:{{user:{{login:'timmy'}},issues:[],pull_requests:[]}},events:[],notifications:[],
revisions:{{context:'0123456789abcdef.1',events:'fedcba9876543210.2',notifications:'0011223344556677.3'}},
freshness:{{fresh_for_seconds:8,sections:{{context:{{degraded:false,age_seconds:2}},events:{{degraded:false,age_seconds:2}},notifications:{{degraded:false,age_seconds:2}}}}}},
}};
const updated={{...initial,context:{{user:{{login:'timmy'}},issues:[{{number:10,title:'New assignment',repository:'stackchain/dashboard',assignees:['timmy']}}],pull_requests:[]}}}};
const context={{module:{{exports:{{}}}},exports:{{}},console,URL,URLSearchParams,AbortController,document}};
vm.createContext(context);
vm.runInContext(fs.readFileSync({json.dumps(str(MY_WORK))},'utf8'),context);
context.buildMyWork=context.module.exports; context.module={{exports:{{}}}};
vm.runInContext(fs.readFileSync({json.dumps(str(Path(__file__).parents[1] / 'frontend' / 'context-poller.js'))},'utf8'),context);
context.createContextPoller=context.module.exports; context.module={{exports:{{}}}};
vm.runInContext(fs.readFileSync({json.dumps(str(MODULE))},'utf8'),context);
const flow=context.module.exports({{
document,lifecycleTarget,
fetchSnapshot:(revisions,options)=>{{
calls.push({{revisions,hasSignal:Boolean(options.signal)}});
if(calls.length===1)return Promise.resolve(initial);
return new Promise(resolve=>{{resolveRefresh=()=>resolve(updated);}});
}},
pollerOptions:{{
setTimer:callback=>{{timer=callback;return 1;}},clearTimer:()=>{{timer=null;}},
setDeadlineTimer:()=>2,clearDeadlineTimer:()=>{{}},intervalMs:5,
}},
}});
(async()=>{{
await flow.start(); const freshnessTimer=timer; freshnessTimer();
handlers.online(); handlers.visibilitychange();
await Promise.resolve();
const callsWhilePending=calls.length;
resolveRefresh(); await Promise.resolve(); await new Promise(resolve=>setImmediate(resolve));
const handoff=flow.handoff(); flow.stop();
console.log(JSON.stringify({{
callsWhilePending,calls,latestTitle:handoff.liveSnapshot.context.issues[0].title,
removed:removed.sort(),status:status.textContent,
}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
result = subprocess.run(["node", "-e", harness], check=True, capture_output=True, text=True)
state = json.loads(result.stdout)
assert state["callsWhilePending"] == 2
assert state["calls"][0] == {"revisions": {}, "hasSignal": True}
assert state["calls"][1] == {
"revisions": {
"context": "0123456789abcdef.1",
"events": "fedcba9876543210.2",
"notifications": "0011223344556677.3",
},
"hasSignal": True,
}
assert state["latestTitle"] == "New assignment"
assert state["removed"] == ["online", "visibilitychange"]
assert state["status"] == "1 assigned work item ready."