@be-link/smart-test 1.0.1-beta.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +386 -323
- package/bin/smart-test.js +15 -0
- package/dist/cli/commands/generate.d.ts +16 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +5 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +618 -0
- package/dist/config/config-loader.d.ts +35 -0
- package/dist/config/config-loader.d.ts.map +1 -0
- package/dist/config/config-schema.d.ts +85 -0
- package/dist/config/config-schema.d.ts.map +1 -0
- package/dist/core/ai-assistant.d.ts +14 -55
- package/dist/core/ai-assistant.d.ts.map +1 -1
- package/dist/core/config.d.ts +4 -12
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/types.d.ts +9 -140
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +102 -289
- package/dist/index.js +101 -291
- package/dist/utils/error-handler.d.ts +41 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +88 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/package.json +18 -3
- package/dist/core/prompts.d.ts +0 -14
- package/dist/core/prompts.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,461 +1,524 @@
|
|
|
1
1
|
# @be-link/smart-test
|
|
2
2
|
|
|
3
|
-
AI-powered visual testing for Playwright
|
|
3
|
+
> AI-powered visual testing for Playwright - 基于 AI 视觉模型的 Playwright 测试工具
|
|
4
|
+
|
|
5
|
+
使用 AI 视觉识别能力,让你的 E2E 测试更智能、更可靠。
|
|
4
6
|
|
|
5
7
|
## ✨ 特性
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
### 🤖 AI 视觉检查
|
|
10
|
+
|
|
11
|
+
- 基于通义千问等视觉模型,智能检查页面是否符合预期
|
|
12
|
+
- 支持自然语言描述测试预期
|
|
13
|
+
- 自动识别页面问题并给出详细反馈
|
|
14
|
+
|
|
15
|
+
### 🎯 零代码测试生成(开发中)
|
|
16
|
+
|
|
17
|
+
- **CLI 工具**:一条命令自动生成测试代码
|
|
18
|
+
- **录制模式**:访问页面自动录制接口和交互
|
|
19
|
+
- **代码分析**:扫描项目代码自动生成 Mock 数据
|
|
20
|
+
- **AI 生成**:基于自然语言描述生成完整测试
|
|
21
|
+
|
|
22
|
+
### 🔧 灵活易用
|
|
23
|
+
|
|
24
|
+
- 与 Playwright 无缝集成,零侵入设计
|
|
25
|
+
- 支持全局配置和单次调用配置
|
|
26
|
+
- 优雅降级:无 API Key 时自动跳过
|
|
27
|
+
- 完整的 TypeScript 类型支持
|
|
28
|
+
|
|
29
|
+
### 📸 自动化
|
|
30
|
+
|
|
31
|
+
- 自动截图并保存
|
|
32
|
+
- 自动生成 Mock 数据
|
|
33
|
+
- 自动生成测试用例
|
|
11
34
|
|
|
12
35
|
## 📦 安装
|
|
13
36
|
|
|
14
37
|
```bash
|
|
38
|
+
# 使用 pnpm(推荐)
|
|
15
39
|
pnpm add -D @be-link/smart-test
|
|
40
|
+
|
|
41
|
+
# 使用 npm
|
|
42
|
+
npm install -D @be-link/smart-test
|
|
43
|
+
|
|
44
|
+
# 使用 yarn
|
|
45
|
+
yarn add -D @be-link/smart-test
|
|
16
46
|
```
|
|
17
47
|
|
|
18
48
|
## 🚀 快速开始
|
|
19
49
|
|
|
20
|
-
###
|
|
50
|
+
### 方式一:使用 CLI 工具(推荐)⭐
|
|
21
51
|
|
|
22
|
-
|
|
23
|
-
import { configure } from '@be-link/smart-test';
|
|
52
|
+
#### 1. 初始化配置
|
|
24
53
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
28
|
-
visionModel: 'qwen3-vl-plus',
|
|
29
|
-
codeModel: 'qwen3-coder-plus',
|
|
30
|
-
});
|
|
54
|
+
```bash
|
|
55
|
+
npx smart-test init
|
|
31
56
|
```
|
|
32
57
|
|
|
33
|
-
|
|
58
|
+
交互式配置向导会引导你完成设置:
|
|
34
59
|
|
|
35
|
-
|
|
60
|
+
```
|
|
61
|
+
━━━ 初始化 Smart Test 配置 ━━━
|
|
36
62
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
? 请输入 AI API Key: sk-xxxxx
|
|
64
|
+
? 选择 AI 模型: 通义千问 (qwen3-vl-plus)
|
|
65
|
+
? 测试文件目录: ./tests/e2e
|
|
66
|
+
? Helpers 目录: ./tests/helpers
|
|
67
|
+
? Playwright Base URL: http://localhost:8080
|
|
68
|
+
? 文件命名风格: kebab-case
|
|
40
69
|
|
|
41
|
-
|
|
42
|
-
|
|
70
|
+
✓ 配置文件已创建: .smarttestrc.json
|
|
71
|
+
```
|
|
43
72
|
|
|
44
|
-
|
|
73
|
+
#### 2. 生成测试代码(开发中)
|
|
45
74
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
50
|
-
```
|
|
75
|
+
```bash
|
|
76
|
+
# 录制模式 - 访问页面自动录制
|
|
77
|
+
npx smart-test record --url http://localhost:8080/coupon
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
# 代码分析模式 - 扫描页面代码
|
|
80
|
+
npx smart-test generate --page src/pages/coupon/index.tsx
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
# 描述生成模式 - 基于自然语言
|
|
83
|
+
npx smart-test generate --desc "优惠券列表页,有可用券和已用券两个 tab"
|
|
55
84
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const result = await aiGenerateTestCase({
|
|
60
|
-
pageUrl: '/coupon?userId=e2e-user&isPopup=true',
|
|
61
|
-
scenario: '优惠券页面展示和 Tab 切换功能',
|
|
62
|
-
apiMocks: [
|
|
63
|
-
{
|
|
64
|
-
path: '**/api/user/info/get-user-detail',
|
|
65
|
-
description: '返回用户信息,包含 id、nickname、memberLevel 等字段',
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
path: '**/api/user/coupon/get-available-coupons-grouped',
|
|
69
|
-
description: '返回可用优惠券列表,按券 ID 分组',
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
aiCheckExpected: '顶部展示门店名称和用户昵称,列表中应显示3张优惠券卡片',
|
|
73
|
-
testSteps: ['验证页面显示"优惠券"标题', '验证显示"夏日满减券"', '点击"核销券"按钮', '验证显示"到店核销券"文本'],
|
|
74
|
-
});
|
|
85
|
+
# 交互式模式 - 引导式生成
|
|
86
|
+
npx smart-test generate --interactive
|
|
87
|
+
```
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
#### 3. 运行测试
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx playwright test
|
|
79
93
|
```
|
|
80
94
|
|
|
81
|
-
|
|
95
|
+
---
|
|
82
96
|
|
|
83
|
-
|
|
97
|
+
### 方式二:手动编程
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
import { aiGenerateMock } from '@be-link/smart-test';
|
|
87
|
-
import fs from 'fs';
|
|
88
|
-
|
|
89
|
-
// 读取类型定义文件
|
|
90
|
-
const addressTypes = fs.readFileSync('./src/api/address/types.ts', 'utf-8');
|
|
91
|
-
|
|
92
|
-
// 提取需要的类型定义
|
|
93
|
-
const result = await aiGenerateMock({
|
|
94
|
-
apiPath: '**/api/user/address/get-default-address',
|
|
95
|
-
description: '返回用户默认地址信息',
|
|
96
|
-
responseType: 'IDefaultAddressResp',
|
|
97
|
-
typeDefinitions: [
|
|
98
|
-
{
|
|
99
|
-
typeName: 'IDefaultAddressResp',
|
|
100
|
-
content: `export interface IDefaultAddressResp {
|
|
101
|
-
/** 默认地址 */
|
|
102
|
-
defaultAddress: IUserAddress | null;
|
|
103
|
-
/** 自提地址 */
|
|
104
|
-
pickupAddress: IPickUpStoreUserInfo | null;
|
|
105
|
-
}`,
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
typeName: 'IUserAddress',
|
|
109
|
-
content: `export interface IUserAddress {
|
|
110
|
-
/** 地址ID */
|
|
111
|
-
id: string;
|
|
112
|
-
/** 用户ID */
|
|
113
|
-
userId: string;
|
|
114
|
-
/** 收件人姓名 */
|
|
115
|
-
receiverName: string;
|
|
116
|
-
/** 收件人手机号 */
|
|
117
|
-
receiverMobile: string;
|
|
118
|
-
/** 省份名称 */
|
|
119
|
-
provinceName: string;
|
|
120
|
-
/** 城市名称 */
|
|
121
|
-
cityName: string;
|
|
122
|
-
/** 区县名称 */
|
|
123
|
-
districtName: string;
|
|
124
|
-
/** 详细地址 */
|
|
125
|
-
detailAddress: string;
|
|
126
|
-
/** 是否默认地址:1-是,0-否 */
|
|
127
|
-
isDefault: number;
|
|
128
|
-
/** 创建时间(毫秒时间戳) */
|
|
129
|
-
createdAt: number;
|
|
130
|
-
}`,
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
typeName: 'IPickUpStoreUserInfo',
|
|
134
|
-
content: `export interface IPickUpStoreUserInfo {
|
|
135
|
-
id?: string;
|
|
136
|
-
/** 用户ID */
|
|
137
|
-
userId: string;
|
|
138
|
-
/** 提货人手机号 */
|
|
139
|
-
receiverMobile: string;
|
|
140
|
-
/** 提货人名称 */
|
|
141
|
-
receiverName: string;
|
|
142
|
-
}`,
|
|
143
|
-
},
|
|
144
|
-
],
|
|
145
|
-
});
|
|
99
|
+
#### 1. 配置 API Key
|
|
146
100
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
101
|
+
在项目根目录创建 `.env` 文件:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
AI_API_KEY=sk-your-api-key-here
|
|
151
105
|
```
|
|
152
106
|
|
|
153
|
-
|
|
107
|
+
#### 2. 初始化配置
|
|
154
108
|
|
|
155
|
-
|
|
109
|
+
在 `playwright.config.ts` 中配置:
|
|
156
110
|
|
|
157
111
|
```typescript
|
|
158
|
-
import {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}`,
|
|
112
|
+
import { defineConfig } from '@playwright/test';
|
|
113
|
+
import { configure } from '@be-link/smart-test';
|
|
114
|
+
|
|
115
|
+
// 初始化 AI 配置
|
|
116
|
+
configure({
|
|
117
|
+
apiKey: process.env.AI_API_KEY,
|
|
118
|
+
screenshotDir: './test-results/ai-screenshots',
|
|
166
119
|
});
|
|
167
120
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
121
|
+
export default defineConfig({
|
|
122
|
+
// ... 其他 Playwright 配置
|
|
123
|
+
});
|
|
171
124
|
```
|
|
172
125
|
|
|
173
|
-
|
|
126
|
+
#### 3. 在测试中使用
|
|
174
127
|
|
|
175
|
-
|
|
128
|
+
```typescript
|
|
129
|
+
import { test, expect } from '@playwright/test';
|
|
130
|
+
import { aiReviewScreenshot } from '@be-link/smart-test';
|
|
176
131
|
|
|
177
|
-
|
|
132
|
+
test.describe('优惠券页面', () => {
|
|
133
|
+
test('AI 检查页面布局', async ({ page }) => {
|
|
134
|
+
await page.goto('/coupon');
|
|
178
135
|
|
|
179
|
-
|
|
180
|
-
|
|
136
|
+
// 使用 AI 检查页面
|
|
137
|
+
const result = await aiReviewScreenshot(page, '页面应该显示优惠券列表,包含标题、优惠券卡片和底部按钮');
|
|
138
|
+
|
|
139
|
+
// 处理结果
|
|
140
|
+
if (result.skipped) {
|
|
141
|
+
test.skip(); // 无 API Key 时跳过
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
expect(result.ok, result.issues?.join(', ')).toBeTruthy();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
181
147
|
```
|
|
182
148
|
|
|
183
|
-
|
|
149
|
+
## 📖 CLI 命令参考
|
|
184
150
|
|
|
185
|
-
- `
|
|
186
|
-
- `options`: 检查选项
|
|
187
|
-
- 字符串:直接传入预期描述
|
|
188
|
-
- 对象:包含 `expected`(预期描述)、`fullPage`(是否全页截图)等配置
|
|
151
|
+
### `smart-test init`
|
|
189
152
|
|
|
190
|
-
|
|
153
|
+
初始化配置文件,交互式设置 AI API Key、模型、输出目录等。
|
|
191
154
|
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
ok: boolean; // 检查是否通过
|
|
195
|
-
issues?: string[]; // 发现的问题列表
|
|
196
|
-
suggestion?: string[]; // 改进建议列表
|
|
197
|
-
skipped?: boolean; // 是否跳过检查
|
|
198
|
-
reason?: string; // 跳过原因
|
|
199
|
-
rawResponse?: string; // AI 原始响应
|
|
200
|
-
}
|
|
155
|
+
```bash
|
|
156
|
+
smart-test init
|
|
201
157
|
```
|
|
202
158
|
|
|
203
|
-
###
|
|
159
|
+
### `smart-test generate` (开发中)
|
|
204
160
|
|
|
205
|
-
|
|
161
|
+
生成测试代码。
|
|
206
162
|
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
|
|
163
|
+
```bash
|
|
164
|
+
# 基础用法
|
|
165
|
+
smart-test generate
|
|
210
166
|
|
|
211
|
-
|
|
167
|
+
# 指定页面(代码分析模式)
|
|
168
|
+
smart-test generate --page <path>
|
|
212
169
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
pageUrl: string; // 页面 URL
|
|
216
|
-
scenario: string; // 测试场景描述
|
|
217
|
-
apiMocks?: ApiMockConfig[]; // API Mock 配置列表
|
|
218
|
-
aiCheckExpected?: string; // AI 视觉检查预期
|
|
219
|
-
testSteps?: string[]; // 测试步骤列表
|
|
220
|
-
referenceCode?: string; // 参考代码
|
|
221
|
-
// ... 以及 AiBaseConfig 的所有配置项
|
|
222
|
-
}
|
|
170
|
+
# 指定 URL(录制模式)
|
|
171
|
+
smart-test generate --url <url>
|
|
223
172
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
173
|
+
# 基于描述(自然语言模式)
|
|
174
|
+
smart-test generate --desc <description>
|
|
175
|
+
|
|
176
|
+
# 交互式模式
|
|
177
|
+
smart-test generate --interactive
|
|
178
|
+
|
|
179
|
+
# 指定输出目录
|
|
180
|
+
smart-test generate --page <path> --output tests/custom
|
|
181
|
+
|
|
182
|
+
# 预览模式(不写入文件)
|
|
183
|
+
smart-test generate --page <path> --dry-run
|
|
228
184
|
```
|
|
229
185
|
|
|
230
|
-
|
|
186
|
+
**选项:**
|
|
231
187
|
|
|
232
|
-
|
|
188
|
+
| 选项 | 简写 | 说明 |
|
|
189
|
+
| ---------------------- | ---- | ---------------------------- |
|
|
190
|
+
| `--page <path>` | `-p` | 页面文件路径(代码分析模式) |
|
|
191
|
+
| `--url <url>` | `-u` | 页面 URL(录制模式) |
|
|
192
|
+
| `--desc <description>` | `-d` | 页面描述(自然语言模式) |
|
|
193
|
+
| `--output <dir>` | `-o` | 输出目录 |
|
|
194
|
+
| `--interactive` | `-i` | 交互式模式 |
|
|
195
|
+
| `--dry-run` | | 预览模式,不写入文件 |
|
|
233
196
|
|
|
234
|
-
|
|
235
|
-
|
|
197
|
+
### `smart-test record` (开发中)
|
|
198
|
+
|
|
199
|
+
录制模式的快捷命令(`generate --url` 的别名)。
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# 基础录制
|
|
203
|
+
smart-test record --url http://localhost:8080/coupon
|
|
204
|
+
|
|
205
|
+
# 手动操作模式
|
|
206
|
+
smart-test record --url http://localhost:8080/coupon --manual
|
|
207
|
+
|
|
208
|
+
# 交互式引导
|
|
209
|
+
smart-test record --url http://localhost:8080/coupon --interactive
|
|
236
210
|
```
|
|
237
211
|
|
|
238
|
-
|
|
212
|
+
**选项:**
|
|
239
213
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
referenceCode?: string; // 参考代码
|
|
247
|
-
// ... 以及 AiBaseConfig 的所有配置项
|
|
248
|
-
}
|
|
214
|
+
| 选项 | 简写 | 说明 |
|
|
215
|
+
| ---------------- | ---- | ------------------------ |
|
|
216
|
+
| `--url <url>` | `-u` | 要录制的页面 URL(必需) |
|
|
217
|
+
| `--output <dir>` | `-o` | 输出目录 |
|
|
218
|
+
| `--manual` | | 手动操作模式 |
|
|
219
|
+
| `--interactive` | | 交互式引导 |
|
|
249
220
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
221
|
+
### `smart-test config`
|
|
222
|
+
|
|
223
|
+
查看当前配置。
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
smart-test config
|
|
254
227
|
```
|
|
255
228
|
|
|
256
|
-
|
|
257
|
-
|
|
229
|
+
### 全局选项
|
|
230
|
+
|
|
231
|
+
| 选项 | 简写 | 说明 |
|
|
232
|
+
| ----------- | ---- | -------------------- |
|
|
233
|
+
| `--version` | `-V` | 显示版本号 |
|
|
234
|
+
| `--verbose` | `-v` | 详细输出(调试模式) |
|
|
235
|
+
| `--help` | `-h` | 显示帮助信息 |
|
|
236
|
+
|
|
237
|
+
## 📖 编程 API 文档
|
|
258
238
|
|
|
259
|
-
###
|
|
239
|
+
### `configure(config)`
|
|
260
240
|
|
|
261
|
-
|
|
241
|
+
配置全局设置。
|
|
262
242
|
|
|
263
243
|
```typescript
|
|
264
|
-
|
|
244
|
+
import { configure } from '@be-link/smart-test';
|
|
245
|
+
|
|
246
|
+
configure({
|
|
247
|
+
apiKey: 'sk-xxx', // AI API 密钥
|
|
248
|
+
baseURL: 'https://...', // AI 服务地址(可选)
|
|
249
|
+
model: 'qwen3-vl-plus', // AI 模型(可选)
|
|
250
|
+
timeout: 30000, // 超时时间(可选)
|
|
251
|
+
});
|
|
265
252
|
```
|
|
266
253
|
|
|
254
|
+
### `aiReviewScreenshot(page, options)`
|
|
255
|
+
|
|
256
|
+
使用 AI 检查页面截图。
|
|
257
|
+
|
|
267
258
|
**参数:**
|
|
268
259
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
prompt: string; // 代码生成描述
|
|
272
|
-
context?: string; // 上下文代码
|
|
273
|
-
// ... 以及 AiBaseConfig 的所有配置项
|
|
274
|
-
}
|
|
275
|
-
```
|
|
260
|
+
- `page: Page` - Playwright Page 对象
|
|
261
|
+
- `options: string | AiCheckOptions` - 检查选项
|
|
276
262
|
|
|
277
|
-
**返回值:**
|
|
263
|
+
**返回值:** `Promise<AiCheckResult>`
|
|
278
264
|
|
|
279
265
|
```typescript
|
|
280
|
-
interface
|
|
281
|
-
ok: boolean; //
|
|
282
|
-
|
|
266
|
+
interface AiCheckResult {
|
|
267
|
+
ok: boolean; // 检查是否通过
|
|
268
|
+
issues?: string[]; // 问题列表
|
|
283
269
|
skipped?: boolean; // 是否跳过
|
|
284
|
-
reason?: string; //
|
|
270
|
+
reason?: string; // 跳过原因
|
|
271
|
+
screenshotPath?: string; // 截图路径
|
|
285
272
|
rawResponse?: string; // AI 原始响应
|
|
286
273
|
}
|
|
287
274
|
```
|
|
288
275
|
|
|
289
|
-
|
|
276
|
+
## 🎨 使用示例
|
|
290
277
|
|
|
291
|
-
|
|
278
|
+
### 基础用法
|
|
292
279
|
|
|
293
280
|
```typescript
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
interface AiBaseConfig {
|
|
297
|
-
apiKey?: string; // AI API 密钥
|
|
298
|
-
baseURL?: string; // AI 服务地址
|
|
299
|
-
visionModel?: string; // 视觉模型名称
|
|
300
|
-
codeModel?: string; // 代码模型名称
|
|
301
|
-
timeout?: number; // 请求超时时间(毫秒)
|
|
302
|
-
}
|
|
281
|
+
// 简单字符串描述
|
|
282
|
+
const result = await aiReviewScreenshot(page, '页面应该显示登录表单');
|
|
303
283
|
```
|
|
304
284
|
|
|
305
|
-
|
|
285
|
+
### 带配置的用法
|
|
306
286
|
|
|
307
|
-
|
|
287
|
+
```typescript
|
|
288
|
+
// 带配置对象
|
|
289
|
+
const result = await aiReviewScreenshot(page, {
|
|
290
|
+
expected: '页面应该显示商品列表',
|
|
291
|
+
model: 'gpt-4-vision-preview', // 使用其他模型
|
|
292
|
+
});
|
|
293
|
+
```
|
|
308
294
|
|
|
309
|
-
|
|
295
|
+
### 完整测试示例
|
|
310
296
|
|
|
311
297
|
```typescript
|
|
312
|
-
test
|
|
313
|
-
|
|
298
|
+
import { test, expect } from '@playwright/test';
|
|
299
|
+
import { aiReviewScreenshot } from '@be-link/smart-test';
|
|
314
300
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
301
|
+
test.describe('商品页面测试', () => {
|
|
302
|
+
test.beforeEach(async ({ page }) => {
|
|
303
|
+
await page.goto('/products');
|
|
304
|
+
await page.waitForLoadState('networkidle');
|
|
318
305
|
});
|
|
319
306
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
307
|
+
test('检查页面布局', async ({ page }) => {
|
|
308
|
+
const result = await aiReviewScreenshot(page, {
|
|
309
|
+
expected: '页面顶部应有搜索框,中间显示商品网格布局,底部有分页器',
|
|
310
|
+
fullPage: true,
|
|
311
|
+
});
|
|
323
312
|
|
|
324
|
-
|
|
313
|
+
if (result.skipped) {
|
|
314
|
+
console.log('跳过原因:', result.reason);
|
|
315
|
+
test.skip();
|
|
316
|
+
}
|
|
325
317
|
|
|
326
|
-
|
|
318
|
+
if (!result.ok && result.issues) {
|
|
319
|
+
console.warn('发现的问题:', result.issues);
|
|
320
|
+
}
|
|
327
321
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const result = await aiGenerateTestCase({
|
|
331
|
-
pageUrl: '/product/123',
|
|
332
|
-
scenario: '产品详情页展示和加入购物车功能',
|
|
333
|
-
apiMocks: [
|
|
334
|
-
{ path: '**/api/product/detail', description: '返回产品详情' },
|
|
335
|
-
{ path: '**/api/cart/add', description: '加入购物车成功' },
|
|
336
|
-
],
|
|
337
|
-
testSteps: ['验证显示产品名称和价格', '点击"加入购物车"按钮', '验证显示成功提示'],
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
// 将生成的代码保存到文件,然后根据实际需要调整
|
|
341
|
-
```
|
|
322
|
+
expect(result.ok, `AI 检查失败: ${result.issues?.join(', ')}`).toBeTruthy();
|
|
323
|
+
});
|
|
342
324
|
|
|
343
|
-
|
|
325
|
+
test('检查响应式布局', async ({ page }) => {
|
|
326
|
+
// 切换到移动端视图
|
|
327
|
+
await page.setViewportSize({ width: 375, height: 667 });
|
|
344
328
|
|
|
345
|
-
|
|
329
|
+
const result = await aiReviewScreenshot(page, '移动端布局:商品应该以单列显示,每个卡片占满宽度');
|
|
346
330
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const addressTypeContent = readFileSync('./src/api/address/types.ts', 'utf-8');
|
|
352
|
-
// 或者直接定义类型字符串
|
|
353
|
-
|
|
354
|
-
const result = await aiGenerateMock({
|
|
355
|
-
apiPath: '**/api/user/address/get-default-address',
|
|
356
|
-
description: '返回用户默认地址',
|
|
357
|
-
responseType: 'IDefaultAddressResp',
|
|
358
|
-
typeDefinitions: [
|
|
359
|
-
{
|
|
360
|
-
typeName: 'IDefaultAddressResp',
|
|
361
|
-
content: extractTypeFromFile(addressTypeContent, 'IDefaultAddressResp'),
|
|
362
|
-
},
|
|
363
|
-
{
|
|
364
|
-
typeName: 'IUserAddress',
|
|
365
|
-
content: extractTypeFromFile(addressTypeContent, 'IUserAddress'),
|
|
366
|
-
},
|
|
367
|
-
],
|
|
331
|
+
if (!result.skipped) {
|
|
332
|
+
expect(result.ok).toBeTruthy();
|
|
333
|
+
}
|
|
334
|
+
});
|
|
368
335
|
});
|
|
369
|
-
|
|
370
|
-
// 生成的 mock 数据会严格符合类型定义,包括:
|
|
371
|
-
// - 字段类型匹配(string、number、boolean、null 等)
|
|
372
|
-
// - 必填/可选字段正确
|
|
373
|
-
// - 嵌套对象结构准确
|
|
374
|
-
// - 根据注释生成合理的测试数据
|
|
375
336
|
```
|
|
376
337
|
|
|
377
|
-
##
|
|
338
|
+
## 🔧 配置文件
|
|
378
339
|
|
|
379
|
-
###
|
|
340
|
+
### `.smarttestrc.json`
|
|
380
341
|
|
|
381
|
-
|
|
342
|
+
使用 `smart-test init` 命令生成的配置文件:
|
|
343
|
+
|
|
344
|
+
```json
|
|
382
345
|
{
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
346
|
+
"version": "1.0.0",
|
|
347
|
+
"ai": {
|
|
348
|
+
"model": "qwen3-vl-plus",
|
|
349
|
+
"apiKey": "${AI_API_KEY}",
|
|
350
|
+
"baseURL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
351
|
+
"timeout": 30000
|
|
352
|
+
},
|
|
353
|
+
"output": {
|
|
354
|
+
"testsDir": "./tests/e2e",
|
|
355
|
+
"helpersDir": "./tests/helpers",
|
|
356
|
+
"naming": "kebab-case"
|
|
357
|
+
},
|
|
358
|
+
"templates": {
|
|
359
|
+
"mock": "default",
|
|
360
|
+
"test": "default"
|
|
361
|
+
},
|
|
362
|
+
"playwright": {
|
|
363
|
+
"baseURL": "http://localhost:8080",
|
|
364
|
+
"viewport": {
|
|
365
|
+
"width": 375,
|
|
366
|
+
"height": 667
|
|
367
|
+
},
|
|
368
|
+
"browser": "chromium"
|
|
369
|
+
}
|
|
387
370
|
}
|
|
388
371
|
```
|
|
389
372
|
|
|
390
|
-
###
|
|
373
|
+
### 支持的配置格式
|
|
374
|
+
|
|
375
|
+
- `.smarttestrc.json`
|
|
376
|
+
- `.smarttestrc.js`
|
|
377
|
+
- `smarttest.config.js`
|
|
378
|
+
- `package.json` 中的 `smarttest` 字段
|
|
391
379
|
|
|
392
|
-
|
|
380
|
+
## 🔧 高级配置
|
|
381
|
+
|
|
382
|
+
### 支持多种 AI 模型
|
|
393
383
|
|
|
394
384
|
```typescript
|
|
395
|
-
//
|
|
385
|
+
// 使用通义千问(默认)
|
|
396
386
|
configure({
|
|
397
|
-
apiKey: process.env.
|
|
398
|
-
|
|
387
|
+
apiKey: process.env.QWEN_API_KEY,
|
|
388
|
+
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
389
|
+
model: 'qwen3-vl-plus',
|
|
399
390
|
});
|
|
400
391
|
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
392
|
+
// 使用 OpenAI GPT-4 Vision
|
|
393
|
+
configure({
|
|
394
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
395
|
+
baseURL: 'https://api.openai.com/v1',
|
|
396
|
+
model: 'gpt-4-vision-preview',
|
|
405
397
|
});
|
|
406
398
|
```
|
|
407
399
|
|
|
408
|
-
|
|
400
|
+
### 自定义截图存储
|
|
409
401
|
|
|
410
|
-
|
|
402
|
+
```typescript
|
|
403
|
+
configure({
|
|
404
|
+
screenshotDir: './custom-screenshots',
|
|
405
|
+
saveScreenshots: true,
|
|
406
|
+
});
|
|
411
407
|
|
|
412
|
-
|
|
408
|
+
// 单次调用自定义
|
|
409
|
+
const result = await aiReviewScreenshot(page, {
|
|
410
|
+
expected: '...',
|
|
411
|
+
screenshotPrefix: 'my-feature',
|
|
412
|
+
screenshotDir: './feature-screenshots',
|
|
413
|
+
});
|
|
414
|
+
```
|
|
413
415
|
|
|
414
|
-
|
|
415
|
-
// playwright.config.ts
|
|
416
|
-
import { defineConfig } from '@playwright/test';
|
|
417
|
-
import { configure } from '@be-link/smart-test';
|
|
416
|
+
### 在 CI 环境中使用
|
|
418
417
|
|
|
418
|
+
```typescript
|
|
419
|
+
// 在 CI 中可能不需要保存截图,减少存储开销
|
|
419
420
|
configure({
|
|
420
421
|
apiKey: process.env.AI_API_KEY,
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
export default defineConfig({
|
|
424
|
-
// ... 其他配置
|
|
422
|
+
saveScreenshots: process.env.CI !== 'true',
|
|
425
423
|
});
|
|
426
424
|
```
|
|
427
425
|
|
|
428
|
-
|
|
426
|
+
## 🤔 常见问题
|
|
429
427
|
|
|
430
|
-
|
|
428
|
+
### Q: 没有配置 API Key 会怎样?
|
|
431
429
|
|
|
432
|
-
|
|
433
|
-
- ✅ 内容完整性检查
|
|
434
|
-
- ✅ 视觉回归测试
|
|
430
|
+
A: 测试会自动跳过 AI 检查,返回 `skipped: true`,不会影响其他测试的执行。
|
|
435
431
|
|
|
436
|
-
|
|
432
|
+
### Q: 支持哪些 AI 模型?
|
|
437
433
|
|
|
438
|
-
|
|
439
|
-
- ❌ 颜色值精确匹配
|
|
440
|
-
- ❌ 性能敏感的场景
|
|
434
|
+
A: 理论上支持所有兼容 OpenAI Chat Completions API 的视觉模型,包括:
|
|
441
435
|
|
|
442
|
-
|
|
436
|
+
- 通义千问 (qwen3-vl-plus) - 默认
|
|
437
|
+
- OpenAI GPT-4 Vision
|
|
438
|
+
- 其他兼容的模型服务
|
|
443
439
|
|
|
444
|
-
|
|
440
|
+
### Q: CLI 生成的测试代码需要手动修改吗?
|
|
445
441
|
|
|
446
|
-
|
|
447
|
-
test('优惠券页面', async ({ page }) => {
|
|
448
|
-
await page.goto('/coupon');
|
|
442
|
+
A: 生成的代码是可以直接运行的,但建议根据实际需求进行调整。AI 会尽量生成合理的测试用例,但可能无法覆盖所有业务场景。
|
|
449
443
|
|
|
450
|
-
|
|
451
|
-
await expect(page.getByText('优惠券')).toBeVisible();
|
|
452
|
-
await expect(page.getByRole('button', { name: '核销券' })).toBeVisible();
|
|
444
|
+
### Q: 录制模式如何处理需要点击才能触发的接口?
|
|
453
445
|
|
|
454
|
-
|
|
455
|
-
const aiResult = await aiReviewScreenshot(page, '页面整体布局合理,优惠券卡片排列整齐,信息展示清晰');
|
|
446
|
+
A: 录制模式支持三种方式:
|
|
456
447
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
448
|
+
1. **手动操作**:`--manual` 参数,用户手动操作浏览器
|
|
449
|
+
2. **交互式引导**:`--interactive` 参数,AI 引导用户操作
|
|
450
|
+
3. **脚本引导**:`--actions` 参数,提供操作脚本自动执行
|
|
451
|
+
|
|
452
|
+
### Q: 如何查看 AI 的原始响应?
|
|
453
|
+
|
|
454
|
+
A: 检查结果中的 `rawResponse` 字段包含了 AI 的原始响应内容。
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
const result = await aiReviewScreenshot(page, '...');
|
|
458
|
+
console.log('AI 原始响应:', result.rawResponse);
|
|
461
459
|
```
|
|
460
|
+
|
|
461
|
+
### Q: 生成的代码文件如何组织?
|
|
462
|
+
|
|
463
|
+
A: 默认按页面分组:
|
|
464
|
+
|
|
465
|
+
- `tests/helpers/mock-{页面名}.ts` - Mock 数据
|
|
466
|
+
- `tests/e2e/{页面名}.spec.ts` - 测试用例
|
|
467
|
+
|
|
468
|
+
可以通过 `--output` 参数自定义输出目录。
|
|
469
|
+
|
|
470
|
+
## 🗺️ 开发路线图
|
|
471
|
+
|
|
472
|
+
### ✅ 已完成
|
|
473
|
+
|
|
474
|
+
- [x] AI 视觉检查核心功能
|
|
475
|
+
- [x] CLI 基础架构
|
|
476
|
+
- [x] 配置系统
|
|
477
|
+
- [x] 交互式初始化
|
|
478
|
+
|
|
479
|
+
### 🚧 开发中
|
|
480
|
+
|
|
481
|
+
- [ ] 录制模式(Phase 2)
|
|
482
|
+
- [ ] 网络请求录制
|
|
483
|
+
- [ ] HAR 文件解析
|
|
484
|
+
- [ ] AI 生成 Mock 代码
|
|
485
|
+
- [ ] 交互操作录制
|
|
486
|
+
|
|
487
|
+
### 📅 计划中
|
|
488
|
+
|
|
489
|
+
- [ ] 代码分析模式(Phase 3)
|
|
490
|
+
- [ ] TypeScript AST 解析
|
|
491
|
+
- [ ] API 调用提取
|
|
492
|
+
- [ ] 类型定义分析
|
|
493
|
+
|
|
494
|
+
- [ ] 描述生成模式(Phase 4)
|
|
495
|
+
- [ ] 自然语言理解
|
|
496
|
+
- [ ] 交互式问答
|
|
497
|
+
- [ ] 代码生成
|
|
498
|
+
|
|
499
|
+
- [ ] 增强功能(Phase 5+)
|
|
500
|
+
- [ ] 增量更新
|
|
501
|
+
- [ ] 自定义模板
|
|
502
|
+
- [ ] 测试覆盖率分析
|
|
503
|
+
- [ ] VS Code 插件
|
|
504
|
+
|
|
505
|
+
## 📄 文档
|
|
506
|
+
|
|
507
|
+
- [CLI 设计文档](./docs/CLI_DESIGN.md) - 完整的 CLI 工具设计方案
|
|
508
|
+
- [使用示例](./examples/) - 更多使用示例
|
|
509
|
+
|
|
510
|
+
## 📝 License
|
|
511
|
+
|
|
512
|
+
MIT
|
|
513
|
+
|
|
514
|
+
## 🤝 贡献
|
|
515
|
+
|
|
516
|
+
欢迎提交 Issue 和 Pull Request!
|
|
517
|
+
|
|
518
|
+
## 📮 联系方式
|
|
519
|
+
|
|
520
|
+
如有问题,请在 [GitHub Issues](https://github.com/snowmountain-top/be-link/issues) 中提出。
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
**版本**:v1.0.0 | **最后更新**:2025-12-30
|