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