stackchain-dashboard/tests/e2e/test_adaptive_mobile_queues_release.py
timmy b0b051184b
All checks were successful
CI / lint (pull_request) Successful in 3m52s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 7m49s
CI / release-candidate (pull_request) Has been skipped
feat: keep adaptive mobile Work actionable offline (Closes #1458)
2026-08-27 05:59:13 +00:00

169 lines
8.4 KiB
Python

import os
from pathlib import Path
import pytest
if os.getenv("STACKCHAIN_RUN_RELEASE_E2E") != "1":
pytest.skip("packaged adaptive Queues 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
ROOT = Path(__file__).parents[2]
FRONTEND = ROOT / "frontend"
@pytest.mark.parametrize("viewport", [
{"width": 320, "height": 568},
{"width": 375, "height": 667},
{"width": 430, "height": 932},
])
def test_adaptive_queues_put_truthful_next_action_above_the_fold(viewport):
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True)
page = browser.new_page(viewport=viewport)
page.set_content((FRONTEND / "index.html").read_text())
page.add_style_tag(path=FRONTEND / "dashboard.css")
page.add_script_tag(path=FRONTEND / "mobile-queue-launcher.js")
page.evaluate("""() => {
const rows = Object.fromEntries(Array.from(document.querySelectorAll('[data-mobile-queue]'))
.map(row => [row.dataset.mobileQueue, row]));
window.adaptiveQueueLauncher = createMobileQueueLauncher({
getCounts:() => ({delivery:1, gate:2, today:3, attentionUnavailable:true}),
rows,
nextAction:document.querySelector('#mobile-queue-next-action'),
activeList:document.querySelector('#mobile-queue-active-list'),
planningList:document.querySelector('#mobile-queue-planning-list'),
allList:document.querySelector('#mobile-queue-all-list'),
activeSection:document.querySelector('#mobile-queue-active-list').parentElement,
});
window.adaptiveQueueLauncher.renderPresentation();
document.querySelector('#mobile-queue-sheet').showModal();
}""")
next_action = page.locator("#mobile-queue-next-action")
expect(next_action).to_be_visible()
expect(next_action).to_have_text("Recover Delivery (1)")
bounds = next_action.bounding_box()
assert bounds and bounds["height"] >= 44
assert bounds["x"] >= 0 and bounds["x"] + bounds["width"] <= viewport["width"]
assert bounds["y"] + bounds["height"] <= viewport["height"]
assert page.locator("#mobile-queue-active-list [data-mobile-queue]").evaluate_all(
"rows => rows.map(row => row.dataset.mobileQueue)"
) == ["delivery", "gate", "attention"]
expect(page.locator('[data-mobile-queue="attention"]')).to_have_attribute("data-unavailable", "true")
assert page.locator("#mobile-queue-planning-list [data-mobile-queue]").evaluate_all(
"rows => rows.map(row => row.dataset.mobileQueue)"
) == ["today", "tomorrow", "week"]
expect(page.locator(".mobile-queue-all")).not_to_have_attribute("open", "")
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
browser.close()
@pytest.mark.parametrize("viewport", [
{"width": 320, "height": 568},
{"width": 375, "height": 667},
{"width": 430, "height": 932},
])
@pytest.mark.parametrize(("queue", "count", "label", "destination"), [
("following", 3, "Review Following (3)", ["following"]),
("authored", 2, "Open My PRs (2)", ["filter:authored", "open:authored"]),
])
def test_adaptive_queues_open_existing_following_and_authored_work(viewport, queue, count, label, destination):
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True)
page = browser.new_page(viewport=viewport)
page.set_content((FRONTEND / "index.html").read_text())
page.add_style_tag(path=FRONTEND / "dashboard.css")
page.add_script_tag(path=FRONTEND / "mobile-queue-launcher.js")
page.evaluate("""({queue, count}) => {
const rows = Object.fromEntries(Array.from(document.querySelectorAll('[data-mobile-queue]'))
.map(row => [row.dataset.mobileQueue, row]));
window.destinations = [];
window.adaptiveQueueLauncher = createMobileQueueLauncher({
getCounts:() => ({[queue]:count}),
rows,
nextAction:document.querySelector('#mobile-queue-next-action'),
activeList:document.querySelector('#mobile-queue-active-list'),
planningList:document.querySelector('#mobile-queue-planning-list'),
allList:document.querySelector('#mobile-queue-all-list'),
activeSection:document.querySelector('#mobile-queue-active-list').parentElement,
openFollowing:() => window.destinations.push('following'),
selectFilter:name => window.destinations.push('filter:' + name),
firstAction:name => name === 'authored' ? {click:() => window.destinations.push('open:authored')} : null,
announce:message => window.destinations.push('announce:' + message),
});
window.adaptiveQueueLauncher.renderPresentation();
document.querySelector('#mobile-queue-next-action').addEventListener(
'click', () => window.adaptiveQueueLauncher.continueWork()
);
document.querySelector('#mobile-queue-sheet').showModal();
}""", {"queue": queue, "count": count})
next_action = page.locator("#mobile-queue-next-action")
expect(next_action).to_be_visible()
expect(next_action).to_have_text(label)
expect(next_action).to_have_attribute("aria-label", "Next up: " + label)
bounds = next_action.bounding_box()
assert bounds and bounds["height"] >= 44
assert bounds["y"] + bounds["height"] <= viewport["height"]
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
assert page.locator("#mobile-queue-active-list [data-mobile-queue]").evaluate_all(
"rows => rows.map(row => row.dataset.mobileQueue)"
) == [queue]
next_action.focus()
next_action.press("Enter")
assert page.evaluate("window.destinations") == destination
browser.close()
def test_adaptive_queues_keep_start_continue_actionable_offline_on_phone():
viewport = {"width": 390, "height": 844}
with sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True)
page = browser.new_page(viewport=viewport)
page.set_content((FRONTEND / "index.html").read_text())
page.add_style_tag(path=FRONTEND / "dashboard.css")
page.add_script_tag(path=FRONTEND / "mobile-queue-launcher.js")
page.evaluate("""() => {
const rows = Object.fromEntries(Array.from(document.querySelectorAll('[data-mobile-queue]'))
.map(row => [row.dataset.mobileQueue, row]));
window.online = false;
window.destinations = [];
window.adaptiveQueueLauncher = createMobileQueueLauncher({
getCounts:() => ({delivery:1, gate:2, today:3}),
isOnline:() => window.online,
rows,
nextAction:document.querySelector('#mobile-queue-next-action'),
activeList:document.querySelector('#mobile-queue-active-list'),
planningList:document.querySelector('#mobile-queue-planning-list'),
allList:document.querySelector('#mobile-queue-all-list'),
activeSection:document.querySelector('#mobile-queue-active-list').parentElement,
openToday:() => window.destinations.push('today'),
});
window.adaptiveQueueLauncher.renderPresentation();
document.querySelector('#mobile-queue-next-action').addEventListener(
'click', () => window.adaptiveQueueLauncher.continueWork()
);
document.querySelector('#mobile-queue-sheet').showModal();
}""")
next_action = page.locator("#mobile-queue-next-action")
expect(next_action).to_be_visible()
expect(next_action).to_have_text("Continue Today (3)")
next_action.press("Enter")
assert page.evaluate("window.destinations") == ["today"]
page.evaluate("""() => {
window.online = true;
window.adaptiveQueueLauncher.renderPresentation();
}""")
expect(next_action).to_have_text("Recover Delivery (1)")
bounds = next_action.bounding_box()
assert bounds and bounds["height"] >= 44
assert bounds["y"] + bounds["height"] <= viewport["height"]
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
browser.close()