feat: review activity before Today updates (Closes #1060)
CI / lint (pull_request) Successful in 2m38s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 1m53s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-18 04:00:58 +00:00
1 parent e290293bf1
commit 47626338ba
5 files changed
+228 -4

No files matched your search

+113 -3
View File
@@ -216,8 +216,9 @@ const elements=Object.fromEntries(selectors.map(selector=>[selector,new Element(
const target={{identity:'issue:stackchain/dashboard:1058:',kind:'issue',repository:'stackchain/dashboard',number:1058,label:'#1058',title:'Voice progress'}};
const calls=[];
const voice={{open:async identity=>calls.push(['open',identity]),cancel:()=>calls.push(['cancel'])}};
const activity={{open:async value=>calls.push(['activity-open',value.identity]),close:()=>calls.push(['activity-close'])}};
const view=createView({{
progress:{{load:()=> 'Saved text',save:()=>true}},currentTarget:()=>target,qs:selector=>elements[selector],voice,
progress:{{load:()=> 'Saved text',save:()=>true}},currentTarget:()=>target,qs:selector=>elements[selector],voice,activity,
photos:{{open:async()=>calls.push(['photos'])}},
}});
(async()=>{{
@@ -231,13 +232,112 @@ const view=createView({{
"opened": {
"body": "Saved text",
"sheet": True,
"calls": [["open", "issue:stackchain/dashboard:1058:"], ["photos"]],
"calls": [["open", "issue:stackchain/dashboard:1058:"], ["photos"], ["activity-open", "issue:stackchain/dashboard:1058:"]],
},
"closed": True,
"calls": [["open", "issue:stackchain/dashboard:1058:"], ["photos"], ["cancel"]],
"calls": [
["open", "issue:stackchain/dashboard:1058:"],
["photos"],
["activity-open", "issue:stackchain/dashboard:1058:"],
["cancel"],
["activity-close"],
],
}
def test_recent_activity_loads_the_exact_issue_newest_page_before_rendering():
script = f"""
const {{createActivity}}=require({json.dumps(str(TODAY_PROGRESS))});
const calls=[]; const paints=[];
const page={{comments:[{{id:2,body:'Latest',user:{{login:'alex'}},created_at:'2026-08-18T01:00:00Z'}}],page:3,older_page:2,total:41}};
const activity=createActivity({{
fetchJson:async path=>{{calls.push(path);return page;}},
createPager:({{loadPage}})=>({{reset:value=>value,loadOlder:()=>loadPage(2)}}),
paint:value=>paints.push(value), setStatus:value=>calls.push('status:'+value),
}});
const target={{identity:'issue:stackchain/dashboard:1060:',kind:'issue',repository:'stackchain/dashboard',number:1060}};
(async()=>{{await activity.open(target);process.stdout.write(JSON.stringify({{calls,paints}}));}})()
.catch(error=>{{console.error(error);process.exit(1);}});
"""
output = run_node(script)
assert output["calls"] == [
"status:Loading recent activity…",
"api/v1/repos/stackchain/dashboard/issues/1060/comments?limit=20",
"status:",
]
assert output["paints"] == [{
"comments": [{
"id": 2,
"body": "Latest",
"user": {"login": "alex"},
"created_at": "2026-08-18T01:00:00Z",
}],
"page": 3,
"older_page": 2,
"total": 41,
}]
def test_recent_activity_rejects_a_slow_response_after_close():
script = f"""
const {{createActivity}}=require({json.dumps(str(TODAY_PROGRESS))});
let resolve; const paints=[]; const statuses=[];
const activity=createActivity({{
fetchJson:()=>new Promise(done=>{{resolve=done;}}),
createPager:()=>({{reset:value=>value}}), paint:value=>paints.push(value), setStatus:value=>statuses.push(value),
}});
const target={{identity:'pull:stackchain/dashboard:9:',kind:'pull',repository:'stackchain/dashboard',number:9}};
(async()=>{{const pending=activity.open(target);activity.close();resolve({{comments:[{{id:1}}]}});const result=await pending;process.stdout.write(JSON.stringify({{result,paints,statuses}}));}})()
.catch(error=>{{console.error(error);process.exit(1);}});
"""
assert run_node(script) == {
"result": False,
"paints": [],
"statuses": ["Loading recent activity…", ""],
}
def test_recent_activity_retry_recovers_the_same_pull_after_failure():
script = f"""
const {{createActivity}}=require({json.dumps(str(TODAY_PROGRESS))});
const calls=[]; const paints=[]; let fail=true;
const activity=createActivity({{
fetchJson:async path=>{{calls.push(path);if(fail)throw new Error('offline');return {{comments:[],page:1,older_page:null,total:0}};}},
createPager:()=>({{reset:value=>value}}), paint:value=>paints.push(value),
}});
const target={{identity:'pull:stackchain/dashboard:9:',kind:'pull',repository:'stackchain/dashboard',number:9}};
(async()=>{{const opened=await activity.open(target);fail=false;const retried=await activity.retry();process.stdout.write(JSON.stringify({{opened,retried,calls,paints}}));}})()
.catch(error=>{{console.error(error);process.exit(1);}});
"""
output = run_node(script)
assert output["opened"] is False
assert output["retried"] is True
assert output["calls"] == [
"api/v1/repos/stackchain/dashboard/pulls/9/comments?limit=20",
"api/v1/repos/stackchain/dashboard/pulls/9/comments?limit=20",
]
assert output["paints"] == [{"comments": [], "page": 1, "older_page": None, "total": 0}]
def test_recent_activity_load_older_paints_the_deduplicated_page():
script = f"""
const {{createActivity}}=require({json.dumps(str(TODAY_PROGRESS))});
const calls=[]; const paints=[];
const activity=createActivity({{
fetchJson:async path=>{{calls.push(path);return {{comments:[],page:2,older_page:null,total:20}};}},
createPager:({{loadPage}})=>({{reset:value=>value,loadOlder:async()=>{{await loadPage(2);return {{comments:[{{id:1}}],page:2,older_page:null,total:21}};}}}}),
paint:value=>paints.push(value),
}});
const target={{identity:'issue:stackchain/dashboard:1060:',kind:'issue',repository:'stackchain/dashboard',number:1060}};
(async()=>{{await activity.open(target);const loaded=await activity.loadOlder();process.stdout.write(JSON.stringify({{loaded,calls,paints}}));}})()
.catch(error=>{{console.error(error);process.exit(1);}});
"""
output = run_node(script)
assert output["loaded"] is True
assert output["calls"][-1] == "api/v1/repos/stackchain/dashboard/issues/1060/comments?page=2&limit=20"
assert output["paints"][-1]["comments"] == [{"id": 1}]
def test_mobile_progress_sheet_is_accessible_bundled_and_safe_area_aware():
html = (ROOT / "frontend" / "index.html").read_text()
css = (ROOT / "frontend" / "dashboard.css").read_text()
@@ -248,6 +348,11 @@ def test_mobile_progress_sheet_is_accessible_bundled_and_safe_area_aware():
assert 'id="today-progress-sheet"' in html
assert 'aria-labelledby="today-progress-title"' in html
assert 'id="today-progress-body"' in html
assert 'id="today-progress-activity"' in html
assert 'id="today-progress-activity-list"' in html
assert 'id="today-progress-activity-status"' in html
assert 'id="retry-today-progress-activity"' in html
assert 'id="load-older-today-progress-activity"' in html
for control in (
"voice-today-progress",
"start-voice-today-progress",
@@ -278,6 +383,11 @@ def test_mobile_progress_sheet_is_accessible_bundled_and_safe_area_aware():
assert "photos:todayProgressPhotos" in dashboard
assert "['today-progress', '#today-progress-body']" in dashboard
assert "voice:todayProgressVoice" in dashboard
assert "mountTodayProgressActivity" in dashboard
assert "activity:mountTodayProgressActivity" in dashboard
assert ".today-progress-activity-list" in css
assert ".today-progress-activity-actions button" in css
assert "min-height:44px" in css
assert "const controller = issueAttachment.mount" in TODAY_PROGRESS.read_text()
assert "const checkpointAttachments = await photos?.serialize?.() || [];" in TODAY_PROGRESS.read_text()
assert "progress.save(openedTarget.identity, body.value, checkpointAttachments)" in TODAY_PROGRESS.read_text()