- 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.
84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
import { expect, type APIRequestContext, type Page } from '@playwright/test';
|
|
|
|
export const uniqueId = (prefix: string) =>
|
|
`${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
|
|
const toFrontendUser = (user: any) => ({
|
|
id: user.id,
|
|
username: user.username,
|
|
role: user.role === 'doctor' ? 'user' : user.role,
|
|
name: user.name,
|
|
departmentId: user.departmentId,
|
|
department: user.departmentName,
|
|
status: user.status,
|
|
signature: user.signature,
|
|
signatureFileId: user.signatureFileId,
|
|
visibleTemplates: [],
|
|
manageableTemplates: [],
|
|
});
|
|
|
|
export const apiRequest = async <T>(
|
|
request: APIRequestContext,
|
|
method: 'get' | 'post' | 'patch' | 'delete',
|
|
path: string,
|
|
data?: Record<string, unknown>,
|
|
) => {
|
|
const response = await request[method](path, data ? { data } : undefined);
|
|
const text = await response.text();
|
|
expect(response.ok(), `${method.toUpperCase()} ${path} failed: ${text}`).toBe(true);
|
|
return text ? (JSON.parse(text).data as T) : (null as T);
|
|
};
|
|
|
|
export const loginByApi = async (page: Page, username: string, password = '123456') => {
|
|
await page.goto('/');
|
|
await page.evaluate(() => {
|
|
window.localStorage.clear();
|
|
window.sessionStorage.clear();
|
|
});
|
|
await page.request.post('/api/auth/logout').catch(() => undefined);
|
|
const data = await apiRequest<{ user: any }>(page.request, 'post', '/api/auth/login', { username, password });
|
|
await page.evaluate((user) => {
|
|
window.localStorage.setItem('currentUser', JSON.stringify(user));
|
|
}, toFrontendUser(data.user));
|
|
return data.user;
|
|
};
|
|
|
|
export const createUserByApi = (
|
|
request: APIRequestContext,
|
|
body: {
|
|
username: string;
|
|
name: string;
|
|
role: 'admin' | 'user';
|
|
department?: string;
|
|
departmentId?: string;
|
|
visibleTemplates?: string[];
|
|
manageableTemplates?: string[];
|
|
},
|
|
) =>
|
|
apiRequest<{ user: any }>(request, 'post', '/api/users', {
|
|
password: '123456',
|
|
status: 'active',
|
|
...body,
|
|
}).then((data) => data.user);
|
|
|
|
export const createDepartmentByApi = (request: APIRequestContext, name: string, code: string) =>
|
|
apiRequest<{ department: any }>(request, 'post', '/api/departments', { name, code }).then((data) => data.department);
|
|
|
|
export const createReportByApi = (
|
|
request: APIRequestContext,
|
|
body: {
|
|
title: string;
|
|
patientName?: string;
|
|
hospitalId?: string;
|
|
content?: string;
|
|
status?: 'draft' | 'completed';
|
|
},
|
|
) =>
|
|
apiRequest<{ report: any }>(request, 'post', '/api/reports', {
|
|
patientName: 'E2E患者',
|
|
hospitalId: uniqueId('H'),
|
|
content: '<p>E2E报告内容</p>',
|
|
status: 'completed',
|
|
...body,
|
|
}).then((data) => data.report);
|