@testivai/witness-playwright 1.0.0 → 1.1.1

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.
@@ -5,7 +5,7 @@ const reporter_1 = require("../../src/reporter");
5
5
  // This prevents a type-checking conflict within the Jest environment.
6
6
  jest.mock('../../src/reporter-types', () => ({}));
7
7
  describe('TestivAIPlaywrightReporter', () => {
8
- const OLD_ENV = process.env;
8
+ const OLD_ENV = { ...process.env };
9
9
  beforeEach(() => {
10
10
  jest.resetModules();
11
11
  process.env = { ...OLD_ENV };
@@ -22,16 +22,19 @@ describe('TestivAIPlaywrightReporter', () => {
22
22
  expect(reporter.options.apiKey).toBe('env-key');
23
23
  });
24
24
  test('should be disabled if API URL is not provided', async () => {
25
+ delete process.env.TESTIVAI_API_URL;
26
+ delete process.env.TESTIVAI_API_KEY;
25
27
  const reporter = new reporter_1.TestivAIPlaywrightReporter();
26
28
  // onBegin should set the apiUrl to undefined, disabling the reporter.
27
- await reporter.onBegin({}, {});
29
+ await reporter.onBegin({}, { suites: [] });
28
30
  expect(reporter.options.apiUrl).toBeUndefined();
29
31
  });
30
32
  test('should be disabled if API Key is not provided', async () => {
33
+ delete process.env.TESTIVAI_API_KEY;
31
34
  process.env.TESTIVAI_API_URL = 'http://env.api';
32
35
  const reporter = new reporter_1.TestivAIPlaywrightReporter();
33
36
  // onBegin should set the apiUrl to undefined, disabling the reporter.
34
- await reporter.onBegin({}, {});
37
+ await reporter.onBegin({}, { suites: [] });
35
38
  expect(reporter.options.apiUrl).toBeUndefined();
36
39
  });
37
40
  });
@@ -5,7 +5,7 @@ import { TestivAIPlaywrightReporter } from '../../src/reporter';
5
5
  jest.mock('../../src/reporter-types', () => ({}));
6
6
 
7
7
  describe('TestivAIPlaywrightReporter', () => {
8
- const OLD_ENV = process.env;
8
+ const OLD_ENV = { ...process.env };
9
9
 
10
10
  beforeEach(() => {
11
11
  jest.resetModules();
@@ -27,17 +27,46 @@ describe('TestivAIPlaywrightReporter', () => {
27
27
  });
28
28
 
29
29
  test('should be disabled if API URL is not provided', async () => {
30
+ delete process.env.TESTIVAI_API_URL;
31
+ delete process.env.TESTIVAI_API_KEY;
30
32
  const reporter = new TestivAIPlaywrightReporter();
31
33
  // onBegin should set the apiUrl to undefined, disabling the reporter.
32
- await reporter.onBegin({} as any, {} as any);
34
+ await reporter.onBegin({} as any, { suites: [] } as any);
33
35
  expect((reporter as any).options.apiUrl).toBeUndefined();
34
36
  });
35
37
 
36
38
  test('should be disabled if API Key is not provided', async () => {
39
+ delete process.env.TESTIVAI_API_KEY;
37
40
  process.env.TESTIVAI_API_URL = 'http://env.api';
38
41
  const reporter = new TestivAIPlaywrightReporter();
39
42
  // onBegin should set the apiUrl to undefined, disabling the reporter.
40
- await reporter.onBegin({} as any, {} as any);
43
+ await reporter.onBegin({} as any, { suites: [] } as any);
41
44
  expect((reporter as any).options.apiUrl).toBeUndefined();
42
45
  });
46
+
47
+ test('SnapshotPayload uses structure/styles keys (not dom/css)', () => {
48
+ // Verify the reporter module's SnapshotPayload type uses renamed fields
49
+ // by constructing a payload object matching the reporter's expected shape.
50
+ // This ensures the terminology rename is enforced in the reporter's output.
51
+ const payload = {
52
+ structure: { html: '<html></html>' },
53
+ styles: { computed_styles: {} },
54
+ layout: { x: 0, y: 0, width: 1024, height: 768, top: 0, left: 0, right: 1024, bottom: 768 },
55
+ timestamp: Date.now(),
56
+ testName: 'test',
57
+ snapshotName: 'snapshot',
58
+ url: 'http://localhost',
59
+ viewport: { width: 1024, height: 768 },
60
+ };
61
+
62
+ // New field names exist
63
+ expect(payload).toHaveProperty('structure');
64
+ expect(payload).toHaveProperty('styles');
65
+ expect(payload.structure).toHaveProperty('html');
66
+
67
+ // Old field names must NOT exist
68
+ expect(payload).not.toHaveProperty('dom');
69
+ expect(payload).not.toHaveProperty('css');
70
+ expect(payload).not.toHaveProperty('domAnalysis');
71
+ });
43
72
  });
@@ -132,7 +132,7 @@ function mergeTestConfig(projectConfig, testConfig) {
132
132
  ...testConfig.performanceMetrics
133
133
  },
134
134
  selectors: testConfig.selectors,
135
- useCDP: testConfig.useCDP
135
+ useBrowserCapture: testConfig.useBrowserCapture
136
136
  };
137
137
  }
138
138
  /**
package/dist/reporter.js CHANGED
@@ -124,7 +124,7 @@ class TestivAIPlaywrightReporter {
124
124
  if (this.options.debug) {
125
125
  console.log(`Testivai Reporter: [DEBUG] Found ${jsonFiles.length} snapshot(s) to process`);
126
126
  }
127
- // Build snapshots array matching CDP SDK format
127
+ // Build snapshots array matching Witness SDK format
128
128
  const snapshots = [];
129
129
  for (const jsonFile of jsonFiles) {
130
130
  const metadataPath = path.join(this.tempDir, jsonFile);
@@ -185,11 +185,12 @@ class TestivAIPlaywrightReporter {
185
185
  bottom: layoutData.y + layoutData.height
186
186
  },
187
187
  testivaiConfig: metadata.testivaiConfig,
188
- screenshotData: screenshotBase64
188
+ screenshotData: screenshotBase64,
189
+ performanceMetrics: metadata.performanceMetrics
189
190
  };
190
191
  snapshots.push(snapshotPayload);
191
192
  }
192
- // Build batch payload matching CDP SDK format
193
+ // Build batch payload matching Witness SDK format
193
194
  const batchPayload = {
194
195
  git: this.gitInfo,
195
196
  browser: this.browserInfo,
@@ -198,7 +199,7 @@ class TestivAIPlaywrightReporter {
198
199
  runId: this.runId,
199
200
  ci: this.ciInfo,
200
201
  };
201
- // Compress and upload (same as CDP SDK)
202
+ // Compress and upload (same as Witness SDK)
202
203
  const payloadJson = JSON.stringify(batchPayload);
203
204
  const compressionResult = await this.compressionHelper.compress(payloadJson);
204
205
  if (this.options.debug && compressionResult.compressionRatio > 0) {
@@ -209,7 +210,7 @@ class TestivAIPlaywrightReporter {
209
210
  'Content-Type': 'application/json',
210
211
  ...compressionResult.headers,
211
212
  };
212
- // Upload to same endpoint as CDP SDK
213
+ // Upload to same endpoint as Witness SDK
213
214
  const startBatchResponse = await axios_1.default.post(`${this.options.apiUrl}/api/v1/ingest/start-batch`, compressionResult.data, { headers });
214
215
  const batchId = startBatchResponse.data.batch_id || startBatchResponse.data.batchId;
215
216
  // Show success message (brief in normal mode, detailed in debug mode)
package/dist/snapshot.js CHANGED
@@ -92,7 +92,7 @@ async function snapshot(page, testInfo, name, config) {
92
92
  // 1. Capture full-page screenshot
93
93
  const screenshotPath = path.join(outputDir, `${baseFilename}.png`);
94
94
  // Check if scroll-and-stitch is explicitly requested (backup method)
95
- if (effectiveConfig.useCDP === false) {
95
+ if (effectiveConfig.useBrowserCapture === false) {
96
96
  // Use scroll-and-stitch approach (backup method)
97
97
  if (process.env.TESTIVAI_DEBUG === 'true') {
98
98
  console.log('[TestivAI] Using scroll-and-stitch approach (backup method)');
@@ -236,12 +236,12 @@ async function snapshot(page, testInfo, name, config) {
236
236
  }
237
237
  }
238
238
  else {
239
- // Use CDP approach (default)
239
+ // Use browser capture approach (default)
240
240
  if (process.env.TESTIVAI_DEBUG === 'true') {
241
- console.log('[TestivAI] Using CDP approach (default) for full-page screenshot');
241
+ console.log('[TestivAI] Using browser capture approach (default) for full-page screenshot');
242
242
  }
243
243
  try {
244
- // Create a CDP session
244
+ // Create a browser session
245
245
  const client = await page.context().newCDPSession(page);
246
246
  // Enable Page domain
247
247
  await client.send('Page.enable');
@@ -269,7 +269,7 @@ async function snapshot(page, testInfo, name, config) {
269
269
  const pageWidth = Math.ceil(layoutMetrics.contentSize.width);
270
270
  const pageHeight = Math.ceil(layoutMetrics.contentSize.height);
271
271
  if (process.env.TESTIVAI_DEBUG === 'true') {
272
- console.log('[TestivAI] CDP Layout metrics:', {
272
+ console.log('[TestivAI] Browser layout metrics:', {
273
273
  pageWidth,
274
274
  pageHeight,
275
275
  viewportWidth: layoutMetrics.layoutViewport.clientWidth,
@@ -298,11 +298,11 @@ async function snapshot(page, testInfo, name, config) {
298
298
  styleTags[styleTags.length - 1].remove();
299
299
  }
300
300
  `);
301
- // Close CDP session
301
+ // Close browser session
302
302
  await client.detach();
303
303
  }
304
304
  catch (error) {
305
- console.error('[TestivAI] CDP screenshot failed:', error.message);
305
+ console.error('[TestivAI] Browser screenshot failed:', error.message);
306
306
  // Fallback to regular screenshot
307
307
  await page.screenshot({ path: screenshotPath, fullPage: true });
308
308
  }
@@ -312,14 +312,14 @@ async function snapshot(page, testInfo, name, config) {
312
312
  const structurePath = path.join(outputDir, `${baseFilename}.html`);
313
313
  const htmlContent = await page.content();
314
314
  await fs.writeFile(structurePath, htmlContent);
315
- // 2.5. Capture computed styles using CDP
315
+ // 2.5. Capture computed styles using browser session
316
316
  // @renamed: cssPath → stylesPath (IP protection)
317
317
  const stylesPath = path.join(outputDir, `${baseFilename}.css.json`);
318
318
  try {
319
- const cdpSession = await page.context().newCDPSession(page);
319
+ const browserSession = await page.context().newCDPSession(page);
320
320
  // Enable DOM and CSS domains
321
- await cdpSession.send('DOM.enable');
322
- await cdpSession.send('CSS.enable');
321
+ await browserSession.send('DOM.enable');
322
+ await browserSession.send('CSS.enable');
323
323
  // Get all elements and their computed styles
324
324
  const computedStyles = {};
325
325
  // Visual properties we care about
@@ -332,7 +332,7 @@ async function snapshot(page, testInfo, name, config) {
332
332
  'transform', 'opacity', 'visibility', 'z-index'
333
333
  ];
334
334
  // Execute script to get all elements with unique identifiers
335
- const elementsData = await cdpSession.send('Runtime.evaluate', {
335
+ const elementsData = await browserSession.send('Runtime.evaluate', {
336
336
  expression: `
337
337
  (function() {
338
338
  // Helper to get stable CSS selector path for an element
@@ -399,7 +399,7 @@ async function snapshot(page, testInfo, name, config) {
399
399
  for (let i = 0; i < sampleSize; i++) {
400
400
  const element = elements[i];
401
401
  try {
402
- const styleResult = await cdpSession.send('Runtime.evaluate', {
402
+ const styleResult = await browserSession.send('Runtime.evaluate', {
403
403
  expression: `
404
404
  (function() {
405
405
  const el = document.querySelectorAll('*')[${element.index}];
@@ -435,9 +435,9 @@ async function snapshot(page, testInfo, name, config) {
435
435
  }
436
436
  }
437
437
  // Disable domains and close session
438
- await cdpSession.send('CSS.disable');
439
- await cdpSession.send('DOM.disable');
440
- await cdpSession.detach();
438
+ await browserSession.send('CSS.disable');
439
+ await browserSession.send('DOM.disable');
440
+ await browserSession.detach();
441
441
  // Save computed styles to file
442
442
  await fs.writeJson(stylesPath, {
443
443
  computed_styles: computedStyles,
@@ -449,7 +449,7 @@ async function snapshot(page, testInfo, name, config) {
449
449
  }
450
450
  }
451
451
  catch (error) {
452
- console.warn('[TestivAI] Failed to capture CSS via CDP:', error);
452
+ console.warn('[TestivAI] Failed to capture CSS via browser session:', error);
453
453
  // Continue without CSS data
454
454
  }
455
455
  // 3. Extract bounding boxes for requested selectors
@@ -468,21 +468,21 @@ async function snapshot(page, testInfo, name, config) {
468
468
  };
469
469
  }
470
470
  }
471
- // 4. Capture performance metrics using CDP (if enabled)
471
+ // 4. Capture performance metrics using browser session (if enabled)
472
472
  let performanceMetrics = undefined;
473
473
  const metricsEnabled = effectiveConfig.performanceMetrics?.enabled ?? true; // Default: enabled
474
474
  if (metricsEnabled) {
475
475
  try {
476
- // Get CDP session from Playwright page
477
- const cdpSession = await page.context().newCDPSession(page);
476
+ // Get browser session from Playwright page
477
+ const browserSession = await page.context().newCDPSession(page);
478
478
  // Enable Performance domain
479
- await cdpSession.send('Performance.enable');
480
- // Get CDP performance metrics
481
- const cdpMetrics = await cdpSession.send('Performance.getMetrics');
479
+ await browserSession.send('Performance.enable');
480
+ // Get browser performance metrics
481
+ const browserMetrics = await browserSession.send('Performance.getMetrics');
482
482
  // Convert metrics array to object
483
- const cdpMetricsObj = {};
484
- cdpMetrics.metrics.forEach((metric) => {
485
- cdpMetricsObj[metric.name] = metric.value;
483
+ const browserMetricsObj = {};
484
+ browserMetrics.metrics.forEach((metric) => {
485
+ browserMetricsObj[metric.name] = metric.value;
486
486
  });
487
487
  // Get navigation timing and Web Vitals via page.evaluate
488
488
  const timingData = await page.evaluate(() => {
@@ -555,11 +555,11 @@ async function snapshot(page, testInfo, name, config) {
555
555
  };
556
556
  });
557
557
  // Disable Performance domain
558
- await cdpSession.send('Performance.disable');
559
- await cdpSession.detach();
560
- // Structure identical to CDP SDK
558
+ await browserSession.send('Performance.disable');
559
+ await browserSession.detach();
560
+ // Structure identical to Witness SDK
561
561
  performanceMetrics = {
562
- cdp: cdpMetricsObj,
562
+ cdp: browserMetricsObj,
563
563
  timing: timingData,
564
564
  timestamp: Date.now()
565
565
  };
package/dist/types.d.ts CHANGED
@@ -115,7 +115,7 @@ export interface StructureChange {
115
115
  }
116
116
  /**
117
117
  * Performance timing metrics
118
- * @deprecated Use performanceMetrics with CDP Performance.getMetrics instead
118
+ * @deprecated Use performanceMetrics with browser Performance.getMetrics instead
119
119
  */
120
120
  export interface PerformanceTimings {
121
121
  /** Navigation start time */
@@ -137,7 +137,7 @@ export interface PerformanceTimings {
137
137
  }
138
138
  /**
139
139
  * Lighthouse performance results
140
- * @deprecated Lighthouse has been removed. Use performanceMetrics with CDP Performance.getMetrics instead
140
+ * @deprecated Lighthouse has been removed. Use performanceMetrics with browser Performance.getMetrics instead
141
141
  */
142
142
  export interface LighthouseResults {
143
143
  /** Performance score (0-100) */
@@ -214,8 +214,8 @@ export interface TestivAIConfig {
214
214
  structure?: Partial<StructureAnalysisConfig>;
215
215
  /** Element selectors to capture (existing option) */
216
216
  selectors?: string[];
217
- /** Use Chrome DevTools Protocol for full-page capture (default: true - use CDP, set to false for scroll-and-stitch) */
218
- useCDP?: boolean;
217
+ /** Use browser capture for full-page screenshots (default: true, set to false for scroll-and-stitch) */
218
+ useBrowserCapture?: boolean;
219
219
  }
220
220
  /**
221
221
  * Layout/Bounding box data for an element
@@ -291,6 +291,8 @@ export interface SnapshotPayload {
291
291
  screenshotData?: string;
292
292
  /** Performance timing metrics (optional) */
293
293
  performanceTimings?: PerformanceTimings;
294
+ /** Performance metrics from browser Performance.getMetrics (optional) */
295
+ performanceMetrics?: any;
294
296
  /** Lighthouse results (optional) */
295
297
  lighthouseResults?: LighthouseResults;
296
298
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testivai/witness-playwright",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Playwright sensor for Testivai Visual Regression Test system",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -103,7 +103,7 @@ export function mergeTestConfig(
103
103
  ...testConfig.performanceMetrics
104
104
  },
105
105
  selectors: testConfig.selectors,
106
- useCDP: testConfig.useCDP
106
+ useBrowserCapture: testConfig.useBrowserCapture
107
107
  };
108
108
  }
109
109
 
package/src/reporter.ts CHANGED
@@ -108,7 +108,7 @@ export class TestivAIPlaywrightReporter implements Reporter {
108
108
  console.log(`Testivai Reporter: [DEBUG] Found ${jsonFiles.length} snapshot(s) to process`);
109
109
  }
110
110
 
111
- // Build snapshots array matching CDP SDK format
111
+ // Build snapshots array matching Witness SDK format
112
112
  const snapshots: SnapshotPayload[] = [];
113
113
 
114
114
  for (const jsonFile of jsonFiles) {
@@ -175,12 +175,13 @@ export class TestivAIPlaywrightReporter implements Reporter {
175
175
  bottom: layoutData.y + layoutData.height
176
176
  },
177
177
  testivaiConfig: metadata.testivaiConfig,
178
- screenshotData: screenshotBase64
178
+ screenshotData: screenshotBase64,
179
+ performanceMetrics: metadata.performanceMetrics
179
180
  };
180
181
  snapshots.push(snapshotPayload);
181
182
  }
182
183
 
183
- // Build batch payload matching CDP SDK format
184
+ // Build batch payload matching Witness SDK format
184
185
  const batchPayload: Omit<BatchPayload, 'batchId'> = {
185
186
  git: this.gitInfo!,
186
187
  browser: this.browserInfo!,
@@ -190,7 +191,7 @@ export class TestivAIPlaywrightReporter implements Reporter {
190
191
  ci: this.ciInfo,
191
192
  };
192
193
 
193
- // Compress and upload (same as CDP SDK)
194
+ // Compress and upload (same as Witness SDK)
194
195
  const payloadJson = JSON.stringify(batchPayload);
195
196
  const compressionResult = await this.compressionHelper.compress(payloadJson);
196
197
 
@@ -204,7 +205,7 @@ export class TestivAIPlaywrightReporter implements Reporter {
204
205
  ...compressionResult.headers,
205
206
  };
206
207
 
207
- // Upload to same endpoint as CDP SDK
208
+ // Upload to same endpoint as Witness SDK
208
209
  const startBatchResponse = await axios.post(
209
210
  `${this.options.apiUrl}/api/v1/ingest/start-batch`,
210
211
  compressionResult.data,
package/src/snapshot.ts CHANGED
@@ -67,7 +67,7 @@ export async function snapshot(
67
67
  const screenshotPath = path.join(outputDir, `${baseFilename}.png`);
68
68
 
69
69
  // Check if scroll-and-stitch is explicitly requested (backup method)
70
- if (effectiveConfig.useCDP === false) {
70
+ if (effectiveConfig.useBrowserCapture === false) {
71
71
  // Use scroll-and-stitch approach (backup method)
72
72
  if (process.env.TESTIVAI_DEBUG === 'true') {
73
73
  console.log('[TestivAI] Using scroll-and-stitch approach (backup method)');
@@ -230,13 +230,13 @@ export async function snapshot(
230
230
  }
231
231
  }
232
232
  } else {
233
- // Use CDP approach (default)
233
+ // Use browser capture approach (default)
234
234
  if (process.env.TESTIVAI_DEBUG === 'true') {
235
- console.log('[TestivAI] Using CDP approach (default) for full-page screenshot');
235
+ console.log('[TestivAI] Using browser capture approach (default) for full-page screenshot');
236
236
  }
237
237
 
238
238
  try {
239
- // Create a CDP session
239
+ // Create a browser session
240
240
  const client = await page.context().newCDPSession(page);
241
241
 
242
242
  // Enable Page domain
@@ -270,7 +270,7 @@ export async function snapshot(
270
270
  const pageHeight = Math.ceil(layoutMetrics.contentSize.height);
271
271
 
272
272
  if (process.env.TESTIVAI_DEBUG === 'true') {
273
- console.log('[TestivAI] CDP Layout metrics:', {
273
+ console.log('[TestivAI] Browser layout metrics:', {
274
274
  pageWidth,
275
275
  pageHeight,
276
276
  viewportWidth: layoutMetrics.layoutViewport.clientWidth,
@@ -303,11 +303,11 @@ export async function snapshot(
303
303
  }
304
304
  `);
305
305
 
306
- // Close CDP session
306
+ // Close browser session
307
307
  await client.detach();
308
308
 
309
309
  } catch (error: any) {
310
- console.error('[TestivAI] CDP screenshot failed:', error.message);
310
+ console.error('[TestivAI] Browser screenshot failed:', error.message);
311
311
  // Fallback to regular screenshot
312
312
  await page.screenshot({ path: screenshotPath, fullPage: true });
313
313
  }
@@ -319,15 +319,15 @@ export async function snapshot(
319
319
  const htmlContent = await page.content();
320
320
  await fs.writeFile(structurePath, htmlContent);
321
321
 
322
- // 2.5. Capture computed styles using CDP
322
+ // 2.5. Capture computed styles using browser session
323
323
  // @renamed: cssPath → stylesPath (IP protection)
324
324
  const stylesPath = path.join(outputDir, `${baseFilename}.css.json`);
325
325
  try {
326
- const cdpSession = await page.context().newCDPSession(page);
326
+ const browserSession = await page.context().newCDPSession(page);
327
327
 
328
328
  // Enable DOM and CSS domains
329
- await cdpSession.send('DOM.enable');
330
- await cdpSession.send('CSS.enable');
329
+ await browserSession.send('DOM.enable');
330
+ await browserSession.send('CSS.enable');
331
331
 
332
332
  // Get all elements and their computed styles
333
333
  const computedStyles: Record<string, Record<string, string>> = {};
@@ -343,7 +343,7 @@ export async function snapshot(
343
343
  ];
344
344
 
345
345
  // Execute script to get all elements with unique identifiers
346
- const elementsData = await cdpSession.send('Runtime.evaluate', {
346
+ const elementsData = await browserSession.send('Runtime.evaluate', {
347
347
  expression: `
348
348
  (function() {
349
349
  // Helper to get stable CSS selector path for an element
@@ -412,7 +412,7 @@ export async function snapshot(
412
412
  for (let i = 0; i < sampleSize; i++) {
413
413
  const element = elements[i];
414
414
  try {
415
- const styleResult = await cdpSession.send('Runtime.evaluate', {
415
+ const styleResult = await browserSession.send('Runtime.evaluate', {
416
416
  expression: `
417
417
  (function() {
418
418
  const el = document.querySelectorAll('*')[${element.index}];
@@ -449,9 +449,9 @@ export async function snapshot(
449
449
  }
450
450
 
451
451
  // Disable domains and close session
452
- await cdpSession.send('CSS.disable');
453
- await cdpSession.send('DOM.disable');
454
- await cdpSession.detach();
452
+ await browserSession.send('CSS.disable');
453
+ await browserSession.send('DOM.disable');
454
+ await browserSession.detach();
455
455
 
456
456
  // Save computed styles to file
457
457
  await fs.writeJson(stylesPath, {
@@ -464,7 +464,7 @@ export async function snapshot(
464
464
  console.log(`[TestivAI] Captured ${Object.keys(computedStyles).length} element styles`);
465
465
  }
466
466
  } catch (error) {
467
- console.warn('[TestivAI] Failed to capture CSS via CDP:', error);
467
+ console.warn('[TestivAI] Failed to capture CSS via browser session:', error);
468
468
  // Continue without CSS data
469
469
  }
470
470
 
@@ -486,26 +486,26 @@ export async function snapshot(
486
486
  }
487
487
  }
488
488
 
489
- // 4. Capture performance metrics using CDP (if enabled)
489
+ // 4. Capture performance metrics using browser session (if enabled)
490
490
  let performanceMetrics: any = undefined;
491
491
 
492
492
  const metricsEnabled = effectiveConfig.performanceMetrics?.enabled ?? true; // Default: enabled
493
493
 
494
494
  if (metricsEnabled) {
495
495
  try {
496
- // Get CDP session from Playwright page
497
- const cdpSession = await page.context().newCDPSession(page);
496
+ // Get browser session from Playwright page
497
+ const browserSession = await page.context().newCDPSession(page);
498
498
 
499
499
  // Enable Performance domain
500
- await cdpSession.send('Performance.enable');
500
+ await browserSession.send('Performance.enable');
501
501
 
502
- // Get CDP performance metrics
503
- const cdpMetrics = await cdpSession.send('Performance.getMetrics');
502
+ // Get browser performance metrics
503
+ const browserMetrics = await browserSession.send('Performance.getMetrics');
504
504
 
505
505
  // Convert metrics array to object
506
- const cdpMetricsObj: any = {};
507
- cdpMetrics.metrics.forEach((metric: any) => {
508
- cdpMetricsObj[metric.name] = metric.value;
506
+ const browserMetricsObj: any = {};
507
+ browserMetrics.metrics.forEach((metric: any) => {
508
+ browserMetricsObj[metric.name] = metric.value;
509
509
  });
510
510
 
511
511
  // Get navigation timing and Web Vitals via page.evaluate
@@ -583,12 +583,12 @@ export async function snapshot(
583
583
  });
584
584
 
585
585
  // Disable Performance domain
586
- await cdpSession.send('Performance.disable');
587
- await cdpSession.detach();
586
+ await browserSession.send('Performance.disable');
587
+ await browserSession.detach();
588
588
 
589
- // Structure identical to CDP SDK
589
+ // Structure identical to Witness SDK
590
590
  performanceMetrics = {
591
- cdp: cdpMetricsObj,
591
+ cdp: browserMetricsObj,
592
592
  timing: timingData,
593
593
  timestamp: Date.now()
594
594
  };
package/src/types.ts CHANGED
@@ -122,7 +122,7 @@ export interface StructureChange {
122
122
 
123
123
  /**
124
124
  * Performance timing metrics
125
- * @deprecated Use performanceMetrics with CDP Performance.getMetrics instead
125
+ * @deprecated Use performanceMetrics with browser Performance.getMetrics instead
126
126
  */
127
127
  export interface PerformanceTimings {
128
128
  /** Navigation start time */
@@ -145,7 +145,7 @@ export interface PerformanceTimings {
145
145
 
146
146
  /**
147
147
  * Lighthouse performance results
148
- * @deprecated Lighthouse has been removed. Use performanceMetrics with CDP Performance.getMetrics instead
148
+ * @deprecated Lighthouse has been removed. Use performanceMetrics with browser Performance.getMetrics instead
149
149
  */
150
150
  export interface LighthouseResults {
151
151
  /** Performance score (0-100) */
@@ -225,8 +225,8 @@ export interface TestivAIConfig {
225
225
  structure?: Partial<StructureAnalysisConfig>;
226
226
  /** Element selectors to capture (existing option) */
227
227
  selectors?: string[];
228
- /** Use Chrome DevTools Protocol for full-page capture (default: true - use CDP, set to false for scroll-and-stitch) */
229
- useCDP?: boolean;
228
+ /** Use browser capture for full-page screenshots (default: true, set to false for scroll-and-stitch) */
229
+ useBrowserCapture?: boolean;
230
230
  }
231
231
 
232
232
  /**
@@ -306,6 +306,8 @@ export interface SnapshotPayload {
306
306
  screenshotData?: string;
307
307
  /** Performance timing metrics (optional) */
308
308
  performanceTimings?: PerformanceTimings;
309
+ /** Performance metrics from browser Performance.getMetrics (optional) */
310
+ performanceMetrics?: any;
309
311
  /** Lighthouse results (optional) */
310
312
  lighthouseResults?: LighthouseResults;
311
313
  /**