@be-link/smart-test 1.0.1-beta.24 → 1.0.1-beta.25
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 +202 -320
- package/dist/bin/smart-test.js +1708 -1942
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/preset.d.ts +7 -0
- package/dist/cli/commands/preset.d.ts.map +1 -0
- package/dist/cli/commands/record.d.ts +1 -1
- package/dist/cli/commands/record.d.ts.map +1 -1
- package/dist/cli/commands/run.d.ts +7 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/loaders/config-loader.d.ts +41 -0
- package/dist/cli/loaders/config-loader.d.ts.map +1 -0
- package/dist/cli/loaders/env-loader.d.ts.map +1 -0
- package/dist/cli/loaders/preset-loader.d.ts +33 -0
- package/dist/cli/loaders/preset-loader.d.ts.map +1 -0
- package/dist/cli/templates/playwright.config.template.ts +12 -1
- package/dist/cli/templates/recording-preset.template.ts +53 -0
- package/dist/cli/templates/smart-test.config.template.ts +22 -14
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/types/ai.types.d.ts +59 -0
- package/dist/core/types/ai.types.d.ts.map +1 -0
- package/dist/core/types/api.types.d.ts +34 -0
- package/dist/core/types/api.types.d.ts.map +1 -0
- package/dist/core/types/index.d.ts +7 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/recorder.types.d.ts +204 -0
- package/dist/core/types/recorder.types.d.ts.map +1 -0
- package/dist/generators/base-generator.d.ts +37 -0
- package/dist/generators/base-generator.d.ts.map +1 -0
- package/dist/generators/index.d.ts +6 -3
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/mock-generator.d.ts +27 -3
- package/dist/generators/mock-generator.d.ts.map +1 -1
- package/dist/generators/test-generator.d.ts +53 -0
- package/dist/generators/test-generator.d.ts.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.mjs +1346 -520
- package/dist/index.js +1347 -522
- package/dist/prompts/fragment-loader.d.ts +28 -0
- package/dist/prompts/fragment-loader.d.ts.map +1 -0
- package/dist/prompts/fragments/system/mock-generation-base.md +1 -0
- package/dist/prompts/fragments/system/mock-generation-example.md +53 -0
- package/dist/prompts/fragments/system/mock-generation-specs.md +25 -0
- package/dist/prompts/fragments/system/test-generation-api-reference.md +46 -0
- package/dist/prompts/fragments/system/test-generation-base.md +8 -0
- package/dist/prompts/fragments/system/test-generation-requirements.md +29 -0
- package/dist/prompts/fragments/user/test-generation-header.md +19 -0
- package/dist/prompts/fragments/user/test-generation-mock-hint.md +9 -0
- package/dist/prompts/fragments/user/test-generation-template.md +35 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/mock-generation.prompt.d.ts +23 -0
- package/dist/prompts/mock-generation.prompt.d.ts.map +1 -0
- package/dist/prompts/test-generation.prompt.d.ts +47 -0
- package/dist/prompts/test-generation.prompt.d.ts.map +1 -0
- package/dist/recorders/action-tracker.d.ts +42 -0
- package/dist/recorders/action-tracker.d.ts.map +1 -0
- package/dist/recorders/index.d.ts +8 -0
- package/dist/recorders/index.d.ts.map +1 -0
- package/dist/recorders/interactive-recorder.d.ts +13 -102
- package/dist/recorders/interactive-recorder.d.ts.map +1 -1
- package/dist/recorders/screenshot-manager.d.ts +23 -0
- package/dist/recorders/screenshot-manager.d.ts.map +1 -0
- package/dist/services/ai/ai-service.d.ts +41 -0
- package/dist/services/ai/ai-service.d.ts.map +1 -0
- package/dist/services/ai/index.d.ts +2 -0
- package/dist/services/ai/index.d.ts.map +1 -0
- package/dist/services/browser/api-interceptor.d.ts +44 -0
- package/dist/services/browser/api-interceptor.d.ts.map +1 -0
- package/dist/services/browser/index.d.ts +2 -0
- package/dist/services/browser/index.d.ts.map +1 -0
- package/dist/utils/data-compressor.d.ts +54 -0
- package/dist/utils/data-compressor.d.ts.map +1 -0
- package/dist/utils/fs-utils.d.ts +7 -0
- package/dist/utils/fs-utils.d.ts.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/path-utils.d.ts +8 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/string-utils.d.ts +16 -0
- package/dist/utils/string-utils.d.ts.map +1 -0
- package/dist/workflows/record-and-generate.d.ts +96 -18
- package/dist/workflows/record-and-generate.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/analyzers/page-api-analyzer.d.ts +0 -106
- package/dist/analyzers/page-api-analyzer.d.ts.map +0 -1
- package/dist/cli/commands/gen.d.ts +0 -2
- package/dist/cli/commands/gen.d.ts.map +0 -1
- package/dist/cli/commands/generate-mock.d.ts +0 -6
- package/dist/cli/commands/generate-mock.d.ts.map +0 -1
- package/dist/cli/commands/generate-testcase.d.ts +0 -6
- package/dist/cli/commands/generate-testcase.d.ts.map +0 -1
- package/dist/cli/core/config-loader.d.ts +0 -69
- package/dist/cli/core/config-loader.d.ts.map +0 -1
- package/dist/cli/core/env-loader.d.ts.map +0 -1
- package/dist/cli/core/validators.d.ts +0 -27
- package/dist/cli/core/validators.d.ts.map +0 -1
- package/dist/cli/ui/interactive.d.ts +0 -32
- package/dist/cli/ui/interactive.d.ts.map +0 -1
- package/dist/core/ai-assistant.d.ts +0 -25
- package/dist/core/ai-assistant.d.ts.map +0 -1
- package/dist/core/prompts.d.ts +0 -14
- package/dist/core/prompts.d.ts.map +0 -1
- package/dist/core/types.d.ts +0 -265
- package/dist/core/types.d.ts.map +0 -1
- package/dist/generators/batch-mock-generator.d.ts +0 -42
- package/dist/generators/batch-mock-generator.d.ts.map +0 -1
- package/dist/generators/midscene-test-generator.d.ts +0 -57
- package/dist/generators/midscene-test-generator.d.ts.map +0 -1
- package/dist/generators/testcase-generator.d.ts +0 -6
- package/dist/generators/testcase-generator.d.ts.map +0 -1
- package/dist/utils/dom-extractor.d.ts +0 -37
- package/dist/utils/dom-extractor.d.ts.map +0 -1
- package/dist/utils/playwright-config-loader.d.ts +0 -52
- package/dist/utils/playwright-config-loader.d.ts.map +0 -1
- package/dist/utils/screenshot.d.ts +0 -8
- package/dist/utils/screenshot.d.ts.map +0 -1
- package/dist/utils/type-extractor.d.ts +0 -10
- package/dist/utils/type-extractor.d.ts.map +0 -1
- package/dist/utils/web-server-manager.d.ts +0 -27
- package/dist/utils/web-server-manager.d.ts.map +0 -1
- /package/dist/cli/{core → loaders}/env-loader.d.ts +0 -0
package/README.md
CHANGED
|
@@ -1,420 +1,302 @@
|
|
|
1
1
|
# @be-link/smart-test
|
|
2
2
|
|
|
3
|
-
AI-powered
|
|
3
|
+
AI-powered test recorder for Playwright | 基于 AI 的 Playwright 测试录制工具
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 特性
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- **交互式录制**:打开浏览器,手动操作页面,自动记录用户行为和 API 请求
|
|
8
|
+
- **预设模式录制**:通过配置文件定义测试场景,批量执行录制
|
|
9
|
+
- **AI 生成 Mock**:基于录制的 API 请求,AI 自动生成类型安全的 Mock 函数
|
|
10
|
+
- **AI 生成测试**:基于录制的操作步骤,AI 自动生成 Midscene 测试用例
|
|
11
|
+
- **TypeScript 支持**:完整的类型定义,生成的代码开箱即用
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## 安装
|
|
13
14
|
|
|
14
15
|
```bash
|
|
16
|
+
# 安装 smart-test
|
|
15
17
|
pnpm add -D @be-link/smart-test
|
|
18
|
+
|
|
19
|
+
# 安装必需的依赖
|
|
20
|
+
pnpm add -D @midscene/web playwright @playwright/test dotenv tsx
|
|
16
21
|
```
|
|
17
22
|
|
|
18
|
-
##
|
|
23
|
+
## 快速开始
|
|
19
24
|
|
|
20
25
|
### 1. 初始化配置
|
|
21
26
|
|
|
22
|
-
首先在项目中初始化配置文件:
|
|
23
|
-
|
|
24
27
|
```bash
|
|
25
|
-
|
|
26
|
-
smart-test init
|
|
28
|
+
npx smart-test init
|
|
27
29
|
```
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
}
|
|
40
|
-
```
|
|
31
|
+
这会在项目中生成:
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
- `tests/smart-test.config.ts` - Smart Test 配置
|
|
34
|
+
- `playwright.config.ts` - Playwright 配置
|
|
35
|
+
- `tests/e2e/` - 测试文件目录
|
|
36
|
+
- `tests/mock/` - Mock 文件目录
|
|
43
37
|
|
|
44
|
-
### 2.
|
|
38
|
+
### 2. 配置环境变量
|
|
45
39
|
|
|
46
|
-
|
|
40
|
+
在项目根目录创建 `.env` 文件,配置 Midscene AI 相关的环境变量:
|
|
47
41
|
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
```bash
|
|
43
|
+
# AI 模型 API 地址
|
|
44
|
+
MIDSCENE_MODEL_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
# AI 模型 API 密钥
|
|
47
|
+
MIDSCENE_MODEL_API_KEY=your_api_key
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
# AI 模型名称
|
|
50
|
+
MIDSCENE_MODEL_NAME=qwen-vl-max-latest
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
});
|
|
52
|
+
# AI 模型家族(可选,根据模型提供商设置)
|
|
53
|
+
MIDSCENE_MODEL_FAMILY=qwen-vl
|
|
61
54
|
```
|
|
62
55
|
|
|
63
|
-
|
|
56
|
+
> 💡 Smart Test 和 Midscene 共用相同的环境变量,无需重复配置
|
|
64
57
|
|
|
65
|
-
|
|
58
|
+
### 3. 开始录制
|
|
66
59
|
|
|
67
|
-
|
|
68
|
-
import { aiGenerateTestCase } from '@be-link/smart-test';
|
|
60
|
+
#### 方式一:预设模式录制(推荐)
|
|
69
61
|
|
|
70
|
-
|
|
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
|
-
});
|
|
62
|
+
先创建预设配置:
|
|
86
63
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
64
|
+
```bash
|
|
65
|
+
npx smart-test preset
|
|
90
66
|
```
|
|
91
67
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
通过传入实际的 TypeScript 类型定义,AI 会生成完全符合接口规范的 Mock 数据:
|
|
68
|
+
编辑生成的 `*.preset.ts` 文件:
|
|
95
69
|
|
|
96
70
|
```typescript
|
|
97
|
-
import {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
},
|
|
71
|
+
import type { RecordingPreset } from '@be-link/smart-test';
|
|
72
|
+
|
|
73
|
+
const preset: RecordingPreset = {
|
|
74
|
+
pageUrl: 'http://localhost:8080/login',
|
|
75
|
+
caseName: 'user-login',
|
|
76
|
+
testCases: [
|
|
143
77
|
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
/** 用户ID */
|
|
148
|
-
userId: string;
|
|
149
|
-
/** 提货人手机号 */
|
|
150
|
-
receiverMobile: string;
|
|
151
|
-
/** 提货人名称 */
|
|
152
|
-
receiverName: string;
|
|
153
|
-
}`,
|
|
78
|
+
name: '用户登录成功',
|
|
79
|
+
steps: ['输入用户名 testuser', '输入密码 123456', '点击登录按钮'],
|
|
80
|
+
expected: '跳转到首页,显示欢迎信息',
|
|
154
81
|
},
|
|
155
82
|
],
|
|
156
|
-
|
|
83
|
+
viewport: 'mobile-ios',
|
|
84
|
+
headless: false,
|
|
85
|
+
};
|
|
157
86
|
|
|
158
|
-
|
|
159
|
-
console.log('生成的 Mock 代码:\n', result.code);
|
|
160
|
-
// AI 会严格按照类型定义生成符合规范的 mock 数据
|
|
161
|
-
}
|
|
87
|
+
export default preset;
|
|
162
88
|
```
|
|
163
89
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
使用 AI 生成任意 TypeScript 代码:
|
|
167
|
-
|
|
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
|
-
});
|
|
90
|
+
然后执行录制:
|
|
178
91
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
92
|
+
```bash
|
|
93
|
+
npx smart-test run login.preset.ts
|
|
182
94
|
```
|
|
183
95
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
### aiReviewScreenshot
|
|
96
|
+
#### 方式二:交互式录制
|
|
187
97
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
function aiReviewScreenshot(page: Page, options: string | AiCheckOptions): Promise<AiCheckResult>;
|
|
98
|
+
```bash
|
|
99
|
+
npx smart-test record
|
|
192
100
|
```
|
|
193
101
|
|
|
194
|
-
|
|
102
|
+
按照提示输入:
|
|
195
103
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
104
|
+
1. 页面 URL
|
|
105
|
+
2. 测试用例名称
|
|
106
|
+
3. 测试场景描述
|
|
107
|
+
4. 视口尺寸
|
|
200
108
|
|
|
201
|
-
|
|
109
|
+
### 4. 运行测试
|
|
202
110
|
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
ok: boolean; // 检查是否通过
|
|
206
|
-
issues?: string[]; // 发现的问题列表
|
|
207
|
-
suggestion?: string[]; // 改进建议列表
|
|
208
|
-
skipped?: boolean; // 是否跳过检查
|
|
209
|
-
reason?: string; // 跳过原因
|
|
210
|
-
rawResponse?: string; // AI 原始响应
|
|
211
|
-
}
|
|
111
|
+
```bash
|
|
112
|
+
npx playwright test
|
|
212
113
|
```
|
|
213
114
|
|
|
214
|
-
|
|
115
|
+
## 生成的文件
|
|
215
116
|
|
|
216
|
-
|
|
117
|
+
录制完成后,会在 `tests/` 目录生成:
|
|
217
118
|
|
|
218
|
-
```typescript
|
|
219
|
-
function aiGenerateTestCase(options: AiTestCaseGenerationOptions): Promise<AiCodeGenerationResult>;
|
|
220
119
|
```
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
scenario: string; // 测试场景描述
|
|
228
|
-
apiMocks?: ApiMockConfig[]; // API Mock 配置列表
|
|
229
|
-
aiCheckExpected?: string; // AI 视觉检查预期
|
|
230
|
-
testSteps?: string[]; // 测试步骤列表
|
|
231
|
-
referenceCode?: string; // 参考代码
|
|
232
|
-
// ... 以及 AiBaseConfig 的所有配置项
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
interface ApiMockConfig {
|
|
236
|
-
path: string; // API 路径(支持通配符)
|
|
237
|
-
description: string; // Mock 数据描述
|
|
238
|
-
}
|
|
120
|
+
tests/
|
|
121
|
+
├── e2e/
|
|
122
|
+
│ └── {caseName}.spec.ts # Midscene 测试用例
|
|
123
|
+
├── mock/
|
|
124
|
+
│ └── {caseName}.mock.ts # Mock 函数
|
|
125
|
+
└── smart-test.config.ts # 配置文件
|
|
239
126
|
```
|
|
240
127
|
|
|
241
|
-
###
|
|
242
|
-
|
|
243
|
-
使用 AI 生成 API Mock 函数。
|
|
128
|
+
### Mock 文件示例
|
|
244
129
|
|
|
245
130
|
```typescript
|
|
246
|
-
|
|
131
|
+
/**
|
|
132
|
+
* 自动生成的 Mock 文件
|
|
133
|
+
* @generated by @be-link/smart-test
|
|
134
|
+
*/
|
|
135
|
+
import type { Page } from '@playwright/test';
|
|
136
|
+
|
|
137
|
+
export async function mockUserLoginApis(page: Page): Promise<void> {
|
|
138
|
+
await page.route('**/api/user/login', async (route) => {
|
|
139
|
+
await route.fulfill({
|
|
140
|
+
status: 200,
|
|
141
|
+
contentType: 'application/json',
|
|
142
|
+
body: JSON.stringify({
|
|
143
|
+
code: 0,
|
|
144
|
+
data: { token: 'xxx', userId: '123' },
|
|
145
|
+
}),
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
247
149
|
```
|
|
248
150
|
|
|
249
|
-
|
|
151
|
+
### 测试文件示例
|
|
250
152
|
|
|
251
153
|
```typescript
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
154
|
+
/**
|
|
155
|
+
* 自动生成的测试文件
|
|
156
|
+
* @generated by @be-link/smart-test
|
|
157
|
+
*/
|
|
158
|
+
import { expect, test } from '@playwright/test';
|
|
159
|
+
import { PlaywrightAgent } from '@midscene/web/playwright';
|
|
160
|
+
|
|
161
|
+
import { mockUserLoginApis } from '../mock/user-login.mock';
|
|
162
|
+
|
|
163
|
+
test.describe('user-login', () => {
|
|
164
|
+
let agent: PlaywrightAgent;
|
|
165
|
+
|
|
166
|
+
test.beforeEach(async ({ page }) => {
|
|
167
|
+
await mockUserLoginApis(page);
|
|
168
|
+
await page.goto('/login');
|
|
169
|
+
await page.waitForLoadState('networkidle');
|
|
170
|
+
agent = new PlaywrightAgent(page);
|
|
171
|
+
});
|
|
260
172
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
173
|
+
test('用户登录成功', async () => {
|
|
174
|
+
await agent.aiAction('输入用户名 testuser');
|
|
175
|
+
await agent.aiAction('输入密码 123456');
|
|
176
|
+
await agent.aiAction('点击登录按钮');
|
|
177
|
+
await agent.aiAssert('跳转到首页,显示欢迎信息');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
265
180
|
```
|
|
266
181
|
|
|
267
|
-
|
|
268
|
-
传入 `typeDefinitions` 和 `responseType`,AI 会严格按照 TypeScript 类型定义生成 mock 数据,确保类型安全和数据准确性
|
|
182
|
+
## CLI 命令
|
|
269
183
|
|
|
270
|
-
###
|
|
184
|
+
### smart-test init
|
|
271
185
|
|
|
272
|
-
|
|
186
|
+
初始化配置文件和目录结构。
|
|
273
187
|
|
|
274
|
-
```
|
|
275
|
-
|
|
188
|
+
```bash
|
|
189
|
+
npx smart-test init
|
|
276
190
|
```
|
|
277
191
|
|
|
278
|
-
|
|
192
|
+
### smart-test preset
|
|
279
193
|
|
|
280
|
-
|
|
281
|
-
interface AiCodeGenerationOptions {
|
|
282
|
-
prompt: string; // 代码生成描述
|
|
283
|
-
context?: string; // 上下文代码
|
|
284
|
-
// ... 以及 AiBaseConfig 的所有配置项
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
**返回值:**
|
|
194
|
+
创建录制预设配置文件。
|
|
289
195
|
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
ok: boolean; // 是否成功生成
|
|
293
|
-
code?: string; // 生成的代码
|
|
294
|
-
skipped?: boolean; // 是否跳过
|
|
295
|
-
reason?: string; // 跳过或失败原因
|
|
296
|
-
rawResponse?: string; // AI 原始响应
|
|
297
|
-
}
|
|
196
|
+
```bash
|
|
197
|
+
npx smart-test preset [filename]
|
|
298
198
|
```
|
|
299
199
|
|
|
300
|
-
|
|
200
|
+
### smart-test run
|
|
301
201
|
|
|
302
|
-
|
|
202
|
+
使用预设配置执行录制。
|
|
303
203
|
|
|
304
|
-
|
|
204
|
+
```bash
|
|
205
|
+
npx smart-test run <preset-file>
|
|
206
|
+
```
|
|
305
207
|
|
|
306
|
-
|
|
307
|
-
test('首页显示正常', async ({ page }) => {
|
|
308
|
-
await page.goto('/');
|
|
208
|
+
### smart-test record
|
|
309
209
|
|
|
310
|
-
|
|
311
|
-
expected: '页面顶部显示网站logo和主导航,中间显示轮播图,下方显示4个产品分类卡片',
|
|
312
|
-
fullPage: true,
|
|
313
|
-
});
|
|
210
|
+
启动交互式录制模式。
|
|
314
211
|
|
|
315
|
-
|
|
316
|
-
|
|
212
|
+
```bash
|
|
213
|
+
npx smart-test record
|
|
317
214
|
```
|
|
318
215
|
|
|
319
|
-
|
|
216
|
+
## 配置文件
|
|
320
217
|
|
|
321
|
-
|
|
218
|
+
### smart-test.config.ts
|
|
322
219
|
|
|
323
220
|
```typescript
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
//
|
|
221
|
+
import type { SmartTestConfig } from '@be-link/smart-test';
|
|
222
|
+
|
|
223
|
+
const config: SmartTestConfig = {
|
|
224
|
+
ai: {
|
|
225
|
+
// AI 模型配置(API Key 和 Base URL 从环境变量读取)
|
|
226
|
+
visionModel: 'qwen3-vl-plus',
|
|
227
|
+
codeModel: 'qwen3-coder-plus',
|
|
228
|
+
},
|
|
229
|
+
recording: {
|
|
230
|
+
// 测试文件输出目录
|
|
231
|
+
testDir: 'tests/e2e',
|
|
232
|
+
// Mock 文件输出目录
|
|
233
|
+
mockDir: 'tests/mock',
|
|
234
|
+
// API 路径匹配模式
|
|
235
|
+
apiPathPattern: '**/c-bff/**',
|
|
236
|
+
// 排除的 API 路径
|
|
237
|
+
excludeApiPatterns: [],
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export default config;
|
|
336
242
|
```
|
|
337
243
|
|
|
338
|
-
###
|
|
339
|
-
|
|
340
|
-
通过传入实际的类型定义,确保生成的 Mock 数据完全符合接口规范:
|
|
244
|
+
### 预设配置 (\*.preset.ts)
|
|
341
245
|
|
|
342
246
|
```typescript
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
responseType: 'IDefaultAddressResp',
|
|
353
|
-
typeDefinitions: [
|
|
247
|
+
import type { RecordingPreset } from '@be-link/smart-test';
|
|
248
|
+
|
|
249
|
+
const preset: RecordingPreset = {
|
|
250
|
+
// 要测试的页面 URL
|
|
251
|
+
pageUrl: 'http://localhost:8080/page',
|
|
252
|
+
// 测试用例名称(用于文件命名)
|
|
253
|
+
caseName: 'test-case',
|
|
254
|
+
// 测试场景列表
|
|
255
|
+
testCases: [
|
|
354
256
|
{
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
{
|
|
359
|
-
typeName: 'IUserAddress',
|
|
360
|
-
content: extractTypeFromFile(addressTypeContent, 'IUserAddress'),
|
|
257
|
+
name: '场景名称',
|
|
258
|
+
steps: ['步骤1', '步骤2'],
|
|
259
|
+
expected: '预期结果',
|
|
361
260
|
},
|
|
362
261
|
],
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
## ⚙️ 配置说明
|
|
373
|
-
|
|
374
|
-
### 默认配置
|
|
375
|
-
|
|
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
|
-
}
|
|
262
|
+
// 子目录(可选)
|
|
263
|
+
subDir: '',
|
|
264
|
+
// 视口尺寸:'mobile-ios' | 'mobile-android' | 'tablet' | 'desktop' | 'desktop-wide'
|
|
265
|
+
viewport: 'mobile-ios',
|
|
266
|
+
// 是否使用无头模式
|
|
267
|
+
headless: false,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
export default preset;
|
|
383
271
|
```
|
|
384
272
|
|
|
385
|
-
##
|
|
273
|
+
## 环境变量
|
|
386
274
|
|
|
387
|
-
|
|
275
|
+
| 变量名 | 说明 | 必填 |
|
|
276
|
+
| ------------------------- | ---------------- | ---- |
|
|
277
|
+
| `MIDSCENE_MODEL_BASE_URL` | AI 模型 API 地址 | 是 |
|
|
278
|
+
| `MIDSCENE_MODEL_API_KEY` | AI 模型 API 密钥 | 是 |
|
|
279
|
+
| `MIDSCENE_MODEL_NAME` | AI 模型名称 | 是 |
|
|
280
|
+
| `MIDSCENE_MODEL_FAMILY` | AI 模型家族 | 否 |
|
|
388
281
|
|
|
389
|
-
|
|
282
|
+
## 依赖说明
|
|
390
283
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
284
|
+
| 依赖 | 说明 |
|
|
285
|
+
| ------------------ | ----------------------------- |
|
|
286
|
+
| `@midscene/web` | Midscene AI 驱动的 Web 测试库 |
|
|
287
|
+
| `playwright` | 浏览器自动化核心库 |
|
|
288
|
+
| `@playwright/test` | Playwright 测试框架 |
|
|
289
|
+
| `dotenv` | 环境变量加载 |
|
|
290
|
+
| `tsx` | TypeScript 执行器 |
|
|
394
291
|
|
|
395
|
-
|
|
292
|
+
## 与 Midscene 配合
|
|
396
293
|
|
|
397
|
-
|
|
398
|
-
- ❌ 颜色值精确匹配
|
|
399
|
-
- ❌ 性能敏感的场景
|
|
294
|
+
生成的测试用例基于 [Midscene](https://midscenejs.com/) 的 AI 驱动 API:
|
|
400
295
|
|
|
401
|
-
|
|
296
|
+
- `agent.aiAction()` - AI 驱动的页面操作
|
|
297
|
+
- `agent.aiQuery()` - AI 驱动的数据查询
|
|
298
|
+
- `agent.aiAssert()` - AI 驱动的断言
|
|
402
299
|
|
|
403
|
-
|
|
300
|
+
## License
|
|
404
301
|
|
|
405
|
-
|
|
406
|
-
test('优惠券页面', async ({ page }) => {
|
|
407
|
-
await page.goto('/coupon');
|
|
408
|
-
|
|
409
|
-
// 传统断言:精确的功能验证
|
|
410
|
-
await expect(page.getByText('优惠券')).toBeVisible();
|
|
411
|
-
await expect(page.getByRole('button', { name: '核销券' })).toBeVisible();
|
|
412
|
-
|
|
413
|
-
// AI 检查:整体视觉验证
|
|
414
|
-
const aiResult = await aiReviewScreenshot(page, '页面整体布局合理,优惠券卡片排列整齐,信息展示清晰');
|
|
415
|
-
|
|
416
|
-
if (!aiResult.skipped && aiResult.issues?.length) {
|
|
417
|
-
console.warn('AI 发现的潜在问题:', aiResult.issues);
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
```
|
|
302
|
+
MIT
|