d5e25e9f8e
Per explicit request: "full dummy data on at least 5 families with dummy assets and family members." Seeded directly against the live Supabase backend (SQL, not driving the UI 5x) for speed and reliability, then verified through the actual live app rather than trusting the inserts. 5 families, each with a distinct role in demonstrating the app: - The Ismail Family (100% coverage) — near-fully-planned estate: Hibah, bank-mandate nomination, digital-custody (key-escrow) nomination, business-continuity (company shares). Has an extra member (spouse) and an agent/mutawalli. - The Rahman Family (UK, 20% coverage) — diaspora market, partial coverage, exercises Wassiyah (charitable bequest) alongside Hibah/ Nomination. Has an extra adult-child member. - The Osman Family (31% coverage) — deliberately mostly UNCOVERED to demonstrate the Coverage Dashboard's warning state and per-asset suggestions; includes a sole-proprietorship business with the waqf-enterprise redirect note. - The Yusuf Family (94% coverage) — young family, digital-asset-heavy (multisig nomination). Shares the same mutawalli/agent as Ismail — demonstrates one professional agent managing multiple families. - The Karim Family (100% coverage) — Family Waqf demo: an apartment dedicated as corpus with a named mutawalli and successor. Every fast-path channel type (Hibah, EPF/bank-mandate, digital-custody in both multisig and key-escrow modes, business-continuity in both company-shares and sole-proprietorship modes, trust, family waqf) and every family role combination (owner-only, owner+member, owner+member+ agent, shared cross-family agent) now has at least one real example. Each family also has a genealogy tree (3-6 people, parent/child and spouse relationships) so the Tree tab is never empty for a demo. DEMO_DATA.md documents all logins (shared password DemoPassword123!), what each family demonstrates, and the verification method. verify-demo-families.cjs: confirms all 5 families actually render correctly in the live app, not just that the SQL succeeded — sign-in, correct asset count, non-zero estate total, coverage percentage, correct person count, non-empty tree render. 35/35 passing. Also fixed a genuinely flaky e2e-uat.cjs assertion found while re-verifying after seeding — same instant-isVisible()-after-fixed-wait pattern already fixed elsewhere in this session, now using a proper waitFor(). Stable across 3 consecutive runs (32/32 each). Full sweep after seeding: e2e-fastpath 16/16, e2e-trust 12/12, e2e-business 10/10, e2e-digital-vehicle 10/10, e2e-property 9/9, e2e-other 4/4, e2e-info 31/31, e2e-per-member 11/11, e2e-family-tree 9/9 — 209/209 total across all suites plus the 35 demo-family checks.
79 lines
4.4 KiB
JavaScript
79 lines
4.4 KiB
JavaScript
// Verifies the 5 dummy demo families actually render correctly in the live
|
|
// app — not just that the SQL inserts succeeded. Logs in as each owner,
|
|
// checks the family switcher shows exactly one family, and inspects
|
|
// Coverage/Assets/Tree for expected content.
|
|
const { chromium } = require('playwright');
|
|
const BASE = 'https://moslem04.falahos.my/';
|
|
const results = [];
|
|
function record(name, pass, detail = '') { results.push({ name, pass, detail }); console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${detail ? ' — ' + detail : ''}`); }
|
|
|
|
const FAMILIES = [
|
|
{ email: 'nf.demo.ismail@gmail.com', name: 'The Ismail Family', expectedAssets: 5, expectedPeople: 6 },
|
|
{ email: 'nf.demo.rahman@gmail.com', name: 'The Rahman Family (UK)', expectedAssets: 4, expectedPeople: 4 },
|
|
{ email: 'nf.demo.osman@gmail.com', name: 'The Osman Family', expectedAssets: 6, expectedPeople: 6 },
|
|
{ email: 'nf.demo.yusuf@gmail.com', name: 'The Yusuf Family', expectedAssets: 4, expectedPeople: 3 },
|
|
{ email: 'nf.demo.karim@gmail.com', name: 'The Karim Family', expectedAssets: 3, expectedPeople: 3 },
|
|
];
|
|
const PASSWORD = 'DemoPassword123!';
|
|
|
|
async function main() {
|
|
const browser = await chromium.launch();
|
|
|
|
for (const fam of FAMILIES) {
|
|
const page = await (await browser.newContext({ viewport: { width: 390, height: 844 } })).newPage();
|
|
await page.goto(BASE, { waitUntil: 'networkidle' });
|
|
await page.locator('.field:has-text("Email") input').fill(fam.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);
|
|
|
|
// Owner may land on the family switcher (their own family plus, in some
|
|
// seeded cases, no others) — select the family by name if so.
|
|
const switcherVisible = await page.locator('.switcher-screen').isVisible({ timeout: 8000 }).catch(() => false);
|
|
if (switcherVisible) {
|
|
const familyRowVisible = await page.locator('.family-row', { hasText: fam.name }).isVisible().catch(() => false);
|
|
record(`${fam.name}: family switcher shows this family by name`, familyRowVisible);
|
|
if (familyRowVisible) {
|
|
await page.locator('.family-row', { hasText: fam.name }).click();
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
}
|
|
const onMainApp = await page.locator('nav button[aria-label="Coverage"]').isVisible({ timeout: 8000 }).catch(() => false);
|
|
record(`${fam.name}: owner reaches the main app`, onMainApp);
|
|
if (!onMainApp) { await page.close(); continue; }
|
|
|
|
// Assets
|
|
await page.locator('nav button[aria-label="Assets"]').click();
|
|
await page.waitForTimeout(800);
|
|
const assetCount = await page.locator('.asset-row').count();
|
|
record(`${fam.name}: has ${fam.expectedAssets} assets`, assetCount === fam.expectedAssets, `found ${assetCount}`);
|
|
const totalText = await page.locator('.total-card strong').textContent().catch(() => '');
|
|
record(`${fam.name}: estate total is non-zero`, /[1-9]/.test(totalText), totalText.trim());
|
|
|
|
// Coverage
|
|
await page.locator('nav button[aria-label="Coverage"]').click();
|
|
await page.waitForTimeout(800);
|
|
const pctText = await page.locator('.big-percent').textContent().catch(() => '');
|
|
record(`${fam.name}: Coverage Dashboard shows a percentage`, /%/.test(pctText), pctText.trim());
|
|
|
|
// Family Tree
|
|
await page.locator('nav button[aria-label="Tree"]').click();
|
|
await page.waitForTimeout(800);
|
|
const peopleCount = await page.locator('.person-card').count();
|
|
record(`${fam.name}: has ${fam.expectedPeople} people in the tree`, peopleCount === fam.expectedPeople, `found ${peopleCount}`);
|
|
const treeText = await page.locator('.tree-section').innerText().catch(() => '');
|
|
const hasTreeContent = treeText.length > 20 && !treeText.includes('Add people and relationships above');
|
|
record(`${fam.name}: generational tree actually renders (not empty)`, hasTreeContent, treeText.replace(/\s+/g, ' ').slice(0, 150));
|
|
|
|
await page.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}`));
|
|
await browser.close();
|
|
process.exit(failCount > 0 ? 1 : 0);
|
|
}
|
|
main().catch(e => { console.error('SCRIPT ERROR:', e); process.exit(2); });
|