feat: batch-claim available work into Today (Closes #671)
This commit is contained in:
1 parent
9e301924b4
commit
fba1f4238d
9 files changed
+323
-5
No files matched your search
@@ -0,0 +1,157 @@
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
BATCH_FIND_WORK = Path(__file__).parents[1] / "frontend" / "batch-find-work.js"
|
||||
PICK_WORK = Path(__file__).parents[1] / "frontend" / "pick-work.js"
|
||||
HTML = Path(__file__).parents[1] / "frontend" / "index.html"
|
||||
DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js"
|
||||
CSS = Path(__file__).parents[1] / "frontend" / "dashboard.css"
|
||||
WORKER = Path(__file__).parents[1] / "frontend" / "service-worker.js"
|
||||
|
||||
|
||||
def run_node(script):
|
||||
return json.loads(subprocess.run(
|
||||
["node", "-e", script], check=True, capture_output=True, text=True
|
||||
).stdout)
|
||||
|
||||
|
||||
def test_batch_preflights_today_capacity_before_claiming_any_issue():
|
||||
script = f"""
|
||||
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
|
||||
const calls=[];
|
||||
const flow=createBatchFindWork({{
|
||||
capacity:()=>1,
|
||||
claim:item=>{{calls.push('claim:'+item.number);return Promise.resolve(item);}},
|
||||
queue:item=>{{calls.push('queue:'+item.number);return 'queued';}},
|
||||
onProgress:progress=>calls.push('progress:'+progress.status),
|
||||
}});
|
||||
flow.run([
|
||||
{{repository:'stackchain/dashboard',number:671}},
|
||||
{{repository:'stackchain/dashboard',number:672}},
|
||||
]).then(result=>process.stdout.write(JSON.stringify({{result,calls}})));
|
||||
"""
|
||||
|
||||
assert run_node(script) == {
|
||||
"result": {
|
||||
"status": "full",
|
||||
"selected": 2,
|
||||
"available": 1,
|
||||
"queued": [],
|
||||
"failed": [],
|
||||
},
|
||||
"calls": ["progress:full"],
|
||||
}
|
||||
|
||||
|
||||
def test_batch_claims_in_order_continues_after_conflict_and_reports_truthfully():
|
||||
script = f"""
|
||||
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
|
||||
const calls=[];
|
||||
const flow=createBatchFindWork({{
|
||||
capacity:()=>3,
|
||||
claim:item=>{{
|
||||
calls.push('claim:'+item.number);
|
||||
return item.number===672 ? Promise.reject(new Error('already claimed')) :
|
||||
Promise.resolve({{...item,assignees:['timmy']}});
|
||||
}},
|
||||
queue:item=>{{calls.push('queue:'+item.number);return 'queued';}},
|
||||
onProgress:progress=>calls.push('progress:'+progress.status+':'+progress.processed),
|
||||
}});
|
||||
flow.run([671,672,673].map(number=>({{repository:'stackchain/dashboard',number}})))
|
||||
.then(result=>process.stdout.write(JSON.stringify({{result,calls}})));
|
||||
"""
|
||||
|
||||
assert run_node(script) == {
|
||||
"result": {
|
||||
"status": "complete",
|
||||
"selected": 3,
|
||||
"available": 3,
|
||||
"queued": ["stackchain/dashboard#671", "stackchain/dashboard#673"],
|
||||
"failed": [{"key": "stackchain/dashboard#672", "reason": "already claimed"}],
|
||||
},
|
||||
"calls": [
|
||||
"claim:671", "queue:671", "progress:running:1",
|
||||
"claim:672", "progress:running:2",
|
||||
"claim:673", "queue:673", "progress:running:3",
|
||||
"progress:complete:3",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_batch_does_not_reclaim_confirmed_issue_when_today_sync_needs_recovery():
|
||||
script = f"""
|
||||
const createBatchFindWork=require({json.dumps(str(BATCH_FIND_WORK))});
|
||||
const calls=[];
|
||||
const flow=createBatchFindWork({{
|
||||
capacity:()=>1,
|
||||
claim:item=>{{calls.push('claim');return Promise.resolve({{...item,assignees:['timmy']}});}},
|
||||
queue:item=>{{calls.push('queue');return 'sync-unavailable';}},
|
||||
}});
|
||||
flow.run([{{repository:'stackchain/dashboard',number:671}}])
|
||||
.then(result=>process.stdout.write(JSON.stringify({{result,calls}})));
|
||||
"""
|
||||
|
||||
assert run_node(script) == {
|
||||
"result": {
|
||||
"status": "complete",
|
||||
"selected": 1,
|
||||
"available": 1,
|
||||
"queued": [],
|
||||
"failed": [{
|
||||
"key": "stackchain/dashboard#671",
|
||||
"reason": "assigned but Today sync is unavailable",
|
||||
"assigned": True,
|
||||
}],
|
||||
},
|
||||
"calls": ["claim", "queue"],
|
||||
}
|
||||
|
||||
|
||||
def test_find_work_selection_survives_loaded_pages_and_removes_confirmed_claims():
|
||||
script = f"""
|
||||
const createFindWork=require({json.dumps(str(PICK_WORK))});
|
||||
const pages={{
|
||||
1:{{items:[{{id:1,repository:'stackchain/dashboard',number:671}}],page:1,total:2,has_more:true}},
|
||||
2:{{items:[{{id:2,repository:'stackchain/dashboard',number:672}}],page:2,total:2,has_more:false}},
|
||||
}};
|
||||
const controller=createFindWork({{
|
||||
fetchJson:path=>path.includes('/claim') ? Promise.resolve({{repository:'stackchain/dashboard',number:671,assignees:['timmy']}}) :
|
||||
Promise.resolve(pages[path.endsWith('=2') ? 2 : 1]),
|
||||
onItems:()=>{{}}, onPagination:()=>{{}}, onStatus:()=>{{}}, onSelection:()=>{{}},
|
||||
}});
|
||||
controller.load().then(()=>{{
|
||||
controller.startSelection();
|
||||
controller.toggleSelection(controller.items()[0]);
|
||||
return controller.loadMore();
|
||||
}}).then(()=>{{
|
||||
controller.toggleSelection(controller.items()[1]);
|
||||
return controller.claim(controller.items()[0]);
|
||||
}}).then(()=>process.stdout.write(JSON.stringify({{
|
||||
selected:controller.selectedItems().map(item=>item.number),
|
||||
selecting:controller.selection().active,
|
||||
}})));
|
||||
"""
|
||||
|
||||
assert run_node(script) == {"selected": [672], "selecting": True}
|
||||
|
||||
|
||||
def test_mobile_find_work_exposes_accessible_batch_controls_and_offline_asset():
|
||||
html = HTML.read_text()
|
||||
dashboard = DASHBOARD.read_text()
|
||||
css = CSS.read_text()
|
||||
worker = WORKER.read_text()
|
||||
|
||||
assert 'id="select-find-work"' in html
|
||||
assert 'id="batch-find-work-actions"' in html
|
||||
assert 'id="claim-selected-work"' in html
|
||||
assert 'aria-live="polite"' in html
|
||||
assert "createBatchFindWork({" in dashboard
|
||||
assert "findWorkController.startSelection()" in dashboard
|
||||
assert "findWorkController.toggleSelection(item)" in dashboard
|
||||
assert ".find-work-batch-actions" in css
|
||||
assert "position:sticky" in css
|
||||
assert "env(safe-area-inset-bottom)" in css
|
||||
assert "BASE + 'static/batch-find-work.js'" in worker
|
||||
assert '<script src="static/batch-find-work.js"></script>' in html
|
||||
@@ -718,6 +718,7 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
|
||||
"/dashboard/static/detail-defer.js",
|
||||
"/dashboard/static/later-picker.js",
|
||||
"/dashboard/static/pick-work.js",
|
||||
"/dashboard/static/batch-find-work.js",
|
||||
"/dashboard/static/conversation.js",
|
||||
"/dashboard/static/comment-actions.js",
|
||||
"/dashboard/static/issue-attachment.js",
|
||||
|
||||
Reference in new issue
Block a user