stackchain-dashboard/tests/e2e/test_mobile_today_summary_release.py
timmy b3a359760c
Some checks failed
CI / lint (pull_request) Successful in 3m20s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Failing after 3m9s
CI / release-candidate (pull_request) Has been skipped
feat: share private Today summaries (Closes #1090)
2026-08-18 18:39:01 +00:00

112 lines
4.9 KiB
Python

from __future__ import annotations
import os
import threading
from pathlib import Path
import pytest
if os.getenv("STACKCHAIN_RUN_RELEASE_E2E") != "1":
pytest.skip("packaged Today summary 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_reviews_and_shares_a_private_mobile_today_summary(
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))
fake_thread = threading.Thread(target=fake.serve_forever, daemon=True)
fake_thread.start()
fake_url = f"http://127.0.0.1:{fake.server_port}"
browser_errors: list[str] = []
try:
with release_server(archives[0], tmp_path, fake_url) as origin, sync_playwright() as playwright:
browser = playwright.chromium.launch(args=["--ignore-certificate-errors"])
context = browser.new_context(
viewport={"width": width, "height": height}, ignore_https_errors=True
)
page = context.new_page()
page.add_init_script(
"Object.defineProperty(navigator, 'share', {configurable:true, value:async payload => { window.__sharedSummary = payload; }});"
)
page.on("pageerror", lambda error: browser_errors.append(error.stack or str(error)))
page.on(
"console",
lambda message: browser_errors.append(message.text)
if message.type == "error"
else None,
)
page.goto(origin + "/", wait_until="networkidle")
page.locator('input[name="device_label"]').fill("Today summary 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")
page.evaluate(
"""
() => {
const summary=createTodaySummary({
storage:localStorage,getLogin:()=> 'timmy',
share:payload=>navigator.share(payload),
copy:text=>navigator.clipboard.writeText(text),
});
const view=createTodaySummaryView({summary,qs:selector=>document.querySelector(selector),escapeHtml:value=>String(value)});
window.__summaryTest={summary,view};
view.open(
[{identity:'issue:acme/mobile:41:',title:'Ship mobile capture',context:'acme/mobile#41',actual_minutes:42}],
[{identity:'issue:acme/mobile:42:',title:'Review release',context:'acme/mobile#42'}],
);
}
"""
)
sheet = page.locator("#today-summary-sheet")
expect(sheet).to_be_visible()
expect(page.locator("#today-summary-preview")).to_have_text(
"Today\n- Ship mobile capture\n\nTomorrow\n- Review release"
)
assert "42m" not in page.locator("#today-summary-preview").inner_text()
for control in page.locator(".today-summary-actions button").all():
bounds = control.bounding_box()
assert bounds and bounds["height"] >= 44
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
page.evaluate(
"""
() => {
window.__summaryTest.summary.includeActuals(true);
window.__summaryTest.summary.setNote('Waiting on review');
window.__summaryTest.view.render();
}
"""
)
expect(page.locator("#today-summary-preview")).to_contain_text(
"Ship mobile capture (42m)"
)
page.evaluate(
"window.__summaryTest.view.shareDraft(document.querySelector('#share-today-summary'))"
)
expect(sheet).to_be_hidden()
assert page.evaluate("window.__sharedSummary") == {
"title": "Today summary",
"text": "Today\n- Ship mobile capture (42m)\n\nTomorrow\n- Review release\n\nNote\nWaiting on review",
}
assert page.evaluate(
"localStorage.getItem('stackchain.today-summary-draft.v1.timmy')"
) is None
assert browser_errors == []
browser.close()
finally:
fake.shutdown()
fake.server_close()
fake_thread.join(timeout=5)