healthapp
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1229 lines
44 KiB

/**
* 健康档案完整功能测试脚本
* 测试内容:
* 1. 基础信息 - 所有9个字段的编辑和保存
* 2. 生活习惯 - 所有10个字段的编辑和保存
* 3. 病史记录 - 添加新记录(5个字段)
* 4. 家族病史 - 添加新记录(3个字段)
* 5. 过敏记录 - 添加新记录(4个字段)
* 6. 验证保存后数据是否正确显示
*/
const { chromium } = require('playwright');
const APP_URL = 'http://localhost:8081';
const TEST_PHONE = '13800138000';
const TEST_CODE = '123456';
// 测试数据
const TEST_DATA = {
basicInfo: {
name: '测试用户' + Date.now().toString().slice(-4),
gender: 'female', // 改为女
birth_date: '1990-05-15',
height: '175',
weight: '68',
blood_type: 'A',
occupation: '软件工程师',
marital_status: 'married', // 改为已婚
region: '北京市海淀区',
},
lifestyle: {
sleep_time: '23:00',
wake_time: '07:30',
sleep_quality: 'good', // 良好
meal_regularity: 'regular', // 规律
diet_preference: '清淡饮食',
daily_water_ml: '2000',
exercise_frequency: 'often', // 经常
exercise_type: '跑步、游泳',
is_smoker: false,
alcohol_frequency: 'sometimes', // 偶尔
},
medicalHistory: {
disease_name: '高血压',
disease_type: 'chronic', // 慢性病
diagnosed_date: '2020-03',
status: 'controlled', // 已控制
notes: '定期服药控制中',
},
familyHistory: {
relation: 'father', // 父亲
disease_name: '糖尿病',
notes: '2型糖尿病',
},
allergyRecord: {
allergy_type: 'drug', // 药物
allergen: '青霉素',
severity: 'severe', // 重度
reaction_desc: '全身过敏反应,需立即就医',
}
};
// 测试结果统计
const testResults = {
passed: 0,
failed: 0,
tests: []
};
function logTest(name, passed, detail = '') {
const status = passed ? '✓' : '✗';
const msg = `${status} ${name}${detail ? ': ' + detail : ''}`;
console.log(msg);
testResults.tests.push({ name, passed, detail });
if (passed) testResults.passed++;
else testResults.failed++;
}
// 使用坐标点击元素
async function clickByText(page, text) {
const pos = await page.evaluate((searchText) => {
const allElements = document.querySelectorAll('*');
for (const el of allElements) {
if (el.textContent?.trim() === searchText && el.children.length === 0) {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
}
}
}
return null;
}, text);
if (pos) {
await page.mouse.click(pos.x, pos.y);
return true;
}
return false;
}
// 等待并检查提示消息
async function waitForToast(page, text, timeout = 3000) {
try {
const toast = page.locator(`text=${text}`).first();
return await toast.isVisible({ timeout });
} catch {
return false;
}
}
// 关闭所有弹窗
async function closeAllModals(page) {
for (let i = 0; i < 5; i++) {
const cancelClicked = await clickByText(page, '取消');
if (cancelClicked) {
await page.waitForTimeout(500);
continue;
}
const closeBtn = page.locator('[aria-label="Close"]').first();
if (await closeBtn.isVisible({ timeout: 300 }).catch(() => false)) {
await closeBtn.click({ force: true });
await page.waitForTimeout(500);
continue;
}
break;
}
}
// 填写 TextInput 输入框
async function fillInput(page, label, value) {
// 方式1: 通过 label 属性查找
let input = page.locator(`input[label="${label}"]`).first();
if (await input.isVisible({ timeout: 500 }).catch(() => false)) {
await input.clear();
await input.fill(value);
return true;
}
// 方式2: 通过 placeholder 查找
input = page.locator(`input[placeholder*="${label}"]`).first();
if (await input.isVisible({ timeout: 500 }).catch(() => false)) {
await input.clear();
await input.fill(value);
return true;
}
// 方式3: 通过文本标签位置查找附近的输入框
const inputPos = await page.evaluate((labelText) => {
const allElements = document.querySelectorAll('*');
for (const el of allElements) {
const text = el.textContent?.trim();
if (text && (text === labelText || text.startsWith(labelText))) {
const rect = el.getBoundingClientRect();
// 找到标签后,查找附近的输入框
const inputs = document.querySelectorAll('input:not([type="checkbox"]):not([role="switch"])');
let closestInput = null;
let minDistance = Infinity;
for (const inp of inputs) {
const inpRect = inp.getBoundingClientRect();
// 输入框在标签下方或右侧
const distance = Math.abs(inpRect.y - rect.y) + Math.abs(inpRect.x - rect.x);
if (distance < minDistance && inpRect.y >= rect.y - 50) {
minDistance = distance;
closestInput = inp;
}
}
if (closestInput) {
return { found: true };
}
}
}
return { found: false };
}, label);
return false;
}
// 点击 SegmentedButtons 中的选项
async function clickSegmentOption(page, optionText) {
const clicked = await page.evaluate((text) => {
const buttons = document.querySelectorAll('*');
for (const btn of buttons) {
if (btn.textContent?.trim() === text && btn.children.length === 0) {
const rect = btn.getBoundingClientRect();
if (rect.width > 20 && rect.height > 15) {
btn.click();
return true;
}
}
}
return false;
}, optionText);
if (!clicked) {
// 备用: 使用 Playwright 定位器
const btn = page.locator(`text=${optionText}`).first();
if (await btn.isVisible({ timeout: 500 }).catch(() => false)) {
await btn.click({ force: true });
return true;
}
}
return clicked;
}
async function login(page) {
console.log('\n【准备工作】登录账号...');
const loginBtn = page.locator('text=登录').first();
if (!(await loginBtn.isVisible({ timeout: 2000 }).catch(() => false))) {
logTest('已登录状态', true, '跳过登录流程');
return true;
}
await page.locator('input').first().fill(TEST_PHONE);
await page.waitForTimeout(300);
const getCodeBtn = page.locator('text=获取验证码').first();
if (await getCodeBtn.isVisible()) {
await getCodeBtn.click();
await page.waitForTimeout(1000);
}
await page.locator('input').nth(1).fill(TEST_CODE);
await page.waitForTimeout(300);
await loginBtn.click();
await page.waitForTimeout(3000);
const homeVisible = await page.locator('text=/.*好,.*$/').first().isVisible({ timeout: 5000 }).catch(() => false);
logTest('登录', homeVisible);
return homeVisible;
}
async function navigateToHealthProfile(page) {
console.log('\n【步骤1】导航到健康档案页面...');
// 点击"我的" Tab
const tabPos = await page.evaluate(() => {
const allElements = document.querySelectorAll('*');
for (const el of allElements) {
if (el.textContent?.trim() === '我的' && el.children.length === 0) {
const rect = el.getBoundingClientRect();
if (rect.y > 500) {
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
}
}
}
return null;
});
if (tabPos) {
await page.mouse.click(tabPos.x, tabPos.y);
await page.waitForTimeout(1500);
}
// 滚动到顶部
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(500);
// 点击健康档案菜单
const healthMenuPos = await page.evaluate(() => {
const allElements = document.querySelectorAll('*');
for (const el of allElements) {
const text = el.textContent;
if (text?.includes('健康档案') &&
text?.includes('查看和管理') &&
!text?.includes('用药/治疗记录')) {
const rect = el.getBoundingClientRect();
if (rect.width > 100 && rect.height > 30 && rect.height < 100) {
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
}
}
}
return null;
});
if (healthMenuPos) {
await page.mouse.click(healthMenuPos.x, healthMenuPos.y);
await page.waitForTimeout(2000);
} else {
// 备用方法
const healthItem = page.locator('text=健康档案').first();
if (await healthItem.isVisible()) {
await healthItem.click({ force: true });
await page.waitForTimeout(2000);
}
}
// 验证进入健康档案页面
const basicInfo = await page.locator('text=基础信息').first().isVisible({ timeout: 3000 }).catch(() => false);
const backBtn = await page.locator('text=← 返回').first().isVisible({ timeout: 2000 }).catch(() => false);
const success = basicInfo && backBtn;
logTest('导航到健康档案页面', success);
await page.screenshot({ path: 'tests/screenshots/hp-initial.png' });
return success;
}
async function testBasicInfoEdit(page) {
console.log('\n【步骤2】测试基础信息编辑(9个字段)...');
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(500);
// 点击基础信息的编辑按钮
const editPos = await page.evaluate(() => {
const basicTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '基础信息'
);
if (basicTitle) {
const rect = basicTitle.getBoundingClientRect();
return { x: window.innerWidth - 50, y: rect.y };
}
return null;
});
if (!editPos) {
logTest('打开基础信息编辑弹窗', false, '未找到编辑按钮');
return false;
}
await page.mouse.click(editPos.x, editPos.y);
await page.waitForTimeout(1000);
const modalOpened = await page.locator('text=编辑基础信息').first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('打开基础信息编辑弹窗', modalOpened);
if (!modalOpened) return false;
await page.screenshot({ path: 'tests/screenshots/hp-basic-edit-before.png' });
// 获取所有输入框
const inputs = page.locator('input:not([type="checkbox"]):not([role="switch"])');
const inputCount = await inputs.count();
console.log(` 找到 ${inputCount} 个输入框`);
// 测试1: 填写姓名
let nameInput = inputs.first();
if (await nameInput.isVisible()) {
await nameInput.clear();
await nameInput.fill(TEST_DATA.basicInfo.name);
console.log(` 填写姓名: ${TEST_DATA.basicInfo.name}`);
logTest('基础信息-姓名输入', true);
} else {
logTest('基础信息-姓名输入', false, '未找到输入框');
}
// 测试2: 选择性别 - 女
const genderClicked = await clickSegmentOption(page, '女');
logTest('基础信息-性别选择', genderClicked);
await page.waitForTimeout(300);
// 测试3: 填写出生日期 - 找到第二个输入框
if (inputCount >= 2) {
const birthInput = inputs.nth(1);
if (await birthInput.isVisible()) {
await birthInput.clear();
await birthInput.fill(TEST_DATA.basicInfo.birth_date);
console.log(` 填写出生日期: ${TEST_DATA.basicInfo.birth_date}`);
logTest('基础信息-出生日期输入', true);
}
}
// 测试4 & 5: 填写身高和体重 - 找到第三、四个输入框
if (inputCount >= 4) {
const heightInput = inputs.nth(2);
const weightInput = inputs.nth(3);
if (await heightInput.isVisible()) {
await heightInput.clear();
await heightInput.fill(TEST_DATA.basicInfo.height);
console.log(` 填写身高: ${TEST_DATA.basicInfo.height}cm`);
logTest('基础信息-身高输入', true);
}
if (await weightInput.isVisible()) {
await weightInput.clear();
await weightInput.fill(TEST_DATA.basicInfo.weight);
console.log(` 填写体重: ${TEST_DATA.basicInfo.weight}kg`);
logTest('基础信息-体重输入', true);
}
}
// 测试6: 填写血型
if (inputCount >= 5) {
const bloodInput = inputs.nth(4);
if (await bloodInput.isVisible()) {
await bloodInput.clear();
await bloodInput.fill(TEST_DATA.basicInfo.blood_type);
console.log(` 填写血型: ${TEST_DATA.basicInfo.blood_type}`);
logTest('基础信息-血型输入', true);
}
}
// 测试7: 填写职业
if (inputCount >= 6) {
const occupationInput = inputs.nth(5);
if (await occupationInput.isVisible()) {
await occupationInput.clear();
await occupationInput.fill(TEST_DATA.basicInfo.occupation);
console.log(` 填写职业: ${TEST_DATA.basicInfo.occupation}`);
logTest('基础信息-职业输入', true);
}
}
// 测试8: 选择婚姻状况 - 已婚
const maritalClicked = await clickSegmentOption(page, '已婚');
logTest('基础信息-婚姻状况选择', maritalClicked);
await page.waitForTimeout(300);
// 测试9: 填写地区
if (inputCount >= 7) {
const regionInput = inputs.nth(6);
if (await regionInput.isVisible()) {
await regionInput.clear();
await regionInput.fill(TEST_DATA.basicInfo.region);
console.log(` 填写地区: ${TEST_DATA.basicInfo.region}`);
logTest('基础信息-地区输入', true);
}
}
await page.screenshot({ path: 'tests/screenshots/hp-basic-edit-after.png' });
// 点击保存按钮
const saveClicked = await clickByText(page, '保存');
if (!saveClicked) {
logTest('基础信息-点击保存', false, '未找到保存按钮');
await closeAllModals(page);
return false;
}
await page.waitForTimeout(2500);
// 验证保存结果
const saveSuccess = await waitForToast(page, '保存成功', 3000);
const modalClosed = !(await page.locator('text=编辑基础信息').first().isVisible({ timeout: 500 }).catch(() => false));
logTest('基础信息-保存成功', saveSuccess || modalClosed, saveSuccess ? '显示保存成功提示' : (modalClosed ? '弹窗已关闭' : ''));
// 等待数据刷新
await page.waitForTimeout(1500);
// 验证保存后的数据是否正确显示
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(500);
const nameDisplayed = await page.locator(`text=${TEST_DATA.basicInfo.name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('基础信息-保存后姓名显示', nameDisplayed);
const regionDisplayed = await page.locator(`text=${TEST_DATA.basicInfo.region}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('基础信息-保存后地区显示', regionDisplayed);
const occupationDisplayed = await page.locator(`text=${TEST_DATA.basicInfo.occupation}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('基础信息-保存后职业显示', occupationDisplayed);
await page.screenshot({ path: 'tests/screenshots/hp-basic-saved.png' });
return saveSuccess || modalClosed;
}
async function testLifestyleEdit(page) {
console.log('\n【步骤3】测试生活习惯编辑(10个字段)...');
await page.evaluate(() => window.scrollTo(0, 300));
await page.waitForTimeout(500);
// 点击生活习惯的编辑按钮
const editPos = await page.evaluate(() => {
const title = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '生活习惯'
);
if (title) {
const rect = title.getBoundingClientRect();
return { x: window.innerWidth - 50, y: rect.y };
}
return null;
});
if (!editPos) {
logTest('打开生活习惯编辑弹窗', false, '未找到编辑按钮');
return false;
}
await page.mouse.click(editPos.x, editPos.y);
await page.waitForTimeout(1000);
const modalOpened = await page.locator('text=编辑生活习惯').first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('打开生活习惯编辑弹窗', modalOpened);
if (!modalOpened) return false;
await page.screenshot({ path: 'tests/screenshots/hp-lifestyle-edit-before.png' });
// 获取所有输入框
const inputs = page.locator('input:not([type="checkbox"]):not([role="switch"])');
const inputCount = await inputs.count();
console.log(` 找到 ${inputCount} 个输入框`);
// 测试1 & 2: 填写入睡时间和起床时间
if (inputCount >= 2) {
const sleepInput = inputs.first();
const wakeInput = inputs.nth(1);
if (await sleepInput.isVisible()) {
await sleepInput.clear();
await sleepInput.fill(TEST_DATA.lifestyle.sleep_time);
console.log(` 填写入睡时间: ${TEST_DATA.lifestyle.sleep_time}`);
logTest('生活习惯-入睡时间输入', true);
}
if (await wakeInput.isVisible()) {
await wakeInput.clear();
await wakeInput.fill(TEST_DATA.lifestyle.wake_time);
console.log(` 填写起床时间: ${TEST_DATA.lifestyle.wake_time}`);
logTest('生活习惯-起床时间输入', true);
}
}
// 测试3: 选择睡眠质量 - 良好
const sleepQualityClicked = await clickSegmentOption(page, '良好');
logTest('生活习惯-睡眠质量选择', sleepQualityClicked);
await page.waitForTimeout(300);
// 测试4: 选择三餐规律 - 规律
const mealClicked = await clickSegmentOption(page, '规律');
logTest('生活习惯-三餐规律选择', mealClicked);
await page.waitForTimeout(300);
// 测试5: 填写饮食偏好
if (inputCount >= 3) {
const dietInput = inputs.nth(2);
if (await dietInput.isVisible()) {
await dietInput.clear();
await dietInput.fill(TEST_DATA.lifestyle.diet_preference);
console.log(` 填写饮食偏好: ${TEST_DATA.lifestyle.diet_preference}`);
logTest('生活习惯-饮食偏好输入', true);
}
}
// 测试6: 填写日饮水量
if (inputCount >= 4) {
const waterInput = inputs.nth(3);
if (await waterInput.isVisible()) {
await waterInput.clear();
await waterInput.fill(TEST_DATA.lifestyle.daily_water_ml);
console.log(` 填写日饮水量: ${TEST_DATA.lifestyle.daily_water_ml}ml`);
logTest('生活习惯-日饮水量输入', true);
}
}
// 测试7: 选择运动频率 - 经常
const exerciseClicked = await clickSegmentOption(page, '经常');
logTest('生活习惯-运动频率选择', exerciseClicked);
await page.waitForTimeout(300);
// 测试8: 填写运动类型
if (inputCount >= 5) {
const exerciseTypeInput = inputs.nth(4);
if (await exerciseTypeInput.isVisible()) {
await exerciseTypeInput.clear();
await exerciseTypeInput.fill(TEST_DATA.lifestyle.exercise_type);
console.log(` 填写运动类型: ${TEST_DATA.lifestyle.exercise_type}`);
logTest('生活习惯-运动类型输入', true);
}
}
// 测试9: 选择是否吸烟 - 否
const smokerClicked = await clickSegmentOption(page, '否');
logTest('生活习惯-吸烟选择', smokerClicked);
await page.waitForTimeout(300);
// 测试10: 选择饮酒频率 - 偶尔
const alcoholClicked = await clickSegmentOption(page, '偶尔');
logTest('生活习惯-饮酒频率选择', alcoholClicked);
await page.waitForTimeout(300);
await page.screenshot({ path: 'tests/screenshots/hp-lifestyle-edit-after.png' });
// 点击保存按钮
const saveClicked = await clickByText(page, '保存');
if (!saveClicked) {
logTest('生活习惯-点击保存', false, '未找到保存按钮');
await closeAllModals(page);
return false;
}
await page.waitForTimeout(2500);
// 验证保存结果
const saveSuccess = await waitForToast(page, '保存成功', 3000);
const modalClosed = !(await page.locator('text=编辑生活习惯').first().isVisible({ timeout: 500 }).catch(() => false));
logTest('生活习惯-保存成功', saveSuccess || modalClosed, saveSuccess ? '显示保存成功提示' : (modalClosed ? '弹窗已关闭' : ''));
// 等待数据刷新
await page.waitForTimeout(1500);
// 验证保存后的数据是否正确显示
await page.evaluate(() => {
const lifestyleTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '生活习惯'
);
if (lifestyleTitle) {
lifestyleTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(500);
const sleepTimeDisplayed = await page.locator(`text=${TEST_DATA.lifestyle.sleep_time}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('生活习惯-保存后入睡时间显示', sleepTimeDisplayed);
const dietDisplayed = await page.locator(`text=${TEST_DATA.lifestyle.diet_preference}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('生活习惯-保存后饮食偏好显示', dietDisplayed);
await page.screenshot({ path: 'tests/screenshots/hp-lifestyle-saved.png' });
return saveSuccess || modalClosed;
}
async function testMedicalHistoryAdd(page) {
console.log('\n【步骤4】测试添加病史记录(5个字段)...');
// 滚动到病史记录卡片
await page.evaluate(() => {
const medicalTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '病史记录'
);
if (medicalTitle) {
medicalTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(800);
// 点击添加按钮
const addPos = await page.evaluate(() => {
const medicalTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '病史记录'
);
if (medicalTitle) {
const rect = medicalTitle.getBoundingClientRect();
return { x: window.innerWidth - 50, y: Math.min(rect.y, window.innerHeight - 100) };
}
return null;
});
if (!addPos) {
logTest('打开添加病史记录弹窗', false, '未找到添加按钮');
return false;
}
await page.mouse.click(addPos.x, addPos.y);
await page.waitForTimeout(1000);
const modalOpened = await page.locator('text=添加病史记录').first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('打开添加病史记录弹窗', modalOpened);
if (!modalOpened) return false;
await page.screenshot({ path: 'tests/screenshots/hp-medical-add-before.png' });
// 获取所有输入框
const inputs = page.locator('input:not([type="checkbox"]):not([role="switch"])');
// 测试1: 填写疾病名称
const diseaseInput = inputs.first();
if (await diseaseInput.isVisible()) {
await diseaseInput.click();
await page.waitForTimeout(200);
await diseaseInput.fill(TEST_DATA.medicalHistory.disease_name);
console.log(` 填写疾病名称: ${TEST_DATA.medicalHistory.disease_name}`);
logTest('病史记录-疾病名称输入', true);
}
// 测试2: 选择疾病类型 - 慢性病
const diseaseTypeClicked = await clickSegmentOption(page, '慢性病');
logTest('病史记录-疾病类型选择', diseaseTypeClicked);
await page.waitForTimeout(300);
// 测试3: 填写诊断日期
const dateInput = inputs.nth(1);
if (await dateInput.isVisible({ timeout: 500 }).catch(() => false)) {
await dateInput.click();
await page.waitForTimeout(200);
await dateInput.fill(TEST_DATA.medicalHistory.diagnosed_date);
console.log(` 填写诊断日期: ${TEST_DATA.medicalHistory.diagnosed_date}`);
logTest('病史记录-诊断日期输入', true);
}
// 测试4: 选择治疗状态 - 已控制
const statusClicked = await clickSegmentOption(page, '已控制');
logTest('病史记录-治疗状态选择', statusClicked);
await page.waitForTimeout(300);
// 测试5: 填写备注 (多行输入框)
const textAreas = page.locator('textarea');
const textAreaCount = await textAreas.count();
if (textAreaCount > 0) {
const notesInput = textAreas.first();
if (await notesInput.isVisible({ timeout: 500 }).catch(() => false)) {
await notesInput.click();
await page.waitForTimeout(200);
await notesInput.fill(TEST_DATA.medicalHistory.notes);
console.log(` 填写备注: ${TEST_DATA.medicalHistory.notes}`);
logTest('病史记录-备注输入', true);
}
}
await page.screenshot({ path: 'tests/screenshots/hp-medical-add-after.png' });
// 点击添加按钮
const addClicked = await clickByText(page, '添加');
if (!addClicked) {
logTest('病史记录-点击添加', false, '未找到添加按钮');
await closeAllModals(page);
return false;
}
await page.waitForTimeout(2500);
// 验证添加结果
const addSuccess = await waitForToast(page, '添加成功', 3000);
const modalClosed = !(await page.locator('text=添加病史记录').first().isVisible({ timeout: 500 }).catch(() => false));
logTest('病史记录-添加成功', addSuccess || modalClosed, addSuccess ? '显示添加成功提示' : (modalClosed ? '弹窗已关闭' : ''));
// 等待数据刷新
await page.waitForTimeout(1500);
// 验证添加后的数据是否正确显示
await page.evaluate(() => {
const medicalTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '病史记录'
);
if (medicalTitle) {
medicalTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(500);
const diseaseDisplayed = await page.locator(`text=${TEST_DATA.medicalHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('病史记录-添加后疾病名称显示', diseaseDisplayed);
await page.screenshot({ path: 'tests/screenshots/hp-medical-saved.png' });
return addSuccess || modalClosed;
}
async function testFamilyHistoryAdd(page) {
console.log('\n【步骤5】测试添加家族病史(3个字段)...');
// 滚动到家族病史卡片
await page.evaluate(() => {
const familyTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '家族病史'
);
if (familyTitle) {
familyTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(800);
// 点击添加按钮
const addPos = await page.evaluate(() => {
const familyTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '家族病史'
);
if (familyTitle) {
const rect = familyTitle.getBoundingClientRect();
return { x: window.innerWidth - 50, y: Math.min(rect.y, window.innerHeight - 100) };
}
return null;
});
if (!addPos) {
logTest('打开添加家族病史弹窗', false, '未找到添加按钮');
return false;
}
await page.mouse.click(addPos.x, addPos.y);
await page.waitForTimeout(1000);
const modalOpened = await page.locator('text=添加家族病史').first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('打开添加家族病史弹窗', modalOpened);
if (!modalOpened) return false;
await page.screenshot({ path: 'tests/screenshots/hp-family-add-before.png' });
// 测试1: 选择亲属关系 - 父亲
const relationClicked = await clickSegmentOption(page, '父亲');
logTest('家族病史-亲属关系选择', relationClicked);
await page.waitForTimeout(300);
// 测试2: 填写疾病名称
const inputs = page.locator('input:not([type="checkbox"]):not([role="switch"])');
const diseaseInput = inputs.first();
if (await diseaseInput.isVisible()) {
await diseaseInput.click();
await page.waitForTimeout(200);
await diseaseInput.fill(TEST_DATA.familyHistory.disease_name);
console.log(` 填写疾病名称: ${TEST_DATA.familyHistory.disease_name}`);
logTest('家族病史-疾病名称输入', true);
}
// 测试3: 填写备注
const textAreas = page.locator('textarea');
if (await textAreas.first().isVisible({ timeout: 500 }).catch(() => false)) {
await textAreas.first().click();
await page.waitForTimeout(200);
await textAreas.first().fill(TEST_DATA.familyHistory.notes);
console.log(` 填写备注: ${TEST_DATA.familyHistory.notes}`);
logTest('家族病史-备注输入', true);
}
await page.screenshot({ path: 'tests/screenshots/hp-family-add-after.png' });
// 点击添加按钮
const addClicked = await clickByText(page, '添加');
if (!addClicked) {
logTest('家族病史-点击添加', false, '未找到添加按钮');
await closeAllModals(page);
return false;
}
await page.waitForTimeout(2500);
// 验证添加结果
const addSuccess = await waitForToast(page, '添加成功', 3000);
const modalClosed = !(await page.locator('text=添加家族病史').first().isVisible({ timeout: 500 }).catch(() => false));
logTest('家族病史-添加成功', addSuccess || modalClosed, addSuccess ? '显示添加成功提示' : (modalClosed ? '弹窗已关闭' : ''));
// 等待数据刷新
await page.waitForTimeout(1500);
// 验证添加后的数据是否正确显示
const diseaseDisplayed = await page.locator(`text=${TEST_DATA.familyHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('家族病史-添加后疾病名称显示', diseaseDisplayed);
await page.screenshot({ path: 'tests/screenshots/hp-family-saved.png' });
return addSuccess || modalClosed;
}
async function testAllergyRecordAdd(page) {
console.log('\n【步骤6】测试添加过敏记录(4个字段)...');
// 滚动到过敏记录卡片
await page.evaluate(() => {
const allergyTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '过敏记录'
);
if (allergyTitle) {
allergyTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(800);
// 点击添加按钮
const addPos = await page.evaluate(() => {
const allergyTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '过敏记录'
);
if (allergyTitle) {
const rect = allergyTitle.getBoundingClientRect();
return { x: window.innerWidth - 50, y: Math.min(rect.y, window.innerHeight - 100) };
}
return null;
});
if (!addPos) {
logTest('打开添加过敏记录弹窗', false, '未找到添加按钮');
return false;
}
await page.mouse.click(addPos.x, addPos.y);
await page.waitForTimeout(1000);
const modalOpened = await page.locator('text=添加过敏记录').first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('打开添加过敏记录弹窗', modalOpened);
if (!modalOpened) return false;
await page.screenshot({ path: 'tests/screenshots/hp-allergy-add-before.png' });
// 测试1: 选择过敏类型 - 药物
const typeClicked = await clickSegmentOption(page, '药物');
logTest('过敏记录-过敏类型选择', typeClicked);
await page.waitForTimeout(300);
// 测试2: 填写过敏原
const inputs = page.locator('input:not([type="checkbox"]):not([role="switch"])');
const allergenInput = inputs.first();
if (await allergenInput.isVisible()) {
await allergenInput.click();
await page.waitForTimeout(200);
await allergenInput.fill(TEST_DATA.allergyRecord.allergen);
console.log(` 填写过敏原: ${TEST_DATA.allergyRecord.allergen}`);
logTest('过敏记录-过敏原输入', true);
}
// 测试3: 选择严重程度 - 重度
const severityClicked = await clickSegmentOption(page, '重度');
logTest('过敏记录-严重程度选择', severityClicked);
await page.waitForTimeout(300);
// 测试4: 填写过敏反应描述
const textAreas = page.locator('textarea');
if (await textAreas.first().isVisible({ timeout: 500 }).catch(() => false)) {
await textAreas.first().click();
await page.waitForTimeout(200);
await textAreas.first().fill(TEST_DATA.allergyRecord.reaction_desc);
console.log(` 填写过敏反应描述: ${TEST_DATA.allergyRecord.reaction_desc}`);
logTest('过敏记录-过敏反应描述输入', true);
}
await page.screenshot({ path: 'tests/screenshots/hp-allergy-add-after.png' });
// 点击添加按钮
const addClicked = await clickByText(page, '添加');
if (!addClicked) {
logTest('过敏记录-点击添加', false, '未找到添加按钮');
await closeAllModals(page);
return false;
}
await page.waitForTimeout(2500);
// 验证添加结果
const addSuccess = await waitForToast(page, '添加成功', 3000);
const modalClosed = !(await page.locator('text=添加过敏记录').first().isVisible({ timeout: 500 }).catch(() => false));
logTest('过敏记录-添加成功', addSuccess || modalClosed, addSuccess ? '显示添加成功提示' : (modalClosed ? '弹窗已关闭' : ''));
// 等待数据刷新
await page.waitForTimeout(1500);
// 验证添加后的数据是否正确显示
const allergenDisplayed = await page.locator(`text=${TEST_DATA.allergyRecord.allergen}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('过敏记录-添加后过敏原显示', allergenDisplayed);
await page.screenshot({ path: 'tests/screenshots/hp-allergy-saved.png' });
return addSuccess || modalClosed;
}
async function testMedicalHistoryDelete(page) {
console.log('\n【步骤7】测试病史记录删除功能...');
// 滚动到病史记录区域
await page.evaluate(() => {
const medicalTitle = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === '病史记录'
);
if (medicalTitle) {
medicalTitle.scrollIntoView({ behavior: 'instant', block: 'center' });
}
});
await page.waitForTimeout(800);
// 检查是否有病史记录可以删除
const diseaseVisible = await page.locator(`text=${TEST_DATA.medicalHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
if (!diseaseVisible) {
logTest('病史记录-删除测试', false, '未找到可删除的记录');
return false;
}
// 长按病史记录项
const diseasePos = await page.evaluate((diseaseName) => {
const diseaseEl = Array.from(document.querySelectorAll('*')).find(
el => el.textContent?.trim() === diseaseName && el.children.length === 0
);
if (diseaseEl) {
const rect = diseaseEl.getBoundingClientRect();
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
}
return null;
}, TEST_DATA.medicalHistory.disease_name);
if (!diseasePos) {
logTest('病史记录-找到删除目标', false, '未找到记录位置');
return false;
}
logTest('病史记录-找到删除目标', true);
// 长按触发删除
console.log(' 长按记录...');
await page.mouse.move(diseasePos.x, diseasePos.y);
await page.mouse.down();
await page.waitForTimeout(800); // 长按时间
await page.mouse.up();
await page.waitForTimeout(1000);
// 检查确认弹窗是否出现
const confirmVisible = await page.locator('text=确认删除').first().isVisible({ timeout: 3000 }).catch(() => false);
logTest('病史记录-删除确认弹窗显示', confirmVisible);
if (!confirmVisible) {
// 尝试点击方式触发 onLongPress
const touchEl = page.locator(`text=${TEST_DATA.medicalHistory.disease_name}`).first();
if (await touchEl.isVisible()) {
// 使用 JavaScript 触发 long press
await page.evaluate((diseaseName) => {
const el = Array.from(document.querySelectorAll('*')).find(
e => e.textContent?.trim() === diseaseName && e.children.length === 0
);
if (el) {
// 尝试找到父级 TouchableOpacity 并触发长按
let parent = el.parentElement;
for (let i = 0; i < 5 && parent; i++) {
if (parent.onclick || parent.onlongpress) {
const event = new Event('longpress', { bubbles: true });
parent.dispatchEvent(event);
break;
}
parent = parent.parentElement;
}
}
}, TEST_DATA.medicalHistory.disease_name);
await page.waitForTimeout(1000);
}
// 再次检查
const confirmAgain = await page.locator('text=确认删除').first().isVisible({ timeout: 2000 }).catch(() => false);
if (!confirmAgain) {
logTest('病史记录-删除功能', false, '无法触发长按删除');
await page.screenshot({ path: 'tests/screenshots/hp-medical-delete-fail.png' });
return false;
}
}
await page.screenshot({ path: 'tests/screenshots/hp-medical-delete-confirm.png' });
// 点击删除按钮
const deleteClicked = await clickByText(page, '删除');
if (!deleteClicked) {
logTest('病史记录-点击删除按钮', false, '未找到删除按钮');
await closeAllModals(page);
return false;
}
logTest('病史记录-点击删除按钮', true);
await page.waitForTimeout(2000);
// 验证删除结果
const deleteSuccess = await waitForToast(page, '删除成功', 3000);
logTest('病史记录-删除成功', deleteSuccess, deleteSuccess ? '显示删除成功提示' : '');
// 验证记录已从页面移除
await page.waitForTimeout(1000);
const diseaseStillVisible = await page.locator(`text=${TEST_DATA.medicalHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('病史记录-记录已删除', !diseaseStillVisible, diseaseStillVisible ? '记录仍然显示' : '');
await page.screenshot({ path: 'tests/screenshots/hp-medical-delete-done.png' });
return deleteSuccess || !diseaseStillVisible;
}
async function verifyAllSavedData(page) {
console.log('\n【步骤8】验证所有保存的数据...');
// 刷新页面获取最新数据
await page.reload();
await page.waitForTimeout(3000);
// 重新导航到健康档案
await navigateToHealthProfile(page);
// 验证基础信息
await page.evaluate(() => window.scrollTo(0, 0));
await page.waitForTimeout(500);
const nameVerified = await page.locator(`text=${TEST_DATA.basicInfo.name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-基础信息姓名', nameVerified);
const regionVerified = await page.locator(`text=${TEST_DATA.basicInfo.region}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-基础信息地区', regionVerified);
// 验证生活习惯
await page.evaluate(() => window.scrollTo(0, 300));
await page.waitForTimeout(500);
const sleepTimeVerified = await page.locator(`text=${TEST_DATA.lifestyle.sleep_time}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-生活习惯入睡时间', sleepTimeVerified);
// 验证病史记录
await page.evaluate(() => window.scrollTo(0, 600));
await page.waitForTimeout(500);
const diseaseVerified = await page.locator(`text=${TEST_DATA.medicalHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-病史记录疾病名称', diseaseVerified);
// 验证家族病史
const familyDiseaseVerified = await page.locator(`text=${TEST_DATA.familyHistory.disease_name}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-家族病史疾病名称', familyDiseaseVerified);
// 验证过敏记录
const allergenVerified = await page.locator(`text=${TEST_DATA.allergyRecord.allergen}`).first().isVisible({ timeout: 2000 }).catch(() => false);
logTest('验证-过敏记录过敏原', allergenVerified);
await page.screenshot({ path: 'tests/screenshots/hp-final-verification.png' });
return nameVerified || sleepTimeVerified;
}
async function runTests() {
console.log('═══════════════════════════════════════════════════════════');
console.log(' 健康档案完整功能自动化测试');
console.log('═══════════════════════════════════════════════════════════');
console.log('\n测试范围:');
console.log(' - 基础信息: 9个字段(姓名、性别、出生日期、身高、体重、血型、职业、婚姻、地区)');
console.log(' - 生活习惯: 10个字段(入睡时间、起床时间、睡眠质量、三餐规律、饮食偏好、');
console.log(' 日饮水量、运动频率、运动类型、吸烟、饮酒)');
console.log(' - 病史记录: 添加(5个字段)+ 长按删除');
console.log(' - 家族病史: 3个字段(亲属关系、疾病名称、备注)');
console.log(' - 过敏记录: 4个字段(过敏类型、过敏原、严重程度、过敏反应描述)');
console.log('');
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext({
viewport: { width: 1280, height: 800 }
});
const page = await context.newPage();
// 监听错误
page.on('console', msg => {
if (msg.type() === 'error') {
console.log(' [Console Error]', msg.text());
}
});
page.on('pageerror', error => {
console.log(' [Page Error]', error.message);
});
try {
console.log('\n打开应用...');
await page.goto(APP_URL);
await page.waitForTimeout(2000);
// 执行测试步骤
const loginOk = await login(page);
if (!loginOk) throw new Error('登录失败');
const navOk = await navigateToHealthProfile(page);
if (!navOk) throw new Error('导航失败');
await testBasicInfoEdit(page);
await testLifestyleEdit(page);
await testMedicalHistoryAdd(page);
await testFamilyHistoryAdd(page);
await testAllergyRecordAdd(page);
await testMedicalHistoryDelete(page);
await verifyAllSavedData(page);
} catch (error) {
console.error('\n测试中断:', error.message);
await page.screenshot({ path: 'tests/screenshots/hp-error.png' });
} finally {
// 打印测试摘要
console.log('\n═══════════════════════════════════════════════════════════');
console.log(' 测试结果摘要');
console.log('═══════════════════════════════════════════════════════════');
console.log(`通过: ${testResults.passed} 失败: ${testResults.failed}`);
console.log('───────────────────────────────────────────────────────────');
// 按类别分组显示
const categories = {
'导航': [],
'基础信息': [],
'生活习惯': [],
'病史记录': [],
'家族病史': [],
'过敏记录': [],
'验证': []
};
for (const test of testResults.tests) {
const icon = test.passed ? '✓' : '✗';
const line = `${icon} ${test.name}${test.detail ? ' - ' + test.detail : ''}`;
if (test.name.includes('导航') || test.name.includes('登录')) {
categories['导航'].push(line);
} else if (test.name.includes('基础信息')) {
categories['基础信息'].push(line);
} else if (test.name.includes('生活习惯')) {
categories['生活习惯'].push(line);
} else if (test.name.includes('病史记录')) {
categories['病史记录'].push(line);
} else if (test.name.includes('家族病史')) {
categories['家族病史'].push(line);
} else if (test.name.includes('过敏记录')) {
categories['过敏记录'].push(line);
} else if (test.name.includes('验证')) {
categories['验证'].push(line);
} else {
categories['导航'].push(line);
}
}
for (const [category, tests] of Object.entries(categories)) {
if (tests.length > 0) {
console.log(`\n${category}`);
for (const test of tests) {
console.log(` ${test}`);
}
}
}
console.log('\n═══════════════════════════════════════════════════════════');
await page.waitForTimeout(2000);
await browser.close();
// 返回退出码
process.exit(testResults.failed > 0 ? 1 : 0);
}
}
// 运行测试
runTests();