376 lines
14 KiB
JavaScript
376 lines
14 KiB
JavaScript
const automator = require('miniprogram-automator');
|
|
const { execSync } = require('child_process');
|
|
const TIMEOUT = 12000;
|
|
|
|
function withTimeout(promise, ms, label) {
|
|
return Promise.race([promise, new Promise((_, r) => setTimeout(() => r(new Error(label + ' timeout')), ms))]);
|
|
}
|
|
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
|
|
async function checkElements(page, selectors) {
|
|
const results = {};
|
|
for (const [name, selector] of Object.entries(selectors)) {
|
|
try {
|
|
const el = await page.$(selector);
|
|
if (el) {
|
|
const text = (await el.textContent()).trim().replace(/\s+/g, ' ');
|
|
results[name] = { found: true, text };
|
|
} else {
|
|
results[name] = { found: false, text: '' };
|
|
}
|
|
} catch (e) {
|
|
results[name] = { found: false, text: '', error: e.message };
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
async function checkMultiElements(page, selector) {
|
|
try {
|
|
const els = await page.$$(selector);
|
|
const items = [];
|
|
for (let i = 0; i < els.length; i++) {
|
|
try {
|
|
const text = (await els[i].textContent()).trim().replace(/\s+/g, ' ');
|
|
items.push(text);
|
|
} catch {}
|
|
}
|
|
return items;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('=== HMS 深度验证 (元素级) ===\n');
|
|
|
|
// Restart automation
|
|
console.log('启动自动化...');
|
|
try {
|
|
execSync('"D:/微信web开发者工具/cli.bat" auto --project "g:/hms/apps/miniprogram" --auto-port 9420', { stdio: 'pipe', timeout: 15000 });
|
|
} catch {}
|
|
|
|
// Retry connection
|
|
let mp;
|
|
for (let attempt = 1; attempt <= 5; attempt++) {
|
|
await sleep(5000);
|
|
try {
|
|
mp = await withTimeout(automator.connect({ wsEndpoint: 'ws://127.0.0.1:9420' }), TIMEOUT, 'connect');
|
|
console.log('[OK] 已连接 (第' + attempt + '次尝试)\n');
|
|
break;
|
|
} catch (e) {
|
|
console.log('连接失败 (第' + attempt + '次)...');
|
|
if (attempt === 5) throw e;
|
|
}
|
|
}
|
|
|
|
const issues = [];
|
|
|
|
// ═══ 1. 首页 ═══
|
|
console.log('━━━ 1. 首页 ━━━');
|
|
try {
|
|
await mp.switchTab('/pages/index/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
// 等待页面渲染
|
|
try { await page.waitFor('.index-page', { timeout: 5000 }); } catch {}
|
|
|
|
const els = await checkElements(page, {
|
|
greeting: '.greeting-hello',
|
|
gName: '.greeting-name',
|
|
gDate: '.greeting-date',
|
|
healthTitle: '.section-title',
|
|
healthItem: '.health-item',
|
|
serviceItem: '.service-item',
|
|
});
|
|
|
|
console.log(' 问候语:', els.greeting.found ? '✓ ' + els.greeting.text : '✗ 缺失');
|
|
console.log(' 用户名:', els.gName.found ? '✓ ' + els.gName.text : '✗ 缺失');
|
|
console.log(' 日期:', els.gDate.found ? '✓ ' + els.gDate.text : '✗ 缺失');
|
|
|
|
const healthItems = await checkMultiElements(page, '.health-item');
|
|
console.log(' 健康卡片:', healthItems.length > 0 ? '✓ ' + healthItems.length + '个' : '✗ 缺失');
|
|
if (healthItems.length > 0) {
|
|
healthItems.forEach((h, i) => console.log(' [' + i + ']', h));
|
|
}
|
|
|
|
const services = await checkMultiElements(page, '.service-item');
|
|
console.log(' 快捷服务:', services.length > 0 ? '✓ ' + services.length + '个' : '✗ 缺失');
|
|
if (services.length > 0) {
|
|
services.forEach((s, i) => console.log(' [' + i + ']', s));
|
|
}
|
|
|
|
if (!els.greeting.found) issues.push('首页: 缺少问候语');
|
|
if (healthItems.length === 0) issues.push('首页: 缺少健康数据卡片');
|
|
if (services.length === 0) issues.push('首页: 缺少快捷服务');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('首页: ' + e.message);
|
|
}
|
|
|
|
// ═══ 2. 健康中心 ═══
|
|
console.log('\n━━━ 2. 健康中心 ━━━');
|
|
try {
|
|
await mp.switchTab('/pages/health/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
try { await page.waitFor('.health-page', { timeout: 5000 }); } catch {}
|
|
|
|
const els = await checkElements(page, {
|
|
header: '.health-header-title',
|
|
inputBtn: '.health-header-btn-text',
|
|
card: '.health-card',
|
|
action: '.action-card',
|
|
});
|
|
|
|
console.log(' 标题:', els.header.found ? '✓ ' + els.header.text : '✗');
|
|
|
|
const cards = await checkMultiElements(page, '.health-card');
|
|
console.log(' 健康卡片:', cards.length > 0 ? '✓ ' + cards.length + '个' : '✗');
|
|
|
|
const actions = await checkMultiElements(page, '.action-card');
|
|
console.log(' 趋势入口:', actions.length > 0 ? '✓ ' + actions.length + '个' : '✗');
|
|
if (actions.length > 0) actions.forEach((a, i) => console.log(' [' + i + ']', a));
|
|
|
|
if (cards.length === 0) issues.push('健康中心: 缺少健康数据卡片');
|
|
if (actions.length === 0) issues.push('健康中心: 缺少趋势快捷入口');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('健康中心: ' + e.message);
|
|
}
|
|
|
|
// ═══ 3. 健康数据录入 ═══
|
|
console.log('\n━━━ 3. 健康数据录入 ━━━');
|
|
try {
|
|
await mp.reLaunch('/pages/health/input/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const els = await checkElements(page, {
|
|
label: '.input-label',
|
|
picker: '.input-picker',
|
|
field: '.input-field',
|
|
submit: '.input-submit',
|
|
});
|
|
|
|
console.log(' 表单标签:', els.label.found ? '✓' : '✗');
|
|
console.log(' 指标选择器:', els.picker.found ? '✓ ' + els.picker.text : '✗');
|
|
console.log(' 数值输入:', els.field.found ? '✓' : '✗');
|
|
console.log(' 提交按钮:', els.submit.found ? '✓ ' + els.submit.text : '✗');
|
|
|
|
if (!els.picker.found) issues.push('健康录入: 缺少指标选择器');
|
|
if (!els.field.found) issues.push('健康录入: 缺少数值输入框');
|
|
if (!els.submit.found) issues.push('健康录入: 缺少提交按钮');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('健康录入: ' + e.message);
|
|
}
|
|
|
|
// ═══ 4. 创建预约 ═══
|
|
console.log('\n━━━ 4. 创建预约 ━━━');
|
|
try {
|
|
await mp.reLaunch('/pages/appointment/create/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const stepEl = await checkElements(page, {
|
|
stepTitle: '.step-title',
|
|
deptCard: '.dept-card',
|
|
nextBtn: '.btn-next',
|
|
});
|
|
|
|
console.log(' 步骤标题:', stepEl.stepTitle.found ? '✓ ' + stepEl.stepTitle.text : '✗');
|
|
|
|
const deptCards = await checkMultiElements(page, '.dept-card');
|
|
console.log(' 科室卡片:', deptCards.length > 0 ? '✓ ' + deptCards.length + '个' : '✗');
|
|
if (deptCards.length > 0) {
|
|
deptCards.forEach((d, i) => console.log(' [' + i + ']', d));
|
|
}
|
|
|
|
console.log(' "下一步"按钮:', stepEl.nextBtn.found ? '✓' : '✗');
|
|
|
|
if (deptCards.length === 0) issues.push('创建预约: 缺少科室选择卡片');
|
|
if (!stepEl.nextBtn.found) issues.push('创建预约: 缺少下一步按钮');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('创建预约: ' + e.message);
|
|
}
|
|
|
|
// ═══ 5. 预约列表 ═══
|
|
console.log('\n━━━ 5. 预约列表 ━━━');
|
|
try {
|
|
await mp.switchTab('/pages/appointment/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const els = await checkElements(page, {
|
|
title: '.page-title',
|
|
fab: '.fab-btn',
|
|
empty: '.empty-text',
|
|
list: '.appointment-list',
|
|
});
|
|
|
|
console.log(' 标题:', els.title.found ? '✓ ' + els.title.text : '✗');
|
|
console.log(' 新建预约按钮:', els.fab.found ? '✓' : '✗');
|
|
console.log(' 空状态:', els.empty.found ? '✓ ' + els.empty.text : ' (有数据)');
|
|
|
|
const cards = await checkMultiElements(page, '.appointment-card');
|
|
console.log(' 预约卡片:', cards.length + '个');
|
|
|
|
if (!els.fab.found) issues.push('预约列表: 缺少新建预约按钮');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('预约列表: ' + e.message);
|
|
}
|
|
|
|
// ═══ 6. 资讯文章 ═══
|
|
console.log('\n━━━ 6. 资讯文章 ━━━');
|
|
try {
|
|
await mp.switchTab('/pages/article/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const articles = await checkMultiElements(page, '.article-card');
|
|
const empty = await checkElements(page, { e: '.empty-text' });
|
|
console.log(' 文章卡片:', articles.length + '个');
|
|
console.log(' 空状态:', empty.e.found ? '✓ ' + empty.e.text : '');
|
|
if (articles.length > 0) {
|
|
articles.forEach((a, i) => console.log(' [' + i + ']', a.substring(0, 50)));
|
|
}
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('资讯文章: ' + e.message);
|
|
}
|
|
|
|
// ═══ 7. 个人中心 ═══
|
|
console.log('\n━━━ 7. 个人中心 ━━━');
|
|
try {
|
|
await mp.switchTab('/pages/profile/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const els = await checkElements(page, {
|
|
avatar: '.profile-avatar-text',
|
|
name: '.profile-name',
|
|
menu: '.menu-item',
|
|
logout: '.logout-text',
|
|
});
|
|
|
|
console.log(' 头像:', els.avatar.found ? '✓ ' + els.avatar.text : '✗');
|
|
console.log(' 用户名:', els.name.found ? '✓ ' + els.name.text : '✗');
|
|
|
|
const menuItems = await checkMultiElements(page, '.menu-item');
|
|
console.log(' 菜单项:', menuItems.length > 0 ? '✓ ' + menuItems.length + '个' : '✗');
|
|
if (menuItems.length > 0) {
|
|
menuItems.forEach((m, i) => console.log(' [' + i + ']', m));
|
|
}
|
|
|
|
console.log(' 退出登录:', els.logout.found ? '✓ ' + els.logout.text : '✗');
|
|
|
|
if (menuItems.length === 0) issues.push('个人中心: 缺少菜单项');
|
|
if (!els.logout.found) issues.push('个人中心: 缺少退出登录按钮');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('个人中心: ' + e.message);
|
|
}
|
|
|
|
// ═══ 8. 就诊人管理 ═══
|
|
console.log('\n━━━ 8. 就诊人管理 ━━━');
|
|
try {
|
|
await mp.reLaunch('/pages/profile/family/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const addBtn = await checkElements(page, { btn: '.family-add-text' });
|
|
console.log(' "添加就诊人":', addBtn.btn.found ? '✓' : '✗');
|
|
|
|
const patients = await checkMultiElements(page, '.family-item');
|
|
console.log(' 就诊人列表:', patients.length + '个');
|
|
|
|
if (!addBtn.btn.found) issues.push('就诊人管理: 缺少添加按钮');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('就诊人管理: ' + e.message);
|
|
}
|
|
|
|
// ═══ 9. 登录页 ═══
|
|
console.log('\n━━━ 9. 登录页 ━━━');
|
|
try {
|
|
await mp.reLaunch('/pages/login/index');
|
|
await sleep(3000);
|
|
const page = await mp.currentPage();
|
|
console.log(' 路径:', page.path);
|
|
|
|
const els = await checkElements(page, {
|
|
logo: '.login-logo-text',
|
|
title: '.login-title',
|
|
subtitle: '.login-subtitle',
|
|
btn: '.login-btn',
|
|
agreement: '.agreement-text',
|
|
checkbox: '.checkbox',
|
|
userLink: '.agreement-link',
|
|
});
|
|
|
|
console.log(' Logo:', els.logo.found ? '✓ ' + els.logo.text : '✗');
|
|
console.log(' 标题:', els.title.found ? '✓ ' + els.title.text : '✗');
|
|
console.log(' 副标题:', els.subtitle.found ? '✓ ' + els.subtitle.text : '✗');
|
|
console.log(' 登录按钮:', els.btn.found ? '✓' : '✗');
|
|
console.log(' 协议勾选:', els.checkbox.found ? '✓' : '✗');
|
|
console.log(' 协议文案:', els.agreement.found ? '✓ ' + els.agreement.text.substring(0, 30) : '✗');
|
|
|
|
const links = await checkMultiElements(page, '.agreement-link');
|
|
console.log(' 协议链接:', links.length > 0 ? '✓ ' + links.length + '个' : '✗');
|
|
|
|
if (!els.btn.found) issues.push('登录页: 缺少登录按钮');
|
|
if (!els.checkbox.found) issues.push('登录页: 缺少协议勾选框');
|
|
if (links.length < 2) issues.push('登录页: 缺少用户协议/隐私政策链接');
|
|
} catch (e) {
|
|
console.log(' [FAIL]', e.message);
|
|
issues.push('登录页: ' + e.message);
|
|
}
|
|
|
|
// ═══ 10. 法律页面 ═══
|
|
console.log('\n━━━ 10. 用户协议 & 隐私政策 ━━━');
|
|
for (const [name, path] of [['用户协议', '/pages/legal/user-agreement'], ['隐私政策', '/pages/legal/privacy-policy']]) {
|
|
try {
|
|
await mp.reLaunch(path);
|
|
await sleep(2000);
|
|
const page = await mp.currentPage();
|
|
const views = await checkMultiElements(page, 'view');
|
|
const hasContent = views.some(v => v.length > 10);
|
|
console.log(' ' + name + ':', hasContent ? '✓ 有内容' : '✗ 内容为空');
|
|
if (!hasContent) issues.push(name + ': 内容为空');
|
|
} catch (e) {
|
|
console.log(' ' + name + ': [FAIL]', e.message);
|
|
issues.push(name + ': ' + e.message);
|
|
}
|
|
}
|
|
|
|
// ═══ 汇总 ═══
|
|
console.log('\n╔══════════════════════════════════════╗');
|
|
if (issues.length === 0) {
|
|
console.log('║ ✓ 全部页面内容验证通过,无问题发现 ║');
|
|
} else {
|
|
console.log('║ 发现 ' + issues.length + ' 个问题: ║');
|
|
issues.forEach((issue, i) => {
|
|
console.log('║ ' + (i + 1) + '. ' + issue.substring(0, 30).padEnd(32) + '║');
|
|
});
|
|
}
|
|
console.log('╚══════════════════════════════════════╝');
|
|
|
|
// 不用 mp.close() 避免关闭自动化
|
|
mp.disconnect();
|
|
}
|
|
|
|
main().catch(err => { console.error('Fatal:', err.message); process.exit(1); });
|