@chen-rmag/ai-runner 0.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 +263 -0
- package/SUMMARY_USAGE.md +359 -0
- package/TOOLS_INTEGRATION_SUMMARY.md +206 -0
- package/dist/agents/error-analyzer.d.ts +62 -0
- package/dist/agents/error-analyzer.d.ts.map +1 -0
- package/dist/agents/error-analyzer.js +168 -0
- package/dist/agents/error-analyzer.js.map +1 -0
- package/dist/agents/heal-agent.d.ts +30 -0
- package/dist/agents/heal-agent.d.ts.map +1 -0
- package/dist/agents/heal-agent.js +76 -0
- package/dist/agents/heal-agent.js.map +1 -0
- package/dist/agents/healer.d.ts +73 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +538 -0
- package/dist/agents/healer.js.map +1 -0
- package/dist/agents/langgraph-agent.d.ts +44 -0
- package/dist/agents/langgraph-agent.d.ts.map +1 -0
- package/dist/agents/langgraph-agent.js +328 -0
- package/dist/agents/langgraph-agent.js.map +1 -0
- package/dist/agents/react-agent.d.ts +52 -0
- package/dist/agents/react-agent.d.ts.map +1 -0
- package/dist/agents/react-agent.js +262 -0
- package/dist/agents/react-agent.js.map +1 -0
- package/dist/agents/tools/form.d.ts +22 -0
- package/dist/agents/tools/form.d.ts.map +1 -0
- package/dist/agents/tools/form.js +134 -0
- package/dist/agents/tools/form.js.map +1 -0
- package/dist/agents/tools/index.d.ts +13 -0
- package/dist/agents/tools/index.d.ts.map +1 -0
- package/dist/agents/tools/index.js +33 -0
- package/dist/agents/tools/index.js.map +1 -0
- package/dist/agents/tools/navigate.d.ts +22 -0
- package/dist/agents/tools/navigate.d.ts.map +1 -0
- package/dist/agents/tools/navigate.js +74 -0
- package/dist/agents/tools/navigate.js.map +1 -0
- package/dist/agents/tools/snapshot.d.ts +22 -0
- package/dist/agents/tools/snapshot.d.ts.map +1 -0
- package/dist/agents/tools/snapshot.js +110 -0
- package/dist/agents/tools/snapshot.js.map +1 -0
- package/dist/agents/tools/verify.d.ts +34 -0
- package/dist/agents/tools/verify.d.ts.map +1 -0
- package/dist/agents/tools/verify.js +169 -0
- package/dist/agents/tools/verify.js.map +1 -0
- package/dist/agents/tools/wait.d.ts +22 -0
- package/dist/agents/tools/wait.d.ts.map +1 -0
- package/dist/agents/tools/wait.js +104 -0
- package/dist/agents/tools/wait.js.map +1 -0
- package/dist/agents/types.d.ts +51 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +6 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/core/ai-heal.d.ts +89 -0
- package/dist/core/ai-heal.d.ts.map +1 -0
- package/dist/core/ai-heal.js +468 -0
- package/dist/core/ai-heal.js.map +1 -0
- package/dist/core/execution-engine.d.ts +16 -0
- package/dist/core/execution-engine.d.ts.map +1 -0
- package/dist/core/execution-engine.js +44 -0
- package/dist/core/execution-engine.js.map +1 -0
- package/dist/core/runner.d.ts +195 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +658 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types/external.d.ts +6 -0
- package/dist/types/external.d.ts.map +1 -0
- package/dist/types/external.js +7 -0
- package/dist/types/external.js.map +1 -0
- package/dist/types/index.d.ts +153 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +26 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/object-registry.d.ts +48 -0
- package/dist/utils/object-registry.d.ts.map +1 -0
- package/dist/utils/object-registry.js +133 -0
- package/dist/utils/object-registry.js.map +1 -0
- package/package.json +37 -0
- package/playwright.config.ts +38 -0
- package/src/agents/heal-agent.ts +85 -0
- package/src/agents/healer.ts +619 -0
- package/src/agents/tools/EXAMPLES.md +347 -0
- package/src/agents/tools/README.md +207 -0
- package/src/agents/tools/form.ts +138 -0
- package/src/agents/tools/index.ts +29 -0
- package/src/agents/tools/navigate.ts +69 -0
- package/src/agents/tools/snapshot.ts +109 -0
- package/src/agents/tools/verify.ts +168 -0
- package/src/agents/tools/wait.ts +103 -0
- package/src/agents/types.ts +79 -0
- package/src/core/runner.ts +756 -0
- package/src/index.ts +29 -0
- package/src/types/external.ts +7 -0
- package/src/types/index.ts +200 -0
- package/tests/agent/test-heal-agent.spec.ts +81 -0
- package/tests/tools/README.md +227 -0
- package/tests/tools/TEST_SUMMARY.md +214 -0
- package/tests/tools/quick-test.ts +88 -0
- package/tests/tools/tools.test.ts +491 -0
- package/tsconfig.json +22 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-runner SDK 主入口
|
|
3
|
+
* 提供 AI 驱动的测试运行能力
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 类型
|
|
7
|
+
export type {
|
|
8
|
+
AIHealConfig,
|
|
9
|
+
ExecutionContext,
|
|
10
|
+
ExecutionResult,
|
|
11
|
+
HealContext,
|
|
12
|
+
HealSummary,
|
|
13
|
+
HealingDetail,
|
|
14
|
+
RunStepOptions,
|
|
15
|
+
VariableBindings
|
|
16
|
+
} from './types';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// 核心类
|
|
20
|
+
export { Runner } from './core/runner';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// Agent
|
|
24
|
+
export type {
|
|
25
|
+
HealAgentState,
|
|
26
|
+
HealAgentResult,
|
|
27
|
+
HealAgentConfig
|
|
28
|
+
} from './agents/types';
|
|
29
|
+
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-heal 核心类型定义 (MVP)
|
|
3
|
+
* 保持简洁,只包含必要的类型
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Page, BrowserContext } from 'playwright';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Ref<T>: 变量包装器
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 变量包装器,支持跨作用域赋值
|
|
14
|
+
*/
|
|
15
|
+
export class Ref<T> {
|
|
16
|
+
constructor(public value: T) {}
|
|
17
|
+
|
|
18
|
+
get current(): T {
|
|
19
|
+
return this.value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set current(value: T) {
|
|
23
|
+
this.value = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// AIHeal 配置
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* AIHeal 配置选项
|
|
33
|
+
*/
|
|
34
|
+
export interface AIHealConfig {
|
|
35
|
+
/** 测试用例 ID */
|
|
36
|
+
testCaseId?: string;
|
|
37
|
+
/** 项目 ID */
|
|
38
|
+
projectId?: string;
|
|
39
|
+
/** 手动注入的脚本内容 */
|
|
40
|
+
scriptContent?: string;
|
|
41
|
+
/** 是否自动保存修复后的脚本 */
|
|
42
|
+
enableAutoSave?: boolean;
|
|
43
|
+
/** 最大自愈重试次数 */
|
|
44
|
+
/** 模型配置 ID(用于加载模型配置) */
|
|
45
|
+
modelConfigId?: string;
|
|
46
|
+
/** Agent 最大执行步数 */
|
|
47
|
+
agentMaxSteps?: number;
|
|
48
|
+
/** 是否开启调试模式 */
|
|
49
|
+
debugMode?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// 执行上下文
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 执行上下文,包含用户的 Playwright 对象
|
|
58
|
+
*/
|
|
59
|
+
export interface ExecutionContext {
|
|
60
|
+
context: BrowserContext;
|
|
61
|
+
page: Page;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// 变量绑定
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 变量绑定映射
|
|
70
|
+
*/
|
|
71
|
+
export type VariableBindings = Record<string, Ref<any>>;
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Step 选项
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* runStep 选项
|
|
79
|
+
*/
|
|
80
|
+
export interface RunStepOptions {
|
|
81
|
+
/** 步骤描述,用于 AI 理解 */
|
|
82
|
+
description: string;
|
|
83
|
+
/** 需要传递的变量绑定 */
|
|
84
|
+
variables?: VariableBindings;
|
|
85
|
+
/** Playwright 上下文对象 */
|
|
86
|
+
context?: ExecutionContext;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ============================================================================
|
|
90
|
+
// 自愈上下文
|
|
91
|
+
// ============================================================================
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* ReAct 循环步骤记录
|
|
95
|
+
*/
|
|
96
|
+
export interface ReActStep {
|
|
97
|
+
/** 步骤序号 */
|
|
98
|
+
stepNumber: number;
|
|
99
|
+
/** 步骤类型 */
|
|
100
|
+
stepType: 'thought' | 'action' | 'observation' | 'reflection';
|
|
101
|
+
/** 时间戳 */
|
|
102
|
+
timestamp: number;
|
|
103
|
+
/** LLM 的思考内容(thought 步骤) */
|
|
104
|
+
thought?: string;
|
|
105
|
+
/** 工具调用信息(action 步骤) */
|
|
106
|
+
toolCall?: {
|
|
107
|
+
name: string;
|
|
108
|
+
input: any;
|
|
109
|
+
};
|
|
110
|
+
/** 工具执行结果(observation 步骤) */
|
|
111
|
+
toolResult?: {
|
|
112
|
+
output: string;
|
|
113
|
+
success: boolean;
|
|
114
|
+
duration?: number;
|
|
115
|
+
};
|
|
116
|
+
/** 反思内容(reflection 步骤) */
|
|
117
|
+
reflection?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 单次自愈过程的上下文信息
|
|
122
|
+
*/
|
|
123
|
+
export interface HealContext {
|
|
124
|
+
/** 时间戳 */
|
|
125
|
+
timestamp: number;
|
|
126
|
+
/** 步骤描述 */
|
|
127
|
+
stepDescription: string;
|
|
128
|
+
/** 错误对象 */
|
|
129
|
+
error?: Error;
|
|
130
|
+
/** 堆栈信息 */
|
|
131
|
+
stackTrace?: string;
|
|
132
|
+
/** 原始代码 */
|
|
133
|
+
originalCode: string;
|
|
134
|
+
/** 恢复尝试次数 */
|
|
135
|
+
recoveryAttempts: number;
|
|
136
|
+
/** 变量快照(执行前) */
|
|
137
|
+
variablesBefore: Record<string, any>;
|
|
138
|
+
/** 变量快照(执行后) */
|
|
139
|
+
variablesAfter: Record<string, any>;
|
|
140
|
+
/** 是否成功 */
|
|
141
|
+
success: boolean;
|
|
142
|
+
/** 自愈耗时 */
|
|
143
|
+
healingTime?: number;
|
|
144
|
+
/** ReAct 循环详细记录(如果使用了 Agent) */
|
|
145
|
+
reactSteps?: ReActStep[];
|
|
146
|
+
/** Agent 的推理过程总结 */
|
|
147
|
+
reasoning?: string;
|
|
148
|
+
/** Agent 执行的总步数 */
|
|
149
|
+
agentSteps?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ============================================================================
|
|
153
|
+
// 执行结果
|
|
154
|
+
// ============================================================================
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 代码执行结果
|
|
158
|
+
*/
|
|
159
|
+
export interface ExecutionResult {
|
|
160
|
+
/** 是否成功 */
|
|
161
|
+
success: boolean;
|
|
162
|
+
/** 返回值 */
|
|
163
|
+
returnValue: any;
|
|
164
|
+
/** 局部变量 */
|
|
165
|
+
locals: Record<string, any>;
|
|
166
|
+
/** 错误信息 */
|
|
167
|
+
error?: Error;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// 自愈摘要
|
|
172
|
+
// ============================================================================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 自愈摘要信息
|
|
176
|
+
*/
|
|
177
|
+
export interface HealSummary {
|
|
178
|
+
/** 总步骤数 */
|
|
179
|
+
totalSteps: number;
|
|
180
|
+
/** 自愈成功的步骤数 */
|
|
181
|
+
healedSteps: number;
|
|
182
|
+
/** 修复后的脚本 */
|
|
183
|
+
modifiedScript?: string;
|
|
184
|
+
/** 自愈详情列表 */
|
|
185
|
+
healingDetails: HealingDetail[];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 单次自愈详情
|
|
190
|
+
*/
|
|
191
|
+
export interface HealingDetail {
|
|
192
|
+
/** 步骤描述 */
|
|
193
|
+
step: string;
|
|
194
|
+
/** 错误信息 */
|
|
195
|
+
error: string;
|
|
196
|
+
/** 恢复时间 */
|
|
197
|
+
recoveryTime: number;
|
|
198
|
+
/** LLM 交互次数 */
|
|
199
|
+
llmInteractions: number;
|
|
200
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自愈 Agent 测试
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { test, expect } from '@playwright/test';
|
|
6
|
+
import { Runner } from '../../src/index';
|
|
7
|
+
|
|
8
|
+
// const heal = new AIHeal({ modelConfigId:'f7858370-daae-4ce9-ba2e-a5cf2270bdc9', scriptContent: ` test('应该调用 ReAct Agent 接管执行', async ({ page, context }) => {
|
|
9
|
+
|
|
10
|
+
// await page.goto('https://example.com');
|
|
11
|
+
|
|
12
|
+
// // 成功步骤不需要 Agent
|
|
13
|
+
// await heal.runStep({
|
|
14
|
+
// description: '获取标题',
|
|
15
|
+
// context: { page, context }
|
|
16
|
+
// }, async () => {
|
|
17
|
+
// const title = await page.title();
|
|
18
|
+
// expect(title).toBeTruthy();
|
|
19
|
+
// });
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// const userUrl = new Ref<string>('');
|
|
23
|
+
// // 失败步骤 - Agent 会接管并尝试修复(可能会失败,但至少执行了)
|
|
24
|
+
// try {
|
|
25
|
+
// await heal.runStep({
|
|
26
|
+
// description: '尝试点击不存在的元素',
|
|
27
|
+
// context: { page, context },
|
|
28
|
+
// variables: {userUrl: userUrl} // key 必须和 Agent set_variable 使用的名称一致
|
|
29
|
+
// }, async () => {
|
|
30
|
+
// await page.click('#non-existent', { timeout: 100 });
|
|
31
|
+
// userUrl.value = await page.url();
|
|
32
|
+
// });
|
|
33
|
+
// } catch (error) {
|
|
34
|
+
// // Agent 可能无法修复,但已经执行了 ReAct 循环
|
|
35
|
+
// }
|
|
36
|
+
|
|
37
|
+
// // 验证变量被 Agent 更新
|
|
38
|
+
// console.log('userUrl.value:', userUrl.value);
|
|
39
|
+
|
|
40
|
+
// const summary = await heal.summary();
|
|
41
|
+
// expect(summary.totalSteps).toBe(2);
|
|
42
|
+
// });` });
|
|
43
|
+
|
|
44
|
+
test.describe('AIHeal - ReAct Agent 集成', () => {
|
|
45
|
+
test.setTimeout(100000000)
|
|
46
|
+
|
|
47
|
+
const runner = Runner.NewInstance('12c57655-0ae2-4580-92f8-b4dc2388a7f5','71692142-7170-4246-898f-21a650dfa6d5' );
|
|
48
|
+
// heal.map()
|
|
49
|
+
test('应该调用 ReAct Agent 接管执行', async ({ page, context }) => {
|
|
50
|
+
await page.goto('https://example.com');
|
|
51
|
+
|
|
52
|
+
// 成功步骤不需要 Agent
|
|
53
|
+
await runner.runStep({
|
|
54
|
+
description: '获取标题',
|
|
55
|
+
context: { page, context }
|
|
56
|
+
}, async () => {
|
|
57
|
+
const title = await page.title();
|
|
58
|
+
|
|
59
|
+
expect(title).toBeTruthy();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// 失败步骤 - Agent 会接管并尝试修复(可能会失败,但至少执行了)
|
|
64
|
+
await runner.runStep({
|
|
65
|
+
description: '尝试点击不存在的元素',
|
|
66
|
+
context: { page, context },
|
|
67
|
+
}, async () => {
|
|
68
|
+
runner.set('current_url', await page.url())
|
|
69
|
+
await page.click('#non-existent', { timeout: 100 });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 验证变量被 Agent 更新
|
|
73
|
+
console.log('userUrl.value:', runner.get('current_url'));
|
|
74
|
+
|
|
75
|
+
const summary = await runner.summary();
|
|
76
|
+
console.log('summary', summary.modifiedScript);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
});
|
|
81
|
+
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Playwright 工具测试
|
|
2
|
+
|
|
3
|
+
本目录包含 LangChain 兼容的 Playwright 工具测试。
|
|
4
|
+
|
|
5
|
+
## 测试文件
|
|
6
|
+
|
|
7
|
+
- **[tools.test.ts](tools.test.ts)** - 完整的工具集成测试
|
|
8
|
+
- **[unit.test.ts](unit.test.ts)** - 单个工具的单元测试
|
|
9
|
+
|
|
10
|
+
## 运行测试
|
|
11
|
+
|
|
12
|
+
### 运行所有工具测试
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# 从 ai-heal 目录运行
|
|
16
|
+
npm run test -- tests/tools/tools.test.ts
|
|
17
|
+
|
|
18
|
+
# 或者使用 Playwright CLI
|
|
19
|
+
npx playwright test tests/tools/
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 运行单元测试
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm run test -- tests/tools/unit.test.ts
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 运行特定测试
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# 只测试导航工具
|
|
32
|
+
npm run test -- tests/tools/tools.test.ts -g "Navigation Tools"
|
|
33
|
+
|
|
34
|
+
# 只测试表单工具
|
|
35
|
+
npm run test -- tests/tools/tools.test.ts -g "Form Tools"
|
|
36
|
+
|
|
37
|
+
# 只测试错误处理
|
|
38
|
+
npm run test -- tests/tools/tools.test.ts -g "Error Handling"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 调试模式
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 显示浏览器
|
|
45
|
+
HEADED=true npm run test -- tests/tools/
|
|
46
|
+
|
|
47
|
+
# 使用 Playwright Inspector
|
|
48
|
+
npm run test:debug -- tests/tools/
|
|
49
|
+
|
|
50
|
+
# 慢速模式
|
|
51
|
+
SLOW_MO=1000 npm run test -- tests/tools/
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 测试覆盖
|
|
55
|
+
|
|
56
|
+
### 工具功能测试
|
|
57
|
+
|
|
58
|
+
✅ **导航工具**
|
|
59
|
+
- 导航到 URL
|
|
60
|
+
- 前进/后退
|
|
61
|
+
|
|
62
|
+
✅ **交互工具**
|
|
63
|
+
- 点击元素(文本选择器)
|
|
64
|
+
- 点击元素(CSS 选择器)
|
|
65
|
+
- 多选择器支持
|
|
66
|
+
- 悬停元素
|
|
67
|
+
|
|
68
|
+
✅ **表单工具**
|
|
69
|
+
- 填写文本输入
|
|
70
|
+
- 通过 label 填写
|
|
71
|
+
- 选择下拉选项
|
|
72
|
+
- 勾选/取消复选框
|
|
73
|
+
|
|
74
|
+
✅ **等待工具**
|
|
75
|
+
- 等待指定时间
|
|
76
|
+
- 等待元素出现
|
|
77
|
+
- 等待 URL 匹配
|
|
78
|
+
- 超时参数
|
|
79
|
+
|
|
80
|
+
✅ **验证工具**
|
|
81
|
+
- 获取 URL
|
|
82
|
+
- 获取页面内容
|
|
83
|
+
- 获取元素文本
|
|
84
|
+
- 截图
|
|
85
|
+
- 验证文本可见
|
|
86
|
+
- 验证元素可见
|
|
87
|
+
|
|
88
|
+
### 错误处理测试
|
|
89
|
+
|
|
90
|
+
- 无效 JSON 输入
|
|
91
|
+
- 不存在的元素
|
|
92
|
+
- 超时处理
|
|
93
|
+
- 选择器失败
|
|
94
|
+
|
|
95
|
+
### 集成测试
|
|
96
|
+
|
|
97
|
+
- 完整表单工作流
|
|
98
|
+
- 导航和交互
|
|
99
|
+
- 等待和验证
|
|
100
|
+
- 多选择器回退
|
|
101
|
+
|
|
102
|
+
## 测试结果
|
|
103
|
+
|
|
104
|
+
运行测试后,结果将保存在:
|
|
105
|
+
- `test-results/` - 测试结果和截图
|
|
106
|
+
- `playwright-report/` - HTML 报告
|
|
107
|
+
|
|
108
|
+
查看报告:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx playwright show-report
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## 调试技巧
|
|
115
|
+
|
|
116
|
+
### 1. 查看 Playwright Inspector
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm run test:debug -- tests/tools/tools.test.ts
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2. 保留浏览器窗口
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
HEADED=true npm run test -- tests/tools/tools.test.ts
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. 保留追踪
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
test.use({ trace: 'on' });
|
|
132
|
+
|
|
133
|
+
test('my test', async ({ page }) => {
|
|
134
|
+
// 测试代码
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 4. 查看控制台日志
|
|
139
|
+
|
|
140
|
+
测试中的 `console.log` 会显示在终端输出中。
|
|
141
|
+
|
|
142
|
+
## 添加新测试
|
|
143
|
+
|
|
144
|
+
### 测试新工具
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
test('my new tool - should do something', async ({ page }) => {
|
|
148
|
+
// 准备测试页面
|
|
149
|
+
await page.setContent(`...`);
|
|
150
|
+
|
|
151
|
+
// 创建工具实例
|
|
152
|
+
const tools = getAllTools(page);
|
|
153
|
+
const myTool = tools.find(t => t.name === 'browser_my_tool');
|
|
154
|
+
|
|
155
|
+
// 调用工具
|
|
156
|
+
const result = await myTool!.invoke('input');
|
|
157
|
+
|
|
158
|
+
// 验证结果
|
|
159
|
+
expect(result).toContain('expected output');
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 测试错误场景
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
test('should handle error gracefully', async ({ page }) => {
|
|
167
|
+
const tools = getAllTools(page);
|
|
168
|
+
const tool = tools.find(t => t.name === 'browser_tool');
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await tool!.invoke('invalid input');
|
|
172
|
+
expect.fail('Should have thrown an error');
|
|
173
|
+
} catch (error) {
|
|
174
|
+
expect((error as Error).message).toContain('expected error');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 已知问题
|
|
180
|
+
|
|
181
|
+
### 1. ariaSnapshot 兼容性
|
|
182
|
+
|
|
183
|
+
`browser_snapshot` 工具使用了 `ariaSnapshot()` API,这在较旧版本的 Playwright 中可能不可用。
|
|
184
|
+
|
|
185
|
+
**解决方案**:确保使用 Playwright 1.40 或更高版本。
|
|
186
|
+
|
|
187
|
+
### 2. 多选择器性能
|
|
188
|
+
|
|
189
|
+
当提供多个选择器时,工具会依次尝试,这可能影响性能。
|
|
190
|
+
|
|
191
|
+
**优化建议**:按最可能成功到最不可能成功的顺序排列选择器。
|
|
192
|
+
|
|
193
|
+
## 持续集成
|
|
194
|
+
|
|
195
|
+
### GitHub Actions 示例
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
name: Test Tools
|
|
199
|
+
|
|
200
|
+
on: [push, pull_request]
|
|
201
|
+
|
|
202
|
+
jobs:
|
|
203
|
+
test:
|
|
204
|
+
runs-on: ubuntu-latest
|
|
205
|
+
|
|
206
|
+
steps:
|
|
207
|
+
- uses: actions/checkout@v3
|
|
208
|
+
- uses: actions/setup-node@v3
|
|
209
|
+
with:
|
|
210
|
+
node-version: 18
|
|
211
|
+
|
|
212
|
+
- run: npm ci
|
|
213
|
+
- run: npm run test -- tests/tools/
|
|
214
|
+
|
|
215
|
+
- uses: actions/upload-artifact@v3
|
|
216
|
+
if: failure()
|
|
217
|
+
with:
|
|
218
|
+
name: test-results
|
|
219
|
+
path: test-results/
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 相关文档
|
|
223
|
+
|
|
224
|
+
- [Playwright Testing Guide](https://playwright.dev/docs/intro)
|
|
225
|
+
- [LangChain Tools](https://js.langchain.com/docs/concepts/tools)
|
|
226
|
+
- [工具 README](../../src/agents/tools/README.md)
|
|
227
|
+
- [工具示例](../../src/agents/tools/EXAMPLES.md)
|