feat: batch mobile review suggestions (Closes #1384)
CI / lint (pull_request) Successful in 3m38s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 6m20s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-25 07:14:54 +00:00
1 parent c8ffde7d96
commit 869543e992
8 files changed
+392 -2

No files matched your search

@@ -0,0 +1,108 @@
from pathlib import Path
import pytest
ROOT = Path(__file__).parents[2]
@pytest.mark.parametrize("viewport", [(320, 568), (390, 844)])
def test_mobile_author_stages_and_commits_two_same_file_suggestions_atomically(viewport):
playwright = pytest.importorskip("playwright.sync_api")
html = (ROOT / "frontend" / "index.html").read_text()
detail = {
"state": "open", "merged": False, "head_sha": "abc1234",
"capabilities": {"authored": True},
"reviewers": [{
"review_id": 42, "login": "sam", "status": "changes_requested",
"head_sha": "abc1234", "blocking": True, "feedback_state": "loaded",
"comments": [
{"id": 99, "path": "src/api.py", "line": 2, "body": "Use the typed empty result.",
"suggestion": {"line": 2, "content": " return empty_result()"}},
{"id": 100, "path": "src/api.py", "line": 3, "body": "Keep the result typed.",
"suggestion": {"line": 3, "content": " return typed_result()"}},
],
}],
"files": [
{"filename": "src/api.py", "status": "modified", "diff_available": True,
"diff_lines": ["@@ -2 +2 @@", "+ return None"]},
],
}
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.batchCalls = [];
globalThis.batchItem = {repository:'stackchain/api', number:7, key:'stackchain/api#7'};
globalThis.batchDetail = detail;
globalThis.batchController = createPullSheet({fetchJson: async (path, options = {}) => {
batchCalls.push({path, options});
if ((options.method || 'GET') === 'GET') {
return {path:'src/api.py', head_sha:'abc1234', blob_sha:'blob1',
content:'def lookup():\\n return None\\n return result\\n'};
}
return {path:'src/api.py', suggestion_count:2, previous_head_sha:'abc1234', head_sha:'def5678'};
}});
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))
).join('');
createPullSheet.bindFeedbackControls(document, batchController,
() => batchItem, () => batchDetail, () => 'alex');
createPullSheet.review(detail, null, document);
}""",
detail,
)
page.locator(".pull-review-feedback summary").click()
page.locator('[data-address-review-feedback="42"]').click()
page.locator("#review-pull-feedback-suggestion").click()
page.wait_for_function("batchCalls.length === 1")
page.locator("#stage-pull-feedback-suggestion").click()
assert page.locator("#review-pull-feedback-batch").text_content() == "Review fixes (1)"
page.locator("#next-pull-feedback").click()
page.locator("#review-pull-feedback-suggestion").click()
page.wait_for_function("batchCalls.length === 2")
page.locator("#stage-pull-feedback-suggestion").click()
assert page.locator("#review-pull-feedback-batch").text_content() == "Review fixes (2)"
page.locator("#review-pull-feedback-batch").click()
assert page.locator("#pull-feedback-batch-review").is_visible()
assert page.locator("#pull-feedback-batch-items li").count() == 1
page.locator("#commit-pull-feedback-batch").click()
page.wait_for_function("batchCalls.length === 3")
result = page.evaluate("""() => ({
calls:batchCalls, head:batchDetail.head_sha,
reviewHidden:document.querySelector('#pull-feedback-batch-review').hidden,
status:document.querySelector('#pull-feedback-status').textContent,
scrollWidth:document.documentElement.scrollWidth,
clientWidth:document.documentElement.clientWidth,
heights:Array.from(document.querySelectorAll('#pull-feedback-pass button'))
.filter(button => button.getClientRects().length > 0).map(button => button.getBoundingClientRect().height),
})""")
browser.close()
assert result["scrollWidth"] <= result["clientWidth"]
assert min(result["heights"]) >= 44
assert result["head"] == "def5678"
assert result["reviewHidden"] is True
assert "2 suggested changes committed" in result["status"].lower()
request = result["calls"][2]
assert request["path"].endswith("/feedback-batch")
assert request["options"]["method"] == "POST"
body = request["options"]["body"]
assert '"expected_head_sha":"abc1234"' in body
assert '"file":{"path":"src/api.py"' in body
assert '"suggestion_count":2' in body
assert 'return empty_result()' in body
assert 'return typed_result()' in body