test(web+mp): E2E 测试全量实施 — Web 5 flow + MP 4 flow + 基础设施
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

Web 端 (Playwright):
- fixtures: test-data 工厂 + API Client (乐观锁 version) + 增强 auth fixture
- pages: LoginPage, PatientListPage, PatientDetailPage, HealthDataPage, AppointmentPage
- flows: 患者全流程, 体征数据链路, 预约排班链路, 随访管理链路, 告警处理链路
- smoke tests 迁移到 smoke/ 目录,import 路径更新
- playwright.config.ts 更新: globalSetup 环境检查, 60s timeout, video retain

小程序端 (Vitest + miniprogram-automator):
- helpers: AutomatorClient, MpApiClient, MpAuthHelper, MpNavigator
- flows: 患者健康数据查看, 体征数据录入, 积分签到兑换, 积分商城浏览
- vitest.config.ts + check-readiness.ts
- vitest 4.1.5 依赖安装

Playwright 发现 15 个测试 (5 flow + 10 smoke),全部就绪
This commit is contained in:
iven
2026-04-29 04:58:01 +08:00
parent 2f4be6dcd0
commit c6e8048bc5
32 changed files with 1798 additions and 5 deletions

View File

@@ -0,0 +1,190 @@
// apps/web/e2e/fixtures/test-data.ts
export interface PatientData {
name: string;
gender?: string;
birth_date?: string;
blood_type?: string;
id_number?: string;
allergy_history?: string;
medical_history_summary?: string;
emergency_contact_name?: string;
emergency_contact_phone?: string;
source?: string;
notes?: string;
phone?: string;
}
export interface DoctorData {
name: string;
department?: string;
title?: string;
specialty?: string;
phone?: string;
license_number?: string;
status?: string;
}
export interface VitalSignsData {
systolic_bp?: number;
diastolic_bp?: number;
heart_rate?: number;
temperature?: number;
spo2?: number;
blood_glucose_fasting?: number;
blood_glucose_postprandial?: number;
weight?: number;
height?: number;
recorded_at?: string;
source?: string;
notes?: string;
}
export interface ScheduleData {
doctor_id: string;
date: string;
start_time: string;
end_time: string;
max_appointments?: number;
}
export interface AppointmentData {
patient_id: string;
doctor_id: string;
schedule_id: string;
appointment_date: string;
start_time: string;
end_time: string;
reason?: string;
}
export interface FollowUpTemplateData {
name: string;
description?: string;
frequency_days: number;
total_rounds: number;
questions?: string;
}
export interface FollowUpTaskData {
patient_id: string;
template_id: string;
assigned_to?: string;
due_date: string;
}
export interface AlertRuleData {
name: string;
indicator: string;
condition: string;
threshold: number;
severity: string;
description?: string;
}
let counter = 0;
function uid(): string {
counter += 1;
return `${Date.now()}_${counter}_${Math.random().toString(36).slice(2, 6)}`;
}
export function makePatient(overrides?: Partial<PatientData>): PatientData {
const id = uid();
return {
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,
};
}
export function makeDoctor(overrides?: Partial<DoctorData>): DoctorData {
const id = uid();
return {
name: `E2E医生_${id}`,
department: '内科',
title: '主治医师',
specialty: '全科',
license_number: `DOC${id}`,
...overrides,
};
}
export function makeVitalSigns(overrides?: Partial<VitalSignsData>): VitalSignsData {
return {
systolic_bp: 120,
diastolic_bp: 80,
heart_rate: 72,
temperature: 36.5,
spo2: 98,
source: 'web_e2e',
...overrides,
};
}
export function makeSchedule(doctorId: string, overrides?: Partial<ScheduleData>): ScheduleData {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const date = tomorrow.toISOString().slice(0, 10);
return {
doctor_id: doctorId,
date,
start_time: '09:00',
end_time: '12:00',
max_appointments: 10,
...overrides,
};
}
export function makeAppointment(patientId: string, doctorId: string, scheduleId: string, overrides?: Partial<AppointmentData>): AppointmentData {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const date = tomorrow.toISOString().slice(0, 10);
return {
patient_id: patientId,
doctor_id: doctorId,
schedule_id: scheduleId,
appointment_date: date,
start_time: '09:00',
end_time: '10:00',
reason: 'E2E测试预约',
...overrides,
};
}
export function makeFollowUpTemplate(overrides?: Partial<FollowUpTemplateData>): FollowUpTemplateData {
return {
name: `E2E随访模板_${uid()}`,
description: 'E2E自动创建的随访模板',
frequency_days: 7,
total_rounds: 3,
questions: JSON.stringify([{ question: '血压是否正常?', type: 'yes_no' }]),
...overrides,
};
}
export function makeFollowUpTask(patientId: string, templateId: string, overrides?: Partial<FollowUpTaskData>): FollowUpTaskData {
const dueDate = new Date();
dueDate.setDate(dueDate.getDate() + 7);
return {
patient_id: patientId,
template_id: templateId,
due_date: dueDate.toISOString().slice(0, 10),
...overrides,
};
}
export function makeAlertRule(overrides?: Partial<AlertRuleData>): AlertRuleData {
return {
name: `E2E告警规则_${uid()}`,
indicator: 'heart_rate',
condition: 'greater_than',
threshold: 50,
severity: 'warning',
description: 'E2E测试低阈值规则用于触发告警',
...overrides,
};
}