tests: refactor to pytest (conftest session fixture, sync wrappers for async, importorskip for playwright)

This commit is contained in:
jozamudi committed 2026-08-19 18:59:48 +00:00
1 parent bb1297f8e2
commit 29fab7ff5f
5 files changed
+130 -135

No files matched your search

+41 -31
View File
@@ -1,16 +1,17 @@
"""Browser tests for ani-web (Playwright, headless Chromium).
Run via tests/run.py or standalone: python tests/test_browser.py <base_url>
Covers what HTTP tests can't: the login UI, the user chip, and real
video playback behind auth. Needs: pip install playwright && playwright install chromium
Run via pytest (see conftest.py for the throwaway server). Covers what
HTTP tests can't: the login UI, the user chip, and real video playback
behind auth. Skipped if playwright is not installed.
"""
import asyncio
import http.cookiejar
import json
import sys
import urllib.request
from playwright.async_api import async_playwright
import pytest
async_playwright = pytest.importorskip("playwright.async_api").async_playwright
BASE = "http://127.0.0.1:8399"
LAUNCH_ARGS = ["--autoplay-policy=no-user-gesture-required"]
@@ -36,7 +37,11 @@ async def _login(page, user, pw):
timeout=5000, polling=POLL)
async def test_login_ui():
def test_login_ui():
asyncio.run(_login_ui())
async def _login_ui():
async with async_playwright() as p:
browser = await p.chromium.launch(args=LAUNCH_ARGS)
page = await (await browser.new_context()).new_page()
@@ -65,7 +70,6 @@ async def test_login_ui():
assert "Seed Anime" in await page.inner_text("#main"), "seeded history must show"
assert not errors, errors
await browser.close()
print("ok test_login_ui")
async def _login_settled(page):
@@ -74,7 +78,11 @@ async def _login_settled(page):
"document.getElementById('me').textContent.length > 0", timeout=5000, polling=POLL)
async def test_video_plays_behind_auth():
def test_video_plays_behind_auth():
asyncio.run(_video_plays_behind_auth())
async def _video_plays_behind_auth():
async with async_playwright() as p:
browser = await p.chromium.launch(args=LAUNCH_ARGS)
page = await (await browser.new_context()).new_page()
@@ -94,10 +102,13 @@ async def test_video_plays_behind_auth():
real_errors = [e for e in errors if "401" not in e]
assert not real_errors, real_errors
await browser.close()
print("ok test_video_plays_behind_auth")
async def test_history_page_and_delete():
def test_history_page_and_delete():
asyncio.run(_history_page_and_delete())
async def _history_page_and_delete():
# self-contained: seed erin's own entries over HTTP first
jar = http.cookiejar.CookieJar()
op = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
@@ -129,10 +140,13 @@ async def test_history_page_and_delete():
await page.wait_for_selector(".toast", timeout=5000)
assert not errors, errors
await browser.close()
print("ok test_history_page_and_delete")
async def test_stream_failure_retry():
def test_stream_failure_retry():
asyncio.run(_stream_failure_retry())
async def _stream_failure_retry():
async with async_playwright() as p:
browser = await p.chromium.launch(args=LAUNCH_ARGS)
page = await (await browser.new_context()).new_page()
@@ -157,10 +171,13 @@ async def test_stream_failure_retry():
"document.querySelector('video').readyState >= 4 && document.querySelector('video').currentTime > 1",
timeout=120000, polling=250)
await browser.close()
print("ok test_stream_failure_retry")
async def test_recent_searches():
def test_recent_searches():
asyncio.run(_recent_searches())
async def _recent_searches():
# UI: focus the (empty) search box -> dropdown of saved searches;
# typing filters; clicking one runs the search.
jar = http.cookiejar.CookieJar()
@@ -192,10 +209,13 @@ async def test_recent_searches():
assert "q=chainsaw" in page.url, page.url
assert not errors, errors
await browser.close()
print("ok test_recent_searches")
async def test_auto_next_countdown():
def test_auto_next_countdown():
asyncio.run(_auto_next_countdown())
async def _auto_next_countdown():
# playing a full episode to its natural end is too slow; simulate the
# `ended` event and verify the countdown overlay + cancel path.
async with async_playwright() as p:
@@ -223,10 +243,13 @@ async def test_auto_next_countdown():
assert "E1" in await page.inner_text(".ep.active")
assert not errors, errors
await browser.close()
print("ok test_auto_next_countdown")
async def test_episode_watch_state():
def test_episode_watch_state():
asyncio.run(_episode_watch_state())
async def _episode_watch_state():
# completed episodes dimmed with a check, first unwatched highlighted.
# seed the first rendered ep as finished (id read off the list),
# reload, and check the classes.
@@ -252,16 +275,3 @@ async def test_episode_watch_state():
await page.wait_for_selector("#eps .ep.nextup", timeout=10000)
assert not errors, errors
await browser.close()
print("ok test_episode_watch_state")
if __name__ == "__main__":
if len(sys.argv) > 1:
BASE = sys.argv[1]
asyncio.run(test_login_ui())
asyncio.run(test_video_plays_behind_auth())
asyncio.run(test_history_page_and_delete())
asyncio.run(test_stream_failure_retry())
asyncio.run(test_recent_searches())
asyncio.run(test_auto_next_countdown())
asyncio.run(test_episode_watch_state())