Step-by-Step Guide — Follow each step exactly as written. All values are provided — do not improvise.

TC-COMM-006: Rich-Text Compose & Send-Test (Detailed)

Module: Communications — Broadcasts (composer)

Primary Test User: comms@demo.membervu.com / Comms123! (Communications Manager)

Priority: P1

URLs for this test:
Frontend: https://stg-rcme.membervu.com/login
Broadcasts: https://stg-rcme.membervu.com/admin/broadcasts
MailHog (email viewer): https://stg-webmail.membervu.com
What changed vs. the old TC pages: earlier Communications pages said "no test-send", "no merge tags", and "raw-HTML <textarea> body". All three are now FALSE — the composer is a rich-text editor with an HTML-source toggle, merge tags resolve per-recipient, and a Send-Test button exists. Trust this page.

Step 1: Log in and open the composer

StepAction (EXACT clicks/typing)Expected Result (EXACT text/behavior)Test Value
1Go to https://stg-rcme.membervu.com/login; sign in.Dashboard loads.comms@demo.membervu.com / Comms123!
2Sidebar → CommunicationsBroadcasts; click New Broadcast (or the inline compose form).Composer opens with Subject, Body, Audience <select>, Tags.
3Set Subject.Field accepts text.Subject QA COMM-006 {{firstName}}

Step 2: Compose with the rich-text editor (CM6-01)

StepActionExpected ResultTest Value
1In the Body editor, type a line, select it, click Bold (B). Add a second line, click Italic (I).Text renders bold / italic in the editor. A formatting toolbar is present (this is NOT a plain textarea).Hi member, welcome to RCME.
2Select a word, click Link, enter a URL.Word becomes a link.https://rotaryclubmanila.org
3Add a bulleted list (2 items) and a numbered list (2 items) via the toolbar.Lists render with markers.
4Save as Draft.Broadcast saved, status DRAFT.
Data assertion: the saved body (view via the edit form's HTML-source toggle, or the broadcast preview API) must contain real HTML tags — <strong>, <em>, <a href>, <ul><li>, <ol><li> — and must NOT contain <script>, <iframe>, or any on*= event handler (server-side sanitizeBroadcastBody strips them regardless of authoring mode). If a <script> survives, that is a security FAIL.

Step 3: HTML-source toggle + lossy-conversion warning (CM6-02)

StepActionExpected ResultTest Value
1Click the Rich text ⇄ HTML toggle to switch to HTML source mode.The editor now shows the raw HTML markup of your body (tags visible as text).
2In HTML mode, paste a fragment containing a table and an image.Raw HTML accepted in the source box.<table><tr><td>A</td></tr></table><img src="x">
3Toggle back to Rich text mode.A confirmation/warning appears before converting ("switching may lose formatting" — tables/images/headings Tiptap can't model). Cancel keeps HTML mode; Confirm converts and drops the unsupported nodes.

Step 4: Sandboxed branded preview (CM6-03)

StepActionExpected ResultTest Value
1Click Preview.A modal opens rendering the full branded email (tenant logo + header/footer bars), not just the raw body.
2Right-click → Inspect the preview frame (or read the DOM).It is an <iframe> with sandbox="allow-same-origin" and NO allow-scripts — so embedded script cannot run in the preview.
3Compare the header/footer bar color to the tenant brand color.Bar color equals the tenant's OrgProfile.primaryColor (RCME brand), NOT a hardcoded blue/purple.

Step 5: Send-Test (CM6-04)

StepActionExpected ResultTest Value
1On the DRAFT broadcast, click Send Test (POST /api/broadcasts/:id/send-test).Success toast; NO bulk send is triggered.
2Open https://stg-webmail.membervu.com.Exactly ONE new email. To = comms@demo.membervu.com (the requesting admin's own address) — NOT any member. Subject begins [TEST].
3Return to the Broadcasts list; inspect the broadcast row.Still DRAFT. sentAt empty, recipient count unchanged — Send-Test does NOT consume/lock the broadcast. It remains editable.

Step 6: Merge-tag substitution + XSS escape (CM6-05 / CM6-06)

This is a security assertion — do not skip. The point is that a member-controlled value (their name) containing markup must NOT be able to inject HTML/script into a broadcast delivered to other members.
StepActionExpected ResultTest Value
1As an admin, temporarily set a test member's first name to a script payload (Members → edit → First name).Saved.First name <script>alert(1)</script>
2In the broadcast body, include a greeting with the merge tag.Editor accepts it.Hello {{firstName}} — {{unknownTag}}
3Send-Test (or preview) with that member as the sample recipient, and view the delivered email source in webmail.{{firstName}} is replaced with the member's name rendered as escaped text — you see literal &lt;script&gt;…, the alert does NOT fire, and there is no live <script> tag in the HTML. A normal member's name substitutes plainly (NOT double-escaped — a name like O'Brien shows as O'Brien).
4Check the unsupported tag.{{unknownTag}} is left inert in preview and stripped from the real send payload — recipients never see a literal {{unknownTag}}.
5Revert the test member's first name.Restored to the original value.

Step 7: Segment-filter validation (CM6-07)

StepActionExpected ResultTest Value
1Create/edit a segment (or POST directly) with an unknown filter key or a Prisma-operator-shaped value.Request rejected with 400 and a validation error — the filter is NOT silently ignored (which would blast the full audience). Only status / roles / membershipTypeId are valid keys.Filter {"in":["ADMIN"]} or {"unpaidInvoices":true}

CLEANUP