diff --git a/openhis-ui-vue3/.gitignore b/openhis-ui-vue3/.gitignore index 78a752d8..f3c43f17 100644 --- a/openhis-ui-vue3/.gitignore +++ b/openhis-ui-vue3/.gitignore @@ -21,3 +21,8 @@ selenium-debug.log package-lock.json yarn.lock + +# Playwright test results +test-results/ +tests/e2e/report/ +tests/tests/ diff --git a/openhis-ui-vue3/playwright.config.ts b/openhis-ui-vue3/playwright.config.ts new file mode 100644 index 00000000..2fb83fae --- /dev/null +++ b/openhis-ui-vue3/playwright.config.ts @@ -0,0 +1,28 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests/e2e/specs', + fullyParallel: true, + timeout: 60_000, + expect: { timeout: 10_000 }, + retries: process.env.CI ? 2 : 1, + workers: process.env.CI ? 2 : undefined, + reporter: [ + ['html', { outputFolder: 'tests/e2e/report', open: 'never' }], + ['list'], + ], + use: { + baseURL: process.env.TEST_BASE_URL || 'http://localhost:81', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + trace: 'retain-on-failure', + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + timezoneId: 'Asia/Shanghai', + actionTimeout: 15_000, + navigationTimeout: 30_000, + }, + projects: [ + { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + ], +}); diff --git a/openhis-ui-vue3/tests/e2e/pages/DoctorStationPage.ts b/openhis-ui-vue3/tests/e2e/pages/DoctorStationPage.ts index 0a6262ac..bf1b8adc 100644 --- a/openhis-ui-vue3/tests/e2e/pages/DoctorStationPage.ts +++ b/openhis-ui-vue3/tests/e2e/pages/DoctorStationPage.ts @@ -1,13 +1,7 @@ import { Page, expect } from '@playwright/test'; -/** - * 门诊医生站页面对象模型 - */ export class DoctorStationPage { readonly page: Page; - readonly categoryItems = page.locator('.el-collapse-item, .category-item'); - readonly patientSearch = page.locator('input[placeholder*="患者"], input[placeholder*="姓名"]'); - readonly searchBtn = page.locator('button:has-text("搜索"), button:has-text("查询")'); constructor(page: Page) { this.page = page; @@ -19,14 +13,14 @@ export class DoctorStationPage { } async expandCategory(index: number = 0) { - const item = this.categoryItems.nth(index); + const item = this.page.locator('.el-collapse-item, .category-item').nth(index); await item.click(); await this.page.waitForTimeout(500); } async searchPatient(name: string) { - await this.patientSearch.fill(name); - await this.searchBtn.click(); + await this.page.fill('input[placeholder*="患者"], input[placeholder*="姓名"]', name); + await this.page.click('button:has-text("搜索"), button:has-text("查询")'); await this.page.waitForLoadState('networkidle'); } } diff --git a/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts b/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts index 841d35e5..cac99bc7 100644 --- a/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts +++ b/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts @@ -1,15 +1,7 @@ import { Page, expect } from '@playwright/test'; -/** - * 登录页面对象模型 (POM) - */ export class LoginPage { readonly page: Page; - readonly usernameInput = page.locator('input[placeholder*="用户名"], input[placeholder*="账号"]'); - readonly passwordInput = page.locator('input[placeholder*="密码"]'); - readonly loginButton = page.locator('button:has-text("登录"), button[type="submit"]'); - readonly errorMessage = page.locator('.el-message--error'); - readonly successMessage = page.locator('.el-message--success'); constructor(page: Page) { this.page = page; @@ -21,9 +13,9 @@ export class LoginPage { } async login(username: string, password: string) { - await this.usernameInput.fill(username); - await this.passwordInput.fill(password); - await this.loginButton.click(); + await this.page.fill('input[placeholder*="用户名"], input[placeholder*="账号"]', username); + await this.page.fill('input[placeholder*="密码"]', password); + await this.page.click('button:has-text("登录")'); await this.page.waitForLoadState('networkidle'); } @@ -32,10 +24,10 @@ export class LoginPage { } async expectLoginFailed() { - await expect(this.errorMessage).toBeVisible({ timeout: 5000 }); + await expect(this.page.locator('.el-message--error')).toBeVisible({ timeout: 5000 }); } async expectOnLoginPage() { - await expect(this.usernameInput).toBeVisible(); + await expect(this.page.locator('input[placeholder*="用户名"], input[placeholder*="账号"]')).toBeVisible(); } } diff --git a/openhis-ui-vue3/tests/e2e/pages/SurgeryBillingPage.ts b/openhis-ui-vue3/tests/e2e/pages/SurgeryBillingPage.ts index 553b8a58..fedaa815 100644 --- a/openhis-ui-vue3/tests/e2e/pages/SurgeryBillingPage.ts +++ b/openhis-ui-vue3/tests/e2e/pages/SurgeryBillingPage.ts @@ -1,18 +1,7 @@ import { Page, expect } from '@playwright/test'; -/** - * 手术计费页面对象模型 - * 覆盖:手术计费、耗材签发、防重复提交 - */ export class SurgeryBillingPage { readonly page: Page; - readonly surgeryList = page.locator('el-table, .el-table'); - readonly generateBtn = page.locator('button:has-text("生成"), button:has-text("生成收费项")'); - readonly addBtn = page.locator('button:has-text("新增")'); - readonly saveBtn = page.locator('button:has-text("保存"), button:has-text("提交")'); - readonly signBtn = page.locator('button:has-text("签发")'); - readonly successMessage = page.locator('.el-message--success'); - readonly errorMessage = page.locator('.el-message--error'); constructor(page: Page) { this.page = page; @@ -23,14 +12,10 @@ export class SurgeryBillingPage { await this.page.waitForLoadState('networkidle'); } - async generateCharges() { - await this.generateBtn.click(); - await this.page.waitForLoadState('networkidle'); - } - async rapidClickGenerate(times: number = 5) { + const btn = this.page.locator('button:has-text("生成"), button:has-text("新增")'); for (let i = 0; i < times; i++) { - await this.generateBtn.click().catch(() => {}); + await btn.click().catch(() => {}); } await this.page.waitForLoadState('networkidle'); } @@ -44,6 +29,6 @@ export class SurgeryBillingPage { } async expectSaveSuccess() { - await expect(this.successMessage).toBeVisible({ timeout: 10000 }); + await expect(this.page.locator('.el-message--success')).toBeVisible({ timeout: 10000 }); } } diff --git a/openhis-ui-vue3/tests/playwright.config.ts b/openhis-ui-vue3/tests/playwright.config.ts index 7f1e9b6c..2215f9d5 100644 --- a/openhis-ui-vue3/tests/playwright.config.ts +++ b/openhis-ui-vue3/tests/playwright.config.ts @@ -1,18 +1,7 @@ import { defineConfig, devices } from '@playwright/test'; -/** - * OpenHIS Playwright E2E 测试配置 v2.0 - * - * 运行命令: - * npx playwright test # 全部测试 - * npx playwright test --project=chromium # 仅Chrome - * npx playwright test login # 仅登录测试 - * npx playwright test --ui # UI交互模式 - * npx playwright test --headed # 有头模式(可视化) - * npx playwright test --tags=@smoke # 仅冒烟测试 - */ export default defineConfig({ - testDir: './tests/e2e/specs', + testDir: './e2e/specs', fullyParallel: true, timeout: 60_000, expect: { timeout: 10_000 }, @@ -35,6 +24,5 @@ export default defineConfig({ }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, - { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, ], });