@testivai/witness-cdp 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/LICENSE +21 -0
- package/README.md +405 -0
- package/dist/__tests__/setup.d.ts +4 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +24 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/cdp/binding.d.ts +44 -0
- package/dist/cdp/binding.d.ts.map +1 -0
- package/dist/cdp/binding.js +183 -0
- package/dist/cdp/binding.js.map +1 -0
- package/dist/cdp/capture.d.ts +42 -0
- package/dist/cdp/capture.d.ts.map +1 -0
- package/dist/cdp/capture.js +235 -0
- package/dist/cdp/capture.js.map +1 -0
- package/dist/cdp/client.d.ts +63 -0
- package/dist/cdp/client.d.ts.map +1 -0
- package/dist/cdp/client.js +279 -0
- package/dist/cdp/client.js.map +1 -0
- package/dist/cdp/discovery.d.ts +33 -0
- package/dist/cdp/discovery.d.ts.map +1 -0
- package/dist/cdp/discovery.js +157 -0
- package/dist/cdp/discovery.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +122 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/capture.d.ts +3 -0
- package/dist/commands/capture.d.ts.map +1 -0
- package/dist/commands/capture.js +143 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +140 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +307 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +244 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file-naming.d.ts +31 -0
- package/dist/utils/file-naming.d.ts.map +1 -0
- package/dist/utils/file-naming.js +137 -0
- package/dist/utils/file-naming.js.map +1 -0
- package/dist/utils/framework-detect.d.ts +31 -0
- package/dist/utils/framework-detect.d.ts.map +1 -0
- package/dist/utils/framework-detect.js +379 -0
- package/dist/utils/framework-detect.js.map +1 -0
- package/dist/utils/logger.d.ts +29 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +114 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/process.d.ts +61 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +208 -0
- package/dist/utils/process.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TestivAI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# @testivai/witness-cdp
|
|
2
|
+
|
|
3
|
+
Framework-agnostic visual regression testing SDK using Chrome DevTools Protocol.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The TestivAI CDP SDK allows you to integrate visual regression testing into any test framework that can control Chrome/Chromium browsers. It works by connecting directly to the Chrome DevTools Protocol (CDP) and injecting a `window.testivaiWitness` function that triggers visual captures.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @testivai/witness-cdp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
1. **Initialize your project**
|
|
18
|
+
```bash
|
|
19
|
+
testivai init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
2. **Authenticate with your API key**
|
|
23
|
+
```bash
|
|
24
|
+
testivai auth <your-api-key>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. **Add visual captures to your tests**
|
|
28
|
+
```javascript
|
|
29
|
+
// In your test code
|
|
30
|
+
await window.testivaiWitness('my-snapshot');
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
4. **Run your tests**
|
|
34
|
+
```bash
|
|
35
|
+
# Make sure Chrome is running with remote debugging
|
|
36
|
+
chrome --remote-debugging-port=9222
|
|
37
|
+
|
|
38
|
+
# Run your tests with TestivAI
|
|
39
|
+
testivai run "npm test"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Framework Integration
|
|
43
|
+
|
|
44
|
+
### Cypress
|
|
45
|
+
|
|
46
|
+
Add this custom command to `cypress/support/commands.js`:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// testivai-witness-cdp.js
|
|
50
|
+
Cypress.Commands.add('witness', (name) => {
|
|
51
|
+
return cy.window().invoke('testivaiWitness', name);
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use in your tests:
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
it('should capture visual snapshot', () => {
|
|
59
|
+
cy.visit('/my-page');
|
|
60
|
+
cy.witness('my-snapshot');
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run tests:
|
|
65
|
+
```bash
|
|
66
|
+
testivai run "cypress run"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Selenium (Python)
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from selenium import webdriver
|
|
73
|
+
|
|
74
|
+
def capture_snapshot(driver, name):
|
|
75
|
+
driver.execute_script(f"return window.testivaiWitness('{name}')")
|
|
76
|
+
|
|
77
|
+
def test_visual_snapshot():
|
|
78
|
+
driver = webdriver.Chrome()
|
|
79
|
+
driver.get("http://localhost:3000")
|
|
80
|
+
capture_snapshot(driver, "my-snapshot")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run tests:
|
|
84
|
+
```bash
|
|
85
|
+
testivai run "pytest tests/"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Selenium (JavaScript)
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
const { Builder, By } = require('selenium-webdriver');
|
|
92
|
+
|
|
93
|
+
async function captureSnapshot(driver, name) {
|
|
94
|
+
await driver.executeScript(`return window.testivaiWitness('${name}')`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
it('should capture visual snapshot', async () => {
|
|
98
|
+
const driver = await new Builder().forBrowser('chrome').build();
|
|
99
|
+
await driver.get('http://localhost:3000');
|
|
100
|
+
await captureSnapshot(driver, 'my-snapshot');
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Run tests:
|
|
105
|
+
```bash
|
|
106
|
+
testivai run "npm test"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### WebdriverIO
|
|
110
|
+
|
|
111
|
+
Add this custom command to your test setup:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
// In wdio.conf.js or test setup
|
|
115
|
+
browser.addCommand('witness', function(name) {
|
|
116
|
+
return this.executeScript('return window.testivaiWitness(arguments[0])', name);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use in your tests:
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
it('should capture visual snapshot', async () => {
|
|
124
|
+
await browser.url('/my-page');
|
|
125
|
+
await browser.witness('my-snapshot');
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Run tests:
|
|
130
|
+
```bash
|
|
131
|
+
testivai run "npx wdio"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## How It Works
|
|
135
|
+
|
|
136
|
+
1. **CDP Connection**: The SDK connects to Chrome's DevTools Protocol (usually on port 9222)
|
|
137
|
+
2. **Binding Injection**: Using `Runtime.addBinding`, it injects a native function `window.testivaiWitness`
|
|
138
|
+
3. **Promise Wrapper**: A client-side script wraps the native binding in a Promise for async/await support
|
|
139
|
+
4. **Capture Trigger**: When `window.testivaiWitness('name')` is called, it triggers:
|
|
140
|
+
- Screenshot capture
|
|
141
|
+
- DOM extraction
|
|
142
|
+
- Layout analysis
|
|
143
|
+
- Performance metrics
|
|
144
|
+
5. **Batch Upload**: All captures are batched and uploaded to TestivAI for analysis
|
|
145
|
+
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
Create a `testivai.config.ts` file in your project root:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import type { CdpConfig } from '@testivai/witness-cdp';
|
|
152
|
+
|
|
153
|
+
const config: CdpConfig = {
|
|
154
|
+
// API key (set via TESTIVAI_API_KEY environment variable)
|
|
155
|
+
// apiKey: 'your-api-key-here',
|
|
156
|
+
|
|
157
|
+
// Project ID from TestivAI dashboard
|
|
158
|
+
// projectId: 'your-project-id-here',
|
|
159
|
+
|
|
160
|
+
// Chrome DevTools Protocol port
|
|
161
|
+
cdpPort: 9222,
|
|
162
|
+
|
|
163
|
+
// Auto-launch Chrome if not running (experimental)
|
|
164
|
+
autoLaunch: false,
|
|
165
|
+
|
|
166
|
+
// Chrome executable path (for auto-launch)
|
|
167
|
+
// chromePath: '/path/to/chrome',
|
|
168
|
+
|
|
169
|
+
// Additional Chrome arguments
|
|
170
|
+
chromeArgs: [
|
|
171
|
+
'--no-sandbox',
|
|
172
|
+
'--disable-dev-shm-usage',
|
|
173
|
+
'--disable-gpu',
|
|
174
|
+
],
|
|
175
|
+
|
|
176
|
+
// Connection settings
|
|
177
|
+
connectionTimeout: 5000,
|
|
178
|
+
connectionRetries: 3,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export default config;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## CLI Commands
|
|
185
|
+
|
|
186
|
+
### `testivai init`
|
|
187
|
+
Initialize TestivAI in your project. Detects your framework and provides setup instructions.
|
|
188
|
+
|
|
189
|
+
### `testivai auth <api-key>`
|
|
190
|
+
Authenticate with your TestivAI API key. Get your key from the [dashboard](https://dashboard.testiv.ai).
|
|
191
|
+
|
|
192
|
+
### `testivai run <command>`
|
|
193
|
+
Run your test command with automatic visual capture.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
testivai run "npm test"
|
|
197
|
+
testivai run "cypress run"
|
|
198
|
+
testivai run "pytest tests/"
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Options:
|
|
202
|
+
- `-p, --port <number>` - Specify CDP port (default: 9222)
|
|
203
|
+
- `-b, --batch-id <id>` - Specify batch ID (auto-generated if not provided)
|
|
204
|
+
|
|
205
|
+
### `testivai capture <name>`
|
|
206
|
+
Capture a single snapshot without running tests.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
testivai capture "my-snapshot" --format json
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Options:
|
|
213
|
+
- `-p, --port <number>` - Specify CDP port
|
|
214
|
+
- `-o, --output <path>` - Output directory (default: .testivai/captures)
|
|
215
|
+
- `-f, --format <format>` - Output format: json|png (default: json)
|
|
216
|
+
|
|
217
|
+
## Chrome Setup
|
|
218
|
+
|
|
219
|
+
### Manual Launch
|
|
220
|
+
|
|
221
|
+
Launch Chrome with remote debugging enabled:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# macOS
|
|
225
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222
|
|
226
|
+
|
|
227
|
+
# Windows
|
|
228
|
+
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
|
|
229
|
+
|
|
230
|
+
# Linux
|
|
231
|
+
google-chrome --remote-debugging-port=9222
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Common Chrome Arguments
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
chrome \
|
|
238
|
+
--remote-debugging-port=9222 \
|
|
239
|
+
--no-sandbox \
|
|
240
|
+
--disable-dev-shm-usage \
|
|
241
|
+
--disable-gpu \
|
|
242
|
+
--headless # For CI environments
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## CI/CD Integration
|
|
246
|
+
|
|
247
|
+
### GitHub Actions
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
name: Visual Tests
|
|
251
|
+
on: [push, pull_request]
|
|
252
|
+
|
|
253
|
+
jobs:
|
|
254
|
+
visual:
|
|
255
|
+
runs-on: ubuntu-latest
|
|
256
|
+
steps:
|
|
257
|
+
- uses: actions/checkout@v3
|
|
258
|
+
|
|
259
|
+
- name: Setup Node.js
|
|
260
|
+
uses: actions/setup-node@v3
|
|
261
|
+
with:
|
|
262
|
+
node-version: '18'
|
|
263
|
+
|
|
264
|
+
- name: Install dependencies
|
|
265
|
+
run: npm ci
|
|
266
|
+
|
|
267
|
+
- name: Install TestivAI CDP SDK
|
|
268
|
+
run: npm install -g @testivai/witness-cdp
|
|
269
|
+
|
|
270
|
+
- name: Start Chrome
|
|
271
|
+
run: |
|
|
272
|
+
google-chrome \
|
|
273
|
+
--remote-debugging-port=9222 \
|
|
274
|
+
--no-sandbox \
|
|
275
|
+
--disable-dev-shm-usage \
|
|
276
|
+
--headless \
|
|
277
|
+
--disable-gpu &
|
|
278
|
+
|
|
279
|
+
- name: Authenticate
|
|
280
|
+
run: testivai auth ${{ secrets.TESTIVAI_API_KEY }}
|
|
281
|
+
|
|
282
|
+
- name: Run visual tests
|
|
283
|
+
run: testivai run "npm test"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Jenkins
|
|
287
|
+
|
|
288
|
+
```groovy
|
|
289
|
+
pipeline {
|
|
290
|
+
agent any
|
|
291
|
+
|
|
292
|
+
stages {
|
|
293
|
+
stage('Setup') {
|
|
294
|
+
steps {
|
|
295
|
+
sh 'npm ci'
|
|
296
|
+
sh 'npm install -g @testivai/witness-cdp'
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
stage('Start Chrome') {
|
|
301
|
+
steps {
|
|
302
|
+
sh '''
|
|
303
|
+
google-chrome \
|
|
304
|
+
--remote-debugging-port=9222 \
|
|
305
|
+
--no-sandbox \
|
|
306
|
+
--disable-dev-shm-usage \
|
|
307
|
+
--headless &
|
|
308
|
+
'''
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
stage('Visual Tests') {
|
|
313
|
+
steps {
|
|
314
|
+
withCredentials([string(credentialsId: 'testivai-api-key', variable: 'API_KEY')]) {
|
|
315
|
+
sh 'testivai auth $API_KEY'
|
|
316
|
+
sh 'testivai run "npm test"'
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Troubleshooting
|
|
325
|
+
|
|
326
|
+
### Chrome not found
|
|
327
|
+
```
|
|
328
|
+
❌ Chrome DevTools Protocol not found
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Solution**: Make sure Chrome is running with remote debugging:
|
|
332
|
+
```bash
|
|
333
|
+
chrome --remote-debugging-port=9222
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Connection timeout
|
|
337
|
+
```
|
|
338
|
+
❌ Failed to connect to CDP: Connection timeout
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Solution**:
|
|
342
|
+
1. Check if Chrome is running
|
|
343
|
+
2. Verify the port number (default: 9222)
|
|
344
|
+
3. Check for firewall issues
|
|
345
|
+
|
|
346
|
+
### Tests hang after calling testivaiWitness
|
|
347
|
+
**Solution**: The Promise wrapper might not be working. Check browser console for errors and ensure CDP is properly connected.
|
|
348
|
+
|
|
349
|
+
### No snapshots captured
|
|
350
|
+
**Solution**:
|
|
351
|
+
1. Verify `window.testivaiWitness` is available in your tests
|
|
352
|
+
2. Check that the SDK is connected before running tests
|
|
353
|
+
3. Enable verbose logging: `testivai run "npm test" --verbose`
|
|
354
|
+
|
|
355
|
+
## Performance
|
|
356
|
+
|
|
357
|
+
- **Package size**: ~270KB (no browser binaries included)
|
|
358
|
+
- **Memory usage**: ~50MB additional overhead
|
|
359
|
+
- **Capture time**: ~100-500ms per snapshot
|
|
360
|
+
- **Upload time**: Depends on network and snapshot size
|
|
361
|
+
|
|
362
|
+
## API Reference
|
|
363
|
+
|
|
364
|
+
### CdpClient
|
|
365
|
+
Main class for connecting to Chrome DevTools Protocol.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { CdpClient } from '@testivai/witness-cdp';
|
|
369
|
+
|
|
370
|
+
const client = new CdpClient();
|
|
371
|
+
await client.connect(9222);
|
|
372
|
+
await client.send('Page.navigate', { url: 'https://example.com' });
|
|
373
|
+
await client.disconnect();
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### CdpCapture
|
|
377
|
+
Handles screenshot and data capture.
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
import { CdpCapture } from '@testivai/witness-cdp';
|
|
381
|
+
|
|
382
|
+
const capture = new CdpCapture(client);
|
|
383
|
+
const snapshot = await capture.captureSnapshot('my-snapshot');
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### CdpBinding
|
|
387
|
+
Manages the `window.testivaiWitness` binding.
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { CdpBinding } from '@testivai/witness-cdp';
|
|
391
|
+
|
|
392
|
+
const binding = new CdpBinding(client);
|
|
393
|
+
await binding.setupBindings();
|
|
394
|
+
const snapshots = binding.getSnapshots();
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT
|
|
400
|
+
|
|
401
|
+
## Support
|
|
402
|
+
|
|
403
|
+
- Documentation: https://docs.testiv.ai/cdp
|
|
404
|
+
- Issues: https://github.com/testivai/testivai-monorepo/issues
|
|
405
|
+
- Email: support@testiv.ai
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Jest test setup
|
|
4
|
+
*/
|
|
5
|
+
// Mock console methods to reduce noise in tests
|
|
6
|
+
global.console = {
|
|
7
|
+
...console,
|
|
8
|
+
// Uncomment to suppress console.log during tests
|
|
9
|
+
// log: jest.fn(),
|
|
10
|
+
// debug: jest.fn(),
|
|
11
|
+
// info: jest.fn(),
|
|
12
|
+
// warn: jest.fn(),
|
|
13
|
+
// error: jest.fn(),
|
|
14
|
+
};
|
|
15
|
+
// Set test timeout
|
|
16
|
+
jest.setTimeout(10000);
|
|
17
|
+
// Mock environment variables
|
|
18
|
+
process.env.NODE_ENV = 'test';
|
|
19
|
+
process.env.TESTIVAI_API_URL = 'http://localhost:3000';
|
|
20
|
+
// Global test utilities
|
|
21
|
+
global.mockFetch = jest.fn();
|
|
22
|
+
// Mock fetch globally
|
|
23
|
+
global.fetch = global.mockFetch;
|
|
24
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":";AAAA;;GAEG;AAEH,gDAAgD;AAChD,MAAM,CAAC,OAAO,GAAG;IACf,GAAG,OAAO;IACV,iDAAiD;IACjD,kBAAkB;IAClB,oBAAoB;IACpB,mBAAmB;IACnB,mBAAmB;IACnB,oBAAoB;CACrB,CAAC;AAEF,mBAAmB;AACnB,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,6BAA6B;AAC7B,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;AAC9B,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,uBAAuB,CAAC;AAEvD,wBAAwB;AACvB,MAAc,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;AAEtC,sBAAsB;AACrB,MAAc,CAAC,KAAK,GAAI,MAAc,CAAC,SAAS,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CdpClient } from './client';
|
|
2
|
+
/**
|
|
3
|
+
* CDP Binding management
|
|
4
|
+
*/
|
|
5
|
+
export declare class CdpBinding {
|
|
6
|
+
private client;
|
|
7
|
+
private capture;
|
|
8
|
+
private snapshots;
|
|
9
|
+
private isBindingRegistered;
|
|
10
|
+
constructor(client: CdpClient);
|
|
11
|
+
/**
|
|
12
|
+
* Set up the Runtime bindings
|
|
13
|
+
*/
|
|
14
|
+
setupBindings(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Inject the client-side script into all pages
|
|
17
|
+
*/
|
|
18
|
+
private injectClientScript;
|
|
19
|
+
/**
|
|
20
|
+
* Set up event listeners for binding events
|
|
21
|
+
*/
|
|
22
|
+
private setupEventListeners;
|
|
23
|
+
/**
|
|
24
|
+
* Handle a witness binding call
|
|
25
|
+
*/
|
|
26
|
+
private handleWitnessCall;
|
|
27
|
+
/**
|
|
28
|
+
* Send ACK to resolve the Promise
|
|
29
|
+
*/
|
|
30
|
+
private sendAck;
|
|
31
|
+
/**
|
|
32
|
+
* Get all captured snapshots
|
|
33
|
+
*/
|
|
34
|
+
getSnapshots(): any[];
|
|
35
|
+
/**
|
|
36
|
+
* Clear all captured snapshots
|
|
37
|
+
*/
|
|
38
|
+
clearSnapshots(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Check if bindings are registered
|
|
41
|
+
*/
|
|
42
|
+
isRegistered(): boolean;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../src/cdp/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AA8CrC;;GAEG;AACH,qBAAa,UAAU;IAKT,OAAO,CAAC,MAAM;IAJ1B,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,mBAAmB,CAAS;gBAEhB,MAAM,EAAE,SAAS;IAKrC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BpC;;OAEG;YACW,kBAAkB;IAwBhC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;OAEG;YACW,iBAAiB;IAuB/B;;OAEG;YACW,OAAO;IAWrB;;OAEG;IACH,YAAY,IAAI,GAAG,EAAE;IAIrB;;OAEG;IACH,cAAc,IAAI,IAAI;IAKtB;;OAEG;IACH,YAAY,IAAI,OAAO;CAGxB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CdpBinding = void 0;
|
|
4
|
+
const capture_1 = require("./capture");
|
|
5
|
+
const logger_1 = require("../utils/logger");
|
|
6
|
+
/**
|
|
7
|
+
* Client-side script injected into the browser
|
|
8
|
+
* This wraps the native binding in a Promise
|
|
9
|
+
*/
|
|
10
|
+
const CLIENT_SCRIPT = `
|
|
11
|
+
(function() {
|
|
12
|
+
// Store the native binding
|
|
13
|
+
const _nativeWitness = window.testivaiWitness;
|
|
14
|
+
|
|
15
|
+
// Create the Promise wrapper
|
|
16
|
+
window.testivaiWitness = function(name) {
|
|
17
|
+
return new Promise(function(resolve) {
|
|
18
|
+
// Store resolve function for ACK
|
|
19
|
+
window.__testivaiResolve = resolve;
|
|
20
|
+
|
|
21
|
+
// Call the native binding
|
|
22
|
+
if (_nativeWitness) {
|
|
23
|
+
_nativeWitness(name);
|
|
24
|
+
} else {
|
|
25
|
+
console.error('TestivAI: Native binding not found');
|
|
26
|
+
resolve();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Log that the binding is ready
|
|
32
|
+
console.log('TestivAI: Binding ready');
|
|
33
|
+
})();
|
|
34
|
+
`;
|
|
35
|
+
/**
|
|
36
|
+
* ACK script to resolve the Promise
|
|
37
|
+
*/
|
|
38
|
+
const ACK_SCRIPT = `
|
|
39
|
+
(function() {
|
|
40
|
+
if (window.__testivaiResolve) {
|
|
41
|
+
window.__testivaiResolve();
|
|
42
|
+
window.__testivaiResolve = null;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
`;
|
|
46
|
+
/**
|
|
47
|
+
* CDP Binding management
|
|
48
|
+
*/
|
|
49
|
+
class CdpBinding {
|
|
50
|
+
constructor(client) {
|
|
51
|
+
this.client = client;
|
|
52
|
+
this.snapshots = [];
|
|
53
|
+
this.isBindingRegistered = false;
|
|
54
|
+
this.capture = new capture_1.CdpCapture(client);
|
|
55
|
+
this.setupEventListeners();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Set up the Runtime bindings
|
|
59
|
+
*/
|
|
60
|
+
async setupBindings() {
|
|
61
|
+
try {
|
|
62
|
+
// Enable Runtime domain
|
|
63
|
+
await this.client.enableDomain('Runtime');
|
|
64
|
+
// Register the main binding for triggering captures
|
|
65
|
+
await this.client.send('Runtime.addBinding', {
|
|
66
|
+
name: 'testivaiWitness',
|
|
67
|
+
});
|
|
68
|
+
logger_1.logger.bindingRegistered('testivaiWitness');
|
|
69
|
+
// Register the ACK binding for resolving Promises
|
|
70
|
+
await this.client.send('Runtime.addBinding', {
|
|
71
|
+
name: 'testivaiWitnessAck',
|
|
72
|
+
});
|
|
73
|
+
logger_1.logger.bindingRegistered('testivaiWitnessAck');
|
|
74
|
+
// Inject the client-side script
|
|
75
|
+
await this.injectClientScript();
|
|
76
|
+
this.isBindingRegistered = true;
|
|
77
|
+
logger_1.logger.success('CDP bindings registered successfully');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
logger_1.logger.error('Failed to setup CDP bindings:', error);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Inject the client-side script into all pages
|
|
86
|
+
*/
|
|
87
|
+
async injectClientScript() {
|
|
88
|
+
try {
|
|
89
|
+
// Enable Page domain
|
|
90
|
+
await this.client.enableDomain('Page');
|
|
91
|
+
// Add script to evaluate on new documents
|
|
92
|
+
await this.client.send('Page.addScriptToEvaluateOnNewDocument', {
|
|
93
|
+
source: CLIENT_SCRIPT,
|
|
94
|
+
});
|
|
95
|
+
// Also evaluate on current document if it exists
|
|
96
|
+
try {
|
|
97
|
+
await this.client.send('Page.reload', {});
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Page might not be loaded yet, which is fine
|
|
101
|
+
}
|
|
102
|
+
logger_1.logger.debug('Client script injected');
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
logger_1.logger.error('Failed to inject client script:', error);
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Set up event listeners for binding events
|
|
111
|
+
*/
|
|
112
|
+
setupEventListeners() {
|
|
113
|
+
// Listen for binding called events
|
|
114
|
+
this.client.on('bindingCalled', async (params) => {
|
|
115
|
+
if (params.name === 'testivaiWitness') {
|
|
116
|
+
await this.handleWitnessCall(params.payload);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Listen for frame navigation to re-inject script
|
|
120
|
+
this.client.on('frameNavigated', async () => {
|
|
121
|
+
if (this.isBindingRegistered) {
|
|
122
|
+
logger_1.logger.debug('Frame navigated, re-injecting client script');
|
|
123
|
+
await this.injectClientScript();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Handle a witness binding call
|
|
129
|
+
*/
|
|
130
|
+
async handleWitnessCall(payload) {
|
|
131
|
+
try {
|
|
132
|
+
const snapshotName = payload || 'snapshot';
|
|
133
|
+
logger_1.logger.bindingCalled('testivaiWitness', snapshotName);
|
|
134
|
+
// Capture the snapshot
|
|
135
|
+
const snapshot = await this.capture.captureSnapshot(snapshotName);
|
|
136
|
+
// Add to snapshots array
|
|
137
|
+
this.snapshots.push(snapshot);
|
|
138
|
+
// Send ACK to resolve the Promise
|
|
139
|
+
await this.sendAck();
|
|
140
|
+
logger_1.logger.debug(`Captured and ACK'd: ${snapshotName}`);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger_1.logger.error(`Failed to handle witness call for ${payload}:`, error);
|
|
144
|
+
// Still send ACK to avoid hanging the test
|
|
145
|
+
await this.sendAck();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Send ACK to resolve the Promise
|
|
150
|
+
*/
|
|
151
|
+
async sendAck() {
|
|
152
|
+
try {
|
|
153
|
+
await this.client.send('Runtime.evaluate', {
|
|
154
|
+
expression: ACK_SCRIPT,
|
|
155
|
+
});
|
|
156
|
+
logger_1.logger.debug('Sent ACK');
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
logger_1.logger.error('Failed to send ACK:', error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get all captured snapshots
|
|
164
|
+
*/
|
|
165
|
+
getSnapshots() {
|
|
166
|
+
return this.snapshots;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Clear all captured snapshots
|
|
170
|
+
*/
|
|
171
|
+
clearSnapshots() {
|
|
172
|
+
this.snapshots = [];
|
|
173
|
+
logger_1.logger.debug('Cleared snapshots');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Check if bindings are registered
|
|
177
|
+
*/
|
|
178
|
+
isRegistered() {
|
|
179
|
+
return this.isBindingRegistered;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
exports.CdpBinding = CdpBinding;
|
|
183
|
+
//# sourceMappingURL=binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binding.js","sourceRoot":"","sources":["../../src/cdp/binding.ts"],"names":[],"mappings":"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"}
|