From f0efbaeabdeff8dca78d1d982185f51b162567bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Wed, 25 Mar 2026 09:52:27 +0800 Subject: [PATCH] feat: configure UAT test automation and reporting --- .woodpecker.yml | 23 ++++++ uat-tests/README.md | 139 +++++++++++++++++++++++++++++++++ uat-tests/package.json | 16 ++++ uat-tests/playwright.config.ts | 39 +++++++++ uat-tests/quality-gate.js | 86 ++++++++++++++++++++ uat-tests/run-uat-tests.sh | 26 ++++++ 6 files changed, 329 insertions(+) create mode 100644 uat-tests/README.md create mode 100644 uat-tests/package.json create mode 100644 uat-tests/playwright.config.ts create mode 100644 uat-tests/quality-gate.js create mode 100755 uat-tests/run-uat-tests.sh diff --git a/.woodpecker.yml b/.woodpecker.yml index 583becf..1aa5a61 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -67,6 +67,26 @@ pipeline: depends_on: - start-test-env + # UAT测试阶段 + uat-tests: + image: mcr.microsoft.com/playwright:v1.58.2-jammy + group: uat + environment: + TEST_BASE_URL: http://frontend-test:80 + API_BASE_URL: http://backend-test:8080 + HEADLESS_BROWSER: "true" + CI: true + commands: + - cd uat-tests + - npm ci + - npx playwright install --with-deps chromium + - npx playwright test --config=playwright.config.ts --reporter=json --reporter=html --reporter=junit + - node quality-gate.js + when: + event: [push, pull_request] + depends_on: + - start-test-env + # 性能测试阶段 performance-tests: image: node:18-alpine @@ -80,6 +100,8 @@ pipeline: when: event: [push, pull_request] branch: [main, develop] + depends_on: + - uat-tests # 质量门禁检查 quality-gate: @@ -119,6 +141,7 @@ pipeline: depends_on: - quality-gate - trend-analysis + - uat-tests # 生成测试报告 generate-reports: diff --git a/uat-tests/README.md b/uat-tests/README.md new file mode 100644 index 0000000..c339c30 --- /dev/null +++ b/uat-tests/README.md @@ -0,0 +1,139 @@ +# UAT Test Suite + +UAT测试套件用于验证Novalon管理系统的用户验收测试场景。 + +## 目录结构 + +``` +uat-tests/ +├── config/ # 配置文件 +│ └── uat-config.ts # UAT配置 +├── data/ # 测试数据 +│ ├── users.json # 用户数据 +│ ├── roles.json # 角色数据 +│ └── scenarios.json # 场景数据 +├── scenarios/ # 测试场景 +│ ├── user-lifecycle/ # 用户生命周期场景 +│ ├── role-management/ # 角色管理场景 +│ ├── collaboration/ # 多角色协作场景 +│ ├── permission/ # 权限验证场景 +│ └── audit/ # 审计场景 +├── utils/ # 工具类 +│ ├── uat-helper.ts # UAT辅助工具 +│ ├── scenario-runner.ts # 场景运行器 +│ └── data-loader.ts # 数据加载器 +├── pages/ # 页面对象 +│ └── UserManagementPage.ts +├── screenshots/ # 截图目录 +├── test-results/ # 测试结果 +├── playwright.config.ts # Playwright配置 +├── run-uat-tests.sh # 测试运行脚本 +├── quality-gate.js # 质量门禁检查 +└── package.json # 依赖配置 +``` + +## 快速开始 + +### 安装依赖 + +```bash +cd uat-tests +npm install +npx playwright install --with-deps +``` + +### 配置环境变量 + +```bash +cp .env.example .env +# 编辑.env文件,设置测试环境URL +``` + +### 运行测试 + +```bash +# 运行所有UAT测试 +npm run test + +# 运行特定场景 +npx playwright test scenarios/user-lifecycle/ + +# 调试模式 +npm run test:debug + +# 查看测试报告 +npm run test:report +``` + +## 测试场景 + +### 用户生命周期场景 + +- 新用户注册与激活 +- 用户信息变更 +- 用户角色演进 + +### 角色管理场景 + +- 角色分配与权限验证 + +### 多角色协作场景 + +- 跨部门协作流程 +- 数据一致性验证 + +## 质量门禁 + +质量门禁标准: + +- 通过率 >= 95% +- 不稳定率 <= 5% +- 执行时间 <= 10分钟 + +运行质量门禁检查: + +```bash +npm run test:quality-gate +``` + +## CI/CD集成 + +UAT测试已集成到Woodpecker CI/CD流水线中,在每次push和pull request时自动运行。 + +## 最佳实践 + +1. **测试数据隔离**:每个测试使用独立的数据,避免相互影响 +2. **智能等待**:使用UATHelper提供的智能等待方法,避免固定等待 +3. **截图记录**:测试失败时自动截图,便于调试 +4. **并行执行**:支持多worker并行执行,提高测试效率 +5. **跨浏览器测试**:支持Chrome、Firefox、Safari浏览器测试 + +## 故障排查 + +### 测试失败 + +1. 查看测试报告:`npm run test:report` +2. 检查截图:`screenshots/`目录 +3. 查看日志:`test-results/`目录 + +### 环境问题 + +确保测试环境已启动: + +```bash +cd .. +docker-compose -f docker-compose.test.yml up -d +``` + +## 贡献指南 + +添加新的UAT测试场景: + +1. 在`scenarios/`目录下创建新的场景文件 +2. 使用`ScenarioRunner`和`UATHelper`工具 +3. 遵循现有的测试模式和命名约定 +4. 添加相应的测试数据到`data/`目录 + +## 许可证 + +Copyright © 2024 Novalon. All rights reserved. diff --git a/uat-tests/package.json b/uat-tests/package.json new file mode 100644 index 0000000..d6b219f --- /dev/null +++ b/uat-tests/package.json @@ -0,0 +1,16 @@ +{ + "name": "novalon-uat-tests", + "version": "1.0.0", + "description": "UAT test suite for Novalon Management System", + "scripts": { + "test": "playwright test", + "test:headed": "playwright test --headed", + "test:debug": "playwright test --debug", + "test:report": "playwright show-report", + "test:quality-gate": "node quality-gate.js", + "install-browsers": "playwright install --with-deps" + }, + "devDependencies": { + "@playwright/test": "^1.40.0" + } +} diff --git a/uat-tests/playwright.config.ts b/uat-tests/playwright.config.ts new file mode 100644 index 0000000..af879fb --- /dev/null +++ b/uat-tests/playwright.config.ts @@ -0,0 +1,39 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './uat-tests/scenarios', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 4 : 8, + reporter: [ + ['html', { outputFolder: 'uat-tests/test-results/html-report' }], + ['json', { outputFile: 'uat-tests/test-results/results.json' }], + ['junit', { outputFile: 'uat-tests/test-results/junit.xml' }], + ['list'] + ], + use: { + baseURL: process.env.TEST_BASE_URL || 'http://localhost:3001', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + headless: process.env.HEADLESS_BROWSER === 'true', + actionTimeout: parseInt(process.env.TEST_TIMEOUT || '30000'), + navigationTimeout: parseInt(process.env.TEST_TIMEOUT || '30000'), + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + outputDir: 'uat-tests/test-results/artifacts', +}); diff --git a/uat-tests/quality-gate.js b/uat-tests/quality-gate.js new file mode 100644 index 0000000..304aad4 --- /dev/null +++ b/uat-tests/quality-gate.js @@ -0,0 +1,86 @@ +const fs = require('fs'); +const path = require('path'); + +const resultsPath = path.join(__dirname, 'test-results', 'results.json'); +const qualityGateThresholds = { + passRate: 95, + flakyRate: 5, + duration: 600000 +}; + +function loadTestResults() { + if (!fs.existsSync(resultsPath)) { + console.error('❌ Test results not found!'); + process.exit(1); + } + + const data = fs.readFileSync(resultsPath, 'utf-8'); + return JSON.parse(data); +} + +function calculateMetrics(results) { + const totalTests = results.stats.expected; + const passedTests = results.stats.expected - results.stats.failed; + const failedTests = results.stats.failed; + const flakyTests = results.stats.flaky; + const duration = results.stats.duration; + + const passRate = (passedTests / totalTests) * 100; + const flakyRate = (flakyTests / totalTests) * 100; + + return { + totalTests, + passedTests, + failedTests, + flakyTests, + duration, + passRate: passRate.toFixed(2), + flakyRate: flakyRate.toFixed(2) + }; +} + +function checkQualityGate(metrics) { + const failures = []; + + if (metrics.passRate < qualityGateThresholds.passRate) { + failures.push(`Pass rate (${metrics.passRate}%) is below threshold (${qualityGateThresholds.passRate}%)`); + } + + if (metrics.flakyRate > qualityGateThresholds.flakyRate) { + failures.push(`Flaky rate (${metrics.flakyRate}%) is above threshold (${qualityGateThresholds.flakyRate}%)`); + } + + if (metrics.duration > qualityGateThresholds.duration) { + failures.push(`Test duration (${metrics.duration}ms) exceeds threshold (${qualityGateThresholds.duration}ms)`); + } + + return failures; +} + +function main() { + console.log('🚦 Checking UAT Quality Gate...\n'); + + const results = loadTestResults(); + const metrics = calculateMetrics(results); + const failures = checkQualityGate(metrics); + + console.log('📊 Test Metrics:'); + console.log(` Total Tests: ${metrics.totalTests}`); + console.log(` Passed: ${metrics.passedTests}`); + console.log(` Failed: ${metrics.failedTests}`); + console.log(` Flaky: ${metrics.flakyTests}`); + console.log(` Pass Rate: ${metrics.passRate}%`); + console.log(` Flaky Rate: ${metrics.flakyRate}%`); + console.log(` Duration: ${metrics.duration}ms\n`); + + if (failures.length === 0) { + console.log('✅ Quality Gate Passed!'); + process.exit(0); + } else { + console.log('❌ Quality Gate Failed:'); + failures.forEach(failure => console.log(` - ${failure}`)); + process.exit(1); + } +} + +main(); diff --git a/uat-tests/run-uat-tests.sh b/uat-tests/run-uat-tests.sh new file mode 100755 index 0000000..f88e1c7 --- /dev/null +++ b/uat-tests/run-uat-tests.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +echo "🚀 Starting UAT Test Suite..." + +cd "$(dirname "$0")" + +if [ ! -f ".env" ]; then + echo "⚠️ Warning: .env file not found, using default values" + cp .env.example .env +fi + +export $(cat .env | xargs) + +echo "📊 Running UAT tests..." +npx playwright test --config=playwright.config.ts + +if [ $? -eq 0 ]; then + echo "✅ All UAT tests passed!" + echo "📈 Generating test report..." + npx playwright show-report uat-tests/test-results/html-report +else + echo "❌ Some tests failed. Check the report for details." + exit 1 +fi