domain project / insurance

Insurance Portal

Test strategies for quote engines, policy management, claims processing, document management, and regulatory compliance.

Quote & comparison enginePolicy managementClaims processingDocument managementPremium paymentsRegulatory compliance

Domain Overview

Insurance portals are form-heavy, rule-driven, and heavily regulated. Quote engines have complex underwriting rules (risk factors, exclusions, pricing bands). Claims require document upload, status tracking, and settlement. GDPR and FCA/state insurance regulations govern data handling. Accessibility is critical — policy documents must be readable.

Typical stack: Angular / React frontend, rules engine (Drools or custom), document generation (Docmosis / iText), payment via GoCardless or Stripe, claims managed via Guidewire or custom.

Key Test Areas

AreaWhat to testRisk
QuotePersonal details, risk questions, coverage options, premium calculation, compareWrong premium, regulatory breach
PolicyView policy, schedule, endorsements, renewal, cancelWrong cover shown, illegal cancellation
ClaimsFNOL, document upload, adjuster assignment, status, settlementClaim lost, underpayment
DocumentsPolicy doc PDF, certificate of insurance, claim forms, download/emailWrong policy wording, missing cert
PaymentsPremium payment, DD setup, instalment plan, late payment handlingDouble charge, lapsed policy
CompliancePolicy wording accuracy, cooling-off period, right to cancel, data deletionRegulatory fine, legal liability

Test Pyramid for Insurance Portal

Unit (60%):        Underwriting rules, premium calculation, eligibility
Integration (30%): Claims API, payment gateway, document generation
E2E (10%):         Get quote → purchase policy → file claim

Common Failure Scenarios

1. Quote price changes between quote step 1 and summary page (rule evaluated twice)
   Test: capture price on step 1, proceed to summary without changing inputs, assert prices match

2. Claim submitted but document not attached (upload appeared to succeed)
   Test: intercept upload API response, assert claim record contains document reference ID

3. Cooling-off period not applied to add-on purchase
   Test: purchase add-on, assert cancellation option with full refund available for statutory period

4. Policy renewal email contains previous year's premium
   Test: update premium before renewal run, assert renewal email reflects new premium amount

5. DD payment date update doesn't apply to next payment (only future ones)
   Test: update DD date after billing cycle opens, assert next payment still uses updated date

Playwright Patterns for Insurance Portal

// Multi-step quote wizard with form state preserved on back navigation
test('quote state is preserved when navigating back', async ({ page }) => {
  await page.goto('/quote/start');
  // Step 1 — personal details
  await page.getByLabel('Date of birth').fill('01/01/1985');
  await page.getByLabel('Postcode').fill('SW1A 1AA');
  await page.getByRole('button', { name: 'Next' }).click();
  // Step 2 — risk questions
  await page.getByLabel('No claims bonus (years)').selectOption('5');
  await page.getByRole('button', { name: 'Next' }).click();
  // Step 3 — navigate back
  await page.getByRole('button', { name: 'Back' }).click();
  await expect(page.getByLabel('No claims bonus (years)')).toHaveValue('5');
  await page.getByRole('button', { name: 'Back' }).click();
  await expect(page.getByLabel('Date of birth')).toHaveValue('01/01/1985');
  await expect(page.getByLabel('Postcode')).toHaveValue('SW1A 1AA');
});

// Document upload with page.route to simulate virus scan delay
test('claim upload shows pending state during virus scan', async ({ page }) => {
  await page.route('**/api/claims/documents/upload', async route => {
    // Simulate a 3-second virus scan delay
    await new Promise(res => setTimeout(res, 3000));
    await route.fulfill({ json: { status: 'clean', documentId: 'doc-123' } });
  });
  await page.goto('/claims/new');
  const fileInput = page.locator('input[type="file"]');
  await fileInput.setInputFiles({ name: 'damage.jpg', mimeType: 'image/jpeg', buffer: Buffer.from('fake') });
  await expect(page.getByTestId('upload-scanning')).toBeVisible();
  await expect(page.getByTestId('upload-complete')).toBeVisible({ timeout: 10000 });
});

// Cooling-off period date calculation
test('cooling-off period cancellation available within 14 days', async ({ page }) => {
  await page.clock.install({ time: new Date('2026-06-01T10:00:00Z') });
  await page.goto('/policy/POL-001');
  // Within 14-day cooling-off period — cancel with full refund must be available
  await expect(page.getByRole('button', { name: 'Cancel policy' })).toBeVisible();
  await expect(page.getByText('Full refund')).toBeVisible();

  // Advance past 14 days
  await page.clock.fastForward(15 * 24 * 60 * 60 * 1000);
  await page.reload();
  // Cooling-off cancel option should be gone; standard cancel may remain
  await expect(page.getByText('Full refund')).not.toBeVisible();
});