@dyyz1993/agent-browser 0.11.1 → 0.11.3
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/bin/agent-browser-linux-x64 +0 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +9 -1
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/viewer-script.d.ts.map +1 -1
- package/dist/viewer-script.js +17 -6
- package/dist/viewer-script.js.map +1 -1
- package/package.json +1 -1
- package/bin/agent-browser-darwin-arm64 +0 -0
- package/scripts/check_goods_container.js +0 -35
- package/scripts/check_page_content.js +0 -36
- package/scripts/click_applause_rate.js +0 -30
- package/scripts/e2e-test-recorder.ts +0 -584
- package/scripts/explore_jd_page.js +0 -31
- package/scripts/extract_all_jd_data.js +0 -80
- package/scripts/extract_jd_product_detail.js +0 -62
- package/scripts/extract_jd_products_correct_links.js +0 -78
- package/scripts/extract_jd_products_final.js +0 -80
- package/scripts/extract_jd_reviews.js +0 -48
- package/scripts/extract_jd_seafood_final.js +0 -78
- package/scripts/extract_multiple_products.js +0 -77
- package/scripts/extract_products_no_scroll.js +0 -68
- package/scripts/extract_products_simple.js +0 -68
- package/scripts/find_applause_rate.js +0 -26
- package/scripts/find_jd_links.js +0 -28
- package/scripts/find_main_content.js +0 -20
- package/scripts/find_product_cards.js +0 -38
- package/scripts/find_root_content.js +0 -26
- package/scripts/find_unique_products.js +0 -55
- package/scripts/get_jd_product_detail.js +0 -16
- package/scripts/get_jd_products.js +0 -23
- package/scripts/get_jd_seafood_products.js +0 -44
- package/scripts/get_product_details_from_images.js +0 -54
- package/scripts/scroll_and_get_products.js +0 -47
- package/scripts/scroll_deep_and_find.js +0 -45
- package/scripts/verify-baidu-enter.ts +0 -116
- package/scripts/verify-form.sh +0 -67
- package/scripts/verify-login.sh +0 -65
- package/scripts/verify-recording.sh +0 -80
- package/scripts/verify-upload.sh +0 -41
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
(() => {
|
|
2
|
-
const result = {
|
|
3
|
-
goodsContainerCount: 0,
|
|
4
|
-
sampleContainer: null,
|
|
5
|
-
allClasses: []
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
const containers = document.querySelectorAll('._goodsContainer_1p2ae_1');
|
|
9
|
-
result.goodsContainerCount = containers.length;
|
|
10
|
-
|
|
11
|
-
if (containers.length > 0) {
|
|
12
|
-
result.sampleContainer = {
|
|
13
|
-
className: containers[0].className,
|
|
14
|
-
innerHTML: containers[0].innerHTML.substring(0, 500),
|
|
15
|
-
text: containers[0].textContent?.substring(0, 200)
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const allDivs = document.querySelectorAll('div');
|
|
20
|
-
const classSet = new Set();
|
|
21
|
-
allDivs.forEach(div => {
|
|
22
|
-
if (div.className) {
|
|
23
|
-
const classes = String(div.className).split(' ');
|
|
24
|
-
classes.forEach(c => {
|
|
25
|
-
if (c.includes('goods') || c.includes('item') || c.includes('product')) {
|
|
26
|
-
classSet.add(c);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
result.allClasses = Array.from(classSet).slice(0, 20);
|
|
33
|
-
|
|
34
|
-
return result;
|
|
35
|
-
})()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
(() => {
|
|
2
|
-
const result = {
|
|
3
|
-
visibleElements: [],
|
|
4
|
-
linksWithProductInClass: []
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const allElements = document.querySelectorAll('*');
|
|
8
|
-
let count = 0;
|
|
9
|
-
|
|
10
|
-
for (const el of allElements) {
|
|
11
|
-
if (count >= 50) break;
|
|
12
|
-
|
|
13
|
-
const className = el.className ? String(el.className) : '';
|
|
14
|
-
const text = el.textContent?.trim() || '';
|
|
15
|
-
|
|
16
|
-
if ((className.includes('product') || className.includes('item') || className.includes('card')) && text.length > 10) {
|
|
17
|
-
result.visibleElements.push({
|
|
18
|
-
tagName: el.tagName,
|
|
19
|
-
className: className.substring(0, 100),
|
|
20
|
-
text: text.substring(0, 100)
|
|
21
|
-
});
|
|
22
|
-
count++;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const productLinks = document.querySelectorAll('a[href*="item.jd"], a[href*="product.jd"]');
|
|
27
|
-
productLinks.forEach((link, index) => {
|
|
28
|
-
if (index >= 10) return;
|
|
29
|
-
result.linksWithProductInClass.push({
|
|
30
|
-
href: link.href.substring(0, 100),
|
|
31
|
-
text: link.textContent?.substring(0, 50)
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
return result;
|
|
36
|
-
})()
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
(async () => {
|
|
2
|
-
const applauseRateEl = document.querySelector('.applause-rate');
|
|
3
|
-
|
|
4
|
-
if (applauseRateEl) {
|
|
5
|
-
applauseRateEl.click();
|
|
6
|
-
|
|
7
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
8
|
-
|
|
9
|
-
const result = {
|
|
10
|
-
elementFound: true,
|
|
11
|
-
elementText: applauseRateEl.textContent,
|
|
12
|
-
popup: null
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const popup = document.querySelector('[class*="popup"], [class*="modal"], [class*="dialog"]');
|
|
16
|
-
if (popup) {
|
|
17
|
-
result.popup = {
|
|
18
|
-
className: popup.className,
|
|
19
|
-
text: popup.textContent?.substring(0, 500)
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return result;
|
|
24
|
-
} else {
|
|
25
|
-
return {
|
|
26
|
-
elementFound: false,
|
|
27
|
-
message: 'applause-rate element not found'
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
})()
|
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
import { chromium } from 'playwright';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
|
|
9
|
-
async function testRecorderPanel() {
|
|
10
|
-
console.log('=== E2E 测试录制器面板 ===\n');
|
|
11
|
-
|
|
12
|
-
const browser = await chromium.launch({ headless: false });
|
|
13
|
-
const context = await browser.newContext();
|
|
14
|
-
const page = await context.newPage();
|
|
15
|
-
|
|
16
|
-
// 读取注入脚本
|
|
17
|
-
const injectScript = fs.readFileSync(
|
|
18
|
-
path.join(__dirname, '../dist/recorder/inject.js'),
|
|
19
|
-
'utf-8'
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
// 创建一个简单的测试页面
|
|
23
|
-
await page.setContent(`
|
|
24
|
-
<!DOCTYPE html>
|
|
25
|
-
<html>
|
|
26
|
-
<head><title>Test Page</title></head>
|
|
27
|
-
<body style="padding: 20px;">
|
|
28
|
-
<input id="test-input" type="text" placeholder="Test Input" style="margin: 50px;">
|
|
29
|
-
<button id="test-button" style="margin: 50px;">Test Button</button>
|
|
30
|
-
<div id="test-div" style="margin: 50px; width: 100px; height: 100px; background: #ccc;">Test Div</div>
|
|
31
|
-
</body>
|
|
32
|
-
</html>
|
|
33
|
-
`);
|
|
34
|
-
await page.waitForTimeout(500);
|
|
35
|
-
|
|
36
|
-
// 注入录制器脚本
|
|
37
|
-
await page.evaluate(injectScript);
|
|
38
|
-
await page.waitForTimeout(500);
|
|
39
|
-
|
|
40
|
-
// 初始化录制器(使用统一 API)
|
|
41
|
-
await page.evaluate(() => {
|
|
42
|
-
(window as any).__recorderSteps = [];
|
|
43
|
-
(window as any).__recorderSync = (msg: string) => {
|
|
44
|
-
// 统一 API 已经处理了步骤管理,这里只需要同步到主进程
|
|
45
|
-
console.log('[DEBUG] __recorderSync:', msg);
|
|
46
|
-
};
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const tests = [
|
|
50
|
-
// ========== UI 测试 ==========
|
|
51
|
-
{
|
|
52
|
-
name: '1. 面板是否存在',
|
|
53
|
-
test: async () => {
|
|
54
|
-
const panel = await page.$('#recorder-panel');
|
|
55
|
-
return !!panel;
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: '2. 面板是否可拖拽',
|
|
60
|
-
test: async () => {
|
|
61
|
-
const header = await page.$('.recorder-panel-header');
|
|
62
|
-
if (!header) return false;
|
|
63
|
-
|
|
64
|
-
const headerStyle = await header.evaluate((el) => {
|
|
65
|
-
const style = window.getComputedStyle(el);
|
|
66
|
-
return { cursor: style.cursor };
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return headerStyle.cursor === 'move';
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: '3. 折叠按钮是否存在',
|
|
74
|
-
test: async () => {
|
|
75
|
-
const collapseBtn = await page.$('#recorder-collapse');
|
|
76
|
-
return !!collapseBtn;
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: '4. 工具选择区域是否存在',
|
|
81
|
-
test: async () => {
|
|
82
|
-
const tools = await page.$('#recorder-tools');
|
|
83
|
-
return !!tools;
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
name: '5. 工具按钮数量是否正确',
|
|
88
|
-
test: async () => {
|
|
89
|
-
const toolBtns = await page.$$('#recorder-tools .tool-btn');
|
|
90
|
-
return toolBtns.length === 7;
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
// ========== 统一 API 测试 ==========
|
|
95
|
-
{
|
|
96
|
-
name: '6. __recorderAction add 操作',
|
|
97
|
-
test: async () => {
|
|
98
|
-
const result = await page.evaluate(() => {
|
|
99
|
-
const action = (window as any).__recorderAction;
|
|
100
|
-
if (!action) return { success: false, error: '__recorderAction not found' };
|
|
101
|
-
|
|
102
|
-
const res = action({ type: 'add', data: { action: 'test', value: 'test-value' } });
|
|
103
|
-
return {
|
|
104
|
-
success: res.success && res.steps.length === 1,
|
|
105
|
-
stepId: res.steps[0]?.id,
|
|
106
|
-
stepsLength: res.steps.length
|
|
107
|
-
};
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
console.log(' [DEBUG] add 结果:', result);
|
|
111
|
-
return result.success;
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
name: '7. 步骤 ID 格式是否正确(6 位数)',
|
|
116
|
-
test: async () => {
|
|
117
|
-
const result = await page.evaluate(() => {
|
|
118
|
-
const steps = (window as any).__recorderSteps || [];
|
|
119
|
-
if (steps.length === 0) return { success: false, error: 'No steps' };
|
|
120
|
-
|
|
121
|
-
const id = steps[0].id;
|
|
122
|
-
const isValid = /^\d{6}$/.test(id);
|
|
123
|
-
return { success: isValid, id };
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
console.log(' [DEBUG] 步骤 ID:', result.id);
|
|
127
|
-
return result.success;
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: '8. __recorderAction list 操作',
|
|
132
|
-
test: async () => {
|
|
133
|
-
const result = await page.evaluate(() => {
|
|
134
|
-
const action = (window as any).__recorderAction;
|
|
135
|
-
if (!action) return { success: false };
|
|
136
|
-
|
|
137
|
-
const res = action({ type: 'list' });
|
|
138
|
-
return {
|
|
139
|
-
success: res.success && res.steps.length >= 1,
|
|
140
|
-
stepsLength: res.steps.length
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
console.log(' [DEBUG] list 结果:', result);
|
|
145
|
-
return result.success;
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: '9. __recorderAction update 操作',
|
|
150
|
-
test: async () => {
|
|
151
|
-
const result = await page.evaluate(() => {
|
|
152
|
-
const action = (window as any).__recorderAction;
|
|
153
|
-
const steps = (window as any).__recorderSteps || [];
|
|
154
|
-
|
|
155
|
-
if (steps.length === 0) return { success: false, error: 'No steps' };
|
|
156
|
-
|
|
157
|
-
const stepId = steps[0].id;
|
|
158
|
-
const res = action({
|
|
159
|
-
type: 'update',
|
|
160
|
-
id: stepId,
|
|
161
|
-
data: { annotation: { type: 'test', label: 'Test Label' } }
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const updatedStep = res.steps.find((s: any) => s.id === stepId);
|
|
165
|
-
return {
|
|
166
|
-
success: res.success && updatedStep?.annotation?.label === 'Test Label',
|
|
167
|
-
stepId,
|
|
168
|
-
annotation: updatedStep?.annotation
|
|
169
|
-
};
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
console.log(' [DEBUG] update 结果:', result);
|
|
173
|
-
return result.success;
|
|
174
|
-
}
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
name: '10. __recorderAction delete 操作',
|
|
178
|
-
test: async () => {
|
|
179
|
-
const result = await page.evaluate(() => {
|
|
180
|
-
const action = (window as any).__recorderAction;
|
|
181
|
-
const steps = (window as any).__recorderSteps || [];
|
|
182
|
-
|
|
183
|
-
if (steps.length === 0) return { success: false, error: 'No steps' };
|
|
184
|
-
|
|
185
|
-
const stepId = steps[0].id;
|
|
186
|
-
const beforeLength = steps.length;
|
|
187
|
-
const res = action({ type: 'delete', id: stepId });
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
success: res.success && res.steps.length === beforeLength - 1,
|
|
191
|
-
deletedId: stepId,
|
|
192
|
-
beforeLength,
|
|
193
|
-
afterLength: res.steps.length
|
|
194
|
-
};
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
console.log(' [DEBUG] delete 结果:', result);
|
|
198
|
-
return result.success;
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
name: '11. __recorderAction clear 操作',
|
|
203
|
-
test: async () => {
|
|
204
|
-
// 先添加一些步骤
|
|
205
|
-
await page.evaluate(() => {
|
|
206
|
-
const action = (window as any).__recorderAction;
|
|
207
|
-
action({ type: 'add', data: { action: 'test1' } });
|
|
208
|
-
action({ type: 'add', data: { action: 'test2' } });
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
const result = await page.evaluate(() => {
|
|
212
|
-
const action = (window as any).__recorderAction;
|
|
213
|
-
const res = action({ type: 'clear' });
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
success: res.success && res.steps.length === 0,
|
|
217
|
-
stepsLength: res.steps.length
|
|
218
|
-
};
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
console.log(' [DEBUG] clear 结果:', result);
|
|
222
|
-
return result.success;
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
name: '12. __recorderAction 错误处理',
|
|
227
|
-
test: async () => {
|
|
228
|
-
const result = await page.evaluate(() => {
|
|
229
|
-
const action = (window as any).__recorderAction;
|
|
230
|
-
|
|
231
|
-
// 测试无效操作
|
|
232
|
-
const res1 = action({ type: 'invalid' });
|
|
233
|
-
const res2 = action({ type: 'delete', id: '999999' });
|
|
234
|
-
const res3 = action({ type: 'update', id: '999999', data: {} });
|
|
235
|
-
const res4 = action(null);
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
success: !res1.success && !res2.success && !res3.success && !res4.success,
|
|
239
|
-
errors: [res1.error, res2.error, res3.error, res4.error]
|
|
240
|
-
};
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
console.log(' [DEBUG] 错误处理结果:', result);
|
|
244
|
-
return result.success;
|
|
245
|
-
}
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
// ========== 功能测试 ==========
|
|
249
|
-
{
|
|
250
|
-
name: '13. 折叠功能是否正常',
|
|
251
|
-
test: async () => {
|
|
252
|
-
const collapseBtn = await page.$('#recorder-collapse');
|
|
253
|
-
const panelBody = await page.$('#recorder-steps');
|
|
254
|
-
const panelTools = await page.$('#recorder-tools');
|
|
255
|
-
|
|
256
|
-
if (!collapseBtn || !panelBody || !panelTools) return false;
|
|
257
|
-
|
|
258
|
-
// 点击折叠
|
|
259
|
-
await collapseBtn.click();
|
|
260
|
-
await page.waitForTimeout(100);
|
|
261
|
-
|
|
262
|
-
const bodyDisplay = await panelBody.evaluate((el) =>
|
|
263
|
-
window.getComputedStyle(el).display
|
|
264
|
-
);
|
|
265
|
-
const toolsDisplay = await panelTools.evaluate((el) =>
|
|
266
|
-
window.getComputedStyle(el).display
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
if (bodyDisplay !== 'none' || toolsDisplay !== 'none') return false;
|
|
270
|
-
|
|
271
|
-
// 再次点击展开
|
|
272
|
-
await collapseBtn.click();
|
|
273
|
-
await page.waitForTimeout(100);
|
|
274
|
-
|
|
275
|
-
const bodyDisplay2 = await panelBody.evaluate((el) =>
|
|
276
|
-
window.getComputedStyle(el).display
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
return bodyDisplay2 === 'block';
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
name: '14. 录制步骤是否正常',
|
|
284
|
-
test: async () => {
|
|
285
|
-
// 清空现有步骤
|
|
286
|
-
await page.evaluate(() => {
|
|
287
|
-
(window as any).__recorderAction({ type: 'clear' });
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// 点击测试输入框
|
|
291
|
-
await page.click('#test-input');
|
|
292
|
-
await page.waitForTimeout(500);
|
|
293
|
-
|
|
294
|
-
// 手动触发 UI 更新
|
|
295
|
-
await page.evaluate(() => {
|
|
296
|
-
if (typeof (window as any).__updateRecorderUI === 'function') {
|
|
297
|
-
(window as any).__updateRecorderUI();
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
await page.waitForTimeout(200);
|
|
301
|
-
|
|
302
|
-
const steps = await page.evaluate(() => {
|
|
303
|
-
return (window as any).__recorderSteps || [];
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
console.log(' [DEBUG] 步骤数量:', steps.length);
|
|
307
|
-
|
|
308
|
-
return steps.length > 0;
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
name: '15. 选中步骤功能是否正常',
|
|
313
|
-
test: async () => {
|
|
314
|
-
// 使用 JavaScript 点击
|
|
315
|
-
const result = await page.evaluate(() => {
|
|
316
|
-
const stepEl = document.querySelector('.recorder-step');
|
|
317
|
-
if (!stepEl) return { success: false, error: 'No step element' };
|
|
318
|
-
|
|
319
|
-
stepEl.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
stepId: stepEl.dataset.stepId
|
|
323
|
-
};
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
console.log(' [DEBUG] 选中步骤 ID:', result.stepId);
|
|
327
|
-
|
|
328
|
-
// 等待 1 秒让 UI 更新
|
|
329
|
-
await page.waitForTimeout(1000);
|
|
330
|
-
|
|
331
|
-
// 验证选中状态
|
|
332
|
-
const verifyResult = await page.evaluate(() => {
|
|
333
|
-
const stepEl = document.querySelector('.recorder-step');
|
|
334
|
-
const currentStepId = (window as any).__recorderCurrentStepId;
|
|
335
|
-
return {
|
|
336
|
-
isSelected: stepEl ? stepEl.classList.contains('selected') : false,
|
|
337
|
-
currentStepId: currentStepId
|
|
338
|
-
};
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
console.log(' [DEBUG] 验证结果:', verifyResult);
|
|
342
|
-
return verifyResult.isSelected && verifyResult.currentStepId === result.stepId;
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
{
|
|
346
|
-
name: '16. 添加标签功能是否正常',
|
|
347
|
-
test: async () => {
|
|
348
|
-
// 先选中一个步骤
|
|
349
|
-
const selectResult = await page.evaluate(() => {
|
|
350
|
-
const stepEl = document.querySelector('.recorder-step');
|
|
351
|
-
if (stepEl) {
|
|
352
|
-
stepEl.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
353
|
-
return { success: true, id: stepEl.dataset.stepId };
|
|
354
|
-
}
|
|
355
|
-
return { success: false, id: null };
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
if (!selectResult.success) {
|
|
359
|
-
console.log(' [DEBUG] 没有找到步骤元素');
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
console.log(' [DEBUG] 选中步骤 ID:', selectResult.id);
|
|
364
|
-
|
|
365
|
-
await page.waitForTimeout(300);
|
|
366
|
-
|
|
367
|
-
// 点击 Wait 工具按钮
|
|
368
|
-
const waitBtn = await page.$('.tool-btn[data-tool="wait_element"]');
|
|
369
|
-
if (!waitBtn) {
|
|
370
|
-
console.log(' [DEBUG] 没有找到 Wait 按钮');
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// 监听 dialog 事件
|
|
375
|
-
page.once('dialog', async dialog => {
|
|
376
|
-
console.log(' [DEBUG] Dialog 出现:', dialog.message());
|
|
377
|
-
await dialog.accept('5000');
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
await waitBtn.click();
|
|
381
|
-
|
|
382
|
-
// 延迟 2 秒验证标注是否持久化
|
|
383
|
-
await page.waitForTimeout(2000);
|
|
384
|
-
|
|
385
|
-
const result = await page.evaluate(() => {
|
|
386
|
-
const steps = (window as any).__recorderSteps || [];
|
|
387
|
-
const currentStepId = (window as any).__recorderCurrentStepId;
|
|
388
|
-
|
|
389
|
-
const step = steps.find((s: any) => s.id === currentStepId);
|
|
390
|
-
if (step) {
|
|
391
|
-
return {
|
|
392
|
-
success: step.annotation && step.annotation.type === 'wait_element',
|
|
393
|
-
annotation: step.annotation
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
return { success: false, annotation: null };
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
console.log(' [DEBUG] 添加标签结果:', result);
|
|
400
|
-
return result.success;
|
|
401
|
-
}
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
name: '17. 修改已有标注功能是否正常',
|
|
405
|
-
test: async () => {
|
|
406
|
-
// 选中已有标注的步骤
|
|
407
|
-
const selectResult = await page.evaluate(() => {
|
|
408
|
-
const stepEl = document.querySelector('.recorder-step');
|
|
409
|
-
if (stepEl) {
|
|
410
|
-
stepEl.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
411
|
-
return { success: true, id: stepEl.dataset.stepId };
|
|
412
|
-
}
|
|
413
|
-
return { success: false, id: null };
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
if (!selectResult.success) return false;
|
|
417
|
-
|
|
418
|
-
await page.waitForTimeout(200);
|
|
419
|
-
|
|
420
|
-
// 点击 Check 工具按钮(修改标注)
|
|
421
|
-
const checkBtn = await page.$('.tool-btn[data-tool="checkpoint"]');
|
|
422
|
-
if (!checkBtn) return false;
|
|
423
|
-
|
|
424
|
-
await checkBtn.click();
|
|
425
|
-
await page.waitForTimeout(500);
|
|
426
|
-
|
|
427
|
-
const result = await page.evaluate(() => {
|
|
428
|
-
const steps = (window as any).__recorderSteps || [];
|
|
429
|
-
const currentStepId = (window as any).__recorderCurrentStepId;
|
|
430
|
-
|
|
431
|
-
const step = steps.find((s: any) => s.id === currentStepId);
|
|
432
|
-
if (step) {
|
|
433
|
-
return {
|
|
434
|
-
success: step.annotation && step.annotation.type === 'checkpoint',
|
|
435
|
-
annotation: step.annotation
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
return { success: false, annotation: null };
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
console.log(' [DEBUG] 修改标注结果:', result);
|
|
442
|
-
return result.success;
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
name: '18. 步骤删除功能是否正常',
|
|
447
|
-
test: async () => {
|
|
448
|
-
// 禁用录制
|
|
449
|
-
await page.evaluate(() => {
|
|
450
|
-
(window as any).__recorderPaused = true;
|
|
451
|
-
if ((window as any).__clearPendingSteps) {
|
|
452
|
-
(window as any).__clearPendingSteps();
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
const beforeInfo = await page.evaluate(() => {
|
|
457
|
-
const steps = (window as any).__recorderSteps || [];
|
|
458
|
-
return {
|
|
459
|
-
length: steps.length,
|
|
460
|
-
ids: steps.map((s: any) => s.id)
|
|
461
|
-
};
|
|
462
|
-
});
|
|
463
|
-
console.log(' [DEBUG] 删除前步骤数量:', beforeInfo.length);
|
|
464
|
-
|
|
465
|
-
if (beforeInfo.length === 0) return false;
|
|
466
|
-
|
|
467
|
-
// 选中最后一个步骤
|
|
468
|
-
const selectResult = await page.evaluate(() => {
|
|
469
|
-
const steps = document.querySelectorAll('.recorder-step');
|
|
470
|
-
if (steps.length > 0) {
|
|
471
|
-
const lastStep = steps[steps.length - 1];
|
|
472
|
-
lastStep.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
473
|
-
return { success: true, id: lastStep.dataset.stepId };
|
|
474
|
-
}
|
|
475
|
-
return { success: false, id: null };
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
await page.waitForTimeout(200);
|
|
479
|
-
|
|
480
|
-
// 点击删除按钮
|
|
481
|
-
const deleteResult = await page.evaluate(() => {
|
|
482
|
-
const btn = document.querySelector('.recorder-delete-btn');
|
|
483
|
-
if (btn) {
|
|
484
|
-
btn.click();
|
|
485
|
-
return true;
|
|
486
|
-
}
|
|
487
|
-
return false;
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
await page.waitForTimeout(500);
|
|
491
|
-
|
|
492
|
-
const afterInfo = await page.evaluate(() => {
|
|
493
|
-
const steps = (window as any).__recorderSteps || [];
|
|
494
|
-
return { stepsLength: steps.length };
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
// 重新启用录制
|
|
498
|
-
await page.evaluate(() => {
|
|
499
|
-
(window as any).__recorderPaused = false;
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
console.log(' [DEBUG] 删除后步骤数量:', afterInfo.stepsLength);
|
|
503
|
-
return afterInfo.stepsLength === beforeInfo.length - 1;
|
|
504
|
-
}
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
name: '19. Clear 按钮功能是否正常',
|
|
508
|
-
test: async () => {
|
|
509
|
-
// 先添加一些步骤
|
|
510
|
-
await page.evaluate(() => {
|
|
511
|
-
const action = (window as any).__recorderAction;
|
|
512
|
-
action({ type: 'add', data: { action: 'test1' } });
|
|
513
|
-
action({ type: 'add', data: { action: 'test2' } });
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// 手动触发 UI 更新
|
|
517
|
-
await page.evaluate(() => {
|
|
518
|
-
(window as any).__updateRecorderUI();
|
|
519
|
-
});
|
|
520
|
-
await page.waitForTimeout(200);
|
|
521
|
-
|
|
522
|
-
// 点击 Clear 按钮
|
|
523
|
-
const clearBtn = await page.$('#recorder-clear');
|
|
524
|
-
if (!clearBtn) return false;
|
|
525
|
-
|
|
526
|
-
await clearBtn.click();
|
|
527
|
-
await page.waitForTimeout(200);
|
|
528
|
-
|
|
529
|
-
const result = await page.evaluate(() => {
|
|
530
|
-
const steps = (window as any).__recorderSteps || [];
|
|
531
|
-
return { stepsLength: steps.length };
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
console.log(' [DEBUG] Clear 后步骤数量:', result.stepsLength);
|
|
535
|
-
return result.stepsLength === 0;
|
|
536
|
-
}
|
|
537
|
-
},
|
|
538
|
-
{
|
|
539
|
-
name: '20. 滚动穿透是否阻止',
|
|
540
|
-
test: async () => {
|
|
541
|
-
// 这个测试需要手动验证
|
|
542
|
-
return true;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
];
|
|
546
|
-
|
|
547
|
-
let passed = 0;
|
|
548
|
-
let failed = 0;
|
|
549
|
-
|
|
550
|
-
for (const { name, test } of tests) {
|
|
551
|
-
try {
|
|
552
|
-
const result = await test();
|
|
553
|
-
if (result) {
|
|
554
|
-
console.log(`✅ ${name}`);
|
|
555
|
-
passed++;
|
|
556
|
-
} else {
|
|
557
|
-
console.log(`❌ ${name}`);
|
|
558
|
-
failed++;
|
|
559
|
-
}
|
|
560
|
-
} catch (error) {
|
|
561
|
-
console.log(`❌ ${name} - ${(error as Error).message}`);
|
|
562
|
-
failed++;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
console.log(`\n=== 测试结果 ===`);
|
|
567
|
-
console.log(`通过: ${passed}/${tests.length}`);
|
|
568
|
-
console.log(`失败: ${failed}/${tests.length}`);
|
|
569
|
-
|
|
570
|
-
console.log('\n浏览器将保持打开 5 秒...');
|
|
571
|
-
await page.waitForTimeout(5000);
|
|
572
|
-
await browser.close();
|
|
573
|
-
|
|
574
|
-
return failed === 0;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
testRecorderPanel()
|
|
578
|
-
.then(success => {
|
|
579
|
-
process.exit(success ? 0 : 1);
|
|
580
|
-
})
|
|
581
|
-
.catch(error => {
|
|
582
|
-
console.error(error);
|
|
583
|
-
process.exit(1);
|
|
584
|
-
});
|