On this page
Core paths to testEdge cases worth deliberately tryingOut of scope for this PR (don't file as bugs against it)For Quality
Core paths to test
- Button visibility. Confirm the Send Template button/entry point only appears when a conversation is actually
expired, on both web (Chats.js:142-148) and mobile (WhatsAppChatHeader.js:38-53) — and that it stays hidden on an active conversation.
- Picker contents. With a mix of messages in a test company — some with no template, some
PENDING, some
REJECTED, some APPROVED — confirm only APPROVED ones appear in the Send Template search (appointmentQueries.js:674-676). Confirm the existing searchMessages usage elsewhere (without approved: true) still returns everything, unaffected.
- Name-only filtering. Test with messages containing:
- No variables at all → eligible.
- Only
@PATIENT_NAMEand/or@FIRST_NAME→ eligible, preview shows the substituted name. @PATIENT_NAMEused twice → still counts as needing one value (dedup viaSet), still eligible.- Any other variable (
@DATE,@DOCTOR_NAME,@AMOUNT, etc.), alone or mixed with a name variable → not
eligible; Send stays disabled and the specific missing variable(s) are listed.
- Client/server agreement. Confirm the server rejects a request for an ineligible message even if it somehow
reaches the mutation (e.g. by calling the mutation directly, bypassing the UI) — the server re-derives eligibility itself rather than trusting the client (sendWhatsappTemplate.js:50-56).
- Company boundaries. Confirm a message id or conversation id from a different company is rejected
(Message Not Found / Not Authorised), not silently cross-company-leaked.
- Permission. Confirm a user without
SEND_WAcan't send via either entry point (disabled button on web; check
the equivalent mobile permission gate).
- WhatsApp-not-connected state. With no
officialWhatsAppconfig (or one with nophoneId), confirm the
mutation fails with WhatsApp Not Connected before attempting any Graph API call or writing any log row.
- Send failure handling. Simulate a Graph API failure (e.g. invalid/expired access token, deleted template) and
confirm:
- A
Communicationrow is still written, withstatus/waStatus: falseand the failure reason inresponse. - The mutation still throws (doesn't silently report success).
- No
ConversationMessagerow or conversation-stats update happens on failure (theonlineConversation.update
only runs after the send succeeds).
- Send success handling. Confirm on success: a
Communicationrow is logged, aConversationMessageis created
with the filled-in text, messagesCount/lastActivity update, and if there was an unanswered ConversationFeedback, its responseTime gets backfilled.
- Optimistic UI. Confirm the sent message appears immediately at the top/bottom of the message list on both
apps via the Apollo cache update (updateOnNewMessage), and doesn't duplicate once the list is next refetched.
Edge cases worth deliberately trying
- A conversation with no linked
patientrecord (a raw WhatsApp contact) — confirm the name falls back to the
conversation's own name field, and @FIRST_NAME splits it sensibly even for a single-word name.
- A message with an empty
detailsstring —unfillableVariables/extractValuesshould treat it as trivially
eligible.
- RTL languages (Arabic, Kurdish (Sorani/Kurmanji)) — confirm the preview text and the "needs {{val}}" warning render
correctly right-to-left; translations for sendTemplate, sendTemplateDescription, and templateNeedsVariables exist in all eight translation files touched by this PR (ar, ckb, el, en, fr, it, ku, nl, pl).
- Selecting a message, then clearing the search field (
hasClear/allowClear) — confirm the preview and Send state
reset correctly rather than sending a stale selection.
- Rapid double-submission — confirm the Send button/OK button disables while
loadingis true, so a second click
can't fire a duplicate send with the same generatedID.
Out of scope for this PR (don't file as bugs against it)
- Anything about creating or submitting a template to Meta for approval — unrelated existing functionality.
- Templates needing variables beyond the patient's name — this is an intentional restriction, not a gap (see