A knock call room, created over the CS API, walks through the lobby's join
states: request to join, the waiting copy with its withdrawal link, a second
request, and an invite from the room's moderator that lands the user in the
call. A decline, a ban with its reason, and a room whose join rule the app
cannot act on are covered too. An invite-only room asserts what happens
today: Synapse serves no summary for it, so the refused join is a full-screen
error rather than a lobby state.
The `spaUser` fixture registers over the admin API and logs in through the
form, and serves `config.devenv.json` itself, since Playwright reuses a dev
server started with another config.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>