UHI Patient Stages 2, 3 & 4 — Manual Test Plan
Use this plan to verify the end-to-end UHI booking implementation before pushing to CI. Each stage has: prerequisites, test steps, expected results, and pass/fail criteria.
🧪 Stage 0 — ABHA in Sign-In Response
Goal: Verify abha_address and abha_number are returned in the sign-in payload.
Prerequisites
- Backend running at
http://localhost:3000 - A patient account that has an ABHA address linked (
identity_patients.abha_addressnot null) - A patient account with NO ABHA address linked (null)
- Chrome DevTools or Postman
Test Steps
T0-1: Patient with ABHA — regular phone/password sign-in
- Open browser DevTools → Network tab → filter by
POST /identity/sessions - Sign in as a regular patient (phone + password) who has
abha_addressinidentity_patientstable - In the Network response, find the JSON body under
data.user - Verify:
abha_addressfield is present with a non-null value (e.g.,"rahul.k001@sbx") - Verify:
abha_numberfield is present with a non-null value (14-digit string) - Verify:
abha_addressis also present inlocalStorage['auth-storage']understate.user.abha_address
T0-2: Patient with ABHA — PHR sign-in (ABHA OTP flow)
- Sign in via the PHR/ABHA OTP flow (mobile OTP → verify_user → KulcareSession)
- In the stored
localStorage['auth-storage'], find the user object - Verify:
abha_addressandabha_numberare present fromsession.patient - Verify: Both sign-in methods (regular + PHR) result in the same
user.abha_addressshape
T0-2: Patient without ABHA — both sign-in methods
- Sign in as a patient whose
identity_patients.abha_addressisNULLvia phone/password - Find the same JSON response under
data.user - Verify:
abha_addressisnull - Verify:
abha_numberisnull - Repeat for PHR sign-in (ABHA OTP flow)
- Verify:
abha_addressandabha_numberare absent from localStorage user object - Verify: The UHI sidebar tab is hidden for this patient (Stage 1 verification)
T0-3: Practitioner sign-in — ABHA fields absent
- Sign in as a practitioner (doctor) user
- Find the JSON response under
data.user - Verify:
abha_addressis absent from the response (not returned at all for practitioners) - Verify:
abha_numberis absent from the response - Verify: Practitioner does not see the UHI Network tab (even if they somehow reach it)
Pass Criteria
- ABHA fields present in patient sign-in, absent for practitioners
- Fields never exposed in non-patient user types
🧪 Stage 1 — ABHA-Gated UHI Tab
Goal: The "UHI Network" sidebar tab is hidden when no ABHA address is linked, visible when linked.
Prerequisites
- Patient app running at
http://localhost:5173(or port frompnpm dev) - Browser with DevTools open
Test Steps
T1-1: No ABHA — tab hidden
- Log in as a patient with no
abha_address - Observe the left sidebar navigation
- Verify: "UHI Network" tab is not visible in the sidebar
- Verify: No broken layout or empty space where the tab should be
T1-2: ABHA linked — tab visible
- Log in as a patient with an
abha_addresslinked - Observe the left sidebar navigation
- Verify: "UHI Network" tab appears with a globe icon
- Verify: Tab is clickable and navigates to
/uhi/search
T1-3: URL direct access without ABHA
- Log in without ABHA
- Navigate directly to
http://localhost:5173/uhi/search - Verify: Page either redirects or shows an appropriate "link your ABHA first" message
- (Check that the route is protected — unauthenticated direct access to
/uhi/searchshould redirect to login)
Pass Criteria
- No ABHA patient: tab hidden, direct URL blocked
- ABHA patient: tab visible and navigable
🧪 Stage 2 — P2P Slot Discovery
Goal: "View Slots" navigates to slot selection, P2P search is sent to the correct HSPA, slots render in a date-grouped grid.
Prerequisites
- Stage 1 complete (UHI tab accessible)
- HSPA sandbox environment reachable (or mock HSPA)
- Network tab open in DevTools
Test Steps
T2-1: View Slots navigation
- From the UHI Search page, enter a search (city: "Delhi", specialty: any)
- Wait for search results to appear (Ably
on_searchevents) - On any provider card, click "View Slots"
- Verify: URL changes to
/uhi/provider/<providerId>/slots - Verify: Provider name appears in the slot selection page header
T2-2: P2P search API call
- Open DevTools → Network tab → filter by
/uhi/eua/search/p2p - Click "View Slots" on a provider card
- Verify: A
POST /uhi/eua/search/p2prequest is fired - Verify: Request body includes:
provider_urimatching the HSPA URL from the provider cardtransaction_id(UUID)service_type: "physical"
- Verify: The request is sent directly to the HSPA URL, not through a gateway
T2-3: Slot grid — date grouping
- Wait for slot results to appear (via Ably or polling fallback)
- Verify: Slots are grouped by date (day selector at the top with prev/next arrows)
- Verify: Each day shows a list of slot cards
- Verify: Each slot card shows: time range, doctor name, mode badge ("Physical"), fee
T2-4: Slot selection → booking page
- Click any available slot card
- Verify: URL changes to
/uhi/book/<providerId>/<slotId> - Verify: Booking page loads with provider name and selected slot time in a summary card
- Verify: "Your Booking" summary card shows correct date, time, and fee
T2-5: No slots available
- Search with a date range that has no slots
- Verify: "No slots available on this date" message appears
- Verify: Day selector still works for adjacent dates
Pass Criteria
- "View Slots" navigates correctly with provider context
- P2P search sent to correct HSPA provider_uri
- Slots grouped by date with day selector
- Slot selection navigates to booking form
🧪 Stage 3 — Booking Init, T&C Acceptance, Confirm, PIN Display
Goal: Patient details → initiate → wait for on_init → accept 5 terms → confirm → PIN displayed.
Prerequisites
- A slot selected from Stage 2 (booking page loaded)
- HSPA sandbox reachable
- Ably real-time connection active
- DevTools open for console + network monitoring
Test Steps
T3-1: Patient details form — pre-fill
- On the booking page (
/uhi/book/...), observe the form - Verify: Name field is pre-filled from the logged-in user's name
- Verify: Phone field is pre-filled
- Verify: Email field is pre-filled (if available)
- Fill remaining required fields (city, state, area code)
- Verify: All NHA-required billing fields are present:
- Name, Phone, Email
- Locality, City, State, Country (pre-filled "INDIA"), Area Code
T3-2: Initiate booking — loading state
- Click "Continue to Terms" on the form
- Verify: Form disappears; a loading spinner appears
- Verify: Text reads "Connecting to healthcare provider..." or similar
- Verify: A
POST /uhi/eua/bookings/initrequest appears in Network tab - Verify: Request body includes:
customer.id= patient'sabha_address(from auth store — populated by either regular sign-in or PHR sign-in)fulfillment.id= selected slot IDfulfillment.type="Physical"(not "Teleconsultation")billingwith all address fields
- Verify:
transaction_idis a UUID in the request - Test both sign-in methods:
- Sign in via regular phone/password → initiate booking → verify
customer.idisuser.abha_address - Sign in via PHR ABHA OTP → initiate booking → verify
customer.idissession.patient.abha_address - Both paths must produce the same
customer.idshape in the init request
- Sign in via regular phone/password → initiate booking → verify
T3-3: on_init arrival — terms displayed
- Wait for the Ably
on_initevent to arrive (check browser console foron_initlog if available, or wait up to 60s) - Verify: After on_init, the view switches to the T&C step
- Verify: All 5 terms are displayed in order: Commercial, Settlement, Cancellation, Refund, Payment
- Verify: Each term has a checkbox
- Verify: "Confirm Booking" button is disabled until all 5 terms are checked
- Verify: Cancellation term shows a warning about reason-required (if
reasonRequired: true)
T3-4: Session expiry warning
- On the T&C step, observe the page
- Verify: Text mentions "15 minutes" session expiry
- Test: (If you can wait 15+ minutes or manipulate the clock) — after 15 minutes of no on_init, an error appears
T3-5: All terms checked → confirm enabled
- Check all 5 term checkboxes
- Verify: "Confirm Booking" button becomes enabled
- Click "Confirm Booking"
- Verify:
POST /uhi/eua/bookings/confirmfires in Network tab - Verify: Confirm request body includes every term with
termsState: "AGREED" - Verify: Cancellation term includes
reason: "Patient agreed to cancellation policy" - Verify: Each term carries the
timePeriodfrom on_init
T3-6: on_confirm arrival — PIN displayed
- Wait for the Ably
on_confirmevent - Verify: Page navigates to
/uhi/book/confirmation/<transactionId>(or similar confirmation route) - Verify: A "Booking Confirmed!" success header appears
- Verify: A large, monospace PIN is displayed (e.g.,
7342) - Verify: PIN validity window (valid from / valid to) is shown
- Verify: Booking details (provider name, date, time) are shown
T3-7: PIN security — no persistence
- Open DevTools → Application tab → Local Storage for localhost:5173
- Search for the PIN value in localStorage
- Verify: PIN is not stored in localStorage
- Verify: PIN is not in URL query parameters or hash
- Verify: PIN is not in the browser console logs
- Refresh the confirmation page
- Verify: PIN is cleared from memory (page still loads but PIN may not re-display since it came from in-memory state — acceptable fallback is to show "PIN not yet available" or fetch from API)
- Open DevTools → Session Storage — PIN is not stored there either
T3-8: Error — HSPA init rejection
- (With sandbox/QA HSPA) Send an init request that the HSPA rejects
- Verify: Error message appears: "Booking initiation failed" or similar
- Verify: "Try Again" button is present and returns to the details form
T3-9: Error — HSPA confirm rejection
- Send a confirm that the HSPA rejects (e.g., terms mismatch)
- Verify: Error message appears: "Booking confirmation failed"
- Verify: User is returned to the terms screen or booking page with error visible
Pass Criteria
- Billing form pre-fills from auth context
- Init fires with correct customer.id (abha_address) and billing
- on_init shows 5 terms in correct order with all checkboxes
- Confirm button only enables when all 5 terms checked
- Confirm fires with all terms AGREED + cancellation reason
- on_confirm shows PIN in large monospace font
- PIN never persisted to localStorage/sessionStorage/URL/console
🧪 Stage 4 — Appointment Status Tracking & No-Show Reporting
Goal: UHI appointments list shows all bookings with status; patient can report doctor no-show.
Prerequisites
- At least one confirmed UHI booking exists (from Stage 3)
- UHI appointments page accessible
Test Steps
T4-1: UHI appointments page — empty state
- Navigate directly to
/uhi/appointments(or click "View My Appointments" from confirmation page) - If no bookings exist: Verify: "No UHI appointments yet." message appears
- Verify: "Find a Provider" button navigates to
/uhi/search
T4-2: Appointments list — with bookings
- (With confirmed booking(s) from Stage 3)
- Open
/uhi/appointments - Verify: Each UHI booking appears as a card with:
- Provider name
- Appointment date and time
- PIN (shown if available — e.g.,
••42) - Status badge: "Initiated", "Confirmed", "Completed", "Cancelled", etc.
- Mode: "Physical"
- Order/Booking reference number
T4-3: Status tracker — confirmed booking
- Click on a "Confirmed" booking card
- Verify: A status panel appears on the right (or below on mobile)
- Verify: Status tracker shows a vertical timeline: Initiated → Confirmed → Fulfilled
- Verify: The "Confirmed" step is highlighted (current state)
- Verify: Completed steps have checkmarks
- Verify: PIN status badge shows "GENERATED" or "VERIFIED"
T4-4: Status tracker — completed booking
- Find a completed booking (if available in sandbox)
- Verify: All timeline steps show checkmarks
- Verify: PIN status shows "VERIFIED"
T4-5: Doctor no-show — button visibility
- Click on a "Confirmed" booking card
- Verify: "Report Doctor No-Show" button appears in the detail panel
- Verify: Button is styled as a warning (amber/orange)
T4-6: Doctor no-show — confirmation dialog
- Click "Report Doctor No-Show"
- Verify: A confirmation dialog appears with clear warning text
- Verify: "Confirm" and "Cancel" buttons present
- Click "Cancel" — Verify: Dialog closes, no API call fired
T4-7: Doctor no-show — report submission
- Click "Report Doctor No-Show"
- Click "Confirm" (or equivalent confirm action)
- Verify:
POST /uhi/eua/bookings/<orderId>/updatefires in Network tab - Verify: Request includes
state: "DOCTOR_NO_SHOW"or equivalent - Verify: Booking card updates to show "No-Show" status
- Verify: "Report Doctor No-Show" button disappears (already reported)
T4-8: Non-confirmed booking — no no-show button
- Click on an "Initiated" or "Cancelled" booking
- Verify: "Report Doctor No-Show" button does not appear
- (Only
CONFIRMEDstate should allow no-show reporting)
Pass Criteria
- Appointments list shows all UHI bookings with PIN (masked), status, date
- Status tracker shows correct timeline for each state
- No-show button only visible for CONFIRMED bookings
- No-show reporting fires correct API call with proper state
🧪 Stage 5 — Real-Time Ably Integration
Goal: All Ably events fire correctly; fallback to polling when Ably is unavailable.
Test Steps
T5-1: Ably connection — on_init
- Open DevTools → Console tab (or a dedicated Ably logger if added)
- Initiate a booking (submit patient details form)
- Verify: Ably channel
private:user:<userId>is subscribed - Verify:
on_initevent arrives on that channel within 60 seconds
T5-2: Ably connection — on_confirm
- Accept all 5 terms and confirm
- Verify:
on_confirmevent arrives on the same channel
T5-3: Ably unavailable — polling fallback
- Disconnect the Ably connection (e.g., block
rest.ably.ioin Network tab filters, or disconnect internet briefly) - Initiate a booking
- Verify: System does not hang — polling fallback activates or a clear error appears
- Verify: "Failed to connect to real-time updates" message (or similar) if Ably is unavailable
T5-4: Ably reconnection
- Re-enable Ably connection
- Verify: New Ably subscription reconnects automatically
Pass Criteria
- on_init and on_confirm arrive via Ably
- System handles Ably unavailability gracefully
🧪 Edge Cases & Error Handling
E1: Network timeout on init
- Throttle network to "Slow 3G" or use DevTools to add a 30s delay to
/uhi/eua/bookings/init - Initiate a booking
- Verify: "Failed to initiate booking" error appears after timeout
- Verify: User can retry from the booking page
E2: HSPA offline during init
- (With sandbox HSPA) Stop the HSPA service
- Initiate a booking
- Verify: "Booking initiation failed" error appears (not a crash)
- Verify: "Try Again" button returns to the form
E3: Terms mismatch on confirm
- Modify a term's
termsStateto something other thanAGREEDbefore sending confirm - Verify: HSPA rejects the confirm; error displayed; user can retry
E4: Session expiry — on_init never arrives
- Initiate a booking, then wait 15+ minutes without receiving on_init
- Verify: "Your booking session expired. Please try again." error
- Verify: User is returned to slot selection
E5: Local storage cleared mid-flow
- Fill patient details, initiate booking
- Before on_init arrives, clear localStorage (DevTools → Application → Clear)
- Verify: App still loads (graceful handling of auth token loss)
- Verify: On reconnect, auth state is restored from the token in localStorage
📋 Test Data Requirements
| Entity | Required for | How to create |
|---|---|---|
| Patient with ABHA (regular sign-in) | T0-1, T1-2, T2–T5 | Seed identity_patients with abha_address: 'test.k001@sbx', create a phone/password account. Sign in with phone + password |
| Patient with ABHA (PHR sign-in) | T0-2, T1-2, T2–T5 | Same patient record; sign in via ABHA OTP flow (mobile OTP → verify_user → KulcareSession) |
| Patient without ABHA | T0-2, T1-1, T1-3 | Seed identity_patients with abha_address: NULL |
| Practitioner user | T0-3 | Seed a practitioner without patient record |
| HSPA provider (sandbox) | T2–T5 | Use NHA sandbox HSPA URL from config |
| Confirmed booking | T4-2 onwards | Complete Stage 3 flow end-to-end |
✅ Sign-Off Checklist
Before pushing to CI, verify all items:
- Backend sign-in returns ABHA fields for patients only (regular phone/password login)
- PHR sign-in (ABHA OTP flow) also surfaces
abha_addressin the auth store and localStorage - Both sign-in methods (
signInphone/password +phrSignInABHA OTP) produce the sameuser.abha_addressshape for UHIcustomer.id - Sidebar hides UHI tab when no ABHA; shows when linked
- Slot discovery navigates to P2P search, slots render in date grid
- Booking form pre-fills from auth context, sends correct
customer.id - 5 T&C terms displayed in order with checkboxes; confirm disabled until all checked
- Cancel reason added to cancellation term in confirm request
- PIN displayed in monospace, never in localStorage
- 15-min expiry warning shown on terms page
- Appointments list shows all UHI bookings with status badges
- Status tracker shows correct timeline for each state
- Doctor no-show button only for CONFIRMED bookings
- TypeScript compiles with zero errors
- Ably events
on_init,on_confirm,on_status,on_updateall wired