feat: add mobile My Work inbox (#115)
This commit is contained in:
1 parent
dce7373845
commit
de2f8d8fcc
7 files changed
+295
-2
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import pytest
|
||||
import json
|
||||
|
||||
from src import gitea_proxy
|
||||
from src import main
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_work_collections_use_supported_assigned_search_endpoint(monkeypatch):
|
||||
requested_paths = []
|
||||
|
||||
async def fake_fetch(path):
|
||||
requested_paths.append(path)
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(gitea_proxy, "fetch", fake_fetch)
|
||||
|
||||
assert await gitea_proxy.issues() == []
|
||||
assert await gitea_proxy.pull_requests() == []
|
||||
assert requested_paths == [
|
||||
"repos/issues/search?state=open&assigned=true&type=issues&limit=50",
|
||||
"repos/issues/search?state=open&assigned=true&type=pulls&limit=50",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_context_preserves_repository_and_update_time_for_cross_repo_work(monkeypatch):
|
||||
async def user():
|
||||
return {"id": 1, "login": "timmy"}
|
||||
|
||||
async def repositories():
|
||||
return []
|
||||
|
||||
async def issues():
|
||||
return [{
|
||||
"id": 10,
|
||||
"number": 7,
|
||||
"title": "Ship mobile flow",
|
||||
"state": "open",
|
||||
"labels": [{"name": "P0"}],
|
||||
"assignees": [{"login": "timmy"}],
|
||||
"repository": {"full_name": "stackchain/mobile"},
|
||||
"updated_at": "2026-08-06T12:00:00Z",
|
||||
"html_url": "https://forge.example/stackchain/mobile/issues/7",
|
||||
}]
|
||||
|
||||
async def pulls():
|
||||
return [{
|
||||
"id": 11,
|
||||
"number": 7,
|
||||
"title": "Review API",
|
||||
"state": "open",
|
||||
"user": {"login": "alex"},
|
||||
"labels": [{"name": "priority-high"}],
|
||||
"assignees": [{"login": "timmy"}],
|
||||
"repository": {"full_name": "stackchain/api"},
|
||||
"updated_at": "2026-08-06T11:00:00Z",
|
||||
"html_url": "https://forge.example/stackchain/api/pulls/7",
|
||||
}]
|
||||
|
||||
monkeypatch.setattr(main, "current_user", user)
|
||||
monkeypatch.setattr(main, "repos", repositories)
|
||||
monkeypatch.setattr(main, "issues", issues)
|
||||
monkeypatch.setattr(main, "pull_requests", pulls)
|
||||
|
||||
payload = json.loads((await main.context()).body)
|
||||
|
||||
assert payload["issues"][0]["repository"] == "stackchain/mobile"
|
||||
assert payload["issues"][0]["updated_at"] == "2026-08-06T12:00:00Z"
|
||||
assert payload["pull_requests"][0]["repository"] == "stackchain/api"
|
||||
assert payload["pull_requests"][0]["updated_at"] == "2026-08-06T11:00:00Z"
|
||||
assert payload["pull_requests"][0]["labels"] == ["priority-high"]
|
||||
assert payload["pull_requests"][0]["assignees"] == ["timmy"]
|
||||
@@ -0,0 +1,86 @@
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from src.views import dashboard
|
||||
|
||||
|
||||
MY_WORK = Path(__file__).parents[1] / "frontend" / "my-work.js"
|
||||
|
||||
|
||||
def test_my_work_queue_prioritizes_labels_then_assignment_and_keeps_repo_identity():
|
||||
payload = {
|
||||
"user": {"login": "timmy"},
|
||||
"issues": [
|
||||
{
|
||||
"id": 1,
|
||||
"number": 7,
|
||||
"title": "Assigned issue",
|
||||
"state": "open",
|
||||
"repository": "stackchain/mobile",
|
||||
"labels": [],
|
||||
"assignees": ["timmy"],
|
||||
"updated_at": "2026-08-06T12:00:00Z",
|
||||
"url": "https://forge.example/mobile/issues/7",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"number": 7,
|
||||
"title": "Priority issue",
|
||||
"state": "open",
|
||||
"repository": "stackchain/api",
|
||||
"labels": ["P0"],
|
||||
"assignees": [],
|
||||
"updated_at": "2026-08-06T11:00:00Z",
|
||||
"url": "https://forge.example/api/issues/7",
|
||||
},
|
||||
],
|
||||
"pull_requests": [
|
||||
{
|
||||
"id": 3,
|
||||
"number": 4,
|
||||
"title": "Review PR",
|
||||
"state": "open",
|
||||
"repository": "stackchain/web",
|
||||
"updated_at": "2026-08-06T13:00:00Z",
|
||||
"url": "https://forge.example/web/pulls/4",
|
||||
}
|
||||
],
|
||||
}
|
||||
script = f"""
|
||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
||||
const queue = buildMyWork({json.dumps(payload)});
|
||||
process.stdout.write(JSON.stringify(queue));
|
||||
"""
|
||||
|
||||
result = subprocess.run(
|
||||
["node", "-e", script], check=True, capture_output=True, text=True
|
||||
)
|
||||
queue = json.loads(result.stdout)
|
||||
|
||||
assert [item["title"] for item in queue] == [
|
||||
"Priority issue",
|
||||
"Assigned issue",
|
||||
"Review PR",
|
||||
]
|
||||
assert queue[0]["key"] == "stackchain/api#7"
|
||||
assert queue[0]["reason"] == "P0 priority"
|
||||
assert queue[1]["reason"] == "Assigned to you"
|
||||
assert queue[2]["kind"] == "pull"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_mobile_dashboard_puts_filterable_my_work_before_auxiliary_panels():
|
||||
html = await dashboard()
|
||||
|
||||
assert html.index('id="my-work"') < html.index('data-panel-key="context"')
|
||||
assert 'data-work-filter="all"' in html
|
||||
assert 'data-work-filter="issue"' in html
|
||||
assert 'data-work-filter="pull"' in html
|
||||
assert '.work-filter' in html and 'min-height: 44px' in html
|
||||
assert '.my-work-card' in html and 'min-height: 44px' in html
|
||||
assert '<script src="static/my-work.js"></script>' in html
|
||||
assert "buildMyWork(data)" in html
|
||||
assert "markMyWorkStale()" in html
|
||||
Reference in new issue
Block a user