Leave the controls down while swiping through reels
Scrolling woke the HUD, so a swipe to the next reel put the controls up over the video arriving -- the one thing the swipe was for -- and left them there for a second. The scroll now puts them away instead. Removing that alone wasn't enough: the container also woke on pointerdown and touchstart, which is how every drag begins, so the HUD still flashed at the start of one. A press only wakes them now if it lands on a control -- which is a press on something the reader can't see while they're hidden, and never the start of a swipe. A press that turns out to be a tap still wakes them through the gesture layer's onSingleTap, the mouse moving still wakes them, and dragging the scrubber keeps them up, being the one drag that is the HUD. Two details follow from it. The sleep sits after onScroll's suppressScroll guard, so a resize or a picture-in-picture re-snap doesn't count as the reader moving. And opening the feed scrolls to the starting step, whose scroll event would put the controls straight back down, so that wake waits a frame -- scroll events run before animation frames. The checks live in their own file rather than in smoke_reels: one panel and one swipe is all they need, and the split-panel suite is the heaviest thing in the tree to run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
124
tests/smoke_hud.py
Normal file
124
tests/smoke_hud.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""The reels HUD: up when you're using it, down when you're swiping past it.
|
||||
|
||||
Run against a locally running backend:
|
||||
|
||||
backend/main.py &
|
||||
.venv/bin/python tests/smoke_hud.py
|
||||
|
||||
One panel and one swipe, deliberately: what's under test is when the controls
|
||||
come and go, not anything about the layout.
|
||||
|
||||
The presses here are dispatched rather than clicked, because moving a mouse is
|
||||
itself a reason to wake the HUD and would hide what is being asked.
|
||||
"""
|
||||
import sys
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
BASE = "http://127.0.0.1:5000/"
|
||||
SERVER = "https://hottubapp.io"
|
||||
CHANNEL = "xvideos"
|
||||
|
||||
SEED = """([server, channel]) => {
|
||||
localStorage.setItem('config', JSON.stringify({ servers: [{ [server]: {} }] }));
|
||||
localStorage.setItem('preferences', JSON.stringify({ [server]: { channelId: channel } }));
|
||||
localStorage.removeItem('session');
|
||||
}"""
|
||||
|
||||
IDLE = """() => document.getElementById('feed-view').classList.contains('feed-hud-idle')"""
|
||||
|
||||
PRESS = """(selector) => {
|
||||
const el = document.querySelector('.feed-slide.is-active ' + selector);
|
||||
if (!el) return 'missing';
|
||||
el.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }));
|
||||
return document.getElementById('feed-view').classList.contains('feed-hud-idle');
|
||||
}"""
|
||||
|
||||
SWIPE = """() => {
|
||||
const s = document.getElementById('feed-scroll');
|
||||
s.scrollTop += s.clientHeight;
|
||||
}"""
|
||||
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
self.failed = 0
|
||||
|
||||
def ok(self, label, condition, detail=""):
|
||||
mark = "PASS" if condition else "FAIL"
|
||||
if not condition:
|
||||
self.failed += 1
|
||||
print(f" [{mark}] {label}" + (f" -- {detail}" if detail and not condition else ""))
|
||||
|
||||
|
||||
def main():
|
||||
c = Checks()
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(args=[
|
||||
"--no-sandbox",
|
||||
"--disable-dev-shm-usage",
|
||||
"--disable-gpu",
|
||||
"--autoplay-policy=no-user-gesture-required",
|
||||
"--renderer-process-limit=1",
|
||||
"--js-flags=--max-old-space-size=512",
|
||||
])
|
||||
page = browser.new_page(viewport={"width": 430, "height": 930})
|
||||
crashes = []
|
||||
page.on("pageerror", lambda e: crashes.append(str(e)))
|
||||
|
||||
page.goto(BASE, wait_until="domcontentloaded")
|
||||
page.evaluate(SEED, [SERVER, CHANNEL])
|
||||
page.goto(BASE, wait_until="load")
|
||||
try:
|
||||
page.wait_for_selector(".video-card", timeout=90000)
|
||||
except Exception:
|
||||
# One reload, in case the status refresh or the listing failed.
|
||||
page.goto(BASE, wait_until="load")
|
||||
page.wait_for_selector(".video-card", timeout=90000)
|
||||
page.wait_for_timeout(2500)
|
||||
page.evaluate("() => App.feed.open()")
|
||||
page.wait_for_selector(".feed-slide", timeout=20000)
|
||||
page.wait_for_timeout(400)
|
||||
|
||||
print("\nopening the feed")
|
||||
c.ok("the controls are up to start with", page.evaluate(IDLE) is False)
|
||||
|
||||
print("\nleaving it alone")
|
||||
page.wait_for_timeout(1600)
|
||||
c.ok("they fade on their own", page.evaluate(IDLE) is True)
|
||||
|
||||
print("\nswiping to the next reel")
|
||||
page.evaluate(SWIPE)
|
||||
page.wait_for_timeout(300)
|
||||
c.ok("the swipe does not put them back up mid-flight", page.evaluate(IDLE) is True)
|
||||
page.wait_for_timeout(2500)
|
||||
c.ok("nor once it has landed", page.evaluate(IDLE) is True)
|
||||
c.ok("and the reel it landed on is playing",
|
||||
page.evaluate("""() => {
|
||||
const v = document.querySelector('.feed-slide.is-active .feed-video');
|
||||
return !!v && !v.paused;
|
||||
}"""))
|
||||
|
||||
print("\nwhat does bring them back")
|
||||
c.ok("pressing the video -- which is how a swipe begins -- does not",
|
||||
page.evaluate(PRESS, ".feed-video") is True)
|
||||
c.ok("pressing a control does", page.evaluate(PRESS, ".feed-pane-mute") is False)
|
||||
page.wait_for_timeout(1600)
|
||||
c.ok("after which they fade again", page.evaluate(IDLE) is True)
|
||||
page.mouse.move(200, 400)
|
||||
page.mouse.move(210, 420)
|
||||
page.wait_for_timeout(200)
|
||||
c.ok("moving the mouse does too", page.evaluate(IDLE) is False)
|
||||
|
||||
print("\na swipe started while they are up")
|
||||
c.ok("puts them away", (page.evaluate(SWIPE), page.wait_for_timeout(500),
|
||||
page.evaluate(IDLE))[2] is True)
|
||||
|
||||
c.ok("nothing threw along the way", not crashes, str(crashes[:2]))
|
||||
browser.close()
|
||||
|
||||
print(f"\n{'OK' if not c.failed else 'FAILED'}: {c.failed} check(s) failed")
|
||||
return 1 if c.failed else 0
|
||||
|
||||
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user