// Verifies that firing a member's death trigger automatically invokes the // notify-emergency-contacts Edge Function (mosque/khairat/police/ambulance // contacts) alongside the existing heir notification — the one place in // this app where "auto-send on trigger" is actually the right behavior. const { chromium } = require('playwright'); const BASE = 'https://moslem04.falahos.my/'; const OWNER = 'nurfalah.e2etest.owner@gmail.com'; const AGENT = 'nurfalah.e2etest.agent@gmail.com'; const PASSWORD = 'TestPassword123!'; const results = []; function record(name, pass, detail = '') { results.push({ name, pass, detail }); console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${detail ? ' — ' + detail : ''}`); } async function signIn(page, email) { await page.goto(BASE, { waitUntil: 'networkidle' }); await page.locator('.field:has-text("Email") input').fill(email); await page.locator('.field:has-text("Password") input').fill(PASSWORD); await page.locator('button.btn-primary', { hasText: 'Sign in' }).click(); await page.waitForTimeout(1500); } async function main() { const familyName = `EmergencyNotify ${Date.now()}-${Math.floor(Math.random() * 1e6)}`; const browser = await chromium.launch(); const ownerCtx = await browser.newContext({ viewport: { width: 390, height: 844 } }); const ownerPage = await ownerCtx.newPage(); await signIn(ownerPage, OWNER); await ownerPage.locator('.field:has-text("Family name") input').fill(familyName); await ownerPage.locator('button.btn-primary', { hasText: 'Create family' }).click(); await ownerPage.waitForTimeout(1200); await ownerPage.locator('nav button[aria-label="Assets"]').click(); await ownerPage.waitForTimeout(500); await ownerPage.locator('.contacts-section .field:has-text("Name") input').fill('Emergency Test Mosque'); await ownerPage.locator('.contacts-section .field:has-text("Category") select').selectOption('mosque'); await ownerPage.locator('.contacts-section .field:has-text("Email") input').fill('mosque-emergency-test@example.com'); await ownerPage.locator('.contacts-section button.btn-secondary', { hasText: 'Add trusted contact' }).click(); await ownerPage.waitForTimeout(1000); record('Setup: mosque contact with email added', await ownerPage.locator('.contact-row', { hasText: 'Emergency Test Mosque' }).isVisible().catch(() => false)); await ownerPage.locator('nav button[aria-label="Family"]').click(); await ownerPage.waitForTimeout(500); await ownerPage.locator('.field:has-text("Invite by email") input').fill(AGENT); await ownerPage.locator('.field:has-text("Role") select').selectOption('agent'); await ownerPage.locator('button.btn-primary', { hasText: 'Send invite' }).click(); await ownerPage.waitForTimeout(1000); const agentCtx = await browser.newContext({ viewport: { width: 390, height: 844 } }); const agentPage = await agentCtx.newPage(); let notifyStatus = null; agentPage.on('response', r => { if (r.url().includes('functions/v1/notify-emergency-contacts')) notifyStatus = r.status(); }); await signIn(agentPage, AGENT); const pending = agentPage.locator('.invite-row', { hasText: familyName }); if (await pending.count()) { await pending.locator('button', { hasText: 'Accept' }).click(); await agentPage.waitForTimeout(1000); } const familyRow = agentPage.locator('.family-row', { hasText: familyName }); if (await familyRow.count()) { await familyRow.click(); await agentPage.waitForTimeout(1000); } await agentPage.locator('nav button[aria-label="Mutawalli"]').click(); await agentPage.waitForTimeout(1000); const memberChip = agentPage.locator('.member-chip', { hasText: 'nurfalah.e2etest.owner' }); record('Mutawalli: owner appears as a member to fire a trigger for', await memberChip.count() === 1); if (await memberChip.count()) await memberChip.click(); await agentPage.waitForTimeout(500); const attestorInputs = agentPage.locator('.attestor-row input'); await attestorInputs.nth(0).fill('Witness A'); await agentPage.locator('.attestor-row .confirm-btn').nth(0).click(); await attestorInputs.nth(1).fill('Witness B'); await agentPage.locator('.attestor-row .confirm-btn').nth(1).click(); await agentPage.locator('.field:has-text("Date of death") input').fill('2026-01-01'); await agentPage.locator('.field:has-text("Death certificate") input').fill(`CERT-${Date.now()}`); await agentPage.waitForTimeout(500); await agentPage.locator('button.btn-danger-solid').click(); await agentPage.waitForTimeout(4000); record('Trigger: fires successfully (triggered banner shown)', await agentPage.locator('.triggered-banner').isVisible().catch(() => false)); record('Emergency notify: auto-invoked on fire, returns 200', notifyStatus === 200, `status: ${notifyStatus}`); record('Emergency notify: status message shown to the mutawalli', await agentPage.locator('.emergency-notify-status', { hasText: 'Notified' }).isVisible().catch(() => false)); await browser.close(); const passCount = results.filter(r => r.pass).length; const failCount = results.length - passCount; console.log(`\n${passCount} passed, ${failCount} failed, ${results.length} total`); if (failCount > 0) results.filter(r => !r.pass).forEach(r => console.log(` - ${r.name}: ${r.detail}`)); process.exit(failCount > 0 ? 1 : 0); } main().catch(e => { console.error('SCRIPT ERROR:', e); process.exit(2); });