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),全部就绪
97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
// apps/miniprogram/e2e/helpers/automator-client.ts
|
|
import automator from 'miniprogram-automator';
|
|
|
|
const DEFAULT_CLI_PATH = 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat';
|
|
const DEFAULT_PROJECT_PATH = process.cwd();
|
|
|
|
export class AutomatorClient {
|
|
private mini: automator.MiniProgram | null = null;
|
|
|
|
async connect(cliPath?: string, projectPath?: string) {
|
|
this.mini = await automator.launch({
|
|
cliPath: cliPath || DEFAULT_CLI_PATH,
|
|
projectPath: projectPath || DEFAULT_PROJECT_PATH,
|
|
});
|
|
}
|
|
|
|
async disconnect() {
|
|
if (this.mini) {
|
|
await this.mini.close();
|
|
this.mini = null;
|
|
}
|
|
}
|
|
|
|
private getMini(): automator.MiniProgram {
|
|
if (!this.mini) throw new Error('AutomatorClient 未连接,请先调用 connect()');
|
|
return this.mini;
|
|
}
|
|
|
|
async currentPage(): Promise<automator.Page> {
|
|
return this.getMini().currentPage();
|
|
}
|
|
|
|
async navigateTo(path: string, _query?: Record<string, string>) {
|
|
const page = await this.getMini().navigateTo(`/${path.replace(/^\//, '')}`);
|
|
return page;
|
|
}
|
|
|
|
async navigateBack() {
|
|
await this.getMini().navigateBack();
|
|
}
|
|
|
|
async reLaunch(path: string) {
|
|
await this.getMini().reLaunch(`/${path.replace(/^\//, '')}`);
|
|
}
|
|
|
|
async tap(selector: string) {
|
|
const page = this.getMini().currentPage();
|
|
const element = await page.$(selector);
|
|
if (!element) throw new Error(`元素未找到: ${selector}`);
|
|
await element.tap();
|
|
}
|
|
|
|
async inputText(selector: string, value: string) {
|
|
const page = this.getMini().currentPage();
|
|
const element = await page.$(selector);
|
|
if (!element) throw new Error(`元素未找到: ${selector}`);
|
|
await element.setValue(value);
|
|
}
|
|
|
|
async getElement(selector: string) {
|
|
const page = this.getMini().currentPage();
|
|
return page.$(selector);
|
|
}
|
|
|
|
async getElements(selector: string) {
|
|
const page = this.getMini().currentPage();
|
|
return page.$$(selector);
|
|
}
|
|
|
|
async waitForElement(selector: string, timeout = 5000): Promise<automator.Element> {
|
|
const start = Date.now();
|
|
while (Date.now() - start < timeout) {
|
|
const el = await this.getElement(selector);
|
|
if (el) return el;
|
|
await new Promise((r) => setTimeout(r, 200));
|
|
}
|
|
throw new Error(`等待元素超时: ${selector} (${timeout}ms)`);
|
|
}
|
|
|
|
async getPageData(path?: string) {
|
|
const page = this.getMini().currentPage();
|
|
return page.data(path);
|
|
}
|
|
|
|
async screenshot(path?: string): Promise<Buffer> {
|
|
const page = this.getMini().currentPage();
|
|
return page.screenshot({ path });
|
|
}
|
|
|
|
async callMethod(selector: string, method: string, ...args: unknown[]) {
|
|
const page = this.getMini().currentPage();
|
|
const element = await page.$(selector);
|
|
if (!element) throw new Error(`元素未找到: ${selector}`);
|
|
return element.callMethod(method, ...args);
|
|
}
|
|
}
|