Add mobile-viewport testing to run-server + require it in CLAUDE.md
Confirmed the theatre-mode preview dialog actually renders correctly at phone widths (390x844) -- centers properly, backdrop and close button both fine, nothing overflows. Added a `viewport` command to the driver (defaults to mobile, since that's the step easy to skip) and a CLAUDE.md rule to screenshot both breakpoints for future UI changes, since the 860px sidebar/mobile-bar fork is a real, previously-hit source of bugs here.
This commit is contained in:
@@ -89,6 +89,20 @@ Screenshots land in `/tmp/run-server-shots/` (override:
|
||||
`SCREENSHOT_DIR`). **Actually look at them** -- a blank or error-page
|
||||
screenshot is a failure to launch, not success.
|
||||
|
||||
**Test every UI change at both a desktop and a mobile viewport.** The
|
||||
driver defaults to a desktop size (1280x900); switch with `viewport`.
|
||||
The app's mobile breakpoint is 860px (`theme.css`) -- below that the
|
||||
sidebar goes off-canvas behind a hamburger (`.mobile-bar`). A page that
|
||||
looks right at 1280px can still overflow, overlap the mobile bar, or
|
||||
mis-center a `<dialog>` at phone widths -- screenshot both:
|
||||
|
||||
```bash
|
||||
tmux send-keys -t runserver 'viewport 1280 900' Enter # desktop (also the default)
|
||||
tmux send-keys -t runserver 'screenshot desktop-x' Enter
|
||||
tmux send-keys -t runserver 'viewport 390 844' Enter # iPhone-ish mobile width
|
||||
tmux send-keys -t runserver 'screenshot mobile-x' Enter
|
||||
```
|
||||
|
||||
### Driver commands
|
||||
|
||||
| command | what it does |
|
||||
@@ -101,6 +115,7 @@ screenshot is a failure to launch, not success.
|
||||
| `screenshot [name]` | → `/tmp/run-server-shots/<name>.png` |
|
||||
| `eval <js>` | evaluate JS in the page, prints JSON |
|
||||
| `console-errors` | prints all captured console/page errors as a JSON array |
|
||||
| `viewport [w] [h]` | resize the viewport, default `390 844` -- use `1280 900` for desktop (see Gotchas re: real touch input) |
|
||||
| `is-open <dialog-selector>` | prints `true`/`false` for a `<dialog>` element's `.open` -- use this instead of `wait-for sel[open]` |
|
||||
| `bootstrap-admin [user] [pass]` | completes first-run `/setup` (defaults `admin`/`testpassword123`); links frame #1 and logs in |
|
||||
| `quit` | closes the browser, exits the driver |
|
||||
@@ -138,6 +153,15 @@ needed beyond the venv.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`viewport` only resizes the window -- it does not emulate touch
|
||||
input.** `click` still dispatches a mouse click, not a tap; there's
|
||||
no touch-delay, no `:hover`-stickiness-after-tap, no `hasTouch`
|
||||
context. It catches real bugs (layout overflow, off-canvas sidebar,
|
||||
a `<dialog>` mis-centering at phone widths) but won't catch anything
|
||||
that's specifically a touch-vs-mouse event difference. Good enough
|
||||
for CSS/layout verification; not a substitute for testing on an
|
||||
actual phone if a change touches touch-specific interaction.
|
||||
|
||||
- **`chrome-headless-shell` (Playwright's default headless target)
|
||||
crashes on basic calls in this container**, e.g. `page.set_content()`
|
||||
returns `TargetClosedError`, even after every `ldd`-reported missing
|
||||
|
||||
@@ -95,6 +95,19 @@ def cmd_console_errors(_arg):
|
||||
print(json.dumps(console_errors))
|
||||
|
||||
|
||||
def cmd_viewport(arg):
|
||||
"""Resize the viewport. No args -> 390x844 (iPhone-ish mobile
|
||||
width); the page itself starts at 1280x900 (desktop) on launch, so
|
||||
`viewport 1280 900` gets back to that. The app's mobile breakpoint
|
||||
is 860px (see theme.css) -- anything under that exercises the
|
||||
off-canvas sidebar/mobile-bar layout."""
|
||||
parts = arg.split()
|
||||
width = int(parts[0]) if len(parts) > 0 else 390
|
||||
height = int(parts[1]) if len(parts) > 1 else 844
|
||||
page.set_viewport_size({"width": width, "height": height})
|
||||
print("viewport:", width, "x", height)
|
||||
|
||||
|
||||
def cmd_is_open(arg):
|
||||
"""App-specific: print whether a <dialog> element is open (true/false)."""
|
||||
print(json.dumps(page.eval_on_selector(arg, "el => el.open")))
|
||||
@@ -132,6 +145,7 @@ COMMANDS = {
|
||||
"screenshot": cmd_screenshot,
|
||||
"eval": cmd_eval,
|
||||
"console-errors": cmd_console_errors,
|
||||
"viewport": cmd_viewport,
|
||||
"is-open": cmd_is_open,
|
||||
"bootstrap-admin": cmd_bootstrap_admin,
|
||||
"quit": cmd_quit,
|
||||
|
||||
Reference in New Issue
Block a user