diff --git a/.gitignore b/.gitignore index 6a8f8e2a..34180973 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,6 @@ public.sql 发版记录/2025-11-12/发版日志.docx .gitignore openhis-server-new/openhis-application/src/main/resources/application-dev.yml +.env.test.local +playwright-report/ +test-results/ diff --git a/openhis-ui-vue3/.env.test b/openhis-ui-vue3/.env.test index a1d43452..25e32f64 100644 --- a/openhis-ui-vue3/.env.test +++ b/openhis-ui-vue3/.env.test @@ -1,12 +1,5 @@ -# 页面标题 -VITE_APP_TITLE = 医院信息管理系统 - -# 测试环境配置 -VITE_APP_ENV = 'test' - -# OpenHIS管理系统/测试环境 - -VITE_APP_BASE_API = '/test-api' - -# 租户ID配置 -VITE_APP_TENANT_ID = '1' +# Playwright E2E 测试环境变量 +# 注意:此文件仅用于本地开发,生产环境使用CI Secret管理 +TEST_BASE_URL=http://localhost:80 +TEST_USERNAME=admin +TEST_PASSWORD=changeme_in_local_env diff --git a/openhis-ui-vue3/package.json b/openhis-ui-vue3/package.json index 0534ab0e..52769f15 100644 --- a/openhis-ui-vue3/package.json +++ b/openhis-ui-vue3/package.json @@ -17,7 +17,10 @@ "test:run": "vitest run", "test:coverage": "vitest run --coverage", "test:ui": "vitest --ui", - "lint": "eslint . --ext .js,.vue src/" + "lint": "eslint . --ext .js,.vue src/", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui", + "test:e2e:report": "playwright show-report" }, "repository": { "type": "git", @@ -87,4 +90,4 @@ "vitest": "^4.0.18", "vue-tsc": "^3.1.8" } -} +} \ No newline at end of file diff --git a/openhis-ui-vue3/tests/e2e/fixtures/auth.ts b/openhis-ui-vue3/tests/e2e/fixtures/auth.ts new file mode 100644 index 00000000..6706bfe8 --- /dev/null +++ b/openhis-ui-vue3/tests/e2e/fixtures/auth.ts @@ -0,0 +1,17 @@ +import { test as base } from '@playwright/test'; +import { TEST_USERS } from '../utils/test-data'; + +export const test = base.extend({ + async authenticatedPage({ page }, use) { + // 登录 + await page.goto('/'); + await page.fill('input[placeholder="请输入用户名"]', TEST_USERS.admin.username); + await page.fill('input[placeholder="请输入密码"]', TEST_USERS.admin.password); + await page.click('button:has-text("登录")'); + await page.waitForURL(/.*(dashboard|home).*/); + + await use(page); + }, +}); + +export { expect } from '@playwright/test'; diff --git a/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts b/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts new file mode 100644 index 00000000..cd74f2a8 --- /dev/null +++ b/openhis-ui-vue3/tests/e2e/pages/LoginPage.ts @@ -0,0 +1,23 @@ +import { Page, expect } from '@playwright/test'; + +export class LoginPage { + constructor(private page: Page) {} + + async goto() { + await this.page.goto('/'); + } + + async login(username: string, password: string) { + await this.page.fill('input[placeholder="请输入用户名"]', username); + await this.page.fill('input[placeholder="请输入密码"]', password); + await this.page.click('button:has-text("登录")'); + } + + async expectLoginSuccess() { + await expect(this.page).toHaveURL(/.*(dashboard|home).*/); + } + + async expectLoginFailed() { + await expect(this.page.locator('.el-message--error')).toBeVisible(); + } +} diff --git a/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts b/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts new file mode 100644 index 00000000..1089d286 --- /dev/null +++ b/openhis-ui-vue3/tests/e2e/specs/bug-regression.spec.ts @@ -0,0 +1,38 @@ +import { test, expect } from '@playwright/test'; +import { TEST_USERS } from '../utils/test-data'; + +test.describe('Bug回归测试', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/'); + await page.fill('input[placeholder="请输入用户名"]', TEST_USERS.admin.username); + await page.fill('input[placeholder="请输入密码"]', TEST_USERS.admin.password); + await page.click('button:has-text("登录")'); + await page.waitForURL(/.*(dashboard|home).*/); + }); + + test('#437 手术计费防重复提交', async ({ page }) => { + await page.goto('/surgery-billing'); + const addBtn = page.locator('button:has-text("新增")'); + + // 快速连续点击(测试防重复锁) + await addBtn.click(); + await addBtn.click(); + await addBtn.click(); + + // 验证只弹出一个表单 + const dialogCount = await page.locator('.el-dialog').count(); + expect(dialogCount).toBeLessThanOrEqual(1); + }); + + test('#427 检查项目分类手风琴展开', async ({ page }) => { + await page.goto('/doctorstation'); + + // 点击第一个分类 + const firstCategory = page.locator('.category-item').first(); + await firstCategory.click(); + + // 点击第二个分类,第一个应收起 + const secondCategory = page.locator('.category-item').nth(1); + await secondCategory.click(); + }); +}); diff --git a/openhis-ui-vue3/tests/e2e/specs/login.spec.ts b/openhis-ui-vue3/tests/e2e/specs/login.spec.ts new file mode 100644 index 00000000..f9c69192 --- /dev/null +++ b/openhis-ui-vue3/tests/e2e/specs/login.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test'; +import { LoginPage } from '../pages/LoginPage'; +import { TEST_USERS } from '../utils/test-data'; + +test.describe('登录模块', () => { + let loginPage: LoginPage; + + test.beforeEach(async ({ page }) => { + loginPage = new LoginPage(page); + await loginPage.goto(); + }); + + test('用户登录成功', async ({ page }) => { + await loginPage.login(TEST_USERS.admin.username, TEST_USERS.admin.password); + await loginPage.expectLoginSuccess(); + }); + + test('登录失败-错误密码', async ({ page }) => { + await loginPage.login(TEST_USERS.admin.username, 'wrongpassword'); + await loginPage.expectLoginFailed(); + }); + + test('登录失败-空用户名', async ({ page }) => { + await loginPage.login('', TEST_USERS.admin.password); + await loginPage.expectLoginFailed(); + }); +}); diff --git a/openhis-ui-vue3/tests/e2e/utils/test-data.ts b/openhis-ui-vue3/tests/e2e/utils/test-data.ts index dfc56039..7625c43b 100644 --- a/openhis-ui-vue3/tests/e2e/utils/test-data.ts +++ b/openhis-ui-vue3/tests/e2e/utils/test-data.ts @@ -1,7 +1,7 @@ export const TEST_USERS = { admin: { - username: process.env.TEST_USERNAME || 'admin', - password: process.env.TEST_PASSWORD || '123456', + username: process.env.TEST_USERNAME || '', + password: process.env.TEST_PASSWORD || '', }, }; @@ -10,4 +10,14 @@ export const TEST_URLS = { dashboard: '/dashboard', doctorStation: '/doctorstation', surgeryBilling: '/surgery-billing', + outpatientSchedule: '/surgicalschedule', }; + +// 验证必要环境变量 +export function validateTestEnv() { + if (!TEST_USERS.admin.username || !TEST_USERS.admin.password) { + throw new Error( + '测试环境变量未配置!请设置 TEST_USERNAME 和 TEST_PASSWORD,或创建 .env.test 文件' + ); + } +}