Files
nur-falah-prevention/e2e-property.cjs
T
wmj 10289e2d92 Verify property/land coverage across all three applicable fast-path channels
No code changes needed — land/property was already correctly wired through
Hibah (offer/acceptance/possession now), Waqf (dedicated corpus via Family
Waqf Designator), and Trust (via Nomination Registry, added earlier this
session). This adds explicit isolated verification since prior tests only
exercised one channel per property asset at a time.

e2e-property.cjs: three separate property assets, each covered through a
different channel, confirming Coverage Dashboard correctly attributes each
(Hibah/Waqf/Trust) and reaches 100%, and Death Trigger generates a
distinct execution packet for all three. 9/9 passing. Re-verified
e2e-uat.cjs (32/32), e2e-fastpath.cjs (16/16), e2e-trust.cjs (12/12),
e2e-business.cjs (10/10), e2e-digital-vehicle.cjs (10/10), e2e-info.cjs
(31/31) — 132/132 total, no regressions.
2026-08-13 18:18:40 +08:00

114 lines
6.3 KiB
JavaScript

// Verifies property/land assets are covered through ALL THREE applicable fast-path
// channels: Hibah, Waqf (Family Waqf Designator), and Trust (Nomination Registry).
const { chromium } = require('playwright');
const BASE = 'https://moslem04.falahos.my/';
const results = [];
const consoleErrors = [];
function record(name, pass, detail = '') { results.push({ name, pass, detail }); console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${detail ? ' — ' + detail : ''}`); }
async function main() {
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 390, height: 844 } });
page.on('console', m => { if (m.type() === 'error') consoleErrors.push(m.text()); });
page.on('pageerror', e => consoleErrors.push(e.message));
await page.goto(BASE, { waitUntil: 'networkidle' });
const clickTab = async label => { await page.locator('nav button.tab', { hasText: label }).click(); await page.waitForTimeout(200); };
const selectByText = async (locator, text) => {
const val = await locator.evaluate((el, t) => Array.from(el.options).find(o => o.textContent.includes(t))?.value, text);
await locator.selectOption(val);
};
// Three separate Property-type assets, one per channel
await clickTab('Assets');
for (const [desc, val] of [['House A (via Hibah)', '350000'], ['House B (via Waqf)', '600000'], ['House C (via Trust)', '450000']]) {
await page.locator('.field:has-text("Description") input').fill(desc);
await page.locator('.field:has-text("Estimated value") input').fill(val);
// Type defaults to Property — leave as-is
await page.locator('button.btn-primary', { hasText: 'Add asset' }).click();
await page.waitForTimeout(200);
}
await clickTab('Coverage');
const totalRows = await page.locator('.asset-row', { hasText: 'House' }).count();
record('Coverage: all 3 property assets show as exposed initially', totalRows === 3, `${totalRows} rows`);
const pctInitial = await page.locator('.big-percent').textContent();
record('Coverage: 0% before covering any property asset', pctInitial.trim() === '0%', pctInitial);
// Channel 1: Hibah
await clickTab('Hibah');
await page.locator('.field:has-text("Recipient") input').fill('My Wife');
await page.locator('.field:has-text("Relation to you") input').fill('wife');
await page.locator('.field:has-text("Asset / gift description") input').fill('House gift');
await selectByText(page.locator('select').first(), 'House A');
await page.locator('button.btn-primary', { hasText: 'Log this hibah' }).click();
await page.waitForTimeout(200);
await page.locator('.guard-buttons button.btn-secondary', { hasText: 'No' }).click();
await page.waitForTimeout(200);
await page.locator('button.btn-primary', { hasText: 'Confirm and save' }).click();
await page.waitForTimeout(300);
await clickTab('Coverage');
const houseARow = await page.locator('.asset-row', { hasText: 'House A' }).textContent();
record('Property via Hibah: House A shows covered', !houseARow.includes('Not covered') && houseARow.includes('Hibah'), houseARow.trim().slice(0, 150));
// Channel 2: Waqf
await clickTab('Family Waqf');
await selectByText(page.locator('select').first(), 'House B');
await page.waitForTimeout(200);
const corpusVal = await page.locator('select').first().inputValue();
record('Family Waqf: House B selectable as corpus asset', corpusVal !== '');
await page.locator('.field:has-text("Mutawalli (trustee)") input').fill('Ahmad bin Ismail');
await page.waitForTimeout(300); // allow $effect to persist waqfCorpusAssetId
await clickTab('Coverage');
const houseBRow = await page.locator('.asset-row', { hasText: 'House B' }).textContent();
record('Property via Waqf: House B shows covered', !houseBRow.includes('Not covered') && houseBRow.includes('Waqf'), houseBRow.trim().slice(0, 150));
// Channel 3: Trust (Nomination Registry)
await clickTab('Nominate');
await selectByText(page.locator('select').first(), 'House C');
await page.waitForTimeout(200);
await page.locator('.form-card select').nth(1).selectOption('trust');
await page.waitForTimeout(200);
const trustInputs = page.locator('.trust-fields .field input');
await trustInputs.nth(0).fill('Trustee Co');
await trustInputs.nth(1).fill('Successor Trustee Co');
await trustInputs.nth(2).fill('Equal split among children');
await page.locator('button.btn-primary', { hasText: 'Add trust setup' }).click();
await page.waitForTimeout(300);
await clickTab('Coverage');
const houseCRow = await page.locator('.asset-row', { hasText: 'House C' }).textContent();
record('Property via Trust: House C shows covered', !houseCRow.includes('Not covered') && houseCRow.includes('Trust'), houseCRow.trim().slice(0, 150));
const finalPct = await page.locator('.big-percent').textContent();
record('Coverage: 100% after all 3 property assets covered via 3 different channels', finalPct.trim() === '100%', finalPct);
// Death Trigger should produce packets for all three
await clickTab('Trigger');
const attestorInputs = page.locator('.attestor-row input');
await attestorInputs.nth(0).fill('Executor A');
await page.locator('.attestor-row .confirm-btn').nth(0).click();
await attestorInputs.nth(1).fill('Witness B');
await page.locator('.attestor-row .confirm-btn').nth(1).click();
await page.locator('.field:has-text("Date of death") input').fill('2026-08-13');
await page.locator('.field:has-text("Death certificate reference") input').fill('DC-PROP-001');
await page.waitForTimeout(200);
await page.locator('button.btn-danger-solid').click();
await page.waitForTimeout(300);
const packetCount = await page.locator('.packet-row', { hasText: 'House' }).count();
record('Death Trigger: execution packets generated for all 3 property assets', packetCount === 3, `${packetCount} packets`);
record('No uncaught JS console errors', consoleErrors.length === 0, consoleErrors.join(' || '));
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); });