feat: promote checklist steps to related issues (Closes #921)
This commit is contained in:
1 parent
08139bc3ee
commit
170828b308
8 files changed
+254
-5
No files matched your search
@@ -72,6 +72,40 @@ outbox.flush('timmy').then(result=>process.stdout.write(JSON.stringify({{result,
|
||||
assert "relatedDraft" not in output["sentBody"]
|
||||
|
||||
|
||||
def test_confirmed_delivery_returns_durable_checklist_promotion_without_sending_parent_context():
|
||||
script = f"""
|
||||
const createIssueOutbox = require({json.dumps(str(OUTBOX))});
|
||||
const values=new Map(); let sentBody;
|
||||
const storage={{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)}};
|
||||
const outbox=createIssueOutbox({{
|
||||
storage,getOwnerLogin:()=> 'timmy',createOperationId:()=> 'promote-operation',
|
||||
fetchJson:async (_url,options)=>{{sentBody=JSON.parse(options.body);return {{repository:'stackchain/dashboard',number:44,title:'Build',url:'https://forge.example/issues/44'}};}},
|
||||
}});
|
||||
outbox.enqueue({{
|
||||
repository:'stackchain/dashboard',title:'Build',body:'Related parent',
|
||||
relatedDraft:{{repository:'stackchain/dashboard',title:'',body:'',labelIds:[],
|
||||
checklistPromotion:{{repository:'stackchain/dashboard',number:17,url:'https://forge.example/issues/17',
|
||||
title:'Parent',body:'- [ ] Build',updatedAt:'old',taskIndex:0}}}},
|
||||
}});
|
||||
outbox.flush('timmy').then(result=>process.stdout.write(JSON.stringify({{result,sentBody}})));
|
||||
"""
|
||||
output = run_node(script)
|
||||
|
||||
assert output["result"]["filings"] == [{
|
||||
"issue": {"repository": "stackchain/dashboard", "number": 44,
|
||||
"title": "Build", "url": "https://forge.example/issues/44"},
|
||||
"relatedDraft": {
|
||||
"repository": "stackchain/dashboard", "title": "", "body": "", "labelIds": [],
|
||||
"checklistPromotion": {
|
||||
"repository": "stackchain/dashboard", "number": 17,
|
||||
"url": "https://forge.example/issues/17", "title": "Parent",
|
||||
"body": "- [ ] Build", "updatedAt": "old", "taskIndex": 0,
|
||||
},
|
||||
},
|
||||
}]
|
||||
assert "checklistPromotion" not in output["sentBody"]
|
||||
|
||||
|
||||
def test_editing_a_queued_issue_refreshes_its_related_plan():
|
||||
script = f"""
|
||||
const createIssueOutbox = require({json.dumps(str(OUTBOX))});
|
||||
|
||||
@@ -2266,6 +2266,69 @@ async def test_mobile_issue_detail_manages_checklist_steps_with_touch_safe_inlin
|
||||
assert "label.focus()" in controller
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_mobile_checklist_manager_files_related_issue_and_links_confirmed_child():
|
||||
html = await dashboard()
|
||||
controller = ISSUE_SHEET.read_text()
|
||||
capture_controller = CREATE_ISSUE_SHEET.read_text()
|
||||
|
||||
assert 'id="file-checklist-step-related" type="button">File as related issue</button>' in html
|
||||
assert "fileRelated, onFileRelated, remove, cancel" in controller
|
||||
assert "onFileRelated({ item:state.item, detail:state.detail, taskIndex:selectedIndex" in controller
|
||||
assert "relatedTaskDraft" in controller
|
||||
assert "linkRelatedTask" in controller
|
||||
assert "startChecklistPromotion" in html
|
||||
assert "createIssueCapture.createChecklistPromotion({" in html
|
||||
assert "checklistPromotion.start(context)" in html
|
||||
assert "checklistPromotion?.cancel()" in html
|
||||
assert "checklistPromotion?.deliveryContext()" in html
|
||||
assert "await issueController.linkRelatedTask(" in capture_controller
|
||||
assert "Related issue created, but the parent link needs attention" in capture_controller
|
||||
assert ".checklist-step-editor button, .checklist-step-editor input { min-height:44px;" in html
|
||||
|
||||
|
||||
def test_checklist_step_promotion_builds_related_draft_and_revision_checked_parent_link():
|
||||
script = f"""
|
||||
const createIssueSheet = require({json.dumps(str(ISSUE_SHEET))});
|
||||
const createIssueCapture = require({json.dumps(str(CREATE_ISSUE_SHEET))});
|
||||
const calls = [];
|
||||
const controller = createIssueSheet({{
|
||||
storage:null,
|
||||
relatedTaskDraft:createIssueCapture.relatedChecklistDraft,
|
||||
linkRelatedTask:createIssueCapture.linkChecklistTask,
|
||||
fetchJson:async (url, options) => {{
|
||||
calls.push({{url, body:JSON.parse(options.body)}});
|
||||
return {{number:17,title:'Release',body:options && JSON.parse(options.body).body,updated_at:'new'}};
|
||||
}},
|
||||
}});
|
||||
const item = {{repository:'stackchain/api',number:17,url:'https://forge.example/stackchain/api/issues/17'}};
|
||||
const detail = {{title:'Release',body:'Intro\\r\\n - [x] Build package\\r\\n - [ ] Ship',updated_at:'old'}};
|
||||
const draft = controller.relatedTaskDraft(item, detail, 0, ' Build package ');
|
||||
controller.linkRelatedTask(item, detail, 0, {{
|
||||
repository:'stackchain/api',number:44,url:'https://forge.example/stackchain/api/issues/44',title:'Build package',
|
||||
}}).then(result => process.stdout.write(JSON.stringify({{draft,calls,result}})));
|
||||
"""
|
||||
output = json.loads(subprocess.run(
|
||||
["node", "-e", script], check=True, capture_output=True, text=True
|
||||
).stdout)
|
||||
|
||||
assert output["draft"] == {
|
||||
"repository": "stackchain/api",
|
||||
"title": "Build package",
|
||||
"body": "Related to [stackchain/api#17](https://forge.example/stackchain/api/issues/17).",
|
||||
"labelIds": [],
|
||||
}
|
||||
assert output["calls"] == [{
|
||||
"url": "api/v1/repos/stackchain/api/issues/17/content",
|
||||
"body": {
|
||||
"title": "Release",
|
||||
"body": "Intro\r\n - [x] [Build package](https://forge.example/stackchain/api/issues/44)\r\n - [ ] Ship",
|
||||
"expected_updated_at": "old",
|
||||
},
|
||||
}]
|
||||
assert output["result"]["updated_at"] == "new"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_mobile_drafts_reviews_and_retries_an_unambiguous_checklist_conflict():
|
||||
html = await dashboard()
|
||||
|
||||
Reference in new issue
Block a user