From 1f9f0f5c22c59b3e6efb3ec73f28d460194b35c3 Mon Sep 17 00:00:00 2001 From: timmy Date: Mon, 24 Aug 2026 08:16:21 +0000 Subject: [PATCH] test: gate mobile review cancellation journey --- .gitea/workflows/ci.yml | 2 +- ...bile_cancel_pull_review_request_release.py | 88 +++++++++++++++++++ tests/test_ci_workflow.py | 8 ++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/test_mobile_cancel_pull_review_request_release.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index ad147fa..e97ccb0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: pip install -r requirements-e2e.txt python3 -m playwright install --with-deps chromium - name: Exercise packaged mobile work journeys - run: python3 -m pytest tests/e2e/test_mobile_offline_issue_release.py tests/e2e/test_mobile_search_preview_navigation.py tests/e2e/test_mobile_search_week_plan.py tests/e2e/test_mobile_find_work_release.py tests/e2e/test_mobile_home_bootstrap_release.py tests/e2e/test_mobile_sign_out_release.py tests/e2e/test_mobile_today_handoff_release.py tests/e2e/test_mobile_today_wrap_up_release.py tests/e2e/test_mobile_today_summary_release.py tests/e2e/test_mobile_tomorrow_conflict_release.py tests/e2e/test_mobile_week_ahead_release.py tests/e2e/test_mobile_today_week_reschedule_release.py tests/e2e/test_mobile_wrap_up_handoff_release.py tests/e2e/test_mobile_following_release.py tests/e2e/test_mobile_detail_watch_release.py tests/e2e/test_mobile_pull_reviewer_status_release.py tests/e2e/test_mobile_pull_reviewer_feedback_release.py -q + run: python3 -m pytest tests/e2e/test_mobile_offline_issue_release.py tests/e2e/test_mobile_search_preview_navigation.py tests/e2e/test_mobile_search_week_plan.py tests/e2e/test_mobile_find_work_release.py tests/e2e/test_mobile_home_bootstrap_release.py tests/e2e/test_mobile_sign_out_release.py tests/e2e/test_mobile_today_handoff_release.py tests/e2e/test_mobile_today_wrap_up_release.py tests/e2e/test_mobile_today_summary_release.py tests/e2e/test_mobile_tomorrow_conflict_release.py tests/e2e/test_mobile_week_ahead_release.py tests/e2e/test_mobile_today_week_reschedule_release.py tests/e2e/test_mobile_wrap_up_handoff_release.py tests/e2e/test_mobile_following_release.py tests/e2e/test_mobile_detail_watch_release.py tests/e2e/test_mobile_pull_reviewer_status_release.py tests/e2e/test_mobile_pull_reviewer_feedback_release.py -q tests/e2e/test_mobile_address_review_feedback_release.py tests/e2e/test_mobile_cancel_pull_review_request_release.py release-candidate: runs-on: ubuntu-latest diff --git a/tests/e2e/test_mobile_cancel_pull_review_request_release.py b/tests/e2e/test_mobile_cancel_pull_review_request_release.py new file mode 100644 index 0000000..ff75c40 --- /dev/null +++ b/tests/e2e/test_mobile_cancel_pull_review_request_release.py @@ -0,0 +1,88 @@ +import os +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).parents[2] + +if os.environ.get("STACKCHAIN_RUN_RELEASE_E2E") != "1": + pytest.skip("release browser journey is opt-in", allow_module_level=True) + + +@pytest.mark.parametrize("viewport", [(320, 568), (390, 844)]) +def test_mobile_author_cancels_pending_review_and_opens_replacement_picker(viewport): + playwright = pytest.importorskip("playwright.sync_api") + html = (ROOT / "frontend" / "index.html").read_text() + + 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( + """() => { + globalThis.confirm = () => true; + globalThis.cancelCalls = []; + globalThis.cancelItem = { + repository:'stackchain/api', number:7, key:'stackchain/api#7' + }; + globalThis.cancelDetail = { + state:'open', draft:false, mergeable:true, merged:false, + ci_state:'success', head_sha:'abc1234', files:[], + reviewers:[{ + login:'sam', status:'waiting', head_sha:'abc1234', blocking:true + }], + }; + globalThis.cancelController = createPullSheet({ + storage:null, + fetchJson:async (path, options={}) => { + cancelCalls.push({path, options}); + return { + repository:'stackchain/api', number:7, reviewer:'sam', + head_sha:'abc1234', requested_reviewers:[], + }; + }, + }); + createPullSheet.bindReviewRequestControls( + document, cancelController, () => cancelItem, () => cancelDetail + ); + createPullSheet.review(cancelDetail, null, document); + document.querySelector('#pull-sheet').classList.add('open'); + document.querySelector('#pull-review').open = true; + }""" + ) + + cancel = page.locator('[data-cancel-review-request="sam"]') + bounds = cancel.bounding_box() + assert bounds and bounds["height"] >= 44 + cancel.click() + page.wait_for_function("cancelCalls.length === 1") + page.wait_for_function( + "document.querySelector('#pull-review-request-status').textContent.includes('Choose a replacement reviewer')" + ) + result = page.evaluate( + """() => ({ + calls:cancelCalls, + waitingRows:document.querySelectorAll('[data-reviewer-status="waiting"]').length, + pickerOpen:document.querySelector('#pull-review-request').open, + focused:document.activeElement?.id, + scrollWidth:document.documentElement.scrollWidth, + clientWidth:document.documentElement.clientWidth, + })""" + ) + browser.close() + + assert result["scrollWidth"] <= result["clientWidth"] + assert result["waitingRows"] == 0 + assert result["pickerOpen"] is True + assert result["focused"] == "load-pull-reviewers" + assert len(result["calls"]) == 1 + assert result["calls"][0]["options"]["method"] == "DELETE" + assert result["calls"][0]["options"]["body"] == ( + '{"reviewer":"sam","expected_head_sha":"abc1234"}' + ) diff --git a/tests/test_ci_workflow.py b/tests/test_ci_workflow.py index 8455d5c..ec6c6b7 100644 --- a/tests/test_ci_workflow.py +++ b/tests/test_ci_workflow.py @@ -85,3 +85,11 @@ def test_browser_job_runs_packaged_today_week_reschedule_journey(): browser = text[text.index(" browser-journey:") : text.index(" release-candidate:")] assert "tests/e2e/test_mobile_today_week_reschedule_release.py" in browser + + +def test_browser_job_gates_latest_pull_review_mobile_journeys(): + text = WORKFLOW.read_text() + browser = text[text.index(" browser-journey:") : text.index(" release-candidate:")] + + assert "tests/e2e/test_mobile_address_review_feedback_release.py" in browser + assert "tests/e2e/test_mobile_cancel_pull_review_request_release.py" in browser