350 lines
13 KiB
TypeScript
350 lines
13 KiB
TypeScript
import { test, expect, Page } from '@playwright/test';
|
|
import { RoleAuthHelper } from '../../helpers/helpers';
|
|
import { ApiHelper, DEPT_ID, sessionPost } from '../../helpers/api-helper';
|
|
|
|
test.describe('Observation Scenario Tests', () => {
|
|
test.setTimeout(120000);
|
|
|
|
async function createObservationInternal(page: Page): Promise<string> {
|
|
await page.goto('/observations/create/');
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const titleInput = page.locator('input[name="title"]');
|
|
if (await titleInput.count() > 0) {
|
|
await titleInput.fill(`E2E Scenario Obs ${Date.now()}`);
|
|
await page.waitForTimeout(600);
|
|
}
|
|
await page.fill('textarea[name="description"]', `E2E scenario test observation ${Date.now()}. Please ignore.`);
|
|
await page.waitForTimeout(600);
|
|
|
|
const incidentDt = page.locator('input[name="incident_datetime"]');
|
|
if (await incidentDt.count() > 0) {
|
|
const now = new Date();
|
|
const pad = (n: number) => String(n).padStart(2, '0');
|
|
const dtStr = `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
|
|
await incidentDt.fill(dtStr);
|
|
await page.waitForTimeout(600);
|
|
}
|
|
|
|
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
await page.waitForTimeout(1200);
|
|
|
|
await page.evaluate(() => {
|
|
const form = document.getElementById('observationForm') as HTMLFormElement;
|
|
if (form) form.submit();
|
|
});
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(3000);
|
|
|
|
const url = page.url();
|
|
if (!url.match(/\/observations\/[0-9a-f-]+\/?$/)) {
|
|
const errors = await page.locator('.field-error').allTextContents();
|
|
const msgText = await page.locator('.bg-red-50').first().textContent().catch(() => '');
|
|
throw new Error(`Observation not created. URL: ${url}. Errors: ${errors.join(', ')}. Msg: ${msgText}`);
|
|
}
|
|
const match = url.match(/\/observations\/([0-9a-f-]+)\/?$/);
|
|
return match ? match[1] : '';
|
|
}
|
|
|
|
test.describe('Scenario A: Raised to Department', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let observationId: string;
|
|
|
|
test('create observation and send to department', async ({ page }) => {
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
observationId = await createObservationInternal(page);
|
|
expect(observationId).toBeTruthy();
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const triageResp = await sessionPost(page, `/observations/${observationId}/triage/`, {
|
|
status: 'triaged',
|
|
assigned_department: DEPT_ID,
|
|
note: 'E2E test - triaging and assigning department',
|
|
});
|
|
expect([200, 302].includes(triageResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const sendResp = await sessionPost(page, `/observations/${observationId}/send-to-department/`, {
|
|
department_id: DEPT_ID,
|
|
note_en: 'E2E test - forwarding to department for response',
|
|
recipient_type: 'staff',
|
|
});
|
|
expect([200, 302].includes(sendResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.reload();
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(1500);
|
|
});
|
|
|
|
test('submit department response, review, resolve and close', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const deptResp = await sessionPost(page, `/observations/${observationId}/department-response/`, {
|
|
response_en: 'E2E test - department has investigated the observation and provided response',
|
|
});
|
|
expect([200, 302].includes(deptResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const reviewResp = await sessionPost(page, `/observations/${observationId}/review-dept-response/`, {
|
|
acceptance_status: 'acceptable',
|
|
acceptance_notes: 'E2E test - department response is acceptable',
|
|
});
|
|
expect([200, 302].includes(reviewResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const resolveResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'resolved',
|
|
comment: 'E2E test - observation resolved after department response',
|
|
});
|
|
expect([200, 302].includes(resolveResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const closeResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'closed',
|
|
comment: 'E2E test - closing observation',
|
|
});
|
|
expect([200, 302].includes(closeResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.reload();
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(1500);
|
|
});
|
|
|
|
test('verify closed status in admin UI', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const pageText = await page.textContent('body');
|
|
expect(pageText).toMatch(/closed|Closed/i);
|
|
await page.waitForTimeout(800);
|
|
});
|
|
});
|
|
|
|
test.describe('Scenario B: Handled Immediately', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let observationId: string;
|
|
|
|
test('create observation, triage, resolve and close directly', async ({ page }) => {
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
observationId = await createObservationInternal(page);
|
|
expect(observationId).toBeTruthy();
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const triageResp = await sessionPost(page, `/observations/${observationId}/triage/`, {
|
|
status: 'assigned',
|
|
assigned_department: DEPT_ID,
|
|
note: 'E2E test - quick triage for immediate handling',
|
|
});
|
|
expect([200, 302].includes(triageResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const progressResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'in_progress',
|
|
comment: 'E2E test - investigating directly',
|
|
});
|
|
expect([200, 302].includes(progressResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const resolveResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'resolved',
|
|
comment: 'E2E test - resolved directly without department escalation',
|
|
});
|
|
expect([200, 302].includes(resolveResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const closeResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'closed',
|
|
comment: 'E2E test - closing',
|
|
});
|
|
expect([200, 302].includes(closeResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.reload();
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(1500);
|
|
});
|
|
|
|
test('verify closed status in admin UI', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const pageText = await page.textContent('body');
|
|
expect(pageText).toMatch(/closed|Closed/i);
|
|
await page.waitForTimeout(800);
|
|
});
|
|
});
|
|
|
|
test.describe('Scenario C: Let Expire (SLA Breach)', () => {
|
|
test.describe.configure({ mode: 'serial' });
|
|
|
|
let observationId: string;
|
|
|
|
test('create observation, triage and backdate SLA', async ({ page }) => {
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
observationId = await createObservationInternal(page);
|
|
expect(observationId).toBeTruthy();
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const triageResp = await sessionPost(page, `/observations/${observationId}/triage/`, {
|
|
status: 'assigned',
|
|
assigned_department: DEPT_ID,
|
|
note: 'E2E test - triaging for SLA expiry test',
|
|
});
|
|
expect([200, 302].includes(triageResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const progressResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'in_progress',
|
|
comment: 'E2E test - starting progress for SLA test',
|
|
});
|
|
expect([200, 302].includes(progressResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
try {
|
|
// @ts-expect-error - child_process types not available in test config
|
|
const { execSync } = await import('child_process');
|
|
execSync(
|
|
`/home/ismail/projects/HH/.venv/bin/python /home/ismail/projects/HH/manage.py backdate_due_at --model observation --id ${observationId} --days-ago 7`,
|
|
{ stdio: 'pipe', timeout: 15000 }
|
|
);
|
|
} catch {
|
|
}
|
|
});
|
|
|
|
test('verify overdue indicator in observation list', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto('/observations/');
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const tableText = await page.locator('table').textContent();
|
|
expect(tableText).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
});
|
|
|
|
test('verify overdue indicator in observation detail', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const pageText = await page.textContent('body');
|
|
expect(pageText).toContain('OBS-');
|
|
await page.waitForTimeout(800);
|
|
});
|
|
|
|
test('observation can still be resolved after SLA breach', async ({ page }) => {
|
|
if (!observationId) { test.skip(); return; }
|
|
|
|
const auth = new RoleAuthHelper(page);
|
|
await auth.login('hospital_admin');
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.goto(`/observations/${observationId}/`);
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(2500);
|
|
|
|
const resolveResp = await sessionPost(page, `/observations/${observationId}/status/`, {
|
|
status: 'resolved',
|
|
comment: 'E2E test - resolved after SLA breach',
|
|
});
|
|
expect([200, 302].includes(resolveResp.status())).toBeTruthy();
|
|
await page.waitForTimeout(800);
|
|
|
|
await page.reload();
|
|
await page.waitForLoadState('domcontentloaded');
|
|
await page.waitForTimeout(1500);
|
|
});
|
|
});
|
|
});
|