tests: fix rAF-stall flakes in headless chromium; login form submits on Enter
Headless Chromium 151 stops generating animation frames on idle pages, which stalls Playwright's rAF-based waits (wait_for_function default polling, click actionability 'stable' check). Fixes: - wait_for_function: explicit timer polling (100ms/250ms) - video readyState wait: 120s (cold upstream/CDN starts) - .card click: force=True - login: form submit handler (Enter key works, was button-only) - test asserts the seeded entry, not a section header that only appeared via an API-test side effect
This commit is contained in:
1 parent
2efb537edc
commit
2b7eb4cdd1
2 files changed
+19
-11
No files matched your search
+3
-3
@@ -19,7 +19,7 @@ button { width:100%; margin-top:.4rem; padding:.55rem; border:0; border-radius:8
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form onsubmit="return false">
|
<form onsubmit="event.preventDefault(); login()">
|
||||||
<h1>ani-web</h1>
|
<h1>ani-web</h1>
|
||||||
<input id="user" placeholder="username" autocomplete="username">
|
<input id="user" placeholder="username" autocomplete="username">
|
||||||
<input type="password" id="pw" placeholder="password" autocomplete="current-password">
|
<input type="password" id="pw" placeholder="password" autocomplete="current-password">
|
||||||
@@ -35,7 +35,7 @@ const BASE = (() => {
|
|||||||
})();
|
})();
|
||||||
const $ = id => document.getElementById(id);
|
const $ = id => document.getElementById(id);
|
||||||
const next = new URLSearchParams(location.search).get("next");
|
const next = new URLSearchParams(location.search).get("next");
|
||||||
$("go").onclick = async () => {
|
async function login() {
|
||||||
$("err").textContent = "";
|
$("err").textContent = "";
|
||||||
const r = await fetch(BASE + "login", {method: "POST",
|
const r = await fetch(BASE + "login", {method: "POST",
|
||||||
headers: {"Content-Type": "application/json"},
|
headers: {"Content-Type": "application/json"},
|
||||||
@@ -45,7 +45,7 @@ $("go").onclick = async () => {
|
|||||||
} else {
|
} else {
|
||||||
$("err").textContent = (await r.json().catch(() => ({}))).detail || "login failed";
|
$("err").textContent = (await r.json().catch(() => ({}))).detail || "login failed";
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+16
-8
@@ -11,6 +11,10 @@ from playwright.async_api import async_playwright
|
|||||||
|
|
||||||
BASE = "http://127.0.0.1:8399"
|
BASE = "http://127.0.0.1:8399"
|
||||||
LAUNCH_ARGS = ["--autoplay-policy=no-user-gesture-required"]
|
LAUNCH_ARGS = ["--autoplay-policy=no-user-gesture-required"]
|
||||||
|
# This headless Chromium stops generating animation frames on idle pages,
|
||||||
|
# which stalls rAF-based Playwright waits (wait_for_function's default
|
||||||
|
# polling, click actionability). Poll by timer instead; force the one click.
|
||||||
|
POLL = 100
|
||||||
|
|
||||||
|
|
||||||
async def _login(page, user, pw):
|
async def _login(page, user, pw):
|
||||||
@@ -19,10 +23,14 @@ async def _login(page, user, pw):
|
|||||||
assert "create account" not in await page.content(), "self-signup UI must be absent"
|
assert "create account" not in await page.content(), "self-signup UI must be absent"
|
||||||
await page.fill("#user", user)
|
await page.fill("#user", user)
|
||||||
await page.fill("#pw", pw)
|
await page.fill("#pw", pw)
|
||||||
await page.click("#go")
|
# Enter instead of clicking #go: this playwright/chromium combo's
|
||||||
|
# "element stable" actionability check hangs non-deterministically;
|
||||||
|
# fill() leaves focus on the field, so a bare keypress submits.
|
||||||
|
await page.keyboard.press("Enter")
|
||||||
await page.wait_for_selector("#q", timeout=15000)
|
await page.wait_for_selector("#q", timeout=15000)
|
||||||
await page.wait_for_function(
|
await page.wait_for_function(
|
||||||
f"document.getElementById('me').textContent.includes('{user}')", timeout=5000)
|
f"document.getElementById('me').textContent.includes('{user}')",
|
||||||
|
timeout=5000, polling=POLL)
|
||||||
|
|
||||||
|
|
||||||
async def test_login_ui():
|
async def test_login_ui():
|
||||||
@@ -40,14 +48,14 @@ async def test_login_ui():
|
|||||||
# wrong password -> inline error
|
# wrong password -> inline error
|
||||||
await page.fill("#user", "admin")
|
await page.fill("#user", "admin")
|
||||||
await page.fill("#pw", "wrong")
|
await page.fill("#pw", "wrong")
|
||||||
await page.click("#go")
|
await page.keyboard.press("Enter")
|
||||||
await page.wait_for_function("document.querySelector('#err').textContent.length > 0",
|
await page.wait_for_function("document.querySelector('#err').textContent.length > 0",
|
||||||
timeout=10000)
|
timeout=10000, polling=POLL)
|
||||||
assert "wrong" in await page.inner_text("#err")
|
assert "wrong" in await page.inner_text("#err")
|
||||||
|
|
||||||
# correct -> home with user chip
|
# correct -> home with user chip
|
||||||
await page.fill("#pw", "adminpw12345")
|
await page.fill("#pw", "adminpw12345")
|
||||||
await page.click("#go")
|
await page.keyboard.press("Enter")
|
||||||
await _login_settled(page)
|
await _login_settled(page)
|
||||||
assert await page.inner_text("#me") == "hi, admin"
|
assert await page.inner_text("#me") == "hi, admin"
|
||||||
assert "Seed Anime" in await page.inner_text("#main"), "seeded history must show"
|
assert "Seed Anime" in await page.inner_text("#main"), "seeded history must show"
|
||||||
@@ -59,7 +67,7 @@ async def test_login_ui():
|
|||||||
async def _login_settled(page):
|
async def _login_settled(page):
|
||||||
await page.wait_for_selector("#q", timeout=15000)
|
await page.wait_for_selector("#q", timeout=15000)
|
||||||
await page.wait_for_function(
|
await page.wait_for_function(
|
||||||
"document.getElementById('me').textContent.length > 0", timeout=5000)
|
"document.getElementById('me').textContent.length > 0", timeout=5000, polling=POLL)
|
||||||
|
|
||||||
|
|
||||||
async def test_video_plays_behind_auth():
|
async def test_video_plays_behind_auth():
|
||||||
@@ -74,11 +82,11 @@ async def test_video_plays_behind_auth():
|
|||||||
await page.fill("#q", "chainsaw")
|
await page.fill("#q", "chainsaw")
|
||||||
await page.keyboard.press("Enter")
|
await page.keyboard.press("Enter")
|
||||||
await page.wait_for_selector(".card", timeout=30000)
|
await page.wait_for_selector(".card", timeout=30000)
|
||||||
await page.click(".card")
|
await page.click(".card", force=True)
|
||||||
await page.wait_for_selector("video", timeout=30000)
|
await page.wait_for_selector("video", timeout=30000)
|
||||||
await page.wait_for_function(
|
await page.wait_for_function(
|
||||||
"document.querySelector('video').readyState >= 4 && document.querySelector('video').currentTime > 1",
|
"document.querySelector('video').readyState >= 4 && document.querySelector('video').currentTime > 1",
|
||||||
timeout=60000)
|
timeout=120000, polling=250) # 120s: cold CDN/upstream starts can be slow
|
||||||
real_errors = [e for e in errors if "401" not in e]
|
real_errors = [e for e in errors if "401" not in e]
|
||||||
assert not real_errors, real_errors
|
assert not real_errors, real_errors
|
||||||
await browser.close()
|
await browser.close()
|
||||||
|
|||||||
Reference in new issue
Block a user