# Lotus Chat โ€” Manual Testing Guide **Generated:** June 2026 **Scope:** Everything landed on the `lotus` branch since the v4.12.3 merge that I (Claude) could **not** verify statically and that needs a human in a real environment to confirm. Work through it top-to-bottom; the highest-risk / hardest-to-reproduce items are first. > **How to report back:** For each numbered check, tell me **PASS** / **FAIL** (or **partial**). On any FAIL, include: what you saw vs. expected, the browser/OS (and whether web LXC 106 or the desktop/Tauri build), the theme you were on, and any **browser console** errors (F12 โ†’ Console). Screenshots help for anything visual. ## Environment notes - You push from your own machine; these commits are local on `lotus` until you do. - Test the **web** build (LXC 106 / `code.lotusguild.org`) first; re-run the **call** + **poll** sections on the **desktop (Tauri)** build too, since CSP and the EC iframe behave differently there. - Several call features need a **second participant** (second account on another device/browser, or a colleague). Items that need this are marked **๐Ÿ‘ฅ 2 people**. - A couple of call items need a **third room/call** in parallel โ€” marked **๐Ÿ‘ฅ๐Ÿ‘ฅ**. --- ## Commits covered | Commit | Area | | :--------- | :--------------------------------------------------------------------------- | | `caf6318a` | Poll vote buttons โ†’ folds tokens (N4) | | `c67aed01` | In-call incoming-call banner (#4b) | | `4a875884` | Selectable ringtone (#4a) | | `0394fce9` | EC iframe load watchdog + recovery UI; avatar decorations on call tiles (#3) | | `d2946c00` | Upload retry/backoff, presence-on-unload, typed m.direct | | `b7e1f89c` | Timeline/composer/emoji perf memoization | | `c0f98672` | Upstream **Element Call 0.20.1** merge (regression sweep) | --- ## A. Calls โ€” new ringtone + notification work (highest priority) ### A1. Ringtone selection โ€” preview in Settings **Steps** 1. Open **Settings โ†’ General**, scroll to the **Calls** section. 2. Find the new **Ringtone** dropdown (just above **Ringtone Volume**). 3. Select each option in turn: **Classic, Chime, Soft, Retro, Silent**. **Expected** - Selecting **Classic** plays the existing `call.ogg` clip (cut off after a few seconds). - **Chime / Soft / Retro** each play a short, distinct synthesized preview. - **Silent** plays nothing. - Changing **Ringtone Volume** then re-selecting a ringtone previews at the new volume. - No console errors. > โš ๏ธ **Known browser limitation:** the synthesized tones use WebAudio. If a preview is ever silent, click anywhere on the page once (a "user gesture") and retry โ€” browsers suspend audio until the page has been interacted with. The Settings preview is _after_ a click so it should always sound; this note matters more for A3. ### A2. Ringtone selection persists 1. Set Ringtone to **Retro**, reload the app. 2. **Expected:** the dropdown still shows **Retro** (setting persisted). 3. Bonus: in devtools, set `localStorage.settings` to a bogus `ringtoneId` and reload โ†’ it should fall back to **Classic**, not break. ### A3. Incoming call uses the selected ringtone โ€” ๐Ÿ‘ฅ 2 people **Setup:** Account A (you) and Account B in a **DM** or a **private (invite-only) group** room. 1. As A, pick a non-silent ringtone (e.g. **Chime**). 2. From B, **start a call** in that DM/room. Do **not** answer on A. **Expected on A** - The full-screen **Incoming Call** dialog appears (caller name, room avatar, Answer / Reject). - The **selected ringtone loops** until you answer/reject/ignore (at the set volume). - Answer โ†’ joins the call. Reject (DM) / Ignore (group) โ†’ dialog dismisses and ring stops. - Set ringtone to **Silent** and repeat โ†’ dialog still appears, **no sound**. ### A4. In-call banner for a second incoming call โ€” ๐Ÿ‘ฅ๐Ÿ‘ฅ (the trickiest one) **Setup:** You (A) already **in a call** in Room 1. Account B can call you in a **different** Room 2 (a DM or private group you share). Ideally a third account C, or B leaves Room 1's call first. 1. While A is **actively in Room 1's call**, trigger an incoming call to A from **Room 2**. **Expected on A** - **No** full-screen takeover. Instead a **compact banner appears in the top-right corner** with the caller's avatar, room name, "Incoming voice/video call", and **Answer / Reject (or Ignore)** buttons. - It plays a **single soft ping**, _not_ a looping ring (so it doesn't talk over your active call). - The banner does **not** cover your active call's controls/PiP in a way that blocks them. - **Answer** โ†’ switches you into Room 2's call. **Reject/Ignore** โ†’ banner disappears. - The banner auto-dismisses if the caller hangs up / the call times out. **Also verify the no-op case:** while in Room 1's call, if a notification for **Room 1 itself** arrives, **nothing** should pop up (no banner, no dialog). ### A5. Camera focus during screenshare (#1) โ€” ๐Ÿ‘ฅ 2 people **Setup:** You (A) and B in a call; B (or another participant) **sharing their screen**, and at least one person with **camera on**. 1. As A, open the **participant glance** (the stacked avatars / member list for the call) and click a participant who has their **camera on**. 2. In the menu, click **"Focus camera"**. **Expected** - The view switches to **spotlight** and **pins that person's camera tile**, overriding the auto-spotlighted screenshare. - It **stays** on that camera (doesn't immediately snap back to the screenshare). - If you pick someone with their camera **off**, it should at worst just toggle spotlight (graceful fallback), not error. ### A6. Avatar decorations on call tiles (#3) โ€” ๐Ÿ‘ฅ 2 people **Setup:** A participant in the call has an **avatar decoration** set (Settings โ†’ Profile decoration). 1. Join a call with that participant. 2. Look at **our** participant roster / prescreen tiles (not the avatars rendered inside the Element Call video grid โ€” those are EC's and out of scope). **Expected:** the decoration ring/overlay renders around that participant's avatar on the call tile, the same way it does in member lists. ### A7. EC iframe load watchdog + recovery UI (#EC) This guards against a permanently-stuck "Loadingโ€ฆ" call. 1. Normal case: **join a call** โ†’ it should connect within a few seconds as usual (the watchdog stays invisible). 2. Failure case (best-effort to reproduce): throttle your network hard (devtools โ†’ Network โ†’ Offline) **right as** you click join, or block the Element Call origin, so the iframe can't finish loading. **Expected** - On a genuine failure/timeout (~25s), instead of an endless spinner you get a **visible error overlay with Retry / Leave** buttons. - **Retry** attempts to reload the call; **Leave** exits cleanly. - Normal joins must **not** trigger the error overlay (no false positives) โ€” this is the important part to confirm. --- ## B. Polls (N4) โ€” render correctly on non-TDS themes This was the actual bug: poll buttons used undefined CSS variables, so on the **default (non-Lotus-Terminal) themes** they rendered with invisible borders / no selected state. ### B1. Poll renders on a default theme 1. Switch to a **default Cinny theme** (Settings โ†’ Appearance โ€” **not** Lotus Terminal / TDS). Test both a **dark** and a **light** theme. 2. In any room, create a poll (composer โ†’ poll button): a **single-choice** poll with 3 options. **Expected** - Each option is a clearly **bordered** button with visible rounded corners. - A **radio circle** indicator is visible on the left of each option. - Text, and (after votes) the percentage, are legible. ### B2. Voting + selected/progress state 1. **Vote** on an option. **Expected** - The selected option shows a **filled accent border + filled radio**, and an **accent progress-bar fill** grows behind it proportional to the vote %. - The percentage and total vote count update. - Click again / pick another option โ†’ selection moves correctly (single-choice replaces; the bar redraws). ### B3. Multiple-choice poll 1. Create a poll allowing **multiple selections**. **Expected** - Indicators are **square checkboxes** (not circles); selected ones show a **โœ“** that's legible against the filled box. - You can select **several** options; each shows its own progress fill. ### B4. Lotus Terminal theme regression 1. Switch to **Lotus Terminal / TDS** theme and re-open a poll. **Expected:** still looks correct (the fix uses theme tokens, so the TDS accent should now drive it) โ€” no worse than before. --- ## C. Robustness / background behavior ### C1. Presence updates on tab close 1. Open the app, then **close the tab** (or quit the browser). 2. From another session/device, check your **presence** shortly after. **Expected:** you go **offline/away** reliably (the unload now uses `fetch({keepalive})`). Previously this could be missed. ### C2. Upload retry on flaky network (best-effort) 1. In devtools โ†’ Network, set a throttle that drops/slows requests, or toggle Offline briefly **during** a file upload. **Expected** - A transient failure **retries** (up to 3ร—, with backoff) and the upload can still succeed once the network recovers. - A genuine, permanent rejection (e.g. file too large / 4xx) still **fails fast** with the usual error โ€” it should **not** spin retrying. ### C3. General timeline/composer perf (no functional regression) The memoization changes are invisible if correct. Just confirm **nothing broke**: - Open a busy room; scrolling, jump-to-latest, mark-as-read all still work. - Composer: send a message, upload a file, share a location, pick an emoji and a sticker โ€” all still work. --- ## D. Element Call 0.20.1 merge โ€” regression sweep (๐Ÿ‘ฅ 2 people) The upstream bump changed EC's internals and DOM selectors; our call controls drive that iframe, so sweep them. In a live call with 2 people, confirm **each** of our control-bar buttons works: - [ ] **Mic** mute/unmute (icon + actual audio) - [ ] **Camera** on/off - [ ] **Deafen / Sound** toggle (your deafen key too) - [ ] **Screenshare** start/stop (and the "Share your screen?" confirm) - [ ] **Screenshare audio** mute toggle - [ ] **Fullscreen** toggle - [ ] **โ‹ฎ More** menu โ†’ **Spotlight/Grid**, **Reactions**, **Settings** each open the right EC panel - [ ] **End** call leaves cleanly - [ ] **PTT** (push-to-talk) if enabled: hold key = transmit, release = mute; releasing on blur works - [ ] **AFK auto-mute** if enabled: goes muted after the timeout - [ ] **PiP** (picture-in-picture) mini window: drag, resize, fullscreen button, return-to-call; the "You muted" / "All muted" badges show on the right person - [ ] **Denoise** (if ML noise suppression enabled): call audio still flows, no silence If any control does nothing, that usually means an EC DOM selector changed โ€” capture the console and tell me which button. --- # Backlog of previously-fixed-but-unverified items > Sections Aโ€“D above are **this session's** work. Everything below was fixed in earlier waves and is still flagged **โš ๏ธ UNTESTED** in `LOTUS_BUGS.md` / `LOTUS_TODO.md`. They're grouped by what kind of environment you need (mobile, desktop, screen reader, etc.) so you can knock out a whole category at once. None of these are urgent the way Aโ€“D are; do them as you have the right device handy. ## E. Mobile / responsive (needs a real phone, or devtools device emulation) ### E1. Composer toolbar touch targets (#7) On a phone, open a room and the composer toolbar. Tap each button (attach, format, sticker, emoji, GIF, location, poll, schedule, send). **Expected:** every button is comfortably tappable (โ‰ฅ44ร—44px), no mis-taps hitting the wrong icon. ### E2. Room Settings โ€” no horizontal overflow (#8) On a narrow phone screen, open **Room Settings**. **Expected:** the settings nav panel fills the full width; **no** horizontal scrollbar / sideways scrolling anywhere in the panel. ### E3. Modals go fullscreen on mobile (#9) On a phone, open several dialogs: Leave Room, Create Room, Create Space, Invite User, Report (room/user/message), Edit History, Forward Message, Remind Me, Schedule Message, Device Verification, Poll Creator. **Expected:** each opens **fullscreen** (no floating box, no rounded corners / max-width margins). On desktop the same modals should still be the normal centered boxes. ### E4. Composer not hidden by the keyboard (#10) โ€” iOS Safari especially On a phone (priority: **iOS Safari**), tap into the composer so the on-screen keyboard appears. **Expected:** the composer input stays **visible above** the keyboard; the layout shrinks rather than the composer sliding under the keyboard. ### E5. Mobile "Saved Messages" access (Mobile Bookmarks) On a phone, **inside a room**, open the room header **ยทยทยท More Options** menu. **Expected:** a **"Saved Messages"** item is present; tapping it opens the bookmarks panel. (This was the only in-room access point missing on mobile.) --- ## F. Visual / theming ### F1. Animated chat background โ€” no flicker (#2) Settings โ†’ set an **animated** chat background (e.g. anim-rain / anim-aurora / anim-stars). Watch the message text and composer while it animates. **Expected:** smooth animation, **no flickering / shimmering** on message text or the composer, especially after scrolling. Note your GPU/browser if you see artifacts. ### F2. Background vs. Seasonal theme are mutually exclusive (#6) In Settings โ†’ Appearance: 1. Pick a **chat background** โ†’ confirm any **seasonal theme** auto-switches off. 2. Pick a **seasonal theme** โ†’ confirm the **chat background** auto-clears to none. 3. (Edge) If you have old data with both set, after reload only one should visibly apply (no double-overlay clutter). --- ## G. Calls โ€” additional unverified (๐Ÿ‘ฅ 2 people) ### G1. PiP mute badges point at the right person (#12) In a call with at least one other person, pop out the **Picture-in-Picture** mini window. - **You** mute your own mic โ†’ a **"You"/muted badge appears bottom-left** (your status). - A **remote** participant (or all of them) mutes โ†’ an **"All muted"** badge appears **top-right** (clearly about other people). **Expected:** the bottom-left badge is **never** triggered by someone else muting โ€” that was the original bug (it looked like your own mic was muted when it wasn't). ### G2. Full-screen camera broadcasts 1. In a **camera-only** call (no screenshare), confirm the **Fullscreen** button is available (previously only showed during screenshare). 2. Use **MemberGlance โ†’ Focus camera** to full-screen/spotlight a specific person's camera. (Overlaps **A5**; if you've done A5 you can skip.) --- ## H. Media / performance (needs a room with many images) ### H1. Lazy image decryption (P5-5 / MediaGallery) Open a room / media gallery with **many images** (ideally encrypted). Scroll down through them. **Expected:** images decrypt/load as they **approach the viewport**, not all at once on open; scrolling stays smooth and memory doesn't balloon. Off-screen images shouldn't all decode up front. ### H2. Thumbnail framing (P5-6) Look at **tall portrait** images in the timeline and in the media gallery. **Expected:** thumbnails are framed **center-top** (so faces/subjects at the top aren't cropped out); no awkward stretching. Opening the full-size viewer still shows the **whole** image (contain, not cropped). --- ## I. Accessibility (needs a screen reader: VoiceOver / NVDA / TalkBack) With a screen reader on, navigate message hover-actions and content and confirm each control **announces a meaningful label** (not "button" / blank): - [ ] **Reaction** buttons announce the emoji + count (e.g. "thumbsup reaction, 3 people"). - [ ] **Edit history** button announces "View edit history". - [ ] **Thread indicator** announces "View thread". - [ ] **Reply** (jump to original) announces "Jump to original message". --- ## J. Desktop / Tauri build only ### J1. Proactive update notifications (P5-40) In the **desktop (Tauri)** build, with an update available, launch the app (and/or leave it running ~12h). **Expected:** an in-app toast/badge alerts you that an update is available, without manually checking Settings. (Needs an actual newer release to point at.) ### J2. DTLN noise suppression sanity In Settings โ†’ Calls, enable **ML noise suppression** with the **DTLN** model, then join a call. **Expected:** your mic audio still flows (no silence/robotic dropouts) and background noise is reduced. Confirmed working earlier but flagged for a final real-call check; verify on **both** web and desktop. --- ## K. Features โ€” end-to-end unverified ### K1. Remind Me Later On a message, **ยทยทยท โ†’ Remind Me**, pick a short preset (the 20-min one, or wait one out). **Expected:** when due, a Lotus toast fires linking to that message; the reminder then clears itself. Survives a reload while pending (stored in account data). ### K2. Advanced search filters (P4-9) In message search: use the **sender picker** (instead of typing `from:@user`), the **date-range** quick presets (Today / Last week / Last month / Last year), and the **Has link** toggle. **Expected:** each narrows results correctly and reflects in the search. ### K3. Notification content + click target (P5-20 partial) Trigger a desktop/browser notification for a new message. **Expected:** it shows the **real message body** (`username: message`, not "New inbox notification fromโ€ฆ"); **clicking it** brings the window to front and navigates **directly to that message** (not just the inbox). --- ## Priority if you're short on time 1. **A4** (in-call banner) + **A3** (ringtone) โ€” newest, most logic, hardest to reproduce. 2. **B1โ€“B3** (polls on a default theme) โ€” the confirmed visual bug. 3. **D** (EC 0.20.1 control sweep) โ€” guards against the upstream merge breaking calls. 4. **A7** false-positive check (normal joins don't show the error overlay). 5. Everything else.