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" REVIEW_SHEET = Path(__file__).parents[1] / "frontend" / "review-sheet.js" def test_my_work_queue_prioritizes_labels_then_reviews_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", "work_reasons": ["review_requested"], "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", "Review PR", "Assigned issue", ] assert queue[0]["key"] == "stackchain/api#7" assert queue[0]["reason"] == "P0 priority" assert queue[1]["reason"] == "Needs your review" assert queue[1]["is_review"] is True assert queue[2]["reason"] == "Assigned to you" def test_my_work_reviews_filter_and_summary_are_actionable(): items = [ {"title": "Issue", "kind": "issue", "is_review": False, "is_assigned": True}, {"title": "Assigned PR", "kind": "pull", "is_review": False, "is_assigned": True}, {"title": "Review PR", "kind": "pull", "is_review": True, "is_assigned": False}, ] script = f""" const buildMyWork = require({json.dumps(str(MY_WORK))}); const items = {json.dumps(items)}; process.stdout.write(JSON.stringify({{ reviews: buildMyWork.filterMyWork(items, 'review'), summary: buildMyWork.summarizeMyWork(items), }})); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) output = json.loads(result.stdout) assert [item["title"] for item in output["reviews"]] == ["Review PR"] assert output["summary"] == "1 review ยท 2 assigned" def test_my_work_filter_counts_distinguish_prs_from_review_requests(): items = [ {"kind": "issue", "is_review": False}, {"kind": "pull", "is_review": False}, {"kind": "pull", "is_review": True}, ] script = f""" const buildMyWork = require({json.dumps(str(MY_WORK))}); process.stdout.write(JSON.stringify(buildMyWork.countMyWork({json.dumps(items)}))); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) assert json.loads(result.stdout) == {"all": 3, "issue": 1, "pull": 1, "review": 1} @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 'data-work-filter="review"' 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 '' in html assert "buildMyWork(data)" in html assert "markMyWorkStale()" in html assert "filterMyWork(lastMyWork, selectedWorkFilter)" in html @pytest.mark.anyio async def test_mobile_filters_wrap_show_counts_and_persist_for_the_session(): html = await dashboard() assert '.work-filters { display:flex; gap:8px; flex-wrap:wrap; }' in html assert 'data-work-count="all"' in html assert 'data-work-count="issue"' in html assert 'data-work-count="pull"' in html assert 'data-work-count="review"' in html assert 'sessionStorage.getItem(WORK_FILTER_KEY)' in html assert 'sessionStorage.setItem(WORK_FILTER_KEY, selectedWorkFilter)' in html def test_review_controller_loads_encoded_cross_repo_detail_path(): script = f""" const createReviewController = require({json.dumps(str(REVIEW_SHEET))}); let request; const controller = createReviewController({{ fetchJson: async (url, options) => {{ request = {{ url, accept: options.headers.Accept }}; return {{ title: 'Review API' }}; }} }}); controller.load({{ repository: 'stackchain/api', number: 7 }}).then(detail => process.stdout.write(JSON.stringify({{ request, title: detail.title }})) ); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) assert json.loads(result.stdout) == { "request": { "url": "api/v1/repos/stackchain/api/pulls/7/review", "accept": "application/json", }, "title": "Review API", } def test_review_diff_rows_escape_content_and_toggle_accessibly(): script = f""" const reviewSheet = require({json.dumps(str(REVIEW_SHEET))}); const escapeHtml = value => String(value) .replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>'); const html = reviewSheet.renderDiffFile({{ filename: 'src/.py', status: 'modified', additions: 1, deletions: 1, diff_available: true, diff_truncated: true, diff_lines: ['@@ -1 +1 @@', '-old ', '+new & safe'] }}, 2, escapeHtml); const button = {{ attrs: {{ 'aria-expanded': 'false' }}, getAttribute(k) {{ return this.attrs[k]; }}, setAttribute(k,v) {{ this.attrs[k]=v; }} }}; const panel = {{ hidden: true }}; reviewSheet.toggleDiff(button, panel); process.stdout.write(JSON.stringify({{ html, expanded: button.attrs['aria-expanded'], hidden: panel.hidden }})); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) output = json.loads(result.stdout) assert 'aria-expanded="false"' in output["html"] assert 'src/<api>.py' in output["html"] assert '-old <token>' in output["html"] assert '+new & safe' in output["html"] assert 'Preview truncated' in output["html"] assert 'class="review-mark"' in output["html"] assert 'data-review-filename="src/<api>.py"' in output["html"] assert 'Mark reviewed' in output["html"] assert output["expanded"] == "true" assert output["hidden"] is False def test_review_progress_is_explicit_and_restores_for_the_same_head_sha(): script = f""" const reviewSheet = require({json.dumps(str(REVIEW_SHEET))}); const values = new Map(); const storage = {{ getItem(key) {{ return values.has(key) ? values.get(key) : null; }}, setItem(key, value) {{ values.set(key, value); }} }}; const options = {{ storage, repository: 'stackchain/api', number: 7, headSha: 'abc123', files: [{{filename:'src/a.py'}}, {{filename:'src/b.py'}}, {{filename:'README.md'}}] }}; const first = reviewSheet.createProgress(options); const before = first.snapshot(); const marked = first.markReviewed('src/a.py'); const restored = reviewSheet.createProgress(options).snapshot(); process.stdout.write(JSON.stringify({{ before, marked, restored }})); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) output = json.loads(result.stdout) assert output["before"] == { "reviewed": [], "reviewedCount": 0, "total": 3, "nextFilename": "src/a.py" } assert output["marked"] == { "reviewed": ["src/a.py"], "reviewedCount": 1, "total": 3, "nextFilename": "src/b.py", } assert output["restored"] == output["marked"] def test_review_progress_resets_when_new_commits_change_the_head_sha(): script = f""" const reviewSheet = require({json.dumps(str(REVIEW_SHEET))}); const values = new Map(); const storage = {{ getItem(key) {{ return values.has(key) ? values.get(key) : null; }}, setItem(key, value) {{ values.set(key, value); }} }}; const base = {{ storage, repository: 'stackchain/api', number: 7, files: [{{filename:'src/a.py'}}] }}; reviewSheet.createProgress({{...base, headSha:'abc123'}}).markReviewed('src/a.py'); const changed = reviewSheet.createProgress({{...base, headSha:'def456'}}).snapshot(); process.stdout.write(JSON.stringify(changed)); """ result = subprocess.run( ["node", "-e", script], check=True, capture_output=True, text=True ) assert json.loads(result.stdout) == { "reviewed": [], "reviewedCount": 0, "total": 1, "nextFilename": "src/a.py", "newHead": True, } @pytest.mark.anyio async def test_review_requests_open_an_accessible_mobile_detail_sheet(): html = await dashboard() assert 'id="review-sheet"' in html assert 'role="dialog"' in html and 'aria-modal="true"' in html assert 'id="review-sheet-status"' in html and 'aria-live="polite"' in html assert 'id="open-review-gitea"' in html and 'rel="noopener noreferrer"' in html assert '' in html assert '@media (max-width: 600px)' in html assert '.review-sheet-panel' in html and 'width:100%' in html assert '.review-action' in html and 'min-height:44px' in html assert '.review-file-toggle' in html and 'min-height:44px' in html assert '.review-mark' in html and 'min-height:44px' in html assert 'id="review-progress"' in html and 'aria-live="polite"' in html assert 'id="next-unreviewed-review"' in html assert '.review-progress-actions' in html and 'position:sticky' in html assert "createReviewController.createProgress" in html assert "progress.markReviewed" in html assert "scrollIntoView" in html assert '.review-diff' in html and 'overflow-x:auto' in html @pytest.mark.anyio async def test_review_sheet_loads_details_and_preserves_safe_gitea_handoff(): html = await dashboard() assert 'data-review-index' in html assert "reviewController.load(selectedReview)" in html assert "review-files" in html assert "review-history" in html assert "open-review-gitea" in html assert "reviewController.submit" not in html