from pathlib import Path import pytest ROOT = Path(__file__).parents[2] @pytest.mark.parametrize("viewport", [(320, 568), (390, 844)]) def test_mobile_author_addresses_every_review_comment_and_requests_updated_review(viewport): playwright = pytest.importorskip("playwright.sync_api") html = (ROOT / "frontend" / "index.html").read_text() detail = { "state": "open", "draft": False, "mergeable": True, "merged": False, "ci_state": "success", "head_sha": "current-head", "reviewers": [{ "review_id": 42, "login": "sam", "status": "changes_requested", "head_sha": "current-head", "blocking": True, "comments": [ {"id": 99, "path": "src/api.py", "body": "Handle the empty state.", "line": 12}, {"id": 100, "path": "frontend/app.js", "body": "Explain the retry.", "line": 8}, ], }], "files": [ {"filename": "src/api.py", "status": "modified", "diff_available": True, "diff_lines": ["@@ -11 +11 @@", "+return empty"]}, {"filename": "frontend/app.js", "status": "modified", "diff_available": True, "diff_lines": ["@@ -7 +7 @@", "+showRetry()"]}, ], } with playwright.sync_playwright() as runtime: try: browser = runtime.chromium.launch(headless=True) except Exception as error: pytest.skip(f"Chromium is not installed: {error}") page = browser.new_page(viewport={"width": viewport[0], "height": viewport[1]}) page.set_content(html, wait_until="domcontentloaded") page.add_style_tag(path=ROOT / "frontend" / "dashboard.css") page.add_script_tag(path=ROOT / "frontend" / "pull-sheet.js") page.evaluate( """detail => { globalThis.confirm = () => true; globalThis.feedbackCalls = []; globalThis.feedbackStored = new Map(); globalThis.feedbackStorage = { getItem:key => feedbackStored.has(key) ? feedbackStored.get(key) : null, setItem:(key, value) => feedbackStored.set(key, String(value)), removeItem:key => feedbackStored.delete(key), }; globalThis.feedbackItem = {repository:'stackchain/api', number:7, key:'stackchain/api#7'}; globalThis.feedbackDetail = detail; globalThis.feedbackController = createPullSheet({ storage: feedbackStorage, createOperationId: () => 'feedback-operation', fetchJson: async (path, options = {}) => { feedbackCalls.push({path, options}); if (path.endsWith('/comments')) return {id: 501, body:JSON.parse(options.body).body}; if (path.endsWith('/request-review')) return { number:7, reviewer:'sam', head_sha:'current-head', requested_reviewers:['sam'] }; throw new Error('unexpected request'); }, }); document.querySelector('#pull-sheet').classList.add('open'); document.querySelector('#pull-review').open = true; document.querySelector('#pull-files').innerHTML = detail.files.map((file, index) => createPullSheet.renderFile(file, index, false, value => String(value) .replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>')) ).join(''); createPullSheet.bindFeedbackControls(document, feedbackController, () => feedbackItem, () => feedbackDetail, () => 'timmy'); createPullSheet.review(detail, null, document); }""", detail, ) page.locator(".pull-review-feedback summary").click() page.locator('[data-address-review-feedback="42"]').click() assert page.locator("#pull-feedback-progress").text_content() == "Comment 1 of 2" assert page.locator("#finish-pull-feedback").is_disabled() page.locator('[data-feedback-disposition="addressed"]').click() page.locator("#next-pull-feedback").click() page.locator("#pull-feedback-note").fill("Added a visible retry message.") page.locator('[data-feedback-disposition="discussion"]').click() assert not page.locator("#finish-pull-feedback").is_disabled() page.locator("#finish-pull-feedback").click() page.locator("#request-feedback-review").click() page.wait_for_function("feedbackCalls.length === 2") result = page.evaluate( """() => ({ calls:feedbackCalls, scrollWidth:document.documentElement.scrollWidth, clientWidth:document.documentElement.clientWidth, controlHeights:Array.from(document.querySelectorAll('#pull-feedback-pass button')) .filter(button => !button.hidden).map(button => button.getBoundingClientRect().height), status:document.querySelector('#pull-feedback-status').textContent, secondExpanded:document.querySelector('[data-pull-filename="frontend/app.js"] .pull-file-toggle') .getAttribute('aria-expanded'), persisted:Array.from(feedbackStored.keys()).some(key => key.includes('feedback-pass')), })""" ) result["resetHidden"] = page.evaluate( """() => { createPullSheet.resetReviewRequestControls(document, feedbackDetail); return document.querySelector('#pull-feedback-pass').hidden; }""" ) browser.close() assert result["scrollWidth"] <= result["clientWidth"] assert min(result["controlHeights"]) >= 44 assert result["secondExpanded"] == "true" assert result["persisted"] is True assert result["resetHidden"] is True assert result["calls"][0]["path"].endswith("/comments") assert result["calls"][0]["options"]["headers"]["Idempotency-Key"] == "feedback-operation" summary = result["calls"][0]["options"]["body"] assert "Handle the empty state" in summary assert "Addressed" in summary assert "Needs discussion" in summary assert "Added a visible retry message" in summary assert result["calls"][1]["path"].endswith("/request-review") assert '"reviewer":"sam"' in result["calls"][1]["options"]["body"] assert result["status"] == "Response posted; updated review requested from @sam."