Files
cinny/LOTUS_TESTING.md
T
jaredandClaude Opus 5 ef5d06eea3
CI / Build & Quality Checks (push) Successful in 1m29s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 4m17s
ci(e2e): tier-3 regression suite against a Synapse the job starts itself (#220)
The e2e job now runs scripts/dev-homeserver.sh start + dev-seed.py before
Playwright (installing python3-venv if the runner lacks it) and stops it
afterwards. e2e/local-homeserver.spec.ts registers its own users and rooms
over the CS API and drives the built client — no prod secrets — covering the
fixes that were reproduced with scratch scripts this week: login/send/receive,
own-message scroll (#212), /kick toast (#216), upload 413 wording (#213),
forward provenance, thread panel at 1400px (#218), timeline lightbox (#219),
clock-skew banner via page.clock (#158), status save under the presence limit
(#226), long-press action sheet on a Pixel 7 emulation (#166). Skips itself
when no homeserver answers, so
> lotus-chat@4.12.7-lotus test:e2e
> playwright test

Running 16 tests using 1 worker

  ✓   1 [chromium] › e2e/boot.spec.ts:8:3 › boot › client boots to the login screen without errors (1.5s)
  ✓   2 [chromium] › e2e/boot.spec.ts:27:3 › boot › service worker script is served and registers (1.4s)
  ✓   3 [chromium] › e2e/boot.spec.ts:54:3 › boot › bundled Element Call loads in a frame (3.8s)
  -   4 [chromium] › e2e/e2ee-composer.spec.ts:67:3 › E2EE composer › logs in with a password and reaches the client
  -   5 [chromium] › e2e/e2ee-composer.spec.ts:84:3 › E2EE composer › creates a private encrypted room and sends a text message
  -   6 [chromium] › e2e/e2ee-composer.spec.ts:146:3 › E2EE composer › attaches a compressed image and it is sent encrypted
  ✓   7 [chromium] › e2e/local-homeserver.spec.ts:31:3 › local homeserver regression › logs in, opens a room, sends and receives (4.5s)
  ✓   8 [chromium] › e2e/local-homeserver.spec.ts:58:3 › local homeserver regression › your own message scrolls into view even after scrolling up (#212) (11.4s)
  ✓   9 [chromium] › e2e/local-homeserver.spec.ts:76:3 › local homeserver regression › /kick failure is reported, not swallowed (#216) (5.4s)
  ✓  10 [chromium] › e2e/local-homeserver.spec.ts:90:3 › local homeserver regression › upload failure shows a plain sentence, never the raw MatrixError (#213) (3.7s)
  ✓  11 [chromium] › e2e/local-homeserver.spec.ts:122:3 › local homeserver regression › forwarded message carries its provenance header (6.7s)
  ✓  12 [chromium] › e2e/local-homeserver.spec.ts:143:3 › local homeserver regression › thread panel: opens from the chip and yields the member drawer at 1400px (#218) (4.7s)
  ✓  13 [chromium] › e2e/local-homeserver.spec.ts:172:3 › local homeserver regression › timeline image opens the gallery lightbox (#219) (3.9s)
  ✓  14 [chromium] › e2e/local-homeserver.spec.ts:204:3 › local homeserver regression › warns when the local clock is far off the server (#158) (6.5s)
  ✓  15 [chromium] › e2e/local-homeserver.spec.ts:221:3 › local homeserver regression › status save survives the presence rate limit (#226) (12.6s)
  ✓  16 [chromium] › e2e/local-homeserver.spec.ts:265:3 › local homeserver regression › touch: long-press opens the message action sheet (#166) (5.0s)

  3 skipped
  13 passed (1.2m) still works cold. 13 pass
locally against dist + the dev homeserver in 1.8 min.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 22:38:39 -04:00

16 KiB

Lotus Chat — Testing

Manual QA checklists no longer live here. Every shipped-but-unverified behaviour is a Gitea issue labelled qa — cinny milestone Manual QA backlog, cinny-desktop milestone Desktop QA backlog. Each issue carries the full steps + expected results; tick items as they pass, comment on FAIL (what you saw vs expected, browser/OS, web vs desktop, theme, console errors), close when green. Migrated from this file on 2026-09-17 (full text in git history).

This file keeps what a contributor needs to run and extend the automated coverage, plus the ops tip at the bottom.

Environment notes

  • Test the web build (chat.lotusguild.org, LXC 106) first; re-run call + poll items on the desktop (Tauri) build too, since CSP and the EC iframe behave differently there.
  • Several call checks need a second participant (marked 👥 2 people in the issues); a couple need a third room/call in parallel (👥👥).

Local dev environment — drive the real UI against a throwaway homeserver

scripts/dev-homeserver.sh start     # Synapse in .dev-homeserver/ (venv, SQLite), open registration, no rate limits, :8008
python3 scripts/dev-seed.py 400     # alice + bob, "Busy Room": 400 messages, an image every 10th
npm start                           # Vite on :5173

Calls too: scripts/dev-homeserver.sh calls adds a LiveKit SFU, a JWT issuer, the real voice-limit-guard from the matrix checkout and an https well-known — real two-party calls in headless Chromium with fake mic/camera (--use-fake-device-for-media-stream, --use-file-for-fake-audio-capture=<tone.wav> to trigger speaking detection, --auto-select-desktop-capture-source="Entire screen" for screenshare; ignoreHTTPSErrors: true). This is how #29, #161, #173/#174 and the guard's live-revoke bug were found and fixed on 2026-09-18.

Log in at http://127.0.0.1:5173/login/http%3A%2F%2Flocalhost%3A8008/ as alice / password123 (bob is the second participant; both can also be driven over the client API with their tokens). Playwright is installed (npm run test:e2e:install), so a scripted reproduction is node + chromium.launch() against :5173 — this is how Gitea #163 was reproduced and its fix verified in both plain and encrypted rooms. scripts/dev-homeserver.sh reset wipes the database; stop shuts it down.

Automated coverage map — what the unit tests already pin (2026-07)

Read this before working a qa issue. Much of the logic the manual checks were written to catch is now locked by deterministic unit tests (npm test, 920+ cases, green in CI). Unit tests do not prove visual rendering, real-call behavior, the desktop build, E2EE, or cross-device sync — those still need a human. But where a decision is pure logic, you can trust the test and spend your manual time on the human-only part. For each row below, the middle column is "don't bother re-deriving this by hand"; the right column is "this is what your manual pass is actually for."

QA item Logic pinned by a unit test (trust it) What still needs you (manual)
A1 ringtone previews callSounds.test.ts — each style's synthesized melody (chime/soft/retro), click-free gain ramps, context unlock/reuse, unknown = no-op that it's actually audible + the WebAudio first-gesture caveat
A2 ringtone persist/fallback settings.test.ts — unknown ringtoneId → default, malformed JSON → defaults, merge-over-defaults (this is A2 step 3) the dropdown shows the persisted value after reload (trivial glance)
B2/B3 poll voting poll.test.ts (18) — vote tally, latest-per-sender, multi-select, cleared/re-vote, winners, results-visible, single-vs-multi validation visual only: borders, radio-vs-checkbox, progress-bar fill, on each theme
O2/P4-1 thread notifications threadNotifications.test.ts (32) — the entire notify decision (participating default, All/Mentions/Mute, @mention+highlight override, room-mute trumps), mode-map + muted-badge hygiene live 2-person delivery + sound + cross-device account-data sync
O3 math / LaTeX mathParse.test.ts (14) — inline $…$, block $$…$$, currency guard ($5 and $10), escaped/unbalanced stay text, adjacency rules KaTeX renders visually + lazy-chunk load; code-block-literal is the markdown pipeline
O4/P4-8 encrypted search cache searchCache.test.ts — the pure helpers mergeSearchResults (merge/dedupe/sort) + computeCoverage (window widening) + resilient-when-IDB-absent. The IDB round-trip test is skipped under npm test (node has no IndexedDB), so it runs only in a browser-like env, not CI the actual IndexedDB persist-across-reload, Clear button, logout wipe (integration — and the round-trip itself)
M1 has:image/file/video useMessageSearch.test.tsfilterGroupsByMsgType union filter, drops empty groups, ignores non-string msgtype the chips render + compose with room/sender/date filters
M4 pinned-only filter useMessageSearch.test.tsfilterGroupsByPinned keeps pinned, drops empty chip renders; needs a room with actual pins
M2 recent searches recentSearches.test.ts (6) — prepend, dedupe+move-to-front, trim, ignore-empty, cap-at-10 chips render/click-re-run; persistence across refresh
Retention (disappearing msgs) retention.test.tsisExpired window math (strict boundary), disabled = never, preset monotonicity the timeline hide + self-redact integration; Synapse-side purge
O5/N97a session hardening sessions.test.ts (22) — blob migration, legacy-key coercion, dual-write blob↔legacy sync, corrupt/partial-blob fallback, token-refresh, AND the subscribeSessionChanges storage-event logic (fires on session/null, ignores unrelated keys) the real cross-tab logout behavior end-to-end (two live tabs)
Q1/Q2 embeds (URL→player) videoEmbed.test.ts (26) — every provider's URL→{provider, kind, embedUrl, height} parse (incl. Mixcloud/Deezer, TikTok, reserved-path guards) the click-to-play facade, no-network-until-Play, the CSP (esp. desktop), visuals
Seasonal theme resolution (part of F2) seasonSchedule.test.tsresolveSeasonTheme (off→none, auto→active season, pinned→that) + getActiveSeason priority/boundary days. NB: this pins which theme shows for a date, NOT F2's background↔seasonal mutual exclusion — that write-side logic is untested all of F2: the picker actually clearing the other setting live, and the overlay suppression when a background is set

Everything else in the guide (calls, screen readers, desktop/Tauri, chat backgrounds, animated visuals, PWA install, real E2EE) is genuinely manual — no unit test substitutes for it. Items already verified live were dropped when the checklists moved to issues.


Playwright smoke test (Gitea #90) — npm run test:e2e

Browser-level smoke tests under e2e/ (config: playwright.config.ts). They boot the built dist/ through vite preview on port 4173, so run npm run build first (one-time: npm run test:e2e:install downloads the pinned Chromium). Three tiers:

Tier File When it runs What it proves
Boot (always) e2e/boot.spec.ts every CI run (e2e job in .gitea/workflows/ci.yml) and locally login page renders with #root populated and no pageerror / unexpected console.error (allowlist in e2e/helpers.ts: the README's avatar-thumbnail 404, the login page's POST /register 401 probe, offline discovery), sw.js is served and registers, bundled Element Call mounts in a frame with every /public/element-call/ asset returning 200
E2EE composer (gated) e2e/e2ee-composer.spec.ts only when E2E_HOMESERVER, E2E_USER, E2E_PASSWORD are all set password login → /home/create/ with the End-to-End Encryption switch on (asserts createRoom carries m.room.encryption) → text message renders → attach a generated JPEG with "Compress image before uploading" ticked, image renders → every PUT …/rooms/*/send/* was m.room.encrypted with ciphertext and no plaintext body / url / file / mxc://
Local homeserver (Gitea #220) e2e/local-homeserver.spec.ts when a Synapse answers at E2E_LOCAL_HS (default http://localhost:8008); the CI e2e job starts one with scripts/dev-homeserver.sh start + dev-seed.py, locally run the same two commands registers its own e2e_alice_*/e2e_bob_* users and rooms over the CS API, then drives the built client: login + send/receive, own message scrolls into view (#212), /kick failure toast (#216), upload 413 sentence (#213), forward provenance header, thread panel + drawer at 1400 px (#218), timeline image → gallery lightbox (#219), clock-skew banner via page.clock (#158), status save under the presence rate limit (#226), long-press action sheet on a Pixel 7 emulation (#166). Helpers in e2e/localHs.ts; add a test here whenever a fix was reproduced with a scratch Playwright script

CI secrets (Gitea → repo → Settings → Actions → Secrets; the e2e job forwards them via env:; until they exist the E2EE tier reports skipped, the boot tier still runs):

  • E2E_HOMESERVER — server name as typed on the login page (e.g. matrix.example.org). Must offer m.login.password; a next-gen-auth (MAS/OIDC-issuer) server shows only the OIDC button and the tier will fail at the username field.
  • E2E_USER / E2E_PASSWORD — a throwaway account: each run logs in as a new device and creates a new e2e-smoke-<timestamp> room. Prune devices/rooms occasionally.

The e2e job is continue-on-error: true for now because playwright install --with-deps needs apt on the runner image — promote it to a hard gate once it is green on the runner. Locally: npm run test:e2e (boot tier only), or E2E_HOMESERVER=… E2E_USER=… E2E_PASSWORD=… npm run test:e2e for both; on failure look in test-results/ (screenshot + trace) and playwright-report/.



Green CI but the fix isn't live?

curl -s https://chat.lotusguild.org/index.html | grep -o 'assets/index-[^"]*\.js' gives the deployed entry chunk; grep it for a string unique to your change (curl -s https://chat.lotusguild.org/<that path> | grep -c <string>). If it's 0 after ~15 min, the deploy trigger was lost — push again (any commit) to re-fire the lotus-deploy webhook. The deploy log lives at /var/log/lotus-deploy.log on LXC 106.