cdp-skill 1.0.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/SKILL.md +543 -0
- package/install.js +92 -0
- package/package.json +47 -0
- package/src/aria.js +1302 -0
- package/src/capture.js +1359 -0
- package/src/cdp.js +905 -0
- package/src/cli.js +244 -0
- package/src/dom.js +3525 -0
- package/src/index.js +155 -0
- package/src/page.js +1720 -0
- package/src/runner.js +2111 -0
- package/src/tests/BrowserClient.test.js +588 -0
- package/src/tests/CDPConnection.test.js +598 -0
- package/src/tests/ChromeDiscovery.test.js +181 -0
- package/src/tests/ConsoleCapture.test.js +302 -0
- package/src/tests/ElementHandle.test.js +586 -0
- package/src/tests/ElementLocator.test.js +586 -0
- package/src/tests/ErrorAggregator.test.js +327 -0
- package/src/tests/InputEmulator.test.js +641 -0
- package/src/tests/NetworkErrorCapture.test.js +458 -0
- package/src/tests/PageController.test.js +822 -0
- package/src/tests/ScreenshotCapture.test.js +356 -0
- package/src/tests/SessionRegistry.test.js +257 -0
- package/src/tests/TargetManager.test.js +274 -0
- package/src/tests/TestRunner.test.js +1529 -0
- package/src/tests/WaitStrategy.test.js +406 -0
- package/src/tests/integration.test.js +431 -0
- package/src/utils.js +1034 -0
- package/uninstall.js +44 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
// Core connection and browser management
|
|
6
|
+
createConnection,
|
|
7
|
+
createDiscovery,
|
|
8
|
+
discoverChrome,
|
|
9
|
+
createTargetManager,
|
|
10
|
+
createSessionRegistry,
|
|
11
|
+
createBrowser,
|
|
12
|
+
createPageSession,
|
|
13
|
+
|
|
14
|
+
// Page operations
|
|
15
|
+
createPageController,
|
|
16
|
+
WaitCondition,
|
|
17
|
+
waitForCondition,
|
|
18
|
+
waitForFunction,
|
|
19
|
+
waitForNetworkIdle,
|
|
20
|
+
waitForDocumentReady,
|
|
21
|
+
waitForSelector,
|
|
22
|
+
waitForText,
|
|
23
|
+
|
|
24
|
+
// Element location and interaction
|
|
25
|
+
createElementHandle,
|
|
26
|
+
createElementLocator,
|
|
27
|
+
createInputEmulator,
|
|
28
|
+
querySelector,
|
|
29
|
+
querySelectorAll,
|
|
30
|
+
findElement,
|
|
31
|
+
getBoundingBox,
|
|
32
|
+
isVisible,
|
|
33
|
+
isActionable,
|
|
34
|
+
scrollIntoView,
|
|
35
|
+
click,
|
|
36
|
+
type,
|
|
37
|
+
fill,
|
|
38
|
+
press,
|
|
39
|
+
scroll,
|
|
40
|
+
|
|
41
|
+
// Capture and monitoring
|
|
42
|
+
createScreenshotCapture,
|
|
43
|
+
captureViewport,
|
|
44
|
+
captureFullPage,
|
|
45
|
+
captureRegion,
|
|
46
|
+
saveScreenshot,
|
|
47
|
+
createConsoleCapture,
|
|
48
|
+
createNetworkCapture,
|
|
49
|
+
createErrorAggregator,
|
|
50
|
+
aggregateErrors,
|
|
51
|
+
|
|
52
|
+
// Test execution
|
|
53
|
+
validateSteps,
|
|
54
|
+
executeStep,
|
|
55
|
+
runSteps,
|
|
56
|
+
createTestRunner,
|
|
57
|
+
|
|
58
|
+
// Errors
|
|
59
|
+
ErrorTypes,
|
|
60
|
+
createError,
|
|
61
|
+
navigationError,
|
|
62
|
+
timeoutError,
|
|
63
|
+
elementNotFoundError,
|
|
64
|
+
staleElementError,
|
|
65
|
+
pageCrashedError,
|
|
66
|
+
contextDestroyedError,
|
|
67
|
+
stepValidationError,
|
|
68
|
+
isErrorType,
|
|
69
|
+
isContextDestroyed,
|
|
70
|
+
isStaleElementError
|
|
71
|
+
} from '../index.js';
|
|
72
|
+
|
|
73
|
+
describe('Integration: Index Exports', () => {
|
|
74
|
+
describe('Core connection and browser management', () => {
|
|
75
|
+
it('should export createConnection', () => {
|
|
76
|
+
assert.ok(createConnection, 'createConnection should be exported');
|
|
77
|
+
assert.strictEqual(typeof createConnection, 'function');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should export createDiscovery', () => {
|
|
81
|
+
assert.ok(createDiscovery, 'createDiscovery should be exported');
|
|
82
|
+
assert.strictEqual(typeof createDiscovery, 'function');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should export discoverChrome', () => {
|
|
86
|
+
assert.ok(discoverChrome, 'discoverChrome should be exported');
|
|
87
|
+
assert.strictEqual(typeof discoverChrome, 'function');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should export createTargetManager', () => {
|
|
91
|
+
assert.ok(createTargetManager, 'createTargetManager should be exported');
|
|
92
|
+
assert.strictEqual(typeof createTargetManager, 'function');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should export createSessionRegistry', () => {
|
|
96
|
+
assert.ok(createSessionRegistry, 'createSessionRegistry should be exported');
|
|
97
|
+
assert.strictEqual(typeof createSessionRegistry, 'function');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should export createBrowser', () => {
|
|
101
|
+
assert.ok(createBrowser, 'createBrowser should be exported');
|
|
102
|
+
assert.strictEqual(typeof createBrowser, 'function');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should export createPageSession', () => {
|
|
106
|
+
assert.ok(createPageSession, 'createPageSession should be exported');
|
|
107
|
+
assert.strictEqual(typeof createPageSession, 'function');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('Page operations', () => {
|
|
112
|
+
it('should export createPageController', () => {
|
|
113
|
+
assert.ok(createPageController, 'createPageController should be exported');
|
|
114
|
+
assert.strictEqual(typeof createPageController, 'function');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should export WaitCondition', () => {
|
|
118
|
+
assert.ok(WaitCondition, 'WaitCondition should be exported');
|
|
119
|
+
assert.strictEqual(typeof WaitCondition, 'object');
|
|
120
|
+
assert.strictEqual(WaitCondition.LOAD, 'load');
|
|
121
|
+
assert.strictEqual(WaitCondition.DOM_CONTENT_LOADED, 'domcontentloaded');
|
|
122
|
+
assert.strictEqual(WaitCondition.NETWORK_IDLE, 'networkidle');
|
|
123
|
+
assert.strictEqual(WaitCondition.COMMIT, 'commit');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should export wait functions', () => {
|
|
127
|
+
assert.ok(waitForCondition, 'waitForCondition should be exported');
|
|
128
|
+
assert.ok(waitForFunction, 'waitForFunction should be exported');
|
|
129
|
+
assert.ok(waitForNetworkIdle, 'waitForNetworkIdle should be exported');
|
|
130
|
+
assert.ok(waitForDocumentReady, 'waitForDocumentReady should be exported');
|
|
131
|
+
assert.ok(waitForSelector, 'waitForSelector should be exported');
|
|
132
|
+
assert.ok(waitForText, 'waitForText should be exported');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('Element location and interaction', () => {
|
|
137
|
+
it('should export createElementHandle', () => {
|
|
138
|
+
assert.ok(createElementHandle, 'createElementHandle should be exported');
|
|
139
|
+
assert.strictEqual(typeof createElementHandle, 'function');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should export createElementLocator', () => {
|
|
143
|
+
assert.ok(createElementLocator, 'createElementLocator should be exported');
|
|
144
|
+
assert.strictEqual(typeof createElementLocator, 'function');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should export createInputEmulator', () => {
|
|
148
|
+
assert.ok(createInputEmulator, 'createInputEmulator should be exported');
|
|
149
|
+
assert.strictEqual(typeof createInputEmulator, 'function');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should export DOM convenience functions', () => {
|
|
153
|
+
assert.ok(querySelector, 'querySelector should be exported');
|
|
154
|
+
assert.ok(querySelectorAll, 'querySelectorAll should be exported');
|
|
155
|
+
assert.ok(findElement, 'findElement should be exported');
|
|
156
|
+
assert.ok(getBoundingBox, 'getBoundingBox should be exported');
|
|
157
|
+
assert.ok(isVisible, 'isVisible should be exported');
|
|
158
|
+
assert.ok(isActionable, 'isActionable should be exported');
|
|
159
|
+
assert.ok(scrollIntoView, 'scrollIntoView should be exported');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should export input convenience functions', () => {
|
|
163
|
+
assert.ok(click, 'click should be exported');
|
|
164
|
+
assert.ok(type, 'type should be exported');
|
|
165
|
+
assert.ok(fill, 'fill should be exported');
|
|
166
|
+
assert.ok(press, 'press should be exported');
|
|
167
|
+
assert.ok(scroll, 'scroll should be exported');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('Capture and monitoring', () => {
|
|
172
|
+
it('should export createScreenshotCapture', () => {
|
|
173
|
+
assert.ok(createScreenshotCapture, 'createScreenshotCapture should be exported');
|
|
174
|
+
assert.strictEqual(typeof createScreenshotCapture, 'function');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should export screenshot convenience functions', () => {
|
|
178
|
+
assert.ok(captureViewport, 'captureViewport should be exported');
|
|
179
|
+
assert.ok(captureFullPage, 'captureFullPage should be exported');
|
|
180
|
+
assert.ok(captureRegion, 'captureRegion should be exported');
|
|
181
|
+
assert.ok(saveScreenshot, 'saveScreenshot should be exported');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should export createConsoleCapture', () => {
|
|
185
|
+
assert.ok(createConsoleCapture, 'createConsoleCapture should be exported');
|
|
186
|
+
assert.strictEqual(typeof createConsoleCapture, 'function');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should export createNetworkCapture', () => {
|
|
190
|
+
assert.ok(createNetworkCapture, 'createNetworkCapture should be exported');
|
|
191
|
+
assert.strictEqual(typeof createNetworkCapture, 'function');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should export createErrorAggregator', () => {
|
|
195
|
+
assert.ok(createErrorAggregator, 'createErrorAggregator should be exported');
|
|
196
|
+
assert.strictEqual(typeof createErrorAggregator, 'function');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should export aggregateErrors', () => {
|
|
200
|
+
assert.ok(aggregateErrors, 'aggregateErrors should be exported');
|
|
201
|
+
assert.strictEqual(typeof aggregateErrors, 'function');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('Test execution', () => {
|
|
206
|
+
it('should export validateSteps', () => {
|
|
207
|
+
assert.ok(validateSteps, 'validateSteps should be exported');
|
|
208
|
+
assert.strictEqual(typeof validateSteps, 'function');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should export executeStep', () => {
|
|
212
|
+
assert.ok(executeStep, 'executeStep should be exported');
|
|
213
|
+
assert.strictEqual(typeof executeStep, 'function');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should export runSteps', () => {
|
|
217
|
+
assert.ok(runSteps, 'runSteps should be exported');
|
|
218
|
+
assert.strictEqual(typeof runSteps, 'function');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should export createTestRunner', () => {
|
|
222
|
+
assert.ok(createTestRunner, 'createTestRunner should be exported');
|
|
223
|
+
assert.strictEqual(typeof createTestRunner, 'function');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('Error utilities', () => {
|
|
228
|
+
it('should export ErrorTypes', () => {
|
|
229
|
+
assert.ok(ErrorTypes, 'ErrorTypes should be exported');
|
|
230
|
+
assert.strictEqual(typeof ErrorTypes, 'object');
|
|
231
|
+
assert.ok(ErrorTypes.CONNECTION);
|
|
232
|
+
assert.ok(ErrorTypes.NAVIGATION);
|
|
233
|
+
assert.ok(ErrorTypes.TIMEOUT);
|
|
234
|
+
assert.ok(ErrorTypes.ELEMENT_NOT_FOUND);
|
|
235
|
+
assert.ok(ErrorTypes.STALE_ELEMENT);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should export error factory functions', () => {
|
|
239
|
+
assert.ok(createError, 'createError should be exported');
|
|
240
|
+
assert.ok(navigationError, 'navigationError should be exported');
|
|
241
|
+
assert.ok(timeoutError, 'timeoutError should be exported');
|
|
242
|
+
assert.ok(elementNotFoundError, 'elementNotFoundError should be exported');
|
|
243
|
+
assert.ok(staleElementError, 'staleElementError should be exported');
|
|
244
|
+
assert.ok(pageCrashedError, 'pageCrashedError should be exported');
|
|
245
|
+
assert.ok(contextDestroyedError, 'contextDestroyedError should be exported');
|
|
246
|
+
assert.ok(stepValidationError, 'stepValidationError should be exported');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should export error check functions', () => {
|
|
250
|
+
assert.ok(isErrorType, 'isErrorType should be exported');
|
|
251
|
+
assert.ok(isContextDestroyed, 'isContextDestroyed should be exported');
|
|
252
|
+
assert.ok(isStaleElementError, 'isStaleElementError should be exported');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should create errors with correct names', () => {
|
|
256
|
+
const navError = navigationError('test error', 'http://example.com');
|
|
257
|
+
assert.ok(navError instanceof Error);
|
|
258
|
+
assert.strictEqual(navError.name, ErrorTypes.NAVIGATION);
|
|
259
|
+
|
|
260
|
+
const toError = timeoutError('test timeout');
|
|
261
|
+
assert.ok(toError instanceof Error);
|
|
262
|
+
assert.strictEqual(toError.name, ErrorTypes.TIMEOUT);
|
|
263
|
+
|
|
264
|
+
const elemError = elementNotFoundError('#selector', 5000);
|
|
265
|
+
assert.ok(elemError instanceof Error);
|
|
266
|
+
assert.strictEqual(elemError.name, ErrorTypes.ELEMENT_NOT_FOUND);
|
|
267
|
+
assert.strictEqual(elemError.selector, '#selector');
|
|
268
|
+
assert.strictEqual(elemError.timeout, 5000);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('Integration: Component Instantiation', () => {
|
|
274
|
+
describe('createBrowser', () => {
|
|
275
|
+
it('should instantiate with default options', () => {
|
|
276
|
+
const client = createBrowser();
|
|
277
|
+
assert.ok(client);
|
|
278
|
+
assert.strictEqual(client.isConnected(), false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should instantiate with custom host/port', () => {
|
|
282
|
+
const client = createBrowser({ host: '127.0.0.1', port: 9223 });
|
|
283
|
+
assert.ok(client);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('createDiscovery', () => {
|
|
288
|
+
it('should instantiate with host and port', () => {
|
|
289
|
+
const discovery = createDiscovery('localhost', 9222);
|
|
290
|
+
assert.ok(discovery);
|
|
291
|
+
assert.ok(typeof discovery.getVersion === 'function');
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('Integration: TestRunner with Mocks', () => {
|
|
297
|
+
it('should work with mock dependencies', async () => {
|
|
298
|
+
const mockPageController = {
|
|
299
|
+
navigate: async () => {}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// Create a full mock handle with stability/scroll methods
|
|
303
|
+
const createMockHandle = (box = { x: 100, y: 200, width: 50, height: 30 }) => ({
|
|
304
|
+
objectId: 'mock-object-id-123',
|
|
305
|
+
scrollIntoView: async () => {},
|
|
306
|
+
waitForStability: async () => box,
|
|
307
|
+
isActionable: async () => ({ actionable: true, reason: null }),
|
|
308
|
+
getBoundingBox: async () => box,
|
|
309
|
+
dispose: async () => {},
|
|
310
|
+
focus: async () => {}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const mockHandle = createMockHandle();
|
|
314
|
+
// Mock session.send to return appropriate values for different CDP calls
|
|
315
|
+
const mockSessionSend = async (method, params) => {
|
|
316
|
+
// Handle Runtime.evaluate for getCurrentUrl (window.location.href)
|
|
317
|
+
if (method === 'Runtime.evaluate' && params?.expression?.includes('window.location.href')) {
|
|
318
|
+
return { result: { value: 'http://example.com' } };
|
|
319
|
+
}
|
|
320
|
+
// Handle Runtime.evaluate for ActionabilityChecker.findElementInternal
|
|
321
|
+
if (method === 'Runtime.evaluate' && params?.expression?.includes('document.querySelector')) {
|
|
322
|
+
return { result: { objectId: 'mock-object-id-123' } };
|
|
323
|
+
}
|
|
324
|
+
// Handle Runtime.evaluate for viewport bounds (ClickExecutor._getViewportBounds)
|
|
325
|
+
if (method === 'Runtime.evaluate' && params?.expression?.includes('innerWidth')) {
|
|
326
|
+
return { result: { value: { width: 1920, height: 1080 } } };
|
|
327
|
+
}
|
|
328
|
+
// Handle Runtime.callFunctionOn for ActionabilityChecker - visible check
|
|
329
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('visibility')) {
|
|
330
|
+
return { result: { value: { matches: true, received: 'visible' } } };
|
|
331
|
+
}
|
|
332
|
+
// Handle Runtime.callFunctionOn for ActionabilityChecker - enabled check
|
|
333
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('aria-disabled')) {
|
|
334
|
+
return { result: { value: { matches: true, received: 'enabled' } } };
|
|
335
|
+
}
|
|
336
|
+
// Handle Runtime.callFunctionOn for ActionabilityChecker - stable check
|
|
337
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('requestAnimationFrame')) {
|
|
338
|
+
return { result: { value: { matches: true, received: 'stable' } } };
|
|
339
|
+
}
|
|
340
|
+
// Handle Runtime.callFunctionOn for ActionabilityChecker - editable check
|
|
341
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('isContentEditable')) {
|
|
342
|
+
return { result: { value: { matches: true, received: 'editable' } } };
|
|
343
|
+
}
|
|
344
|
+
// Handle Runtime.callFunctionOn for getClickablePoint (getBoundingClientRect)
|
|
345
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('getBoundingClientRect')) {
|
|
346
|
+
return { result: { value: { x: 125, y: 215, rect: { x: 100, y: 200, width: 50, height: 30 } } } };
|
|
347
|
+
}
|
|
348
|
+
// Handle Runtime.callFunctionOn for JS click execution
|
|
349
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('.click()')) {
|
|
350
|
+
return { result: { value: { success: true, targetReceived: true } } };
|
|
351
|
+
}
|
|
352
|
+
// Handle Runtime.callFunctionOn for focus calls
|
|
353
|
+
if (method === 'Runtime.callFunctionOn' && params?.functionDeclaration?.includes('focus')) {
|
|
354
|
+
return { result: { value: true } };
|
|
355
|
+
}
|
|
356
|
+
// Handle Runtime.releaseObject (cleanup)
|
|
357
|
+
if (method === 'Runtime.releaseObject') {
|
|
358
|
+
return {};
|
|
359
|
+
}
|
|
360
|
+
// Default response for other calls
|
|
361
|
+
return { result: { value: true } };
|
|
362
|
+
};
|
|
363
|
+
const mockElementLocator = {
|
|
364
|
+
waitForSelector: async () => ({ dispose: async () => {} }),
|
|
365
|
+
waitForText: async () => true,
|
|
366
|
+
findElement: async () => ({ nodeId: '123', _handle: mockHandle }),
|
|
367
|
+
getBoundingBox: async () => ({ x: 0, y: 0, width: 100, height: 50 }),
|
|
368
|
+
session: { send: mockSessionSend }
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const mockInputEmulator = {
|
|
372
|
+
click: async () => {},
|
|
373
|
+
type: async () => {},
|
|
374
|
+
press: async () => {},
|
|
375
|
+
selectAll: async () => {}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const mockScreenshotCapture = {
|
|
379
|
+
captureToFile: async (path) => path,
|
|
380
|
+
getViewportDimensions: async () => ({ width: 1920, height: 1080 })
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const runner = createTestRunner({
|
|
384
|
+
pageController: mockPageController,
|
|
385
|
+
elementLocator: mockElementLocator,
|
|
386
|
+
inputEmulator: mockInputEmulator,
|
|
387
|
+
screenshotCapture: mockScreenshotCapture
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const result = await runner.run([
|
|
391
|
+
{ goto: 'http://example.com' },
|
|
392
|
+
{ wait: '#main' },
|
|
393
|
+
{ click: '#button' },
|
|
394
|
+
{ fill: { selector: '#input', value: 'test' } },
|
|
395
|
+
{ press: 'Enter' },
|
|
396
|
+
{ screenshot: '/tmp/test.png' }
|
|
397
|
+
]);
|
|
398
|
+
|
|
399
|
+
assert.strictEqual(result.status, 'passed');
|
|
400
|
+
assert.strictEqual(result.steps.length, 6);
|
|
401
|
+
assert.strictEqual(result.errors.length, 0);
|
|
402
|
+
assert.strictEqual(result.screenshots.length, 1);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe('Integration: Error Aggregator with Mocks', () => {
|
|
407
|
+
it('should aggregate errors from console and network captures', () => {
|
|
408
|
+
const mockConsoleCapture = {
|
|
409
|
+
getErrors: () => [{ level: 'error', text: 'Test error', type: 'console' }],
|
|
410
|
+
getWarnings: () => [{ level: 'warning', text: 'Test warning' }]
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const mockNetworkCapture = {
|
|
414
|
+
getNetworkFailures: () => [{ type: 'network-failure', url: 'http://test.com', errorText: 'Failed' }],
|
|
415
|
+
getHttpErrors: () => [{ type: 'http-error', status: 500, url: 'http://api.test.com' }],
|
|
416
|
+
getAllErrors: () => [
|
|
417
|
+
{ type: 'network-failure', url: 'http://test.com', errorText: 'Failed', timestamp: 1 },
|
|
418
|
+
{ type: 'http-error', status: 500, url: 'http://api.test.com', timestamp: 2 }
|
|
419
|
+
]
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const aggregator = createErrorAggregator(mockConsoleCapture, mockNetworkCapture);
|
|
423
|
+
const summary = aggregator.getSummary();
|
|
424
|
+
|
|
425
|
+
assert.ok(summary.hasErrors);
|
|
426
|
+
assert.strictEqual(summary.counts.consoleErrors, 1);
|
|
427
|
+
assert.strictEqual(summary.counts.consoleWarnings, 1);
|
|
428
|
+
assert.strictEqual(summary.counts.networkFailures, 1);
|
|
429
|
+
assert.strictEqual(summary.counts.httpServerErrors, 1);
|
|
430
|
+
});
|
|
431
|
+
});
|