Add mobile-viewport testing to run-server + require it in CLAUDE.md
Build and push server image / test (push) Successful in 24s
Build and push server image / build-and-push (push) Successful in 2m2s
Build and push server image / deploy (push) Successful in 55s

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:
Thomas Faour
2026-07-25 01:19:44 +00:00
parent 20c7620393
commit 0e35735a2a
3 changed files with 48 additions and 1 deletions
+10 -1
View File
@@ -54,7 +54,16 @@ Start here, don't re-derive from scratch:
live (mobile viewport CSS collapse, a dialog's status message landing
behind its own backdrop, a JSON/form-urlencoded body mismatch). Spin up
`uvicorn app.main:app` against a scratch `DATABASE_URL`/`CONFIG_PATH`
sqlite file, don't touch the real deployment's data.
sqlite file, don't touch the real deployment's data. `server/.claude/skills/run-server/`
(`/run-server`) has a driver for exactly this.
- **New/changed UI must work at both desktop and mobile widths --
screenshot both, don't assume one implies the other.** The layout
genuinely forks at the 860px breakpoint (`theme.css`): the sidebar
goes off-canvas behind a hamburger below it. A dialog, header
control, or new widget that looks right at a wide viewport can
overflow, overlap the mobile bar, or mis-center at phone widths.
`run-server`'s driver has a `viewport` command for exactly this
(defaults to a phone size; switch to `1280 900` for desktop).
- **Deploy**: Gitea Actions at `git.thumeit.com/tfaour/espresso_frame`
(`.gitea/workflows/server-docker-build.yml`: `test` -> `build-and-push`
-> `deploy` on any push to `main` touching `server/**`; `deploy` SSHes
+24
View File
@@ -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,