# 项目架构说明 ## 设计理念 本项目采用 **Page Object Model (POM)** 设计模式,遵循以下原则: 1. **关注点分离**:页面逻辑与测试逻辑分离 2. **DRY 原则**:避免重复代码 3. **单一职责**:每个类只负责一个页面或功能 4. **可测试性**:易于编写和维护测试 5. **可扩展性**:便于添加新功能 ## 架构图 ``` ┌─────────────────────────────────────────────────┐ │ 测试文件层 │ │ (文书管理-refactored.spec.ts) │ │ - 组织测试流程 │ │ - 使用 Page Objects │ │ - 错误处理 │ └─────────────────┬───────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────┐ │ Page Object 层 │ │ - LoginPage (登录) │ │ - StudentPage (学生管理) │ │ - DreamiExplorePage (AI聊天) │ │ - EssayWritingPage (文书写作) │ │ - BasePage (基础页面类) │ └─────────────────┬───────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────┐ │ 工具层 │ │ - ErrorHandler (错误处理) │ │ - 其他工具类... │ └─────────────────┬───────────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────────┐ │ 配置层 │ │ - TestConfig (测试配置) │ │ - 环境变量、超时设置、测试数据 │ └─────────────────────────────────────────────────┘ ``` ## 核心组件 ### 1. 配置层 (config/) **职责**:集中管理所有配置信息 #### test.config.ts ```typescript export const TestConfig = { env: { ... }, // 环境配置 credentials: { ... }, // 登录凭证 timeouts: { ... }, // 超时设置 testData: { ... }, // 测试数据 waits: { ... }, // 等待时间 report: { ... }, // 报告配置 } ``` **优势**: - ✅ 环境切换方便 - ✅ 配置集中管理 - ✅ 避免硬编码 - ✅ 易于维护 ### 2. Page Object 层 (pages/) **职责**:封装页面操作,提供高级 API #### BasePage.ts 基础页面类,所有页面对象的父类 ```typescript export class BasePage { protected page: Page; protected config = TestConfig; // 通用方法 async waitForPageStable() { } async goto(url: string) { } async click(selector: string) { } // ... } ``` #### LoginPage.ts 登录页面对象 ```typescript export class LoginPage extends BasePage { async visitHomePage() { } async clickLoginButton() { } async login(email, password) { } async performLogin() { } } ``` **设计特点**: - 继承 BasePage 获取通用功能 - 方法名语义化,易于理解 - 封装页面元素定位 - 提供高级业务方法 #### StudentPage.ts 学生管理页面对象 ```typescript export class StudentPage extends BasePage { async goToStudentWorkbench() { } async createNewStudent(studentData) { } } ``` #### EssayWritingPage.ts 文书写作页面对象(最复杂) ```typescript export class EssayWritingPage extends BasePage { // 基础操作 async enterEssayWriting() { } async addMaterial(title, content, expectedText) { } // AI 生成功能 async generateEssayIdea() { } async generateEssay() { } async generateOutline() { } async generateDraft(level, length) { } // 检查功能 async performGrammarCheck() { } async performPlagiarismCheck() { } async performAIDetection() { } async performHumanize() { } async performPolish() { } async performGetRated() { } async performImprovementCheck() { } } ``` **优势**: - ✅ 业务逻辑清晰 - ✅ 方法可复用 - ✅ UI 变化只需修改 Page Object - ✅ 测试文件更简洁 ### 3. 工具层 (utils/) **职责**:提供通用工具和辅助功能 #### ErrorHandler.ts 错误处理工具类 ```typescript export class ErrorHandler { private errors: ErrorRecord[] = []; // 记录错误 recordError(stepName, error, page) { } // 执行步骤并捕获错误 async executeStep(stepName, stepFunction, page) { } // 生成错误报告 generateErrorReport() { } // 打印摘要 printSummary() { } } ``` **功能**: - ✅ 自动捕获并记录错误 - ✅ 清理错误信息,去除技术细节 - ✅ 生成友好的错误报告 - ✅ 允许测试继续执行 ### 4. 测试文件层 **职责**:组织测试流程,调用 Page Objects #### 文书管理-refactored.spec.ts ```typescript test('文书管理完整流程测试', async ({ page }) => { // 初始化 const errorHandler = new ErrorHandler(TestConfig.credentials); const loginPage = new LoginPage(page); const essayWritingPage = new EssayWritingPage(page); // 测试流程 await executeStep('访问首页', async () => { await loginPage.visitHomePage(); }); await executeStep('生成 Essay', async () => { await essayWritingPage.generateEssay(); }); // 生成报告 errorHandler.printSummary(); }); ``` **特点**: - ✅ 代码清晰易读 - ✅ 业务流程一目了然 - ✅ 错误处理自动化 - ✅ 配置与代码分离 ## 数据流 ``` 测试开始 ↓ 读取 TestConfig ↓ 初始化 Page Objects ↓ 执行测试步骤 (executeStep) ↓ 调用 Page Object 方法 ↓ Page Object 执行页面操作 ↓ 发生错误? ├─ 是 → ErrorHandler 记录错误 → 继续下一步 └─ 否 → 继续下一步 ↓ 所有步骤完成 ↓ 生成错误报告 ↓ 测试结束 ``` ## 扩展指南 ### 添加新页面 1. **创建 Page Object** ```typescript // e2e/pages/NewPage.ts import { Page, expect } from '@playwright/test'; import { BasePage } from './BasePage'; export class NewPage extends BasePage { constructor(page: Page) { super(page); } async yourMethod(): Promise { // 实现页面操作 } } ``` 2. **在测试中使用** ```typescript import { NewPage } from './pages/NewPage'; test('your test', async ({ page }) => { const newPage = new NewPage(page); await newPage.yourMethod(); }); ``` ### 添加新配置 在 `test.config.ts` 中添加: ```typescript export const TestConfig = { // ... 现有配置 newConfig: { // 新配置项 } } ``` ### 添加新工具类 ```typescript // e2e/utils/YourUtil.ts export class YourUtil { // 实现工具方法 } ``` ## 最佳实践 ### 1. Page Object 设计 - ✅ 一个页面一个类 - ✅ 方法返回 Promise - ✅ 使用描述性方法名 - ✅ 避免在 Page Object 中写断言(除非是验证页面状态) ### 2. 测试文件编写 - ✅ 使用 executeStep 包装每个步骤 - ✅ 步骤名称清晰描述操作 - ✅ 复杂流程拆分为多个步骤 - ✅ 在测试末尾生成报告 ### 3. 配置管理 - ✅ 不要硬编码任何配置值 - ✅ 环境相关的配置放在 env 中 - ✅ 测试数据放在 testData 中 - ✅ 超时设置放在 timeouts 中 ### 4. 错误处理 - ✅ 所有关键步骤都用 executeStep 包装 - ✅ 不要在 Page Object 中处理错误 - ✅ 让错误冒泡到测试层处理 - ✅ 记录足够的错误上下文信息 ## 代码复用 ### 前后对比 **重构前**: ```typescript // 代码重复,难以维护 await page.getByRole('button', { name: 'Add Material' }).click(); await page.getByRole('menuitem', { name: 'Manual Add' }).click(); await page.getByRole('textbox', { name: 'Title' }).fill('title'); // ... 10+ 行相似代码 ``` **重构后**: ```typescript // 简洁清晰,易于维护 await essayWritingPage.addMaterial( TestConfig.testData.material.title, TestConfig.testData.material.content, TestConfig.testData.material.expectedButtonText ); ``` ## 性能优化 - Page Object 方法中使用合理的超时 - 配置中集中管理超时时间 - 避免不必要的等待 - 使用页面对象缓存(如需要) ## 总结 这个架构具有以下优势: 1. **可维护性**:模块化设计,职责清晰 2. **可扩展性**:易于添加新功能 3. **可读性**:代码清晰,易于理解 4. **可测试性**:每个组件独立可测 5. **健壮性**:完善的错误处理机制 通过这种架构,测试代码变得像文档一样易读,维护成本大大降低。