from __future__ import annotations import os import threading from datetime import date, timedelta from pathlib import Path import pytest if os.getenv("STACKCHAIN_RUN_RELEASE_E2E") != "1": pytest.skip("packaged Today to Week Ahead journey runs only in its gated CI job", allow_module_level=True) pytest.importorskip("playwright.sync_api") from playwright.sync_api import expect, sync_playwright from fake_gitea import FakeGiteaServer from test_mobile_offline_issue_release import ACCESS_TOKEN, ROOT, release_server @pytest.mark.parametrize(("width", "height"), [(320, 568), (390, 844)]) def test_release_artifact_reschedules_active_today_into_week_ahead( tmp_path: Path, width: int, height: int ): archives = sorted((ROOT / "dist").glob("stackchain-dashboard-*.tar.gz")) assert len(archives) == 1, "browser job must download exactly one assembled release archive" fake = FakeGiteaServer(("127.0.0.1", 0)) thread = threading.Thread(target=fake.serve_forever, daemon=True) thread.start() try: with release_server( archives[0], tmp_path, f"http://127.0.0.1:{fake.server_port}" ) as origin, sync_playwright() as playwright: browser = playwright.chromium.launch(args=["--ignore-certificate-errors"]) page = browser.new_page( viewport={"width": width, "height": height}, timezone_id="UTC" ) page_errors: list[str] = [] page.on("pageerror", lambda error: page_errors.append(str(error))) page.goto(origin + "/", wait_until="networkidle") page.locator('input[name="device_label"]').fill("Reschedule release phone") page.locator('input[name="access_token"]').fill(ACCESS_TOKEN) page.locator("#submit-sign-in").click() page.wait_for_url(origin + "/", wait_until="networkidle") plan_date = (date.today() + timedelta(days=2)).isoformat() saved_week = page.evaluate( """async ({planDate}) => { const response=await fetch('api/v1/week',{method:'PUT',headers:{'Content-Type':'application/json'}, body:JSON.stringify({base_revision:0,timezone:'UTC',days:[{ plan_date:planDate,ids:[],capacity_minutes:90,estimates:{} }]})}); return {status:response.status,body:await response.json()}; }""", {"planDate": plan_date}, ) assert saved_week["status"] == 200 page.locator('[data-mobile-task="work"]').click() page.locator("#plan-today-available").fill("90") page.locator("#plan-today-available").press("Tab") page.locator("#build-today-plan").click() missing = page.locator("[data-plan-missing-estimate]") for _ in range(2): missing.nth(0).fill("30") missing.nth(0).press("Tab") save_and_start = page.locator("#save-and-start-today") expect(save_and_start).to_be_enabled() save_and_start.click() expect(page.locator("#plan-today-sheet")).to_be_hidden(timeout=10_000) expect(page.locator("#issue-sheet-title")).to_have_text("Ship mobile capture") if page.locator("#plan-today-sheet").is_visible(): page.locator("#cancel-plan-today").click() issue_sheet = page.locator("#issue-sheet") if issue_sheet.is_visible(): page.locator("#close-issue-sheet").click() expect(issue_sheet).to_be_hidden() if page.locator("#plan-today-sheet").is_visible(): page.locator("#cancel-plan-today").click() launcher = page.locator("[data-work-session-reschedule-week]") page.locator("[data-mobile-today-more]").click() expect(launcher).to_be_visible() launcher_bounds = launcher.bounding_box() assert launcher_bounds and launcher_bounds["height"] >= 44 launcher.click() dialog = page.locator("#today-week-reschedule") expect(dialog).to_be_visible() days = page.locator("#today-week-reschedule-days button") try: expect(days).to_have_count(7) except AssertionError as error: raise AssertionError({ "status": page.locator("#today-week-reschedule-status").text_content(), "page_errors": page_errors, "today": page.evaluate("async()=>await (await fetch('api/v1/today')).json()"), "week": page.evaluate("async()=>await (await fetch('api/v1/week')).json()"), }) from error for control in [*days.all(), page.locator("#cancel-today-week-reschedule"), page.locator("#confirm-today-week-reschedule")]: bounds = control.bounding_box() assert bounds and bounds["height"] >= 44 assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth") page.locator("#cancel-today-week-reschedule").click() expect(dialog).to_be_hidden() expect(launcher).to_be_focused() launcher.click() destination = page.locator( f'#today-week-reschedule-days button[data-plan-date="{plan_date}"]' ) destination.click() page.locator("#confirm-today-week-reschedule").click() expect(dialog).to_be_hidden() expect(page.locator("#issue-sheet-title")).to_have_text("Polish desktop filters") week = page.evaluate("async()=>await (await fetch('api/v1/week')).json()") today = page.evaluate("async()=>await (await fetch('api/v1/today')).json()") assert week["days"][0]["plan_date"] == plan_date assert len(week["days"][0]["ids"]) == 1 assert len(today["ids"]) == 1 assert not page_errors assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth") browser.close() finally: fake.shutdown() fake.server_close() thread.join(timeout=2)