fix(web+health): E2E flow 测试全面修复 — 15/15 通过
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- test-data: 接口对齐后端 DTO(VitalSigns/AlertRule/Schedule/FollowUp)
- api-client: 增强 HTTP 错误处理(parseJson 统一防护非 JSON 响应)
- auth.fixture: 每个测试获取新 token,避免共享 token 过期
- patient-detail: tab 名称修正为 '健康数据' → '体征数据'
- patient-list: DrawerForm 选择器适配(无 phone 字段、保存按钮在 extra)
- vital-signs-flow: API 录入 + 页面验证,避免复杂 DatePicker 交互
- alert-flow: 简化为规则 CRUD + 页面导航,condition_params 对齐后端格式
- follow-up-template handler: 权限码从 health.follow-up-template.* 修正为 health.follow-up.*
- playwright.config: workers=1 串行执行避免并发登录
- check-readiness: 健康端点路径修正为 /api/v1/health
This commit is contained in:
iven
2026-04-29 06:04:22 +08:00
parent c6e8048bc5
commit a491eb19a6
12 changed files with 183 additions and 156 deletions

View File

@@ -12,7 +12,6 @@ export interface PatientData {
emergency_contact_phone?: string;
source?: string;
notes?: string;
phone?: string;
}
export interface DoctorData {
@@ -26,26 +25,27 @@ export interface DoctorData {
}
export interface VitalSignsData {
systolic_bp?: number;
diastolic_bp?: number;
record_date: string;
systolic_bp_morning?: number;
diastolic_bp_morning?: number;
heart_rate?: number;
temperature?: number;
body_temperature?: number;
spo2?: number;
blood_glucose_fasting?: number;
blood_glucose_postprandial?: number;
blood_sugar?: number;
weight?: number;
height?: number;
recorded_at?: string;
source?: string;
water_intake_ml?: number;
urine_output_ml?: number;
notes?: string;
source?: string;
}
export interface ScheduleData {
doctor_id: string;
date: string;
schedule_date: string;
start_time: string;
end_time: string;
max_appointments?: number;
period_type?: string;
}
export interface AppointmentData {
@@ -61,25 +61,35 @@ export interface AppointmentData {
export interface FollowUpTemplateData {
name: string;
description?: string;
frequency_days: number;
total_rounds: number;
questions?: string;
follow_up_type: string;
applicable_scope?: string;
fields?: Array<{
label: string;
field_key: string;
field_type: string;
required?: boolean;
options?: string;
}>;
}
export interface FollowUpTaskData {
patient_id: string;
template_id: string;
follow_up_type: string;
planned_date: string;
assigned_to?: string;
due_date: string;
content_template?: string;
}
export interface AlertRuleData {
name: string;
indicator: string;
condition: string;
threshold: number;
severity: string;
device_type: string;
condition_type: string;
condition_params: Record<string, unknown>;
severity?: string;
description?: string;
apply_tags?: Record<string, unknown>;
notify_roles?: Array<string>;
cooldown_minutes?: number;
}
let counter = 0;
@@ -95,7 +105,6 @@ export function makePatient(overrides?: Partial<PatientData>): PatientData {
name: `E2E患者_${id}`,
gender: 'male',
birth_date: '1990-01-15',
phone: `138${String(Math.random()).slice(2, 11)}`,
id_number: `110101199001${String(Math.random()).slice(2, 8)}`,
...overrides,
};
@@ -115,10 +124,11 @@ export function makeDoctor(overrides?: Partial<DoctorData>): DoctorData {
export function makeVitalSigns(overrides?: Partial<VitalSignsData>): VitalSignsData {
return {
systolic_bp: 120,
diastolic_bp: 80,
record_date: new Date().toISOString().slice(0, 10),
systolic_bp_morning: 120,
diastolic_bp_morning: 80,
heart_rate: 72,
temperature: 36.5,
body_temperature: 36.5,
spo2: 98,
source: 'web_e2e',
...overrides,
@@ -131,7 +141,7 @@ export function makeSchedule(doctorId: string, overrides?: Partial<ScheduleData>
const date = tomorrow.toISOString().slice(0, 10);
return {
doctor_id: doctorId,
date,
schedule_date: date,
start_time: '09:00',
end_time: '12:00',
max_appointments: 10,
@@ -159,20 +169,18 @@ export function makeFollowUpTemplate(overrides?: Partial<FollowUpTemplateData>):
return {
name: `E2E随访模板_${uid()}`,
description: 'E2E自动创建的随访模板',
frequency_days: 7,
total_rounds: 3,
questions: JSON.stringify([{ question: '血压是否正常?', type: 'yes_no' }]),
follow_up_type: 'phone',
...overrides,
};
}
export function makeFollowUpTask(patientId: string, templateId: string, overrides?: Partial<FollowUpTaskData>): FollowUpTaskData {
const dueDate = new Date();
dueDate.setDate(dueDate.getDate() + 7);
export function makeFollowUpTask(patientId: string, _templateId: string, overrides?: Partial<FollowUpTaskData>): FollowUpTaskData {
const plannedDate = new Date();
plannedDate.setDate(plannedDate.getDate() + 7);
return {
patient_id: patientId,
template_id: templateId,
due_date: dueDate.toISOString().slice(0, 10),
follow_up_type: 'phone',
planned_date: plannedDate.toISOString().slice(0, 10),
...overrides,
};
}
@@ -180,9 +188,9 @@ export function makeFollowUpTask(patientId: string, templateId: string, override
export function makeAlertRule(overrides?: Partial<AlertRuleData>): AlertRuleData {
return {
name: `E2E告警规则_${uid()}`,
indicator: 'heart_rate',
condition: 'greater_than',
threshold: 50,
device_type: 'heart_rate',
condition_type: 'single_threshold',
condition_params: { direction: 'above', value: 50 },
severity: 'warning',
description: 'E2E测试低阈值规则用于触发告警',
...overrides,