232 lines
12 KiB
Python
232 lines
12 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()
|
|
|
|
|
|
@pytest.mark.parametrize("viewport", [
|
|
{"width": 320, "height": 568},
|
|
{"width": 375, "height": 667},
|
|
{"width": 430, "height": 932},
|
|
])
|
|
def test_operator_reorders_routine_queues_without_leaking_priority_between_accounts(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-priority.js")
|
|
page.add_script_tag(path=FRONTEND / "mobile-queue-launcher.js")
|
|
page.evaluate("""() => {
|
|
const values = new Map();
|
|
window.login = 'alice';
|
|
const rows = Object.fromEntries(Array.from(document.querySelectorAll('[data-mobile-queue]'))
|
|
.map(row => [row.dataset.mobileQueue, row]));
|
|
window.launcher = createMobileQueueLauncher({
|
|
getCounts:() => ({attention:2, following:3, authored:1}),
|
|
getRoutineOrder:() => window.priority.getOrder(), 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.priority = createMobileQueuePriority({
|
|
storage:{getItem:key => values.get(key) || null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)},
|
|
getLogin:() => window.login, document,
|
|
list:document.querySelector('#mobile-queue-priority-list'),
|
|
resetButton:document.querySelector('#reset-mobile-queue-priority'),
|
|
status:document.querySelector('#mobile-queue-priority-status'),
|
|
labels:{attention:'Attention',today:'Today',update:'Updates',agenda:'Agenda',following:'Following',authored:'My PRs',filed:'Filed',later:'Later',draft:'Drafts'},
|
|
onChange:() => window.launcher.renderPresentation(),
|
|
});
|
|
window.priority.start(); window.launcher.renderPresentation();
|
|
document.querySelector('#mobile-queue-priority').open = true;
|
|
document.querySelector('#mobile-queue-sheet').showModal();
|
|
}""")
|
|
|
|
for _ in range(4):
|
|
page.get_by_role("button", name="Move Following earlier").click()
|
|
expect(page.locator("#mobile-queue-next-action")).to_have_text("Review Following (3)")
|
|
assert page.locator("#mobile-queue-active-list [data-mobile-queue]").evaluate_all(
|
|
"rows => rows.map(row => row.dataset.mobileQueue)"
|
|
) == ["following", "attention", "authored"]
|
|
expect(page.locator("#mobile-queue-priority-status")).to_have_text("Sync pending.")
|
|
controls = page.locator(".mobile-queue-priority-controls button")
|
|
assert controls.count() == 18
|
|
assert all((controls.nth(i).bounding_box() or {}).get("height", 0) >= 44 for i in range(controls.count()))
|
|
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
|
|
|
|
page.evaluate("""() => {
|
|
window.login = 'bob'; window.priority.render(); window.launcher.renderPresentation();
|
|
}""")
|
|
expect(page.locator("#mobile-queue-next-action")).to_have_text("Start Attention (2)")
|
|
assert page.locator("#mobile-queue-priority-list [data-queue-priority]").evaluate_all(
|
|
"rows => rows.map(row => row.dataset.queuePriority)"
|
|
)[:3] == ["attention", "today", "update"]
|
|
browser.close()
|