import automator from 'miniprogram-automator'; const WS = 'ws://127.0.0.1:9420'; const TIMEOUT = 30000; function withTimeout(promise, ms, label) { return Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timeout after ${ms}ms`)), ms) ) ]); } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } async function retry(fn, retries = 3, delay = 2000) { for (let i = 0; i < retries; i++) { try { return await fn(); } catch (e) { console.log(` [retry ${i + 1}/${retries}] ${e.message}`); if (i < retries - 1) await sleep(delay); else throw e; } } } async function main() { console.log('=== 小程序验证 V2 ===\n'); // Step 1: Connect console.log('[1/5] 连接开发者工具...'); let mp; try { mp = await withTimeout( automator.connect({ wsEndpoint: WS }), TIMEOUT, 'connect' ); console.log(' [OK] 已连接\n'); } catch (e) { console.error(' [FAIL] 连接失败:', e.message); process.exit(1); } // Step 2: Get current page console.log('[2/5] 获取当前页面...'); let currentPage; try { currentPage = await retry(async () => { const page = await withTimeout(mp.currentPage(), TIMEOUT, 'currentPage'); return page; }); console.log(' [OK] 当前页面:', currentPage.path, '\n'); } catch (e) { console.log(' [WARN] 无法获取页面:', e.message, '\n'); } // Step 3: Take screenshot console.log('[3/5] 截图...'); try { const screenshot = await withTimeout(mp.screenshot(), TIMEOUT, 'screenshot'); const fs = await import('fs'); const outPath = 'g:/hms/apps/miniprogram/verify-screenshot.png'; if (Buffer.isBuffer(screenshot)) { fs.writeFileSync(outPath, screenshot); } else { fs.writeFileSync(outPath, Buffer.from(screenshot, 'base64')); } console.log(' [OK] 截图已保存: verify-screenshot.png\n'); } catch (e) { console.log(' [WARN] 截图失败:', e.message, '\n'); } // Step 4: Navigate pages console.log('[4/5] 导航测试...'); const testPages = [ { path: '/pages/index/index', name: '首页', method: 'switchTab' }, { path: '/pages/health/input/index', name: '健康数据录入', method: 'reLaunch' }, { path: '/pages/health/trend/index', name: '健康趋势', method: 'reLaunch' }, { path: '/pages/appointment/index', name: '预约列表', method: 'switchTab' }, { path: '/pages/appointment/create/index', name: '创建预约', method: 'reLaunch' }, { path: '/pages/article/index', name: '资讯文章', method: 'switchTab' }, { path: '/pages/profile/index', name: '个人中心', method: 'switchTab' }, { path: '/pages/profile/family/index', name: '就诊人管理', method: 'reLaunch' }, { path: '/pages/profile/reports/index', name: '我的报告', method: 'reLaunch' }, { path: '/pages/login/index', name: '登录页', method: 'reLaunch' }, ]; for (const p of testPages) { try { await withTimeout(mp.reLaunch(p.path), TIMEOUT, `reLaunch ${p.path}`); await sleep(3000); const page = await withTimeout(mp.currentPage(), TIMEOUT, 'currentPage'); console.log(` [OK] ${p.name}: ${page.path}`); // Take screenshot of each page try { const ss = await withTimeout(mp.screenshot(), TIMEOUT, 'screenshot'); const fs = await import('fs'); const safeName = p.path.replace(/\//g, '_').replace(/^_/, ''); const ssPath = `g:/hms/apps/miniprogram/screenshots/${safeName}.png`; const dir = 'g:/hms/apps/miniprogram/screenshots'; if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); if (Buffer.isBuffer(ss)) { fs.writeFileSync(ssPath, ss); } else { fs.writeFileSync(ssPath, Buffer.from(ss, 'base64')); } console.log(` 截图: screenshots/${safeName}.png`); } catch (e) { console.log(` 截图失败: ${e.message}`); } } catch (e) { console.log(` [FAIL] ${p.name}: ${e.message}`); } } // Step 5: System info console.log('\n[5/5] 系统信息...'); try { const systemInfo = await withTimeout(mp.evaluate(() => { try { return wx.getSystemInfoSync(); } catch { return null; } }), TIMEOUT, 'evaluate'); if (systemInfo) { console.log(' [OK]', JSON.stringify({ model: systemInfo.model, system: systemInfo.system, SDKVersion: systemInfo.SDKVersion, screenWidth: systemInfo.screenWidth, screenHeight: systemInfo.screenHeight, }, null, 2)); } } catch (e) { console.log(' [WARN]', e.message); } await mp.close(); console.log('\n=== 验证完成 ==='); } main().catch(err => { console.error('Fatal:', err.message); process.exit(1); });