feat: keep delegated filings in My Work (Closes #870)
This commit is contained in:
1 parent
1fdbba1ca1
commit
a9ffc2d879
11 files changed
+179
-15
No files matched your search
@@ -66,6 +66,64 @@ async def test_work_page_preserves_total_and_reason_without_loading_other_pages(
|
||||
assert result["items"][0]["work_reasons"] == ["review_requested"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_filed_work_page_loads_open_authored_issues_with_a_distinct_reason():
|
||||
requests = []
|
||||
|
||||
def upstream(request):
|
||||
requests.append(str(request.url))
|
||||
return httpx.Response(200, headers={"X-Total-Count": "61"}, json=[{
|
||||
"id": 870, "number": 870, "title": "Delegated filing", "state": "open",
|
||||
"repository": {"full_name": "stackchain/dashboard"},
|
||||
"html_url": "https://forge.example/stackchain/dashboard/issues/870",
|
||||
}])
|
||||
|
||||
gitea_proxy.start_client(transport=httpx.MockTransport(upstream))
|
||||
try:
|
||||
result = await gitea_proxy.work_page("filed", page=2)
|
||||
finally:
|
||||
await gitea_proxy.stop_client()
|
||||
|
||||
assert requests == [
|
||||
"http://127.0.0.1:3000/api/v1/repos/issues/search?state=open&created=true&type=issues&limit=50&page=2"
|
||||
]
|
||||
assert result == {
|
||||
"stream": "filed", "page": 2, "total": 61, "has_more": False,
|
||||
"items": [{
|
||||
"id": 870, "number": 870, "title": "Delegated filing", "state": "open",
|
||||
"repository": {"full_name": "stackchain/dashboard"},
|
||||
"html_url": "https://forge.example/stackchain/dashboard/issues/870",
|
||||
"work_reasons": ["created_by_me"],
|
||||
}],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_issue_context_merges_self_assigned_and_filed_streams_without_duplicates(monkeypatch):
|
||||
async def page(stream, page=1, limit=50):
|
||||
del page, limit
|
||||
common = {
|
||||
"id": 7, "number": 7, "title": "My own filing", "state": "open",
|
||||
"repository": {"full_name": "stackchain/api"}, "html_url": "https://forge.example/issues/7",
|
||||
}
|
||||
if stream == "issue":
|
||||
return {"items": [{**common, "assignees": [{"login": "timmy"}]}], "page": 1, "total": 1, "has_more": False}
|
||||
return {"items": [{**common, "work_reasons": ["created_by_me"]}, {
|
||||
**common, "id": 8, "number": 8, "title": "Delegated",
|
||||
"work_reasons": ["created_by_me"],
|
||||
}], "page": 1, "total": 2, "has_more": False}
|
||||
|
||||
monkeypatch.setattr(gitea_proxy, "work_page", page)
|
||||
result = await gitea_proxy.issues()
|
||||
|
||||
assert [item["number"] for item in result] == [7, 8]
|
||||
assert result[0]["work_reasons"] == ["created_by_me"]
|
||||
assert result.pagination == {
|
||||
"issue": {"page": 1, "total": 1, "has_more": False},
|
||||
"filed": {"page": 1, "total": 2, "has_more": False},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_available_issue_page_filters_assigned_and_pull_items_then_ranks_priority():
|
||||
requests = []
|
||||
@@ -568,7 +626,7 @@ async def test_confirmed_claim_is_removed_from_retained_available_snapshot(monke
|
||||
async def test_initial_work_collections_expose_independent_pagination(monkeypatch):
|
||||
async def fake_page(stream, page=1, limit=50):
|
||||
assert page == 1
|
||||
totals = {"issue": 84, "pull": 61, "review": 73}
|
||||
totals = {"issue": 84, "filed": 62, "pull": 61, "review": 73}
|
||||
return {
|
||||
"items": [], "page": 1, "total": totals[stream],
|
||||
"has_more": True, "stream": stream,
|
||||
@@ -580,7 +638,8 @@ async def test_initial_work_collections_expose_independent_pagination(monkeypatc
|
||||
pulls = await gitea_proxy.pull_requests()
|
||||
|
||||
assert assigned_issues.pagination == {
|
||||
"issue": {"page": 1, "total": 84, "has_more": True}
|
||||
"issue": {"page": 1, "total": 84, "has_more": True},
|
||||
"filed": {"page": 1, "total": 62, "has_more": True},
|
||||
}
|
||||
assert pulls.pagination == {
|
||||
"pull": {"page": 1, "total": 61, "has_more": True},
|
||||
@@ -602,6 +661,7 @@ async def test_work_collections_include_supported_review_request_search(monkeypa
|
||||
assert await gitea_proxy.pull_requests() == []
|
||||
assert requested_streams == [
|
||||
("issue", 1, 50),
|
||||
("filed", 1, 50),
|
||||
("pull", 1, 50),
|
||||
("review", 1, 50),
|
||||
]
|
||||
|
||||
@@ -89,6 +89,24 @@ process.stdout.write(JSON.stringify({{plans,hidden:elements.root.hidden,restored
|
||||
}], "hidden": True, "restored": 1}
|
||||
|
||||
|
||||
def test_view_in_filed_closes_receipt_and_routes_the_exact_confirmed_issue():
|
||||
script = f"""
|
||||
const createReceipt = require({json.dumps(str(RECEIPT))});
|
||||
const element = () => ({{hidden:true,focusCount:0,focus(){{this.focusCount++;}},addEventListener(name, fn){{this[name]=fn;}}}});
|
||||
const elements = {{root:element(),heading:element(),key:element(),title:element(),ownership:element(),openLink:element(),shareButton:element(),filedButton:element(),fileAnotherButton:element(),doneButton:element(),status:element()}};
|
||||
const filed=[];
|
||||
const controller=createReceipt({{...elements,onViewFiled:issue=>filed.push(issue)}});
|
||||
controller.show({{repository:'stackchain/dashboard',number:870,title:'Delegated',assignees:['alex'],url:'https://forge.example/issues/870'}},elements.doneButton);
|
||||
elements.filedButton.click();
|
||||
process.stdout.write(JSON.stringify({{filed,hidden:elements.root.hidden,restored:elements.doneButton.focusCount}}));
|
||||
"""
|
||||
output = run_node(script)
|
||||
assert output == {
|
||||
"filed": [{"repository": "stackchain/dashboard", "number": 870, "title": "Delegated", "assignees": ["alex"], "url": "https://forge.example/issues/870"}],
|
||||
"hidden": True, "restored": 1,
|
||||
}
|
||||
|
||||
|
||||
def test_mobile_shell_wires_confirmed_non_my_work_issues_to_the_receipt():
|
||||
index = INDEX.read_text()
|
||||
css = CSS.read_text()
|
||||
@@ -97,6 +115,7 @@ def test_mobile_shell_wires_confirmed_non_my_work_issues_to_the_receipt():
|
||||
assert 'id="issue-filing-receipt" role="dialog" aria-modal="true"' in index
|
||||
assert 'id="issue-filing-receipt-heading"' in index
|
||||
assert 'id="issue-filing-receipt-open"' in index
|
||||
assert 'id="issue-filing-receipt-filed"' in index
|
||||
assert 'id="issue-filing-receipt-share"' in index
|
||||
assert 'id="issue-filing-receipt-related"' in index
|
||||
assert 'id="issue-filing-receipt-another"' in index
|
||||
@@ -107,6 +126,9 @@ def test_mobile_shell_wires_confirmed_non_my_work_issues_to_the_receipt():
|
||||
assert '.issue-filing-receipt-actions' in css
|
||||
assert 'min-height:44px' in css
|
||||
assert 'createIssueFilingReceipt({' in dashboard
|
||||
assert "filedButton:qs('#issue-filing-receipt-filed')" in dashboard
|
||||
assert 'onViewFiled:issue =>' in dashboard
|
||||
assert "confirmed.work_reasons = ['created_by_me']" in dashboard
|
||||
assert "relatedButton:qs('#issue-filing-receipt-related')" in dashboard
|
||||
assert 'relatedDraft:issueCapture.buildRelatedDraft(durableDraft)' in dashboard
|
||||
assert "issueCapture.saveDraft(plan)" in dashboard
|
||||
|
||||
+28
-3
@@ -3767,7 +3767,7 @@ process.stdout.write(JSON.stringify(buildMyWork.countMyWork({json.dumps(items)})
|
||||
)
|
||||
|
||||
assert json.loads(result.stdout) == {
|
||||
"all": 3, "attention": 1, "issue": 1, "pull": 1, "review": 1, "update": 0
|
||||
"all": 3, "attention": 1, "filed": 0, "issue": 1, "pull": 1, "review": 1, "update": 0
|
||||
}
|
||||
|
||||
|
||||
@@ -4927,7 +4927,7 @@ process.stdout.write(JSON.stringify({{
|
||||
assert output["updates"][1]["kind"] == "update"
|
||||
assert output["updates"][1]["update_reason"] == ""
|
||||
assert output["counts"] == {
|
||||
"all": 2, "attention": 2, "issue": 1, "pull": 0, "review": 0, "update": 2
|
||||
"all": 2, "attention": 2, "filed": 0, "issue": 1, "pull": 0, "review": 0, "update": 2
|
||||
}
|
||||
assert output["summary"] == "2 unread updates · 0 reviews · 1 assigned"
|
||||
|
||||
@@ -6896,7 +6896,7 @@ async def test_mobile_filters_wrap_show_counts_and_persist_for_the_session():
|
||||
assert 'data-work-count="review"' in html
|
||||
assert 'data-work-count="update"' in html
|
||||
assert 'data-work-count="later"' in html
|
||||
assert "['all', 'today', 'agenda', 'attention', 'issue', 'pull', 'review', 'update', 'later', 'draft'].includes(savedFilter)" in html
|
||||
assert "['all', 'today', 'agenda', 'attention', 'filed', 'issue', 'pull', 'review', 'update', 'later', 'draft'].includes(savedFilter)" in html
|
||||
assert 'data-work-count="draft"' in html
|
||||
assert 'sessionStorage.getItem(WORK_FILTER_KEY)' in html
|
||||
assert 'sessionStorage.setItem(WORK_FILTER_KEY, selectedWorkFilter)' in html
|
||||
@@ -7647,3 +7647,28 @@ first.submit(item, payload).catch(() => {{
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True)
|
||||
assert json.loads(result.stdout) == {"calls": ["review-op-185", "review-op-185"], "keys": []}
|
||||
|
||||
def test_filed_queue_contains_authored_issues_and_deduplicates_self_assigned_filings():
|
||||
payload = {
|
||||
"user": {"login": "timmy"},
|
||||
"issues": [
|
||||
{"id": 1, "repository": "stackchain/api", "number": 1, "title": "Delegated", "assignees": ["alex"], "work_reasons": ["created_by_me"]},
|
||||
{"id": 2, "repository": "stackchain/api", "number": 2, "title": "Mine", "assignees": ["timmy"], "work_reasons": ["created_by_me"]},
|
||||
{"id": 3, "repository": "stackchain/api", "number": 3, "title": "Assigned only", "assignees": ["timmy"]},
|
||||
],
|
||||
"pull_requests": [],
|
||||
}
|
||||
script = f"""
|
||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
||||
const items = buildMyWork({json.dumps(payload)});
|
||||
process.stdout.write(JSON.stringify({{
|
||||
filed: buildMyWork.filterMyWork(items, 'filed').map(item => item.number).sort((a,b) => a-b),
|
||||
count: buildMyWork.countMyWork(items).filed,
|
||||
identities: items.map(item => item.key).sort(),
|
||||
}}));
|
||||
"""
|
||||
result = subprocess.run(["node", "-e", script], check=True, capture_output=True, text=True)
|
||||
assert json.loads(result.stdout) == {
|
||||
"filed": [1, 2], "count": 2,
|
||||
"identities": ["stackchain/api#1", "stackchain/api#2", "stackchain/api#3"],
|
||||
}
|
||||
@@ -41,6 +41,7 @@ async def test_work_page_endpoint_normalizes_requested_page_and_is_not_cacheable
|
||||
"items": [{
|
||||
"id": 51, "number": 51, "title": "Older issue", "state": "open",
|
||||
"labels": [], "assignees": ["timmy"], "repository": "stackchain/api",
|
||||
"work_reasons": [],
|
||||
"updated_at": "2026-08-07T10:00:00Z",
|
||||
"due_date": "2026-08-09T23:59:59Z",
|
||||
"milestone": {"id": 9, "title": "August RC"},
|
||||
@@ -66,6 +67,29 @@ async def test_work_page_endpoint_rejects_unknown_stream_before_upstream_io(monk
|
||||
assert called is False
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_filed_work_page_endpoint_preserves_authored_reason(monkeypatch):
|
||||
async def page_loader(stream, page):
|
||||
return {
|
||||
"stream": stream, "page": page, "total": 1, "has_more": False,
|
||||
"items": [{
|
||||
"id": 870, "number": 870, "title": "Delegated", "state": "open",
|
||||
"labels": [], "assignees": [{"login": "alex"}],
|
||||
"work_reasons": ["created_by_me"],
|
||||
"repository": {"full_name": "stackchain/dashboard"},
|
||||
"html_url": "https://forge.example/issues/870",
|
||||
}],
|
||||
}
|
||||
|
||||
monkeypatch.setattr(main.gitea_proxy, "work_page", page_loader)
|
||||
transport = httpx.ASGITransport(app=main.app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
response = await client.get("/api/v1/work/filed?page=2")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["items"][0]["work_reasons"] == ["created_by_me"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_pwa_assets_expose_root_scoped_share_target_without_caching_api_data():
|
||||
transport = httpx.ASGITransport(app=main.app)
|
||||
|
||||
Reference in new issue
Block a user