diff --git a/README.md b/README.md index 5bbd50cff..549188eb3 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,187 @@ # testring - [![license](https://img.shields.io/github/license/ringcentral/testring.svg)](https://github.com/ringcentral/testring/blob/master/LICENSE) [![npm](https://img.shields.io/npm/v/testring.svg)](https://www.npmjs.com/package/testring) [![Node.js CI](https://github.com/ringcentral/testring/actions/workflows/node.js.yml/badge.svg)](https://github.com/ringcentral/testring/actions/workflows/node.js.yml) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ringcentral_testring&metric=coverage)](https://sonarcloud.io/summary/new_code?id=ringcentral_testring) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ringcentral_testring&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ringcentral_testring) -A simple way to create, run and support automatic UI tests, based on Node.js. +基于 Node.js 的简单、强大的自动化 UI 测试框架。 + +## 项目概述 + +testring 是一个现代化的测试框架,专门为 Web 应用的自动化测试而设计。它提供了: + +- 🚀 **高性能** - 多进程并行执行测试 +- 🔧 **可扩展** - 丰富的插件系统 +- 🌐 **多浏览器** - 支持 Chrome、Firefox、Safari、Edge +- 📱 **现代化** - 支持 Selenium 和 Playwright 驱动 +- 🛠️ **开发友好** - 完整的开发工具链 + +## 项目结构 + +``` +testring/ +├── core/ # 核心模块 - 框架的基础功能 +│ ├── api/ # 测试 API 控制器 +│ ├── cli/ # 命令行界面 +│ ├── logger/ # 分布式日志系统 +│ ├── transport/ # 进程间通信 +│ ├── test-worker/ # 测试工作进程 +│ └── ... # 其他核心模块 +├── packages/ # 扩展包 - 插件和工具 +│ ├── plugin-selenium-driver/ # Selenium 驱动插件 +│ ├── plugin-playwright-driver/ # Playwright 驱动插件 +│ ├── web-application/ # Web 应用测试 +│ ├── devtool-frontend/ # 开发工具前端 +│ └── ... # 其他扩展包 +├── docs/ # 文档目录 +├── utils/ # 构建和维护工具 +└── README.md # 项目说明 +``` + +### 核心模块 (core/) + +核心模块提供了框架的基础功能: +- **API 层** - 测试运行和控制接口 +- **CLI 工具** - 命令行界面和参数处理 +- **进程管理** - 多进程测试执行和通信 +- **文件系统** - 测试文件查找和读取 +- **日志系统** - 分布式日志记录和管理 +- **插件系统** - 可扩展的插件架构 -Documentation: +### 扩展包 (packages/) +扩展包提供了额外的功能和工具: -[API reference](docs/api.md) -| -[Config API reference](docs/config.md) -| -[Plugin API reference](docs/plugin-handbook.md) +- **浏览器驱动** - Selenium 和 Playwright 支持 +- **Web 测试** - Web 应用专用测试功能 +- **开发工具** - 调试和监控工具 +- **网络通信** - WebSocket 和 HTTP 支持 +- **文件处理** - 文件上传下载和存储 +## 快速开始 -## Getting Started +### 安装 -Let's start by installing testring inside your project: +```bash +# 安装主框架 +npm install testring +# 安装 Selenium 驱动(推荐) +npm install @testring/plugin-selenium-driver + +# 或安装 Playwright 驱动 +npm install @testring/plugin-playwright-driver ``` -$ npm install testring + +### 基本配置 + +创建 `.testringrc` 配置文件: + +```json +{ + "tests": "./tests/**/*.spec.js", + "plugins": [ + "@testring/plugin-selenium-driver" + ], + "workerLimit": 2, + "retryCount": 3 +} ``` -or + +### 编写测试 + +```javascript +// tests/example.spec.js +describe('示例测试', () => { + it('应该能够访问首页', async () => { + await browser.url('https://example.com'); + + const title = await browser.getTitle(); + expect(title).toBe('Example Domain'); + }); +}); ``` -git clone https://github.com/testring -### * installation -> 1. install java last version (1.11) +### 运行测试 + +```bash +# 运行所有测试 +testring run -### * check installation +# 运行特定测试 +testring run --tests "./tests/login.spec.js" -run +# 设置并行数 +testring run --workerLimit 4 + +# 调试模式 +testring run --logLevel debug ``` -$ npm run test:e2e + +## 文档 + +详细文档请参考: + +- [API 参考](docs/api.md) - 框架 API 说明 +- [配置参考](docs/config.md) - 完整配置选项 +- [插件手册](docs/plugin-handbook.md) - 插件开发指南 + +## 主要特性 + +### 多进程并行执行 +- 支持多个测试同时运行 +- 进程间隔离,避免测试干扰 +- 智能负载均衡 + +### 多浏览器支持 +- Chrome、Firefox、Safari、Edge +- Headless 模式支持 +- 移动端浏览器测试 + +### 插件系统 +- 丰富的官方插件 +- 简单的插件开发 API +- 社区插件支持 + +### 开发工具 +- 可视化调试界面 +- 实时测试监控 +- 详细的测试报告 + +## 开发 + +### 项目设置 +```bash +# 克隆项目 +git clone https://github.com/ringcentral/testring.git + +# 安装依赖 +npm install + +# 构建项目 +npm run build + +# 运行测试 +npm test ``` -the execution must finish without an error. + +### 贡献 + +欢迎贡献代码!请参考: +1. Fork 项目 +2. 创建功能分支 +3. 提交更改 +4. 创建 Pull Request + +## 许可证 + +MIT License - 详见 [LICENSE](LICENSE) 文件。 + +## 支持 + +- 📖 [文档](docs/) +- 🐛 [问题反馈](https://github.com/ringcentral/testring/issues) +- 💬 [讨论](https://github.com/ringcentral/testring/discussions) diff --git a/core/README.md b/core/README.md new file mode 100644 index 000000000..f44e59526 --- /dev/null +++ b/core/README.md @@ -0,0 +1,59 @@ +# Core 核心模块 + +`core/` 目录包含了 testring 测试框架的核心模块,提供了框架的基础功能和核心服务。这些模块实现了测试运行、进程管理、文件系统操作、日志记录等关键功能。 + +## 目录结构 + +### 核心运行模块 +- **`api/`** - 测试 API 控制器,提供测试运行的主要接口 +- **`cli/`** - 命令行界面,处理命令行参数和用户交互 +- **`testring/`** - 主要的 testring 入口模块 + +### 测试执行模块 +- **`test-worker/`** - 测试工作进程,负责在独立进程中执行测试 +- **`test-run-controller/`** - 测试运行控制器,管理测试队列和执行流程 +- **`sandbox/`** - 沙箱环境,为测试提供隔离的执行环境 + +### 进程和通信模块 +- **`child-process/`** - 子进程管理,提供进程创建和管理功能 +- **`transport/`** - 传输层,处理进程间通信 + +### 文件系统模块 +- **`fs-reader/`** - 文件系统读取器,负责查找和读取测试文件 +- **`fs-store/`** - 文件系统存储,提供文件存储和缓存功能 + +### 配置和工具模块 +- **`cli-config/`** - 命令行配置解析器,处理配置文件和参数 +- **`logger/`** - 日志系统,提供分布式日志记录功能 +- **`types/`** - TypeScript 类型定义,为整个框架提供类型支持 +- **`utils/`** - 实用工具函数集合 + +### 插件和扩展模块 +- **`plugin-api/`** - 插件 API,为插件开发提供接口 +- **`pluggable-module/`** - 可插拔模块基类,支持钩子和插件机制 + +### 开发和调试模块 +- **`async-assert/`** - 异步断言库,提供测试断言功能 +- **`async-breakpoints/`** - 异步断点,用于调试和流程控制 +- **`dependencies-builder/`** - 依赖构建器,管理模块依赖关系 + +## 主要特性 + +1. **模块化设计** - 每个核心功能都独立为一个模块,便于维护和扩展 +2. **插件支持** - 通过插件 API 支持功能扩展 +3. **异步处理** - 全面支持异步操作和并发执行 +4. **进程管理** - 完整的子进程管理和通信机制 +5. **配置灵活** - 支持多种配置方式和环境参数 +6. **日志记录** - 分布式日志系统,支持多进程日志聚合 + +## 使用说明 + +这些核心模块主要供框架内部使用,开发者通常不需要直接使用这些模块。如果需要扩展框架功能,建议通过插件 API 来实现。 + +## 模块间依赖关系 + +- `cli` 依赖 `cli-config` 和 `api` +- `api` 依赖 `test-run-controller` 和 `test-worker` +- `test-run-controller` 依赖 `test-worker` 和 `fs-reader` +- `logger` 和 `transport` 为其他模块提供基础服务 +- `types` 为所有模块提供类型定义 \ No newline at end of file diff --git a/core/api/README.md b/core/api/README.md index 4f20d5ba2..eb3f5b7e5 100644 --- a/core/api/README.md +++ b/core/api/README.md @@ -1,16 +1,446 @@ -# `@testring/api` +# @testring/api +测试 API 控制器模块,提供了 testring 框架的核心 API 接口和测试执行控制功能。 +## 功能概述 -## Install -Using npm: +该模块是 testring 框架的核心 API 层,负责: +- 提供测试运行的主要入口点和生命周期管理 +- 管理测试状态、参数和环境变量 +- 处理测试事件的发布和订阅 +- 提供测试上下文和工具(HTTP客户端、Web应用等) +- 集成异步断点和日志记录功能 +## 主要特性 + +### 测试生命周期管理 +- **完整的测试生命周期控制**:从测试开始到结束的全过程管理 +- **回调机制**:支持beforeRun和afterRun回调注册 +- **异步断点支持**:与@testring/async-breakpoints集成 + +### 测试状态管理 +- **测试ID管理**:唯一标识每个测试 +- **参数管理**:支持测试参数和环境参数的设置和获取 +- **事件总线**:统一的事件发布和订阅机制 + +### 测试上下文 +- **集成工具**:HTTP客户端、Web应用、日志记录等 +- **自定义应用**:支持自定义Web应用实例 +- **参数访问**:便捷的参数和环境变量访问 + +## 安装 + +```bash +npm install @testring/api +``` + +## 主要组件 + +### 1. TestAPIController + +测试API控制器,管理测试的执行状态和参数: + +```typescript +import { testAPIController } from '@testring/api'; + +// 设置测试ID +testAPIController.setTestID('user-login-test'); + +// 设置测试参数 +testAPIController.setTestParameters({ + username: 'testuser', + password: 'testpass', + timeout: 5000 +}); + +// 设置环境参数 +testAPIController.setEnvironmentParameters({ + baseUrl: 'https://api.example.com', + apiKey: 'secret-key' +}); + +// 获取当前测试ID +const testId = testAPIController.getTestID(); + +// 获取测试参数 +const params = testAPIController.getTestParameters(); + +// 获取环境参数 +const env = testAPIController.getEnvironmentParameters(); +``` + +### 2. BusEmitter + +事件总线,处理测试事件的发布和订阅: + +```typescript +import { testAPIController } from '@testring/api'; + +const bus = testAPIController.getBus(); + +// 监听测试事件 +bus.on('started', () => { + console.log('测试开始执行'); +}); + +bus.on('finished', () => { + console.log('测试执行完成'); +}); + +bus.on('failed', (error: Error) => { + console.error('测试执行失败:', error.message); +}); + +// 手动触发事件 +await bus.startedTest(); +await bus.finishedTest(); +await bus.failedTest(new Error('测试失败')); +``` + +### 3. run 函数 + +测试运行的主要入口点: + +```typescript +import { run, beforeRun, afterRun } from '@testring/api'; + +// 注册生命周期回调 +beforeRun(() => { + console.log('准备执行测试'); +}); + +afterRun(() => { + console.log('测试执行完毕'); +}); + +// 定义测试函数 +const loginTest = async (api) => { + await api.log('开始登录测试'); + + // 使用HTTP客户端 + const response = await api.http.post('/login', { + username: 'testuser', + password: 'testpass' + }); + + await api.log('登录请求完成', response.status); + + // 使用Web应用 + await api.application.url('https://example.com/dashboard'); + const title = await api.application.getTitle(); + + await api.log('页面标题:', title); +}; + +// 执行测试 +await run(loginTest); +``` + +### 4. TestContext + +测试上下文类,提供测试环境和工具: + +```typescript +// 在测试函数中使用 +const myTest = async (api) => { + // HTTP客户端 + const response = await api.http.get('/api/users'); + + // Web应用操作 + await api.application.url('https://example.com'); + const element = await api.application.findElement('#login-button'); + await element.click(); + + // 日志记录 + await api.log('用户操作完成'); + await api.logWarning('这是一个警告'); + await api.logError('这是一个错误'); + + // 业务日志 + await api.logBusiness('用户登录流程'); + // ... 执行业务逻辑 + await api.stopLogBusiness(); + + // 获取参数 + const params = api.getParameters(); + const env = api.getEnvironment(); + + // 自定义应用 + const customApp = api.initCustomApplication(MyCustomWebApp); + await customApp.doSomething(); +}; +``` + +## 完整使用示例 + +### 基本测试示例 + +```typescript +import { run, testAPIController, beforeRun, afterRun } from '@testring/api'; + +// 设置测试配置 +testAPIController.setTestID('e2e-user-workflow'); +testAPIController.setTestParameters({ + username: 'testuser@example.com', + password: 'securepass123', + timeout: 10000 +}); + +testAPIController.setEnvironmentParameters({ + baseUrl: 'https://staging.example.com', + apiKey: 'staging-api-key' +}); + +// 注册生命周期回调 +beforeRun(async () => { + console.log('测试准备阶段'); + // 初始化测试数据 + await setupTestData(); +}); + +afterRun(async () => { + console.log('测试清理阶段'); + // 清理测试数据 + await cleanupTestData(); +}); + +// 定义测试函数 +const userRegistrationTest = async (api) => { + await api.logBusiness('用户注册流程测试'); + + try { + // 步骤1:访问注册页面 + await api.application.url(`${api.getEnvironment().baseUrl}/register`); + await api.log('已访问注册页面'); + + // 步骤2:填写注册表单 + const params = api.getParameters(); + await api.application.setValue('#email', params.username); + await api.application.setValue('#password', params.password); + await api.application.click('#register-btn'); + + // 步骤3:验证注册成功 + const successMessage = await api.application.getText('.success-message'); + await api.log('注册成功消息:', successMessage); + + // 步骤4:API验证 + const response = await api.http.get('/api/user/profile', { + headers: { + 'Authorization': `Bearer ${api.getEnvironment().apiKey}` + } + }); + + await api.log('用户资料获取成功', response.data); + + } catch (error) { + await api.logError('测试执行失败:', error); + throw error; + } finally { + await api.stopLogBusiness(); + } +}; + +// 执行测试 +await run(userRegistrationTest); +``` + +### 多测试函数示例 + +```typescript +import { run } from '@testring/api'; + +const loginTest = async (api) => { + await api.logBusiness('用户登录测试'); + + await api.application.url('/login'); + await api.application.setValue('#username', 'testuser'); + await api.application.setValue('#password', 'testpass'); + await api.application.click('#login-btn'); + + const dashboard = await api.application.findElement('.dashboard'); + await api.log('登录成功,进入仪表板'); + + await api.stopLogBusiness(); +}; + +const profileTest = async (api) => { + await api.logBusiness('用户资料测试'); + + await api.application.click('#profile-link'); + const profileData = await api.application.getText('.profile-info'); + await api.log('用户资料:', profileData); + + await api.stopLogBusiness(); +}; + +const logoutTest = async (api) => { + await api.logBusiness('用户登出测试'); + + await api.application.click('#logout-btn'); + const loginForm = await api.application.findElement('#login-form'); + await api.log('登出成功,返回登录页'); + + await api.stopLogBusiness(); +}; + +// 按顺序执行多个测试 +await run(loginTest, profileTest, logoutTest); +``` + +### 自定义应用示例 + +```typescript +import { WebApplication } from '@testring/web-application'; + +class CustomWebApp extends WebApplication { + async loginWithCredentials(username: string, password: string) { + await this.url('/login'); + await this.setValue('#username', username); + await this.setValue('#password', password); + await this.click('#login-btn'); + + // 等待登录完成 + await this.waitForElement('.dashboard', 5000); + } + + async getUnreadNotifications() { + const notifications = await this.findElements('.notification.unread'); + return notifications.length; + } +} + +const customAppTest = async (api) => { + const customApp = api.initCustomApplication(CustomWebApp); + + await customApp.loginWithCredentials('testuser', 'testpass'); + const unreadCount = await customApp.getUnreadNotifications(); + + await api.log(`未读通知数量: ${unreadCount}`); + + // 访问自定义应用列表 + const customApps = api.getCustomApplicationsList(); + await api.log(`自定义应用数量: ${customApps.length}`); +}; +``` + +## 错误处理 + +```typescript +import { run, testAPIController } from '@testring/api'; + +// 监听测试失败事件 +const bus = testAPIController.getBus(); +bus.on('failed', (error: Error) => { + console.error('测试失败详情:', { + testId: testAPIController.getTestID(), + error: error.message, + stack: error.stack + }); +}); + +const errorHandlingTest = async (api) => { + try { + await api.logBusiness('错误处理测试'); + + // 可能失败的操作 + await api.application.url('/invalid-url'); + + } catch (error) { + await api.logError('捕获到错误:', error); + + // 可以选择重新抛出或处理错误 + throw error; + } finally { + await api.stopLogBusiness(); + } +}; + +await run(errorHandlingTest); +``` + +## 性能优化 + +### HTTP请求优化 +```typescript +const optimizedHttpTest = async (api) => { + // 配置HTTP客户端 + const httpOptions = { + timeout: 5000, + retries: 3, + headers: { + 'User-Agent': 'testring-test-client' + } + }; + + // 并发请求 + const [user, posts, comments] = await Promise.all([ + api.http.get('/api/user', httpOptions), + api.http.get('/api/posts', httpOptions), + api.http.get('/api/comments', httpOptions) + ]); + + await api.log('并发请求完成'); +}; +``` + +### 资源清理 +```typescript +afterRun(async () => { + // 确保所有资源被正确清理 + await api.end(); +}); ``` -npm install --save-dev @testring/api + +## 配置选项 + +### TestAPIController配置 +```typescript +interface TestAPIControllerOptions { + testID: string; // 测试ID + testParameters: object; // 测试参数 + environmentParameters: object; // 环境参数 +} ``` -or using yarn: +### TestContext配置 +```typescript +interface TestContextConfig { + httpThrottle?: number; // HTTP请求限流 + runData?: ITestQueuedTestRunData; // 运行数据 +} +``` +## 事件类型 + +```typescript +enum TestEvents { + started = 'started', // 测试开始 + finished = 'finished', // 测试完成 + failed = 'failed' // 测试失败 +} ``` -yarn add @testring/api --dev -``` \ No newline at end of file + +## 依赖 + +- `@testring/web-application` - Web应用测试功能 +- `@testring/async-breakpoints` - 异步断点支持 +- `@testring/logger` - 日志记录系统 +- `@testring/http-api` - HTTP客户端 +- `@testring/transport` - 传输层 +- `@testring/utils` - 工具函数 +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/test-run-controller` - 测试运行控制器 +- `@testring/test-worker` - 测试工作进程 +- `@testring/cli` - 命令行界面 +- `@testring/async-assert` - 异步断言库 + +## 最佳实践 + +1. **合理设置测试ID**:使用有意义的测试ID,便于日志追踪 +2. **参数管理**:将可变参数和环境变量分离管理 +3. **生命周期回调**:合理使用beforeRun和afterRun进行初始化和清理 +4. **错误处理**:监听测试事件,实现完整的错误处理机制 +5. **资源清理**:确保测试结束时正确清理所有资源 \ No newline at end of file diff --git a/core/async-assert/README.md b/core/async-assert/README.md index fa61b9c82..e87f98727 100644 --- a/core/async-assert/README.md +++ b/core/async-assert/README.md @@ -1,11 +1,312 @@ -# `assert` +# @testring/async-assert -> TODO: description +基于 Chai 的异步断言库,为 testring 框架提供完整的异步断言支持。 -## Usage +## 功能概述 +该模块是 Chai 断言库的异步包装器,提供了: +- 将所有 Chai 断言方法转换为异步版本 +- 支持软断言和硬断言模式 +- 错误收集和自定义处理机制 +- 完整的 TypeScript 类型支持 + +## 主要特性 + +### 异步断言支持 +- 所有断言方法返回 Promise +- 适配异步测试环境 +- 与多进程测试框架完美集成 + +### 软断言机制 +- **硬断言**:失败时立即抛出错误(默认模式) +- **软断言**:失败时收集错误,继续执行后续断言 + +### 错误处理 +- 自动收集断言失败信息 +- 支持自定义成功/失败回调 +- 提供详细的错误上下文 + +## 安装 + +```bash +npm install @testring/async-assert +``` + +## 基本用法 + +### 创建断言实例 + +```typescript +import { createAssertion } from '@testring/async-assert'; + +// 创建默认断言实例(硬断言模式) +const assert = createAssertion(); + +// 创建软断言实例 +const softAssert = createAssertion({ isSoft: true }); +``` + +### 异步断言示例 + +```typescript +// 基本断言 +await assert.equal(actual, expected, '值应该相等'); +await assert.isTrue(condition, '条件应该为真'); +await assert.lengthOf(array, 3, '数组长度应该为3'); + +// 类型断言 +await assert.isString(value, '值应该是字符串'); +await assert.isNumber(count, '计数应该是数字'); +await assert.isArray(list, '应该是数组'); + +// 包含断言 +await assert.include(haystack, needle, '应该包含指定值'); +await assert.property(object, 'prop', '对象应该有指定属性'); + +// 异常断言 +await assert.throws(() => { + throw new Error('测试错误'); +}, '应该抛出错误'); +``` + +## 软断言模式 + +软断言允许测试继续执行,即使某些断言失败: + +```typescript +import { createAssertion } from '@testring/async-assert'; + +const assert = createAssertion({ isSoft: true }); + +// 执行多个断言 +await assert.equal(user.name, 'John', '用户名检查'); +await assert.equal(user.age, 25, '年龄检查'); +await assert.isTrue(user.isActive, '激活状态检查'); + +// 获取所有错误信息 +const errors = assert._errorMessages; +if (errors.length > 0) { + console.log('发现以下断言失败:'); + errors.forEach(error => console.log('- ' + error)); +} +``` + +## 自定义回调处理 + +```typescript +const assert = createAssertion({ + onSuccess: async (data) => { + console.log(`✓ ${data.assertMessage}`); + // 记录成功的断言 + }, + + onError: async (data) => { + console.log(`✗ ${data.assertMessage}`); + console.log(` 错误: ${data.errorMessage}`); + + // 可以返回自定义错误对象 + return new Error(`自定义错误: ${data.errorMessage}`); + } +}); + +await assert.equal(actual, expected); +``` + +## 支持的断言方法 + +### 相等性断言 +```typescript +await assert.equal(actual, expected); // 非严格相等 (==) +await assert.notEqual(actual, expected); // 非严格不等 (!=) +await assert.strictEqual(actual, expected); // 严格相等 (===) +await assert.notStrictEqual(actual, expected); // 严格不等 (!==) +await assert.deepEqual(actual, expected); // 深度相等 +await assert.notDeepEqual(actual, expected); // 深度不等 +``` + +### 真值断言 +```typescript +await assert.ok(value); // 真值检查 +await assert.notOk(value); // 假值检查 +await assert.isTrue(value); // 严格 true +await assert.isFalse(value); // 严格 false +await assert.isNotTrue(value); // 非 true +await assert.isNotFalse(value); // 非 false +``` + +### 类型断言 +```typescript +await assert.isString(value); // 字符串类型 +await assert.isNumber(value); // 数字类型 +await assert.isBoolean(value); // 布尔类型 +await assert.isArray(value); // 数组类型 +await assert.isObject(value); // 对象类型 +await assert.isFunction(value); // 函数类型 +await assert.typeOf(value, 'string'); // 类型检查 +await assert.instanceOf(value, Array); // 实例检查 +``` + +### 空值断言 +```typescript +await assert.isNull(value); // null 检查 +await assert.isNotNull(value); // 非 null 检查 +await assert.isUndefined(value); // undefined 检查 +await assert.isDefined(value); // 已定义检查 +await assert.exists(value); // 存在检查 +await assert.notExists(value); // 不存在检查 ``` -const assert = require('assert'); -// TODO: DEMONSTRATE API +### 数值断言 +```typescript +await assert.isAbove(valueToCheck, valueToBeAbove); // 大于 +await assert.isAtLeast(valueToCheck, valueToBeAtLeast); // 大于等于 +await assert.isBelow(valueToCheck, valueToBeBelow); // 小于 +await assert.isAtMost(valueToCheck, valueToBeAtMost); // 小于等于 +await assert.closeTo(actual, expected, delta); // 近似相等 ``` + +### 包含断言 +```typescript +await assert.include(haystack, needle); // 包含检查 +await assert.notInclude(haystack, needle); // 不包含检查 +await assert.deepInclude(haystack, needle); // 深度包含 +await assert.property(object, 'prop'); // 属性存在 +await assert.notProperty(object, 'prop'); // 属性不存在 +await assert.propertyVal(object, 'prop', val); // 属性值检查 +await assert.lengthOf(object, length); // 长度检查 +``` + +### 异常断言 +```typescript +await assert.throws(() => { + throw new Error('test'); +}); // 抛出异常 + +await assert.doesNotThrow(() => { + // 正常代码 +}); // 不抛出异常 +``` + +### 集合断言 +```typescript +await assert.sameMembers(set1, set2); // 相同成员 +await assert.sameDeepMembers(set1, set2); // 深度相同成员 +await assert.includeMembers(superset, subset); // 包含成员 +await assert.oneOf(value, list); // 值在列表中 +``` + +## 插件支持 + +支持 Chai 插件来扩展断言功能: + +```typescript +import chaiAsPromised from 'chai-as-promised'; + +const assert = createAssertion({ + plugins: [chaiAsPromised] +}); + +// 现在可以使用插件提供的断言 +await assert.eventually.equal(promise, expectedValue); +``` + +## 配置选项 + +```typescript +interface IAssertionOptions { + isSoft?: boolean; // 是否使用软断言模式 + plugins?: Array; // Chai 插件列表 + onSuccess?: (data: SuccessData) => Promise; // 成功回调 + onError?: (data: ErrorData) => Promise; // 错误回调 +} +``` + +### 回调数据结构 + +```typescript +interface SuccessData { + isSoft: boolean; // 是否软断言 + successMessage: string; // 成功消息 + assertMessage: string; // 断言消息 + args: any[]; // 断言参数 + originalMethod: string; // 原始方法名 +} + +interface ErrorData { + isSoft: boolean; // 是否软断言 + successMessage: string; // 成功消息 + assertMessage: string; // 断言消息 + errorMessage: string; // 错误消息 + error: Error; // 错误对象 + args: any[]; // 断言参数 + originalMethod: string; // 原始方法名 +} +``` + +## 与 testring 框架集成 + +在 testring 测试中使用: + +```typescript +import { createAssertion } from '@testring/async-assert'; + +// 在测试文件中 +const assert = createAssertion(); + +describe('用户管理测试', () => { + it('应该能够创建用户', async () => { + const user = await createUser({ name: 'John', age: 25 }); + + await assert.equal(user.name, 'John', '用户名应该正确'); + await assert.equal(user.age, 25, '年龄应该正确'); + await assert.property(user, 'id', '应该有用户ID'); + await assert.isString(user.id, 'ID应该是字符串'); + }); +}); +``` + +## 性能优化 + +### 批量断言 +```typescript +// 软断言模式下的批量验证 +const assert = createAssertion({ isSoft: true }); + +const validateUser = async (user) => { + await assert.isString(user.name, '姓名必须是字符串'); + await assert.isNumber(user.age, '年龄必须是数字'); + await assert.isAbove(user.age, 0, '年龄必须大于0'); + await assert.isBelow(user.age, 150, '年龄必须小于150'); + await assert.match(user.email, /\S+@\S+\.\S+/, '邮箱格式无效'); + + return assert._errorMessages; +}; +``` + +## 错误处理最佳实践 + +```typescript +const assert = createAssertion({ + isSoft: true, + onError: async (data) => { + // 记录详细的断言失败信息 + console.error(`断言失败: ${data.originalMethod}`); + console.error(`参数: ${JSON.stringify(data.args)}`); + console.error(`错误: ${data.errorMessage}`); + + // 可以发送到监控系统 + // sendToMonitoring(data); + } +}); +``` + +## 依赖 + +- `chai` - 底层断言库 +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/test-worker` - 测试工作进程 +- `@testring/api` - 测试 API 控制器 +- `@testring/logger` - 日志系统 diff --git a/core/async-breakpoints/README.md b/core/async-breakpoints/README.md index dde9f24be..7f674b9b9 100644 --- a/core/async-breakpoints/README.md +++ b/core/async-breakpoints/README.md @@ -1,16 +1,383 @@ -# `@testring/async-breakpoints` +# @testring/async-breakpoints +异步断点系统模块,提供了测试执行过程中的暂停点控制和调试功能。 +## 功能概述 -## Install -Using npm: +该模块提供了一个基于事件的异步断点系统,用于: +- 在测试执行过程中设置暂停点 +- 控制测试流程的执行时序 +- 支持调试和测试协调 +- 提供指令前后的断点控制 +## 主要组件 + +### AsyncBreakpoints +主要的断点管理类,继承自 EventEmitter: + +```typescript +export class AsyncBreakpoints extends EventEmitter { + // 指令前断点 + addBeforeInstructionBreakpoint(): void + waitBeforeInstructionBreakpoint(callback?: HasBreakpointCallback): Promise + resolveBeforeInstructionBreakpoint(): void + isBeforeInstructionBreakpointActive(): boolean + + // 指令后断点 + addAfterInstructionBreakpoint(): void + waitAfterInstructionBreakpoint(callback?: HasBreakpointCallback): Promise + resolveAfterInstructionBreakpoint(): void + isAfterInstructionBreakpointActive(): boolean + + // 断点控制 + breakStack(): void // 中断所有断点 +} +``` + +### BreakStackError +断点中断错误类,用于处理断点被强制中断的情况: + +```typescript +export class BreakStackError extends Error { + constructor(message: string) +} +``` + +## 断点类型 + +### BreakpointsTypes +```typescript +export enum BreakpointsTypes { + beforeInstruction = 'beforeInstruction', // 指令前断点 + afterInstruction = 'afterInstruction' // 指令后断点 +} +``` + +### BreakpointEvents +```typescript +export enum BreakpointEvents { + resolverEvent = 'resolveEvent', // 断点解析事件 + breakStackEvent = 'breakStack' // 断点中断事件 +} +``` + +## 使用方法 + +### 基本使用 +```typescript +import { AsyncBreakpoints } from '@testring/async-breakpoints'; + +const breakpoints = new AsyncBreakpoints(); + +// 设置指令前断点 +breakpoints.addBeforeInstructionBreakpoint(); + +// 等待断点 +await breakpoints.waitBeforeInstructionBreakpoint(); + +// 在另一个地方解析断点 +breakpoints.resolveBeforeInstructionBreakpoint(); +``` + +### 使用默认实例 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 使用全局默认实例 +asyncBreakpoints.addBeforeInstructionBreakpoint(); +await asyncBreakpoints.waitBeforeInstructionBreakpoint(); +asyncBreakpoints.resolveBeforeInstructionBreakpoint(); +``` + +### 指令前断点 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 设置指令前断点 +asyncBreakpoints.addBeforeInstructionBreakpoint(); + +// 检查断点状态 +if (asyncBreakpoints.isBeforeInstructionBreakpointActive()) { + console.log('指令前断点已激活'); +} + +// 等待断点(会阻塞直到断点被解析) +await asyncBreakpoints.waitBeforeInstructionBreakpoint(); + +// 解析断点(通常在另一个执行流中) +asyncBreakpoints.resolveBeforeInstructionBreakpoint(); +``` + +### 指令后断点 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 设置指令后断点 +asyncBreakpoints.addAfterInstructionBreakpoint(); + +// 等待断点 +await asyncBreakpoints.waitAfterInstructionBreakpoint(); + +// 解析断点 +asyncBreakpoints.resolveAfterInstructionBreakpoint(); +``` + +### 断点回调 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +asyncBreakpoints.addBeforeInstructionBreakpoint(); + +// 带回调的断点等待 +await asyncBreakpoints.waitBeforeInstructionBreakpoint(async (hasBreakpoint) => { + if (hasBreakpoint) { + console.log('断点已设置,等待解析...'); + } else { + console.log('没有断点,继续执行'); + } +}); +``` + +## 断点控制 + +### 中断断点 +```typescript +import { asyncBreakpoints, BreakStackError } from '@testring/async-breakpoints'; + +asyncBreakpoints.addBeforeInstructionBreakpoint(); + +// 等待断点 +asyncBreakpoints.waitBeforeInstructionBreakpoint() + .catch((error) => { + if (error instanceof BreakStackError) { + console.log('断点被中断'); + } + }); + +// 中断所有断点 +asyncBreakpoints.breakStack(); +``` + +### 并发断点处理 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 同时设置多个断点 +asyncBreakpoints.addBeforeInstructionBreakpoint(); +asyncBreakpoints.addAfterInstructionBreakpoint(); + +// 并发等待 +const promises = Promise.all([ + asyncBreakpoints.waitBeforeInstructionBreakpoint(), + asyncBreakpoints.waitAfterInstructionBreakpoint() +]); + +// 按顺序解析断点 +setTimeout(() => { + asyncBreakpoints.resolveBeforeInstructionBreakpoint(); + asyncBreakpoints.resolveAfterInstructionBreakpoint(); +}, 1000); + +await promises; +``` + +## 实际应用场景 + +### 测试协调 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 在测试执行前设置断点 +asyncBreakpoints.addBeforeInstructionBreakpoint(); + +// 测试执行流程 +async function runTest() { + console.log('准备执行测试'); + + // 等待断点解析 + await asyncBreakpoints.waitBeforeInstructionBreakpoint(); + + console.log('开始执行测试'); + // 实际测试逻辑 +} + +// 控制流程 +async function controlFlow() { + setTimeout(() => { + console.log('解析断点,允许测试继续'); + asyncBreakpoints.resolveBeforeInstructionBreakpoint(); + }, 2000); +} + +// 并发执行 +Promise.all([runTest(), controlFlow()]); +``` + +### 调试支持 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 调试模式下的断点 +if (process.env.DEBUG_MODE) { + asyncBreakpoints.addBeforeInstructionBreakpoint(); + + // 等待用户输入或调试器连接 + await asyncBreakpoints.waitBeforeInstructionBreakpoint(async (hasBreakpoint) => { + if (hasBreakpoint) { + console.log('调试断点激活,等待调试器...'); + } + }); +} +``` + +### 多进程同步 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +// 子进程中设置断点 +asyncBreakpoints.addAfterInstructionBreakpoint(); + +// 执行某些操作 +performSomeOperation(); + +// 等待主进程信号 +await asyncBreakpoints.waitAfterInstructionBreakpoint(); + +// 继续执行 +continueExecution(); +``` + +## 错误处理 + +### BreakStackError 处理 +```typescript +import { asyncBreakpoints, BreakStackError } from '@testring/async-breakpoints'; + +try { + asyncBreakpoints.addBeforeInstructionBreakpoint(); + await asyncBreakpoints.waitBeforeInstructionBreakpoint(); +} catch (error) { + if (error instanceof BreakStackError) { + console.log('断点被强制中断:', error.message); + // 处理中断逻辑 + } else { + console.error('其他错误:', error); + } +} +``` + +### 超时处理 +```typescript +import { asyncBreakpoints } from '@testring/async-breakpoints'; + +asyncBreakpoints.addBeforeInstructionBreakpoint(); + +// 设置超时 +const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('断点超时')), 5000); +}); + +try { + await Promise.race([ + asyncBreakpoints.waitBeforeInstructionBreakpoint(), + timeoutPromise + ]); +} catch (error) { + console.log('断点处理失败:', error.message); + // 强制中断断点 + asyncBreakpoints.breakStack(); +} +``` + +## 事件监听 + +### 自定义事件处理 +```typescript +import { asyncBreakpoints, BreakpointEvents } from '@testring/async-breakpoints'; + +// 监听断点解析事件 +asyncBreakpoints.on(BreakpointEvents.resolverEvent, (type) => { + console.log(`断点类型 ${type} 已解析`); +}); + +// 监听断点中断事件 +asyncBreakpoints.on(BreakpointEvents.breakStackEvent, () => { + console.log('断点栈被中断'); +}); ``` -npm install --save-dev @testring/browser-proxy + +## 最佳实践 + +### 1. 断点生命周期管理 +```typescript +// 确保断点被正确清理 +try { + asyncBreakpoints.addBeforeInstructionBreakpoint(); + await asyncBreakpoints.waitBeforeInstructionBreakpoint(); +} finally { + // 确保断点被清理 + if (asyncBreakpoints.isBeforeInstructionBreakpointActive()) { + asyncBreakpoints.resolveBeforeInstructionBreakpoint(); + } +} ``` -or using yarn: +### 2. 避免死锁 +```typescript +// 使用超时避免无限等待 +const waitWithTimeout = (breakpointPromise, timeout = 5000) => { + return Promise.race([ + breakpointPromise, + new Promise((_, reject) => + setTimeout(() => reject(new Error('断点超时')), timeout) + ) + ]); +}; +``` +### 3. 调试信息 +```typescript +// 添加调试信息 +const debugBreakpoint = async (name: string) => { + console.log(`[DEBUG] 设置断点: ${name}`); + asyncBreakpoints.addBeforeInstructionBreakpoint(); + + await asyncBreakpoints.waitBeforeInstructionBreakpoint(async (hasBreakpoint) => { + console.log(`[DEBUG] 断点 ${name} 状态: ${hasBreakpoint ? '激活' : '未激活'}`); + }); + + console.log(`[DEBUG] 断点 ${name} 已解析`); +}; ``` -yarn add @testring/browser-proxy --dev + +## 安装 + +```bash +npm install @testring/async-breakpoints ``` + +## 类型定义 + +```typescript +type HasBreakpointCallback = (state: boolean) => Promise | void; + +interface AsyncBreakpoints extends EventEmitter { + addBeforeInstructionBreakpoint(): void; + waitBeforeInstructionBreakpoint(callback?: HasBreakpointCallback): Promise; + resolveBeforeInstructionBreakpoint(): void; + isBeforeInstructionBreakpointActive(): boolean; + + addAfterInstructionBreakpoint(): void; + waitAfterInstructionBreakpoint(callback?: HasBreakpointCallback): Promise; + resolveAfterInstructionBreakpoint(): void; + isAfterInstructionBreakpointActive(): boolean; + + breakStack(): void; +} +``` + +## 相关模块 + +- `@testring/api` - 测试 API,使用断点进行流程控制 +- `@testring/test-worker` - 测试工作进程,使用断点进行进程同步 +- `@testring/devtool-backend` - 开发工具后端,使用断点进行调试 diff --git a/core/child-process/README.md b/core/child-process/README.md index 0fa505139..3f0f0250f 100644 --- a/core/child-process/README.md +++ b/core/child-process/README.md @@ -1,16 +1,538 @@ -# `@testring/child-process` +# @testring/child-process +子进程管理模块,提供了跨平台的子进程创建和管理功能,支持 JavaScript 和 TypeScript 文件的直接执行。 +## 功能概述 -## Install -Using npm: +该模块提供了增强的子进程管理功能,包括: +- 支持 JavaScript 和 TypeScript 文件的直接执行 +- 跨平台兼容性(Windows、Linux、macOS) +- 调试模式支持 +- 进程间通信(IPC) +- 自动端口分配 +- 进程状态检测 +## 主要功能 + +### fork +增强的子进程创建函数,支持多种文件类型: + +```typescript +export async function fork( + filePath: string, + args?: Array, + options?: Partial +): Promise +``` + +### spawn +基本的子进程启动功能: + +```typescript +export function spawn( + command: string, + args?: Array +): childProcess.ChildProcess +``` + +### spawnWithPipes +带管道的子进程启动: + +```typescript +export function spawnWithPipes( + command: string, + args?: Array +): childProcess.ChildProcess +``` + +### isChildProcess +检查当前进程是否是子进程: + +```typescript +export function isChildProcess(argv?: string[]): boolean +``` + +## 使用方法 + +### 基本使用 + +#### 执行 JavaScript 文件 +```typescript +import { fork } from '@testring/child-process'; + +// 执行 JavaScript 文件 +const childProcess = await fork('./worker.js'); + +childProcess.on('message', (data) => { + console.log('收到消息:', data); +}); + +childProcess.send({ type: 'start', data: 'hello' }); +``` + +#### 执行 TypeScript 文件 +```typescript +import { fork } from '@testring/child-process'; + +// 直接执行 TypeScript 文件(自动处理 ts-node) +const childProcess = await fork('./worker.ts'); + +childProcess.on('message', (data) => { + console.log('收到消息:', data); +}); +``` + +#### 传递参数 +```typescript +import { fork } from '@testring/child-process'; + +// 传递命令行参数 +const childProcess = await fork('./worker.js', ['--mode', 'production']); + +// 子进程中访问参数 +// process.argv 包含传递的参数 +``` + +### 调试模式 + +#### 启用调试 +```typescript +import { fork } from '@testring/child-process'; + +// 启用调试模式 +const childProcess = await fork('./worker.js', [], { + debug: true +}); + +// 访问调试端口 +console.log('调试端口:', childProcess.debugPort); +// 可以使用 Chrome DevTools 或 VS Code 连接到此端口 +``` + +#### 自定义调试端口范围 +```typescript +import { fork } from '@testring/child-process'; + +const childProcess = await fork('./worker.js', [], { + debug: true, + debugPortRange: [9229, 9230, 9231, 9232] +}); +``` + +### 进程间通信 + +#### 父进程代码 +```typescript +import { fork } from '@testring/child-process'; + +const childProcess = await fork('./worker.js'); + +// 发送消息到子进程 +childProcess.send({ + type: 'task', + data: { id: 1, action: 'process' } +}); + +// 监听子进程消息 +childProcess.on('message', (message) => { + if (message.type === 'result') { + console.log('任务结果:', message.data); + } +}); + +// 监听子进程退出 +childProcess.on('exit', (code, signal) => { + console.log(`子进程退出: code=${code}, signal=${signal}`); +}); +``` + +#### 子进程代码 (worker.js) +```javascript +// 监听父进程消息 +process.on('message', (message) => { + if (message.type === 'task') { + const result = processTask(message.data); + + // 发送结果回父进程 + process.send({ + type: 'result', + data: result + }); + } +}); + +function processTask(data) { + // 处理任务逻辑 + return { id: data.id, status: 'completed' }; +} +``` + +### 进程状态检测 + +#### 检查是否为子进程 +```typescript +import { isChildProcess } from '@testring/child-process'; + +if (isChildProcess()) { + console.log('运行在子进程中'); + // 子进程特定的逻辑 +} else { + console.log('运行在主进程中'); + // 主进程特定的逻辑 +} +``` + +#### 检查特定参数 +```typescript +import { isChildProcess } from '@testring/child-process'; + +// 检查自定义参数 +const customArgs = ['--testring-parent-pid=12345']; +if (isChildProcess(customArgs)) { + console.log('这是 testring 子进程'); +} +``` + +### 使用 spawn 功能 + +#### 基本 spawn +```typescript +import { spawn } from '@testring/child-process'; + +// 启动基本子进程 +const childProcess = spawn('node', ['--version']); + +childProcess.stdout.on('data', (data) => { + console.log(`输出: ${data}`); +}); + +childProcess.stderr.on('data', (data) => { + console.error(`错误: ${data}`); +}); ``` -npm install --save-dev @testring/child-process + +#### 带管道的 spawn +```typescript +import { spawnWithPipes } from '@testring/child-process'; + +// 启动带管道的子进程 +const childProcess = spawnWithPipes('node', ['script.js']); + +// 向子进程发送数据 +childProcess.stdin.write('hello\n'); +childProcess.stdin.end(); + +// 读取输出 +childProcess.stdout.on('data', (data) => { + console.log(`输出: ${data}`); +}); +``` + +## 跨平台支持 + +### Windows 特殊处理 +模块自动处理 Windows 平台的差异: + +```typescript +// 在 Windows 上会自动使用 'node' 命令 +// 在 Unix 系统上会使用 ts-node 或 node 根据文件类型 +const childProcess = await fork('./worker.ts'); ``` -or using yarn: +### TypeScript 支持 +自动检测和处理 TypeScript 文件: + +```typescript +// .ts 文件会自动使用 ts-node 执行 +const tsProcess = await fork('./worker.ts'); + +// .js 文件使用 node 执行 +const jsProcess = await fork('./worker.js'); + +// 无扩展名文件根据环境自动选择 +const process = await fork('./worker'); +``` + +## 配置选项 + +### IChildProcessForkOptions +```typescript +interface IChildProcessForkOptions { + debug: boolean; // 是否启用调试模式 + debugPortRange: Array; // 调试端口范围 +} +``` +### 默认配置 +```typescript +const DEFAULT_FORK_OPTIONS = { + debug: false, + debugPortRange: [9229, 9222, ...getNumberRange(9230, 9240)] +}; ``` -yarn add @testring/child-process --dev -``` \ No newline at end of file + +## 实际应用场景 + +### 测试工作进程 +```typescript +import { fork } from '@testring/child-process'; + +// 创建测试工作进程 +const createTestWorker = async (testFile: string) => { + const worker = await fork('./test-runner.js', [testFile]); + + return new Promise((resolve, reject) => { + worker.on('message', (message) => { + if (message.type === 'test-result') { + resolve(message.data); + } else if (message.type === 'test-error') { + reject(new Error(message.error)); + } + }); + + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error(`工作进程异常退出: ${code}`)); + } + }); + }); +}; + +// 使用 +const result = await createTestWorker('./my-test.spec.js'); +``` + +### 并行任务处理 +```typescript +import { fork } from '@testring/child-process'; + +const processTasks = async (tasks: any[]) => { + const workers = await Promise.all( + tasks.map(task => fork('./task-worker.js')) + ); + + const results = await Promise.all( + workers.map((worker, index) => { + return new Promise((resolve) => { + worker.on('message', (result) => { + resolve(result); + }); + + worker.send(tasks[index]); + }); + }) + ); + + // 清理工作进程 + workers.forEach(worker => worker.kill()); + + return results; +}; +``` + +### 调试支持 +```typescript +import { fork } from '@testring/child-process'; + +const createDebugWorker = async (script: string) => { + const worker = await fork(script, [], { + debug: true, + debugPortRange: [9229, 9230, 9231] + }); + + console.log(`调试端口: ${worker.debugPort}`); + console.log(`可以使用以下命令连接调试器:`); + console.log(`chrome://inspect 或 VS Code 连接到 localhost:${worker.debugPort}`); + + return worker; +}; +``` + +## 错误处理 + +### 进程异常处理 +```typescript +import { fork } from '@testring/child-process'; + +const createRobustWorker = async (script: string) => { + try { + const worker = await fork(script); + + worker.on('error', (error) => { + console.error('进程错误:', error); + }); + + worker.on('exit', (code, signal) => { + if (code !== 0) { + console.error(`进程异常退出: code=${code}, signal=${signal}`); + } + }); + + return worker; + } catch (error) { + console.error('创建进程失败:', error); + throw error; + } +}; +``` + +### 超时处理 +```typescript +import { fork } from '@testring/child-process'; + +const createWorkerWithTimeout = async (script: string, timeout: number) => { + const worker = await fork(script); + + const timeoutId = setTimeout(() => { + console.log('进程超时,强制终止'); + worker.kill('SIGTERM'); + }, timeout); + + worker.on('exit', () => { + clearTimeout(timeoutId); + }); + + return worker; +}; +``` + +## 性能优化 + +### 进程池管理 +```typescript +import { fork } from '@testring/child-process'; + +class WorkerPool { + private workers: any[] = []; + private maxWorkers: number; + + constructor(maxWorkers: number = 4) { + this.maxWorkers = maxWorkers; + } + + async getWorker(script: string) { + if (this.workers.length < this.maxWorkers) { + const worker = await fork(script); + this.workers.push(worker); + return worker; + } + + // 重用现有工作进程 + return this.workers[this.workers.length - 1]; + } + + async cleanup() { + await Promise.all( + this.workers.map(worker => + new Promise(resolve => { + worker.on('exit', resolve); + worker.kill(); + }) + ) + ); + this.workers = []; + } +} +``` + +### 内存管理 +```typescript +import { fork } from '@testring/child-process'; + +const createManagedWorker = async (script: string) => { + const worker = await fork(script); + + // 监控内存使用 + const memoryCheck = setInterval(() => { + const usage = process.memoryUsage(); + if (usage.heapUsed > 100 * 1024 * 1024) { // 100MB + console.warn('内存使用过高,考虑重启进程'); + } + }, 5000); + + worker.on('exit', () => { + clearInterval(memoryCheck); + }); + + return worker; +}; +``` + +## 最佳实践 + +### 1. 进程生命周期管理 +```typescript +// 确保进程正确清理 +process.on('exit', () => { + // 清理所有子进程 + workers.forEach(worker => worker.kill()); +}); + +process.on('SIGTERM', () => { + // 优雅关闭 + workers.forEach(worker => worker.kill('SIGTERM')); +}); +``` + +### 2. 错误边界 +```typescript +// 使用错误边界保护主进程 +const safeExecute = async (script: string, data: any) => { + try { + const worker = await fork(script); + + return await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + worker.kill(); + reject(new Error('执行超时')); + }, 30000); + + worker.on('message', (result) => { + clearTimeout(timeout); + resolve(result); + }); + + worker.on('error', (error) => { + clearTimeout(timeout); + reject(error); + }); + + worker.send(data); + }); + } catch (error) { + console.error('执行失败:', error); + throw error; + } +}; +``` + +### 3. 调试友好 +```typescript +// 开发模式下启用调试 +const isDevelopment = process.env.NODE_ENV === 'development'; + +const worker = await fork('./worker.js', [], { + debug: isDevelopment +}); + +if (isDevelopment && worker.debugPort) { + console.log(`🐛 调试端口: ${worker.debugPort}`); +} +``` + +## 安装 + +```bash +npm install @testring/child-process +``` + +## 依赖 + +- `@testring/utils` - 工具函数(端口检测等) +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/test-worker` - 测试工作进程管理 +- `@testring/transport` - 进程间通信 +- `@testring/utils` - 实用工具函数 \ No newline at end of file diff --git a/core/cli/README.md b/core/cli/README.md index 820ba1f4b..8aa7c9e95 100644 --- a/core/cli/README.md +++ b/core/cli/README.md @@ -1,16 +1,153 @@ -# `@testring/cli` +# @testring/cli +命令行界面模块,提供了 testring 框架的命令行工具和用户交互功能。 +## 功能概述 -## Install -Using npm: +该模块是 testring 框架的命令行入口,负责: +- 解析命令行参数 +- 处理用户输入 +- 管理测试运行流程 +- 提供命令行帮助信息 +## 主要功能 + +### 命令支持 +- **`run`** - 运行测试命令(默认命令) +- **`--help`** - 显示帮助信息 +- **`--version`** - 显示版本信息 + +### 配置选项 +支持以下命令行参数: + +- `--config` - 自定义配置文件路径 +- `--tests` - 测试文件搜索模式(glob 模式) +- `--plugins` - 插件列表 +- `--bail` - 测试失败后立即停止 +- `--workerLimit` - 并行测试工作进程数量 +- `--retryCount` - 重试次数 +- `--retryDelay` - 重试延迟时间 +- `--logLevel` - 日志级别 +- `--envConfig` - 环境配置文件路径 +- `--devtool` - 启用开发工具(已弃用) + +## 使用方法 + +### 基本命令 +```bash +# 运行测试(默认) +testring +testring run + +# 指定测试文件 +testring run --tests "./tests/**/*.spec.js" + +# 使用自定义配置 +testring run --config ./my-config.json + +# 设置并行工作进程数 +testring run --workerLimit 4 + +# 设置重试次数 +testring run --retryCount 3 + +# 设置日志级别 +testring run --logLevel debug ``` -npm install --save-dev @testring/cli + +### 插件配置 +```bash +# 使用单个插件 +testring run --plugins @testring/plugin-selenium-driver + +# 使用多个插件 +testring run --plugins @testring/plugin-selenium-driver --plugins @testring/plugin-babel ``` -or using yarn: +### 环境配置 +```bash +# 使用环境配置覆盖主配置 +testring run --config ./config.json --envConfig ./env.json +``` + +## 配置文件 +### 基本配置文件 (.testringrc) +```json +{ + "tests": "./tests/**/*.spec.js", + "plugins": [ + "@testring/plugin-selenium-driver", + "@testring/plugin-babel" + ], + "workerLimit": 2, + "retryCount": 3, + "retryDelay": 2000, + "logLevel": "info", + "bail": false +} ``` -yarn add @testring/cli --dev -``` \ No newline at end of file + +### JavaScript 配置文件 +```javascript +module.exports = { + tests: "./tests/**/*.spec.js", + plugins: [ + "@testring/plugin-selenium-driver" + ], + workerLimit: 2, + // 可以是函数 + retryCount: process.env.CI ? 1 : 3 +}; +``` + +### 异步配置文件 +```javascript +module.exports = async () => { + const config = await loadConfiguration(); + return { + tests: "./tests/**/*.spec.js", + plugins: config.plugins, + workerLimit: config.workerLimit + }; +}; +``` + +## 错误处理 + +CLI 模块提供了完善的错误处理机制: +- 捕获并格式化运行时错误 +- 提供详细的错误信息 +- 支持优雅的进程退出 +- 处理用户中断信号(Ctrl+C) + +## 进程管理 + +支持以下进程信号: +- `SIGINT` - 用户中断(Ctrl+C) +- `SIGUSR1` - 用户信号1 +- `SIGUSR2` - 用户信号2 +- `SIGHUP` - 终端挂起 +- `SIGQUIT` - 退出信号 +- `SIGABRT` - 异常终止 +- `SIGTERM` - 终止信号 + +## 安装 + +```bash +npm install @testring/cli +``` + +## 依赖 + +- `yargs` - 命令行参数解析 +- `@testring/logger` - 日志记录 +- `@testring/cli-config` - 配置管理 +- `@testring/transport` - 进程通信 +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/cli-config` - 配置文件处理 +- `@testring/api` - 测试 API +- `@testring/test-run-controller` - 测试运行控制 \ No newline at end of file diff --git a/core/fs-reader/README.md b/core/fs-reader/README.md index 8390b4954..f10262fd3 100644 --- a/core/fs-reader/README.md +++ b/core/fs-reader/README.md @@ -1,16 +1,304 @@ -# `@testring/fs-reader` +# @testring/fs-reader +文件系统读取器模块,提供了测试文件的查找、读取和解析功能。 +## 功能概述 -## Install -Using npm: +该模块负责处理测试文件的文件系统操作,包括: +- 根据 glob 模式查找测试文件 +- 读取和解析测试文件内容 +- 支持插件化的文件处理 +- 提供文件缓存和优化 +## 主要组件 + +### FSReader +文件系统读取器主类: + +```typescript +export class FSReader extends PluggableModule implements IFSReader { + // 根据模式查找文件 + find(pattern: string): Promise + + // 读取单个文件 + readFile(fileName: string): Promise +} +``` + +### 文件接口 +```typescript +interface IFile { + path: string; // 文件路径 + content: string; // 文件内容 + dependencies?: string[]; // 依赖文件 +} +``` + +## 主要功能 + +### 文件查找 +使用 glob 模式查找测试文件: + +```typescript +import { FSReader } from '@testring/fs-reader'; + +const fsReader = new FSReader(); + +// 查找所有测试文件 +const files = await fsReader.find('./tests/**/*.spec.js'); +console.log('找到的测试文件:', files.map(f => f.path)); + +// 支持复杂的 glob 模式 +const unitTests = await fsReader.find('./src/**/*.{test,spec}.{js,ts}'); +``` + +### 文件读取 +读取单个文件的内容: + +```typescript +import { FSReader } from '@testring/fs-reader'; + +const fsReader = new FSReader(); + +// 读取特定文件 +const file = await fsReader.readFile('./tests/login.spec.js'); +if (file) { + console.log('文件路径:', file.path); + console.log('文件内容:', file.content); +} +``` + +## 支持的文件格式 + +### JavaScript 文件 +```javascript +// tests/example.spec.js +describe('示例测试', () => { + it('应该通过测试', () => { + expect(true).toBe(true); + }); +}); ``` -npm install --save-dev @testring/fs-reader + +### TypeScript 文件 +```typescript +// tests/example.spec.ts +describe('示例测试', () => { + it('应该通过测试', () => { + expect(true).toBe(true); + }); +}); ``` -or using yarn: +### 模块化测试 +```javascript +// tests/modular.spec.js +import { helper } from './helper'; + +describe('模块化测试', () => { + it('使用辅助函数', () => { + expect(helper.add(1, 2)).toBe(3); + }); +}); +``` + +## 插件支持 + +FSReader 支持插件来扩展文件处理功能: +### 插件钩子 +- `beforeResolve` - 文件解析前处理 +- `afterResolve` - 文件解析后处理 + +### 自定义文件处理插件 +```typescript +export default (pluginAPI) => { + const fsReader = pluginAPI.getFSReader(); + + if (fsReader) { + // 文件解析前处理 + fsReader.beforeResolve((files) => { + // 过滤掉某些文件 + return files.filter(file => !file.path.includes('skip')); + }); + + // 文件解析后处理 + fsReader.afterResolve((files) => { + // 添加额外的文件信息 + return files.map(file => ({ + ...file, + lastModified: fs.statSync(file.path).mtime + })); + }); + } +}; ``` -yarn add @testring/fs-reader --dev -``` \ No newline at end of file + +## Glob 模式支持 + +### 基本模式 +```typescript +// 匹配所有 .js 文件 +await fsReader.find('**/*.js'); + +// 匹配特定目录 +await fsReader.find('./tests/**/*.spec.js'); + +// 匹配多种文件类型 +await fsReader.find('**/*.{js,ts}'); +``` + +### 高级模式 +```typescript +// 排除某些文件 +await fsReader.find('**/*.spec.js', { ignore: ['**/node_modules/**'] }); + +// 匹配特定命名模式 +await fsReader.find('**/*.{test,spec}.{js,ts}'); + +// 深度限制 +await fsReader.find('**/!(node_modules)/**/*.spec.js'); +``` + +## 文件解析 + +### 依赖解析 +自动解析文件依赖关系: + +```typescript +// 主测试文件 +import { helper } from './helper'; +import { config } from '../config'; + +// FSReader 会自动识别依赖 +const files = await fsReader.find('./tests/**/*.spec.js'); +// 结果中包含依赖信息 +// file.dependencies = ['./helper.js', '../config.js'] +``` + +### 内容解析 +解析文件内容并提取信息: + +```typescript +const file = await fsReader.readFile('./tests/example.spec.js'); +// file.content 包含完整的文件内容 +// 可以进一步解析 AST 或提取测试用例信息 +``` + +## 性能优化 + +### 文件缓存 +- 自动缓存已读取的文件 +- 监听文件变化,自动更新缓存 +- 减少重复的文件系统访问 + +### 并行处理 +- 并行读取多个文件 +- 异步处理文件内容 +- 优化大量文件的处理性能 + +### 内存管理 +- 智能的内存使用 +- 及时释放不需要的文件内容 +- 支持大型项目的文件处理 + +## 错误处理 + +### 文件不存在 +```typescript +try { + const files = await fsReader.find('./nonexistent/**/*.js'); +} catch (error) { + console.error('没有找到匹配的文件:', error.message); +} +``` + +### 文件读取错误 +```typescript +const file = await fsReader.readFile('./protected-file.js'); +if (!file) { + console.log('文件读取失败或文件不存在'); +} +``` + +### 权限问题 +```typescript +try { + await fsReader.find('./protected-dir/**/*.js'); +} catch (error) { + if (error.code === 'EACCES') { + console.error('没有权限访问文件'); + } +} +``` + +## 配置选项 + +### 查找选项 +```typescript +interface FindOptions { + ignore?: string[]; // 忽略的文件模式 + absolute?: boolean; // 返回绝对路径 + maxDepth?: number; // 最大搜索深度 +} +``` + +### 读取选项 +```typescript +interface ReadOptions { + encoding?: string; // 文件编码 + cache?: boolean; // 是否使用缓存 +} +``` + +## 使用示例 + +### 基本用法 +```typescript +import { FSReader } from '@testring/fs-reader'; + +const fsReader = new FSReader(); + +// 查找所有测试文件 +const testFiles = await fsReader.find('./tests/**/*.spec.js'); + +// 处理每个文件 +for (const file of testFiles) { + console.log(`处理文件: ${file.path}`); + // 执行测试或其他处理 +} +``` + +### 与其他模块集成 +```typescript +import { FSReader } from '@testring/fs-reader'; +import { TestRunner } from '@testring/test-runner'; + +const fsReader = new FSReader(); +const testRunner = new TestRunner(); + +// 查找并执行测试 +const files = await fsReader.find('./tests/**/*.spec.js'); +for (const file of files) { + await testRunner.execute(file); +} +``` + +## 安装 + +```bash +npm install @testring/fs-reader +``` + +## 依赖 + +- `@testring/pluggable-module` - 插件支持 +- `@testring/logger` - 日志记录 +- `@testring/types` - 类型定义 +- `glob` - 文件模式匹配 + +## 相关模块 + +- `@testring/test-run-controller` - 测试运行控制器 +- `@testring/dependencies-builder` - 依赖构建器 +- `@testring/plugin-api` - 插件 API \ No newline at end of file diff --git a/core/logger/README.md b/core/logger/README.md index 1da67d5d8..4d7b00151 100644 --- a/core/logger/README.md +++ b/core/logger/README.md @@ -1,16 +1,231 @@ -# `@testring/logger` +# @testring/logger +分布式日志系统模块,提供了多进程环境下的日志记录和管理功能。 +## 功能概述 -## Install -Using npm: +该模块提供了完整的日志记录解决方案,支持: +- 多进程环境下的日志聚合 +- 可配置的日志级别过滤 +- 插件化的日志处理 +- 格式化的日志输出 +## 主要组件 + +### LoggerServer +日志服务器,负责处理和输出日志: + +```typescript +export class LoggerServer extends PluggableModule { + constructor( + config: IConfigLogger, + transportInstance: ITransport, + stdout: NodeJS.WritableStream + ) +} +``` + +### LoggerClient +日志客户端,提供日志记录接口: + +```typescript +export interface ILoggerClient { + verbose(...args: any[]): void + debug(...args: any[]): void + info(...args: any[]): void + warn(...args: any[]): void + error(...args: any[]): void +} +``` + +## 日志级别 + +支持以下日志级别(按优先级排序): + +1. **`verbose`** - 最详细的调试信息 +2. **`debug`** - 调试信息 +3. **`info`** - 一般信息(默认级别) +4. **`warning`** - 警告信息 +5. **`error`** - 错误信息 +6. **`silent`** - 静默模式,不输出任何日志 + +## 使用方法 + +### 基本使用 +```typescript +import { loggerClient } from '@testring/logger'; + +// 记录不同级别的日志 +loggerClient.verbose('详细的调试信息'); +loggerClient.debug('调试信息'); +loggerClient.info('一般信息'); +loggerClient.warn('警告信息'); +loggerClient.error('错误信息'); +``` + +### 配置日志级别 +```typescript +import { LoggerServer } from '@testring/logger'; + +const config = { + logLevel: 'debug', // 只显示 debug 及以上级别的日志 + silent: false // 是否静默模式 +}; + +const loggerServer = new LoggerServer(config, transport, process.stdout); +``` + +### 日志格式化 +```typescript +// 日志会自动格式化输出 +loggerClient.info('测试开始', { testId: 'test-001' }); +// 输出: [INFO] 测试开始 { testId: 'test-001' } +``` + +## 配置选项 + +### 日志级别配置 +```typescript +interface IConfigLogger { + logLevel: 'verbose' | 'debug' | 'info' | 'warning' | 'error' | 'silent'; + silent: boolean; // 快速静默模式 +} +``` + +### 命令行配置 +```bash +# 设置日志级别 +testring run --logLevel debug + +# 静默模式 +testring run --silent + +# 或者 +testring run --logLevel silent +``` + +### 配置文件 +```json +{ + "logLevel": "debug", + "silent": false +} +``` + +## 插件支持 + +日志系统支持插件扩展,可以自定义日志处理逻辑: + +### 插件钩子 +- `beforeLog` - 日志输出前处理 +- `onLog` - 日志输出时处理 +- `onError` - 错误处理 + +### 自定义日志插件 +```typescript +export default (pluginAPI) => { + const logger = pluginAPI.getLogger(); + + logger.beforeLog((logEntity, meta) => { + // 日志预处理 + return { + ...logEntity, + timestamp: new Date().toISOString() + }; + }); + + logger.onLog((logEntity, meta) => { + // 自定义日志处理 + if (logEntity.logLevel === 'error') { + // 发送错误报告 + sendErrorReport(logEntity.content); + } + }); +}; +``` + +## 多进程支持 + +### 进程间日志聚合 +日志系统在多进程环境中会自动聚合所有进程的日志: + +```typescript +// 子进程中的日志 +loggerClient.info('子进程日志'); + +// 会自动传输到主进程并统一输出 +// [INFO] [Worker-1] 子进程日志 +``` + +### 进程标识 +每个进程的日志都会带有进程标识,便于调试: +- 主进程:无标识 +- 子进程:`[Worker-{ID}]` + +## 日志格式 + +### 标准格式 +``` +[LEVEL] [ProcessID] Message ``` -npm install --save-dev @testring/logger + +### 示例输出 +``` +[INFO] 测试开始 +[DEBUG] [Worker-1] 加载测试文件: test.spec.js +[WARN] [Worker-2] 测试重试: 第2次 +[ERROR] [Worker-1] 测试失败: 断言错误 +``` + +## 性能优化 + +### 异步日志处理 +- 使用队列系统处理日志 +- 避免阻塞主流程 +- 支持批量处理 + +### 内存管理 +- 自动清理日志队列 +- 防止内存泄漏 +- 可配置的缓冲区大小 + +## 调试功能 + +### 日志追踪 +```typescript +// 启用详细日志追踪 +const config = { + logLevel: 'verbose' +}; + +// 会输出详细的执行信息 +loggerClient.verbose('详细的调试信息', { + stack: new Error().stack +}); ``` -or using yarn: +### 错误上下文 +错误日志会包含完整的上下文信息: +- 错误堆栈 +- 进程信息 +- 时间戳 +- 相关参数 +## 安装 + +```bash +npm install @testring/logger ``` -yarn add @testring/logger --dev -``` \ No newline at end of file + +## 依赖 + +- `@testring/pluggable-module` - 插件支持 +- `@testring/utils` - 工具函数 +- `@testring/transport` - 进程间通信 +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/cli` - 命令行工具 +- `@testring/plugin-api` - 插件接口 +- `@testring/transport` - 传输层 \ No newline at end of file diff --git a/core/test-worker/README.md b/core/test-worker/README.md index c3011e0bb..f8eaf111d 100644 --- a/core/test-worker/README.md +++ b/core/test-worker/README.md @@ -1,16 +1,162 @@ -# `@testring/test-worker` +# @testring/test-worker +测试工作进程模块,提供了测试的并行执行和隔离环境支持。 +## 功能概述 -## Install -Using npm: +该模块负责创建和管理测试工作进程,确保测试在独立的环境中执行: +- 创建子进程来运行测试 +- 管理测试工作进程的生命周期 +- 提供进程间通信机制 +- 处理测试执行结果 +## 主要组件 + +### TestWorker +测试工作进程管理器,负责创建和管理工作进程: + +```typescript +export class TestWorker { + spawn(): ITestWorkerInstance // 创建新的工作进程实例 +} +``` + +### TestWorkerInstance +测试工作进程实例,代表一个运行中的测试工作进程: + +```typescript +export interface ITestWorkerInstance { + getWorkerID(): string // 获取工作进程ID + execute(test: IQueuedTest): Promise // 执行测试 + kill(): Promise // 终止工作进程 +} ``` -npm install --save-dev @testring/test-worker + +## 工作原理 + +### 进程隔离 +- 每个测试在独立的 Node.js 子进程中运行 +- 避免测试之间的相互影响 +- 支持并行执行多个测试 + +### 通信机制 +- 使用 IPC(进程间通信)传递测试数据 +- 支持双向通信(父进程 ↔ 子进程) +- 处理测试结果和错误信息 + +### 生命周期管理 +1. **创建阶段** - 启动子进程 +2. **初始化阶段** - 设置测试环境 +3. **执行阶段** - 运行测试代码 +4. **清理阶段** - 清理资源并终止进程 + +## 使用方法 + +### 基本使用 +```typescript +import { TestWorker } from '@testring/test-worker'; + +// 创建测试工作进程管理器 +const testWorker = new TestWorker(config, transport); + +// 生成工作进程实例 +const workerInstance = testWorker.spawn(); + +// 执行测试 +await workerInstance.execute(queuedTest); + +// 终止工作进程 +await workerInstance.kill(); ``` -or using yarn: +### 并行执行 +```typescript +import { TestWorker } from '@testring/test-worker'; +const testWorker = new TestWorker(config, transport); + +// 创建多个工作进程 +const workers = Array.from({ length: 4 }, () => testWorker.spawn()); + +// 并行执行测试 +const promises = workers.map(worker => worker.execute(test)); +await Promise.all(promises); + +// 清理所有工作进程 +await Promise.all(workers.map(worker => worker.kill())); ``` -yarn add @testring/test-worker --dev -``` \ No newline at end of file + +## 配置选项 + +### 工作进程限制 +```typescript +interface IConfig { + workerLimit: number | 'local'; // 工作进程数量限制 + restartWorker: boolean; // 是否在测试完成后重启工作进程 +} +``` + +### 本地模式 +当 `workerLimit` 设置为 `'local'` 时,测试在主进程中运行: +- 不创建子进程 +- 适用于调试和开发 +- 性能更高但缺少隔离 + +## 错误处理 + +### 进程异常处理 +- 捕获子进程的异常退出 +- 提供详细的错误信息 +- 支持自动重启机制 + +### 通信错误处理 +- 处理 IPC 通信失败 +- 超时检测和处理 +- 优雅的进程终止 + +## 性能优化 + +### 进程复用 +- 支持工作进程的复用 +- 减少进程创建开销 +- 可配置的进程重启策略 + +### 内存管理 +- 监控工作进程的内存使用 +- 防止内存泄漏 +- 自动清理资源 + +## 调试支持 + +### 调试模式 +```typescript +// 启用调试模式 +const config = { + workerLimit: 'local', // 在主进程中运行 + debug: true // 启用调试输出 +}; +``` + +### 日志记录 +- 详细的进程创建和销毁日志 +- 测试执行状态跟踪 +- 错误和异常记录 + +## 安装 + +```bash +npm install @testring/test-worker +``` + +## 依赖 + +- `@testring/child-process` - 子进程管理 +- `@testring/transport` - 进程间通信 +- `@testring/logger` - 日志记录 +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/test-run-controller` - 测试运行控制器 +- `@testring/child-process` - 子进程管理 +- `@testring/transport` - 传输层 \ No newline at end of file diff --git a/core/transport/README.md b/core/transport/README.md index e9179149a..7396fe5b2 100644 --- a/core/transport/README.md +++ b/core/transport/README.md @@ -1,16 +1,274 @@ -# `@testring/transport` +# @testring/transport +传输层模块,提供了多进程环境下的通信机制和消息传递功能。 +## 功能概述 -## Install -Using npm: +该模块是 testring 框架的核心通信层,负责: +- 进程间通信(IPC)管理 +- 消息路由和传递 +- 广播和点对点通信 +- 子进程注册和管理 +## 主要组件 + +### Transport +主要的传输层类,提供完整的通信功能: + +```typescript +export class Transport implements ITransport { + // 点对点通信 + send(processID: string, messageType: string, payload: T): Promise + + // 广播通信 + broadcast(messageType: string, payload: T): void + broadcastLocal(messageType: string, payload: T): void + broadcastUniversally(messageType: string, payload: T): void + + // 事件监听 + on(messageType: string, callback: TransportMessageHandler): void + once(messageType: string, callback: TransportMessageHandler): void + onceFrom(processID: string, messageType: string, callback: TransportMessageHandler): void + + // 进程管理 + registerChild(processID: string, child: IWorkerEmitter): void + getProcessesList(): Array +} +``` + +### DirectTransport +直接传输,用于点对点通信: + +```typescript +export class DirectTransport { + send(processID: string, messageType: string, payload: T): Promise + registerChild(processID: string, child: IWorkerEmitter): void + getProcessesList(): Array +} +``` + +### BroadcastTransport +广播传输,用于广播通信: + +```typescript +export class BroadcastTransport { + broadcast(messageType: string, payload: T): void + broadcastLocal(messageType: string, payload: T): void +} +``` + +## 通信模式 + +### 点对点通信 +用于向特定进程发送消息: + +```typescript +import { transport } from '@testring/transport'; + +// 向指定进程发送消息 +await transport.send('worker-1', 'execute-test', { + testFile: 'test.spec.js', + config: {...} +}); +``` + +### 广播通信 +用于向所有进程发送消息: + +```typescript +import { transport } from '@testring/transport'; + +// 向所有子进程广播 +transport.broadcast('config-updated', newConfig); + +// 向本地进程广播 +transport.broadcastLocal('shutdown', null); + +// 通用广播(根据环境自动选择) +transport.broadcastUniversally('status-update', status); ``` -npm install --save-dev @testring/transport + +### 事件监听 +监听来自其他进程的消息: + +```typescript +import { transport } from '@testring/transport'; + +// 监听特定类型的消息 +transport.on('test-result', (result, processID) => { + console.log(`收到来自 ${processID} 的测试结果:`, result); +}); + +// 一次性监听 +transport.once('init-complete', (data) => { + console.log('初始化完成'); +}); + +// 监听来自特定进程的消息 +transport.onceFrom('worker-1', 'ready', () => { + console.log('Worker-1 已就绪'); +}); ``` -or using yarn: +## 进程管理 + +### 子进程注册 +```typescript +import { transport } from '@testring/transport'; + +// 注册子进程 +const childProcess = fork('./worker.js'); +transport.registerChild('worker-1', childProcess); + +// 获取所有已注册的进程 +const processes = transport.getProcessesList(); +console.log('已注册进程:', processes); +``` + +### 进程检测 +```typescript +import { transport } from '@testring/transport'; + +// 检查是否为子进程 +if (transport.isChildProcess()) { + console.log('运行在子进程中'); +} else { + console.log('运行在主进程中'); +} +``` + +## 消息格式 + +### 标准消息格式 +```typescript +interface ITransportDirectMessage { + type: string; // 消息类型 + payload: any; // 消息内容 +} +``` +### 消息处理器 +```typescript +type TransportMessageHandler = (message: T, processID?: string) => void; ``` -yarn add @testring/transport --dev -``` \ No newline at end of file + +## 使用场景 + +### 测试执行协调 +```typescript +// 主进程:分发测试任务 +transport.send('worker-1', 'execute-test', { + testFile: 'login.spec.js', + config: testConfig +}); + +// 子进程:监听测试任务 +transport.on('execute-test', async (task) => { + const result = await executeTest(task.testFile, task.config); + transport.send('main', 'test-result', result); +}); +``` + +### 日志收集 +```typescript +// 子进程:发送日志 +transport.send('main', 'log', { + level: 'info', + message: '测试开始执行' +}); + +// 主进程:收集日志 +transport.on('log', (logEntry, processID) => { + console.log(`[${processID}] ${logEntry.level}: ${logEntry.message}`); +}); +``` + +### 配置同步 +```typescript +// 主进程:广播配置更新 +transport.broadcast('config-update', newConfig); + +// 所有子进程:接收配置更新 +transport.on('config-update', (config) => { + updateLocalConfig(config); +}); +``` + +## 错误处理 + +### 通信错误 +```typescript +try { + await transport.send('worker-1', 'test-command', data); +} catch (error) { + console.error('发送消息失败:', error); + // 处理通信错误 +} +``` + +### 超时处理 +```typescript +// 设置超时监听 +const timeout = setTimeout(() => { + console.error('消息响应超时'); +}, 5000); + +transport.onceFrom('worker-1', 'response', (data) => { + clearTimeout(timeout); + console.log('收到响应:', data); +}); +``` + +## 性能优化 + +### 消息缓存 +- 自动缓存未送达的消息 +- 进程就绪后自动发送缓存消息 +- 避免消息丢失 + +### 连接池管理 +- 复用进程连接 +- 自动清理断开的连接 +- 优化内存使用 + +## 调试功能 + +### 消息追踪 +```typescript +// 启用调试模式 +process.env.DEBUG = 'testring:transport'; + +// 会输出详细的消息传递日志 +transport.send('worker-1', 'test-message', data); +// 输出: [DEBUG] 发送消息 test-message 到 worker-1 +``` + +### 连接状态监控 +```typescript +// 监控进程连接状态 +transport.on('process-connected', (processID) => { + console.log(`进程 ${processID} 已连接`); +}); + +transport.on('process-disconnected', (processID) => { + console.log(`进程 ${processID} 已断开`); +}); +``` + +## 安装 + +```bash +npm install @testring/transport +``` + +## 依赖 + +- `@testring/child-process` - 子进程管理 +- `@testring/types` - 类型定义 +- `events` - Node.js 事件模块 + +## 相关模块 + +- `@testring/test-worker` - 测试工作进程 +- `@testring/logger` - 日志系统 +- `@testring/child-process` - 子进程管理 \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..768bf432c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +# 文档目录 + +`docs/` 目录包含了 testring 测试框架的完整文档,提供了 API 参考、配置说明和插件开发指南。 + +## 文档内容 + +### 核心文档 +- **`api.md`** - API 参考文档,详细说明了框架提供的主要 API 接口 +- **`config.md`** - 配置 API 参考,完整的配置选项说明和使用示例 +- **`plugin-handbook.md`** - 插件开发手册,插件系统的使用和开发指南 + +## 文档说明 + +### API 参考 (api.md) +提供了 testring 框架的主要命令和接口说明: +- `run` - 运行测试命令 +- `record` - 录制测试会话命令 + +### 配置参考 (config.md) +详细说明了框架的配置系统: +- 配置文件优先级:CLI 参数 > 环境配置 > 配置文件 +- 所有可用的配置选项和参数说明 +- 配置示例和最佳实践 + +### 插件手册 (plugin-handbook.md) +插件系统的完整指南: +- 插件的使用方法 +- 插件开发规范 +- 插件 API 说明 +- 插件开发示例 + +## 使用指南 + +### 快速开始 +1. 阅读 `api.md` 了解基本命令 +2. 查看 `config.md` 配置测试环境 +3. 根据需要参考 `plugin-handbook.md` 开发或使用插件 + +### 开发者指南 +- 如果要开发插件,重点参考 `plugin-handbook.md` +- 如果要集成框架,重点参考 `api.md` 和 `config.md` +- 如果要贡献代码,建议阅读所有文档 + +## 文档维护 + +这些文档与代码同步更新,确保文档的准确性和时效性。如果发现文档问题或需要补充,请提交 Issue 或 Pull Request。 \ No newline at end of file diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 000000000..7c6d9fe42 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,97 @@ +# Packages 扩展包 + +`packages/` 目录包含了 testring 测试框架的扩展包和插件,提供了框架的额外功能和集成能力。这些包主要用于浏览器驱动、Web 应用测试、开发工具等功能扩展。 + +## 目录结构 + +### 浏览器驱动包 +- **`plugin-selenium-driver/`** - Selenium WebDriver 插件,支持多种浏览器自动化 +- **`plugin-playwright-driver/`** - Playwright 驱动插件,现代浏览器自动化解决方案 +- **`browser-proxy/`** - 浏览器代理服务,提供浏览器与测试框架的通信桥梁 + +### Web 应用测试包 +- **`web-application/`** - Web 应用测试包,提供 Web 应用的测试功能 +- **`element-path/`** - 元素路径定位,提供 DOM 元素的精确定位功能 +- **`e2e-test-app/`** - 端到端测试应用,包含完整的测试用例和示例 + +### 开发工具包 +- **`devtool-frontend/`** - 开发工具前端,提供测试调试和监控界面 +- **`devtool-backend/`** - 开发工具后端,提供开发工具的后端服务 +- **`devtool-extension/`** - 开发工具扩展,浏览器扩展形式的开发工具 + +### 网络和通信包 +- **`client-ws-transport/`** - WebSocket 传输客户端,支持 WebSocket 通信 +- **`http-api/`** - HTTP API 包,提供 HTTP 接口支持 + +### 文件和存储包 +- **`plugin-fs-store/`** - 文件系统存储插件,提供文件存储功能 +- **`download-collector-crx/`** - 下载收集器 Chrome 扩展,收集浏览器下载文件 + +### 构建和工具包 +- **`plugin-babel/`** - Babel 插件,支持 ES6+ 语法转换 +- **`test-utils/`** - 测试工具包,提供测试相关的实用工具函数 + +## 主要特性 + +1. **浏览器支持** - 支持多种浏览器驱动(Selenium、Playwright) +2. **Web 应用测试** - 专门针对 Web 应用的测试功能 +3. **开发工具** - 完整的开发和调试工具链 +4. **网络通信** - 多种网络通信方式支持 +5. **文件处理** - 文件上传、下载和存储功能 +6. **现代化构建** - 支持现代 JavaScript 语法和构建工具 + +## 插件分类 + +### 驱动插件 +- `plugin-selenium-driver` - 传统 Selenium 驱动 +- `plugin-playwright-driver` - 现代 Playwright 驱动 + +### 功能插件 +- `plugin-babel` - 代码转换插件 +- `plugin-fs-store` - 文件存储插件 + +### 工具包 +- `browser-proxy` - 浏览器代理 +- `element-path` - 元素定位 +- `test-utils` - 测试工具 +- `http-api` - HTTP 接口 + +### 开发工具 +- `devtool-frontend` - 前端界面 +- `devtool-backend` - 后端服务 +- `devtool-extension` - 浏览器扩展 + +### 应用和示例 +- `web-application` - Web 应用测试 +- `e2e-test-app` - E2E 测试示例 + +## 使用说明 + +这些包可以通过 npm 独立安装使用,也可以作为 testring 框架的插件使用。每个包都有独立的版本管理和发布周期。 + +### 安装示例 +```bash +# 安装 Selenium 驱动插件 +npm install @testring/plugin-selenium-driver + +# 安装 Playwright 驱动插件 +npm install @testring/plugin-playwright-driver + +# 安装 Web 应用测试包 +npm install @testring/web-application +``` + +### 插件配置示例 +```json +{ + "plugins": [ + "@testring/plugin-selenium-driver", + "@testring/plugin-playwright-driver", + "@testring/plugin-babel" + ] +} +``` + +## 开发和扩展 + +如果需要开发新的插件或扩展包,可以参考现有包的结构和实现方式。每个包都遵循统一的项目结构和开发规范。 \ No newline at end of file diff --git a/packages/plugin-selenium-driver/README.md b/packages/plugin-selenium-driver/README.md index b3efe67dc..3f6769931 100644 --- a/packages/plugin-selenium-driver/README.md +++ b/packages/plugin-selenium-driver/README.md @@ -1,16 +1,370 @@ -# `@testring/plugin-selenium-driver` +# @testring/plugin-selenium-driver +Selenium WebDriver 插件,为 testring 框架提供浏览器自动化测试功能。 +## 功能概述 -## Install -Using npm: +该插件集成了 Selenium WebDriver,提供了: +- 多浏览器支持(Chrome、Firefox、Safari、Edge) +- 自动化浏览器操作 +- 元素定位和交互 +- 页面导航和操作 +- 截图和调试功能 +## 主要特性 + +### 浏览器支持 +- **Chrome** - 最常用的测试浏览器 +- **Firefox** - 跨平台支持 +- **Safari** - macOS 原生浏览器 +- **Edge** - Windows 现代浏览器 +- **Headless 模式** - 无界面运行 + +### 元素操作 +- 元素查找和定位 +- 点击、输入、选择操作 +- 鼠标和键盘事件 +- 拖拽和触摸操作 + +### 页面管理 +- 页面导航和跳转 +- 窗口和标签页管理 +- 框架和弹窗处理 +- 等待和同步机制 + +## 安装 + +```bash +npm install @testring/plugin-selenium-driver +``` + +### 额外依赖 +需要安装对应的 WebDriver: + +```bash +# Chrome +npm install chromedriver + +# Firefox +npm install geckodriver + +# Safari (macOS) +# 需要在系统中启用 Safari 开发者选项 + +# Edge +npm install edgedriver +``` + +## 配置 + +### 基本配置 +```json +{ + "plugins": [ + ["@testring/plugin-selenium-driver", { + "browser": "chrome", + "headless": true, + "windowSize": "1920x1080" + }] + ] +} +``` + +### 完整配置选项 +```json +{ + "plugins": [ + ["@testring/plugin-selenium-driver", { + "browser": "chrome", + "headless": false, + "windowSize": "1920x1080", + "seleniumHub": "http://localhost:4444/wd/hub", + "capabilities": { + "browserName": "chrome", + "browserVersion": "latest", + "platformName": "linux" + }, + "chromeOptions": { + "args": ["--disable-web-security", "--allow-running-insecure-content"] + }, + "firefoxOptions": { + "prefs": { + "network.http.phishy-userpass-length": 255 + } + } + }] + ] +} +``` + +## 使用方法 + +### 基本用法 +```javascript +// 测试文件 +describe('登录测试', () => { + it('应该能够成功登录', async () => { + // 导航到登录页面 + await browser.url('https://example.com/login'); + + // 输入用户名和密码 + await browser.setValue('#username', 'testuser'); + await browser.setValue('#password', 'testpass'); + + // 点击登录按钮 + await browser.click('#login-button'); + + // 验证登录成功 + const welcomeText = await browser.getText('#welcome'); + expect(welcomeText).toContain('欢迎'); + }); +}); +``` + +### 元素定位 +```javascript +// 多种定位方式 +await browser.click('#button-id'); // ID +await browser.click('.button-class'); // Class +await browser.click('button[type="submit"]'); // CSS 选择器 +await browser.click('//button[@type="submit"]'); // XPath +await browser.click('=Submit'); // 文本内容 +await browser.click('*=Submit'); // 部分文本 ``` -npm install --save-dev @testring/plugin-selenium-driver + +### 页面操作 +```javascript +// 页面导航 +await browser.url('https://example.com'); +await browser.back(); +await browser.forward(); +await browser.refresh(); + +// 窗口操作 +await browser.newWindow('https://example.com'); +await browser.switchWindow('window-name'); +await browser.closeWindow(); + +// 框架操作 +await browser.switchToFrame('#frame-id'); +await browser.switchToParentFrame(); +``` + +### 等待机制 +```javascript +// 等待元素出现 +await browser.waitForVisible('#element', 5000); + +// 等待元素消失 +await browser.waitForHidden('#loading', 10000); + +// 等待文本内容 +await browser.waitForText('#status', 'Complete', 5000); + +// 等待值变化 +await browser.waitForValue('#input', 'expected-value', 3000); + +// 自定义等待条件 +await browser.waitUntil(() => { + return browser.isVisible('#submit-button'); +}, 5000, '提交按钮未出现'); ``` -or using yarn: +### 表单操作 +```javascript +// 输入框操作 +await browser.setValue('#input', 'test value'); +await browser.addValue('#input', ' additional'); +await browser.clearValue('#input'); + +// 选择框操作 +await browser.selectByVisibleText('#select', 'Option 1'); +await browser.selectByValue('#select', 'option1'); +await browser.selectByIndex('#select', 0); + +// 复选框和单选框 +await browser.click('#checkbox'); +await browser.click('#radio'); +// 文件上传 +await browser.chooseFile('#file-input', './test-file.txt'); ``` -yarn add @testring/plugin-selenium-driver --dev -``` \ No newline at end of file + +### 断言和验证 +```javascript +// 元素存在性 +const isVisible = await browser.isVisible('#element'); +expect(isVisible).toBe(true); + +// 文本内容 +const text = await browser.getText('#element'); +expect(text).toBe('Expected Text'); + +// 属性值 +const value = await browser.getValue('#input'); +expect(value).toBe('expected-value'); + +// 元素属性 +const className = await browser.getAttribute('#element', 'class'); +expect(className).toContain('active'); +``` + +## 高级功能 + +### 多浏览器测试 +```javascript +// 配置多个浏览器 +const browsers = ['chrome', 'firefox', 'safari']; + +browsers.forEach(browserName => { + describe(`${browserName} 测试`, () => { + beforeEach(async () => { + await browser.switchBrowser(browserName); + }); + + it('应该在所有浏览器中正常工作', async () => { + await browser.url('https://example.com'); + // 测试逻辑 + }); + }); +}); +``` + +### 截图功能 +```javascript +// 全屏截图 +await browser.saveScreenshot('./screenshots/full-page.png'); + +// 元素截图 +await browser.saveElementScreenshot('#element', './screenshots/element.png'); + +// 失败时自动截图 +afterEach(async function() { + if (this.currentTest.state === 'failed') { + await browser.saveScreenshot(`./screenshots/failed-${this.currentTest.title}.png`); + } +}); +``` + +### 性能监控 +```javascript +// 页面加载时间 +const startTime = Date.now(); +await browser.url('https://example.com'); +const loadTime = Date.now() - startTime; +console.log(`页面加载时间: ${loadTime}ms`); + +// 网络请求监控 +await browser.setupNetworkCapture(); +await browser.url('https://example.com'); +const networkLogs = await browser.getNetworkLogs(); +``` + +## 调试功能 + +### 调试模式 +```javascript +// 启用调试模式 +await browser.debug(); + +// 暂停执行 +await browser.pause(3000); + +// 控制台日志 +const logs = await browser.getLogs('browser'); +console.log('浏览器日志:', logs); +``` + +### 元素检查 +```javascript +// 获取元素信息 +const element = await browser.$('#element'); +const location = await element.getLocation(); +const size = await element.getSize(); +const tagName = await element.getTagName(); + +console.log('元素位置:', location); +console.log('元素大小:', size); +console.log('元素标签:', tagName); +``` + +## Selenium Grid 支持 + +### 配置 Selenium Grid +```json +{ + "plugins": [ + ["@testring/plugin-selenium-driver", { + "seleniumHub": "http://selenium-hub:4444/wd/hub", + "capabilities": { + "browserName": "chrome", + "browserVersion": "latest", + "platformName": "linux" + } + }] + ] +} +``` + +### Docker 支持 +```yaml +# docker-compose.yml +version: '3' +services: + selenium-hub: + image: selenium/hub:latest + ports: + - "4444:4444" + + chrome: + image: selenium/node-chrome:latest + depends_on: + - selenium-hub + environment: + - HUB_HOST=selenium-hub +``` + +## 故障排除 + +### 常见问题 +1. **浏览器驱动不匹配** + - 确保 ChromeDriver 版本与 Chrome 版本匹配 + - 使用 `chromedriver --version` 检查版本 + +2. **元素定位失败** + - 使用 `browser.debug()` 调试 + - 检查元素是否在框架中 + - 等待元素加载完成 + +3. **超时问题** + - 增加等待时间 + - 使用显式等待而非隐式等待 + - 检查网络连接 + +### 性能优化 +```javascript +// 优化配置 +{ + "chromeOptions": { + "args": [ + "--disable-dev-shm-usage", + "--no-sandbox", + "--disable-gpu", + "--disable-extensions" + ] + } +} +``` + +## 依赖 + +- `selenium-webdriver` - Selenium WebDriver 核心库 +- `@testring/plugin-api` - 插件 API +- `@testring/types` - 类型定义 + +## 相关模块 + +- `@testring/plugin-playwright-driver` - 现代浏览器驱动 +- `@testring/browser-proxy` - 浏览器代理 +- `@testring/element-path` - 元素定位 +- `@testring/web-application` - Web 应用测试 \ No newline at end of file diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 000000000..22037e921 --- /dev/null +++ b/utils/README.md @@ -0,0 +1,74 @@ +# 工具脚本目录 + +`utils/` 目录包含了 testring 项目的构建和维护工具脚本,主要用于项目的自动化管理和开发流程支持。 + +## 目录结构 + +### 构建工具脚本 +- **`add-package-files.js`** - 添加包文件脚本,自动为包添加必要的文件 +- **`check-packages-versions.js`** - 检查包版本脚本,验证包版本的一致性 +- **`cleanup.js`** - 清理脚本,清理构建产物和临时文件 +- **`generate-readme.js`** - 生成 README 脚本,自动生成包的 README 文件 +- **`publish.js`** - 发布脚本,用于包的发布流程 + +### 模板文件 +- **`templates/`** - 模板目录,包含项目模板文件 + - **`tsconfig.json`** - TypeScript 配置模板 + +## 脚本功能 + +### 包管理工具 +- **`add-package-files.js`** - 为每个包添加标准的项目文件(如 .gitignore、.eslintrc 等) +- **`check-packages-versions.js`** - 检查所有包的版本号是否符合 lerna 管理规范 +- **`generate-readme.js`** - 根据模板自动生成各个包的 README.md 文件 + +### 构建和发布工具 +- **`cleanup.js`** - 清理所有包的构建产物,包括 dist 目录、node_modules 等 +- **`publish.js`** - 自动化发布流程,支持批量发布和版本管理 + +## 使用方法 + +这些工具脚本主要通过 npm scripts 调用,在项目根目录的 package.json 中定义了相应的命令: + +```bash +# 清理项目 +npm run cleanup + +# 添加包文件 +npm run add-package-files + +# 生成 README 文件 +npm run generate-readme + +# 检查依赖版本 +npm run check-deps:validate + +# 发布项目 +npm run publish:ci +``` + +## 脚本特点 + +1. **自动化管理** - 支持批量操作多个包 +2. **版本控制** - 严格的版本管理和检查 +3. **模板化** - 使用模板确保项目结构的一致性 +4. **CI/CD 支持** - 支持持续集成和持续部署流程 + +## 开发说明 + +### 添加新脚本 +如果需要添加新的工具脚本,请遵循以下规范: +1. 使用 JavaScript 编写 +2. 支持命令行参数 +3. 包含错误处理和日志输出 +4. 在 package.json 中添加相应的 npm script + +### 模板管理 +模板文件位于 `templates/` 目录下,用于生成新包时的标准化配置。修改模板时请确保向后兼容性。 + +## 维护说明 + +这些工具脚本是项目维护的重要组成部分,定期更新以支持新的功能和改进开发流程。修改时请注意: +1. 测试脚本的兼容性 +2. 更新相关的文档 +3. 考虑对现有包的影响 \ No newline at end of file