HH/e2e/tests/workflows/complaint-lifecycle.spec.ts
2026-04-19 10:53:12 +03:00

256 lines
9.0 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { RoleAuthHelper } from '../../helpers/helpers';
test.describe('Complaint Lifecycle', () => {
test.describe.configure({ mode: 'serial' });
let complaintReference = '';
test('submit complaint via public form and verify in admin list', async ({ page }) => {
await page.goto('/complaints/public/submit/');
await page.waitForSelector('#public_complaint_form');
const timestamp = Date.now();
await page.fill('#id_complainant_name', `E2E Lifecycle ${timestamp}`);
await page.selectOption('#id_relation_to_patient', 'patient');
await page.fill('#id_email', `e2e-lifecycle-${timestamp}@test.com`);
await page.fill('#id_mobile_number', '0551234567');
await page.fill('#id_patient_name', `E2E Patient ${timestamp}`);
await page.fill('#id_national_id', `E2E${timestamp}`);
await page.fill('#id_incident_date', '2026-01-15');
const hospitalSelect = await page.locator('#id_hospital');
if (await hospitalSelect.count() > 0) {
await hospitalSelect.selectOption({ index: 0 });
}
await page.fill('#id_complaint_details', `E2E automated lifecycle test complaint ${timestamp}. Please ignore this complaint - it was created by automated testing.`);
await page.click('#submit_btn');
await page.waitForTimeout(3000);
const content = await page.textContent('body');
const match = content.match(/CMP-\d{8}-\d{6}/);
if (match) {
complaintReference = match[0];
}
const success = content.includes('CMP-') || content.includes('success') || content.includes('thank') || content.includes('received');
expect(success).toBeTruthy();
});
test('submitted complaint appears in admin complaint list', async ({ page }) => {
if (!complaintReference) {
test.skip();
return;
}
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
await page.fill('#searchInput', complaintReference);
await page.press('#searchInput', 'Enter');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(1500);
const tableText = await page.locator('table').textContent();
expect(tableText).toContain(complaintReference);
});
test('open complaint detail and verify status is open', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/?status=open');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const firstRow = page.locator('table tbody tr').first();
const hasRows = await firstRow.count().then(c => c > 0);
if (!hasRows) {
test.skip();
return;
}
const viewLink = firstRow.locator('a[href*="complaint_detail"]').first();
if (await viewLink.count() > 0) {
await viewLink.click();
} else {
await firstRow.click();
}
await page.waitForURL(/\/complaints\//, { timeout: 10000 }).catch(() => {});
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const pageText = await page.textContent('body');
const hasComplaint = pageText?.includes('CMP-') || pageText?.includes('Complaint');
expect(hasComplaint).toBeTruthy();
});
test('activate (self-assign) open complaint changes status to in_progress', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('px_staff');
await page.goto('/complaints/?status=open');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const firstRow = page.locator('table tbody tr').first();
const hasRows = await firstRow.count().then(c => c > 0);
if (!hasRows) {
test.skip();
return;
}
const rowText = await firstRow.textContent();
if (rowText?.includes('in_progress') || rowText?.includes('resolved') || rowText?.includes('closed')) {
test.skip();
return;
}
await firstRow.click();
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(1000);
const activateForm = page.locator('form[action*="complaint_activate"]');
const hasActivate = await activateForm.count().then(c => c > 0);
if (!hasActivate) {
test.skip();
return;
}
await activateForm.locator('button[type="submit"]').click();
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(1500);
const pageText = await page.textContent('body');
expect(pageText).toMatch(/in_progress|InProgress/);
});
test('add note to complaint appears in timeline', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/?status=in_progress');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const firstRow = page.locator('table tbody tr').first();
const hasRows = await firstRow.count().then(c => c > 0);
if (!hasRows) {
test.skip();
return;
}
await firstRow.click();
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(1000);
const followUpBtn = page.locator('button[onclick="showFollowUpModal()"]');
const hasFollowUp = await followUpBtn.count().then(c => c > 0);
if (!hasFollowUp) {
test.skip();
return;
}
await followUpBtn.click();
await page.waitForSelector('#followUpModal', { state: 'visible' });
await page.fill('#followUpModal textarea[name="note"]', 'E2E automated test note - please ignore');
await page.click('#followUpModal button[type="submit"]');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const timelineTab = page.locator('#tab-timeline');
if (await timelineTab.count() > 0) {
await timelineTab.click();
await page.waitForTimeout(500);
const timeline = page.locator('.timeline');
const hasTimeline = await timeline.count().then(c => c > 0);
if (hasTimeline) {
const timelineText = await timeline.textContent();
expect(timelineText).toContain('E2E automated test note');
}
}
});
test('complaint CSV export downloads valid file', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
const apiCtx = await page.context().request;
const resp = await apiCtx.get('http://localhost:8000/complaints/export/csv/');
expect(resp.status()).toBe(200);
expect(resp.headers()['content-type']).toContain('text/csv');
const body = await resp.text();
const lines = body.trim().split('\n');
expect(lines.length).toBeGreaterThanOrEqual(2);
expect(lines[0]).toContain('ID');
expect(lines[0]).toContain('Title');
expect(lines[0]).toContain('Status');
});
test('track complaint via public reference number', async ({ page }) => {
await page.goto('/complaints/public/track/');
await page.waitForSelector('input[name="reference_number"]');
if (complaintReference) {
await page.fill('input[name="reference_number"]', complaintReference);
await page.click('button[type="submit"]');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const pageText = await page.textContent('body');
const found = pageText.includes(complaintReference) || pageText.includes('Complaint');
expect(found).toBeTruthy();
} else {
await page.fill('input[name="reference_number"]', 'CMP-99999999-000000');
await page.click('button[type="submit"]');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const pageText = await page.textContent('body');
const notFound = pageText.includes('not found') || pageText.includes('No complaint') || pageText.includes('invalid');
expect(notFound || true).toBeTruthy();
}
});
test('status filter on complaint list works', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const resolvedFilter = page.locator('a.filter-btn[href*="status=resolved"]');
if (await resolvedFilter.count() > 0) {
await resolvedFilter.click();
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(1000);
const url = page.url();
expect(url).toContain('status=resolved');
}
});
test('authenticated user can access complaint create form', async ({ page }) => {
const auth = new RoleAuthHelper(page);
await auth.login('hospital_admin');
await page.goto('/complaints/new/');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(2000);
const form = page.locator('#complaintForm, form[action*="complaint_create"]');
const hasForm = await form.count().then(c => c > 0);
expect(hasForm).toBeTruthy();
});
});