features: auto-next w/ 5s countdown, prev/next, playback speed, hide fillers, recent searches
This commit is contained in:
1 parent
9fdf3944fc
commit
98fb4e5461
6 files changed
+269
-25
No files matched your search
@@ -76,6 +76,8 @@ def main():
|
||||
asyncio.run(test_browser.test_video_plays_behind_auth())
|
||||
asyncio.run(test_browser.test_history_page_and_delete())
|
||||
asyncio.run(test_browser.test_stream_failure_retry())
|
||||
asyncio.run(test_browser.test_recent_searches())
|
||||
asyncio.run(test_browser.test_auto_next_countdown())
|
||||
|
||||
print("ALL TESTS PASSED")
|
||||
finally:
|
||||
|
||||
@@ -138,6 +138,21 @@ def test_history_all_and_delete(base=BASE):
|
||||
jar=jar, base=base)["removed"] == 0
|
||||
|
||||
|
||||
def test_recent_searches(base=BASE):
|
||||
a = login("alice", "alicepw12345", base)
|
||||
assert req("POST", "/api/searches", {"q": "naruto"}, jar=a, base=base) == 200
|
||||
assert req("POST", "/api/searches", {"q": "frieren"}, jar=a, base=base) == 200
|
||||
# case-insensitive dedupe, newest first
|
||||
assert req("POST", "/api/searches", {"q": "NARUTO"}, jar=a, base=base) == 200
|
||||
assert _json("GET", "/api/searches", jar=a, base=base) == ["NARUTO", "frieren"]
|
||||
# per-user isolation (browser tests write admin's searches, but only after
|
||||
# the API phase, so admin is clean here)
|
||||
b = login("admin", "adminpw12345", base)
|
||||
assert _json("GET", "/api/searches", jar=b, base=base) == []
|
||||
# blank q rejected
|
||||
assert req("POST", "/api/searches", {"q": " "}, jar=a, base=base) == 422
|
||||
|
||||
|
||||
def test_self_delete(base=BASE):
|
||||
jar = login("dave", "davepw12345", base)
|
||||
assert req("POST", "/account/delete", jar=jar, base=base) == 200
|
||||
|
||||
@@ -159,6 +159,72 @@ async def test_stream_failure_retry():
|
||||
print("ok test_stream_failure_retry")
|
||||
|
||||
|
||||
async def test_recent_searches():
|
||||
# UI: focus the (empty) search box -> dropdown of saved searches;
|
||||
# typing filters; clicking one runs the search.
|
||||
jar = http.cookiejar.CookieJar()
|
||||
op = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
|
||||
op.open(urllib.request.Request(BASE + "/login",
|
||||
data=json.dumps({"username": "alice", "password": "alicepw12345"}).encode(),
|
||||
headers={"Content-Type": "application/json"}, method="POST"))
|
||||
op.open(urllib.request.Request(BASE + "/api/searches",
|
||||
data=json.dumps({"q": "chainsaw"}).encode(),
|
||||
headers={"Content-Type": "application/json"}, method="POST"))
|
||||
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(args=LAUNCH_ARGS)
|
||||
page = await (await browser.new_context()).new_page()
|
||||
errors = []
|
||||
page.on("pageerror", lambda e: errors.append(str(e)))
|
||||
|
||||
await _login(page, "alice", "alicepw12345")
|
||||
await page.focus("#q")
|
||||
await page.wait_for_selector(".recent.open", timeout=10000)
|
||||
assert "chainsaw" in await page.inner_text(".recent")
|
||||
# typing narrows the list to the matching entry
|
||||
await page.fill("#q", "cha")
|
||||
await page.wait_for_function(
|
||||
"document.querySelectorAll('.recent button').length === 1",
|
||||
timeout=5000, polling=POLL)
|
||||
await page.click(".recent button", force=True)
|
||||
await page.wait_for_selector("a.card", timeout=30000)
|
||||
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():
|
||||
# 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:
|
||||
browser = await p.chromium.launch(args=LAUNCH_ARGS)
|
||||
page = await (await browser.new_context()).new_page()
|
||||
errors = []
|
||||
page.on("pageerror", lambda e: errors.append(str(e)))
|
||||
|
||||
await _login(page, "admin", "adminpw12345")
|
||||
# straight to the 12-ep series (the first search card is the 1-ep
|
||||
# movie — no next episode there)
|
||||
await page.goto(BASE + "/?anime=chainsaw-man-922")
|
||||
await page.wait_for_selector("video", timeout=30000)
|
||||
await page.wait_for_function(
|
||||
"document.querySelector('video').readyState >= 4",
|
||||
timeout=120000, polling=250) # cold CDN/upstream starts can be slow
|
||||
await page.evaluate(
|
||||
"document.querySelector('video').dispatchEvent(new Event('ended'))")
|
||||
await page.wait_for_selector(".pl-overlay .btn", timeout=10000)
|
||||
assert "Next: E2" in await page.inner_text(".pl-overlay")
|
||||
# cancel (x): overlay gone, still on the same episode
|
||||
await page.click(".pl-overlay .cnt-x", force=True)
|
||||
await page.wait_for_function(
|
||||
"!document.querySelector('.pl-overlay')", timeout=5000, polling=POLL)
|
||||
assert "E1" in await page.inner_text(".ep.active")
|
||||
assert not errors, errors
|
||||
await browser.close()
|
||||
print("ok test_auto_next_countdown")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
BASE = sys.argv[1]
|
||||
@@ -166,3 +232,5 @@ if __name__ == "__main__":
|
||||
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())
|
||||
Reference in new issue
Block a user