Add audit log UI and backend API seeded E2E
- Add Auth Context route role guards so doctors cannot directly enter template management, user management, or audit logs. - Add Audit Logs page, sidebar entry, frontend audit API client, and API client test. - Add backend audit log query endpoint with super/admin visibility rules and query filtering. - Extend PostgreSQL integration tests to cover audit log query permissions. - Move Playwright E2E away from localStorage seed data to real backend API login and seed helpers. - Add E2E coverage for route guards and audit log visibility. - Run Playwright backend on port 3100 and proxy Vite API requests there to avoid local port conflicts. - Make server:dev use the compiled NestJS server path, avoiding tsx parameter-property injection issues. - Update README, AGENTS, feature, testing, security, deployment, progress, API, backendization, and auth/user module docs.
This commit is contained in:
@@ -1,82 +1,60 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { baseTemplates, baseUsers, seedLocalStorage } from './helpers';
|
||||
|
||||
const reports = [
|
||||
{
|
||||
id: 'RPT_SURGERY_SELF',
|
||||
title: '外科本人报告',
|
||||
patientName: '患者甲',
|
||||
hospitalId: 'H001',
|
||||
department: '外科',
|
||||
content: '<p>外科本人报告</p>',
|
||||
author: '0001',
|
||||
authorName: '张医生',
|
||||
createdAt: '2026-05-01',
|
||||
status: 'completed',
|
||||
revision: 1,
|
||||
},
|
||||
{
|
||||
id: 'RPT_SURGERY_OTHER',
|
||||
title: '外科他人报告',
|
||||
patientName: '患者乙',
|
||||
hospitalId: 'H002',
|
||||
department: '外科',
|
||||
content: '<p>外科他人报告</p>',
|
||||
author: '0003',
|
||||
authorName: '王医生',
|
||||
createdAt: '2026-05-01',
|
||||
status: 'completed',
|
||||
revision: 1,
|
||||
},
|
||||
{
|
||||
id: 'RPT_INTERNAL',
|
||||
title: '内科报告',
|
||||
patientName: '患者丙',
|
||||
hospitalId: 'H003',
|
||||
department: '内科',
|
||||
content: '<p>内科报告</p>',
|
||||
author: '0002',
|
||||
authorName: '李医生',
|
||||
createdAt: '2026-05-01',
|
||||
status: 'completed',
|
||||
revision: 1,
|
||||
},
|
||||
];
|
||||
import {
|
||||
createDepartmentByApi,
|
||||
createReportByApi,
|
||||
createUserByApi,
|
||||
loginByApi,
|
||||
uniqueId,
|
||||
} from './helpers';
|
||||
|
||||
test('admin only sees department reports, doctor only sees own reports, super sees all', async ({ page }) => {
|
||||
await seedLocalStorage(page, {
|
||||
users: baseUsers,
|
||||
templates: baseTemplates,
|
||||
reports,
|
||||
currentUser: baseUsers[1],
|
||||
const suffix = uniqueId('perm');
|
||||
const ownTitle = `外科本人报告 ${suffix}`;
|
||||
const otherSurgeryTitle = `外科他人报告 ${suffix}`;
|
||||
const internalTitle = `内科报告 ${suffix}`;
|
||||
|
||||
await loginByApi(page, 'admin');
|
||||
const internalDepartment = await createDepartmentByApi(page.request, `内科E2E ${suffix}`, `internal_${suffix}`);
|
||||
const internalAdmin = await createUserByApi(page.request, {
|
||||
username: `internal_admin_${suffix}`,
|
||||
name: '内科E2E管理员',
|
||||
role: 'admin',
|
||||
departmentId: internalDepartment.id,
|
||||
});
|
||||
const otherSurgeryDoctor = await createUserByApi(page.request, {
|
||||
username: `surgery_doctor_${suffix}`,
|
||||
name: '外科E2E医生',
|
||||
role: 'user',
|
||||
department: '外科',
|
||||
});
|
||||
|
||||
await loginByApi(page, '0001');
|
||||
await createReportByApi(page.request, { title: ownTitle, content: `<p>${ownTitle}</p>` });
|
||||
|
||||
await loginByApi(page, otherSurgeryDoctor.username);
|
||||
await createReportByApi(page.request, { title: otherSurgeryTitle, content: `<p>${otherSurgeryTitle}</p>` });
|
||||
|
||||
await loginByApi(page, internalAdmin.username);
|
||||
await createReportByApi(page.request, { title: internalTitle, content: `<p>${internalTitle}</p>` });
|
||||
|
||||
await loginByApi(page, 'manager');
|
||||
await page.goto('/report-manage');
|
||||
|
||||
await expect(page.getByText('外科本人报告')).toBeVisible();
|
||||
await expect(page.getByText('外科他人报告')).toBeVisible();
|
||||
await expect(page.getByText('内科报告')).not.toBeVisible();
|
||||
await expect(page.getByText(ownTitle)).toBeVisible();
|
||||
await expect(page.getByText(otherSurgeryTitle)).toBeVisible();
|
||||
await expect(page.getByText(internalTitle)).not.toBeVisible();
|
||||
|
||||
await seedLocalStorage(page, {
|
||||
users: baseUsers,
|
||||
templates: baseTemplates,
|
||||
reports,
|
||||
currentUser: baseUsers[2],
|
||||
});
|
||||
await loginByApi(page, '0001');
|
||||
await page.goto('/report-manage');
|
||||
|
||||
await expect(page.getByText('外科本人报告')).toBeVisible();
|
||||
await expect(page.getByText('外科他人报告')).not.toBeVisible();
|
||||
await expect(page.getByText('内科报告')).not.toBeVisible();
|
||||
await expect(page.getByText(ownTitle)).toBeVisible();
|
||||
await expect(page.getByText(otherSurgeryTitle)).not.toBeVisible();
|
||||
await expect(page.getByText(internalTitle)).not.toBeVisible();
|
||||
|
||||
await seedLocalStorage(page, {
|
||||
users: baseUsers,
|
||||
templates: baseTemplates,
|
||||
reports,
|
||||
currentUser: baseUsers[0],
|
||||
});
|
||||
await loginByApi(page, 'admin');
|
||||
await page.goto('/report-manage');
|
||||
|
||||
await expect(page.getByText('外科本人报告')).toBeVisible();
|
||||
await expect(page.getByText('外科他人报告')).toBeVisible();
|
||||
await expect(page.getByText('内科报告')).toBeVisible();
|
||||
await expect(page.getByText(ownTitle)).toBeVisible();
|
||||
await expect(page.getByText(otherSurgeryTitle)).toBeVisible();
|
||||
await expect(page.getByText(internalTitle)).toBeVisible();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user