@alternative-path/testlens-playwright-reporter 0.3.7 → 0.3.9

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.
Files changed (4) hide show
  1. package/index.d.ts +9 -0
  2. package/index.js +229 -18
  3. package/index.ts +248 -18
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -26,6 +26,8 @@ export interface TestLensReporterConfig {
26
26
  rejectUnauthorized?: boolean;
27
27
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
28
28
  ignoreSslErrors?: boolean;
29
+ /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
30
+ caCertificate?: string;
29
31
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
30
32
  customMetadata?: Record<string, string | string[]>;
31
33
  }
@@ -56,6 +58,8 @@ export interface TestLensReporterOptions {
56
58
  rejectUnauthorized?: boolean;
57
59
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
58
60
  ignoreSslErrors?: boolean;
61
+ /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
62
+ caCertificate?: string;
59
63
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
60
64
  customMetadata?: Record<string, string | string[]>;
61
65
  }
@@ -153,6 +157,11 @@ export interface SpecData {
153
157
  export declare class TestLensReporter implements Reporter {
154
158
  private config;
155
159
  private axiosInstance;
160
+ /**
161
+ * Automatically detect and load system CA certificates
162
+ * Uses Node.js built-in root certificates (works across all platforms)
163
+ */
164
+ private static getSystemCaCertificates;
156
165
  private runId;
157
166
  private runMetadata;
158
167
  private specMap;
package/index.js CHANGED
@@ -7,6 +7,7 @@ const os = tslib_1.__importStar(require("os"));
7
7
  const path = tslib_1.__importStar(require("path"));
8
8
  const fs = tslib_1.__importStar(require("fs"));
9
9
  const https = tslib_1.__importStar(require("https"));
10
+ const tls = tslib_1.__importStar(require("tls"));
10
11
  const axios_1 = tslib_1.__importDefault(require("axios"));
11
12
  const child_process_1 = require("child_process");
12
13
  // Lazy-load mime module to support ESM
@@ -20,6 +21,63 @@ async function getMime() {
20
21
  return mimeModule;
21
22
  }
22
23
  class TestLensReporter {
24
+ /**
25
+ * Automatically detect and load system CA certificates
26
+ * Uses Node.js built-in root certificates (works across all platforms)
27
+ */
28
+ static getSystemCaCertificates() {
29
+ // Use Node.js's built-in root certificates (available in Node.js 12.3.0+)
30
+ // This includes all common root CAs and works reliably across Windows, macOS, and Linux
31
+ try {
32
+ if (tls.rootCertificates && Array.isArray(tls.rootCertificates) && tls.rootCertificates.length > 0) {
33
+ // Convert string certificates to Buffer
34
+ const rootCerts = tls.rootCertificates.map(cert => Buffer.from(cert));
35
+ if (process.env.DEBUG) {
36
+ console.log(`✓ Using Node.js built-in root certificates (${rootCerts.length} certificates)`);
37
+ }
38
+ return rootCerts;
39
+ }
40
+ }
41
+ catch (error) {
42
+ // Fall through to file-based detection if tls.rootCertificates is not available
43
+ if (process.env.DEBUG) {
44
+ console.log('⚠️ Node.js built-in root certificates not available, trying file-based detection');
45
+ }
46
+ }
47
+ // Fallback: Try to load from file system (for older Node.js versions or special cases)
48
+ const platform = os.platform();
49
+ const certificates = [];
50
+ // Fallback: Try to load certificates from common file system locations
51
+ const certPaths = [];
52
+ if (platform === 'darwin') {
53
+ // macOS: Common certificate locations
54
+ certPaths.push('/etc/ssl/cert.pem', '/usr/local/etc/openssl/cert.pem', '/opt/homebrew/etc/openssl/cert.pem', '/System/Library/OpenSSL/certs/cert.pem');
55
+ }
56
+ else {
57
+ // Linux and other Unix-like systems
58
+ certPaths.push('/etc/ssl/certs/ca-certificates.crt', '/etc/ssl/certs/ca-bundle.crt', '/etc/pki/tls/certs/ca-bundle.crt', '/etc/ssl/ca-bundle.pem', '/usr/share/ssl/certs/ca-bundle.crt', '/usr/local/share/certs/ca-root-nss.crt', '/etc/ca-certificates/extracted/tls-ca-bundle.pem');
59
+ }
60
+ // Try to load certificates from common locations
61
+ for (const certPath of certPaths) {
62
+ try {
63
+ if (certPath && fs.existsSync(certPath)) {
64
+ const certData = fs.readFileSync(certPath);
65
+ certificates.push(certData);
66
+ // Only log in debug mode to avoid noise
67
+ if (process.env.DEBUG) {
68
+ console.log(`✓ Loaded CA certificates from: ${certPath}`);
69
+ }
70
+ }
71
+ }
72
+ catch (error) {
73
+ // Silently continue if a certificate file can't be read
74
+ // This is expected as not all paths will exist on every system
75
+ }
76
+ }
77
+ // Return undefined if no certificates found (let Node.js use defaults)
78
+ // Return certificates array if we found any
79
+ return certificates.length > 0 ? certificates : undefined;
80
+ }
23
81
  /**
24
82
  * Parse custom metadata from environment variables
25
83
  * Checks for common metadata environment variables
@@ -97,6 +155,44 @@ class TestLensReporter {
97
155
  }
98
156
  // Determine SSL validation behavior
99
157
  let rejectUnauthorized = true; // Default to secure
158
+ let ca = undefined;
159
+ // Automatically detect and load system CA certificates
160
+ // This helps resolve "unable to get local issuer certificate" errors
161
+ const systemCerts = TestLensReporter.getSystemCaCertificates();
162
+ // Load custom CA certificate if explicitly provided (for advanced users)
163
+ if (this.config.caCertificate) {
164
+ try {
165
+ if (fs.existsSync(this.config.caCertificate)) {
166
+ const customCert = fs.readFileSync(this.config.caCertificate);
167
+ // Combine system certs with custom cert if system certs are available
168
+ if (systemCerts && Array.isArray(systemCerts) && systemCerts.length > 0) {
169
+ ca = [...systemCerts, customCert];
170
+ }
171
+ else {
172
+ ca = customCert;
173
+ }
174
+ console.log(`✓ Using custom CA certificate: ${this.config.caCertificate}`);
175
+ }
176
+ else {
177
+ console.warn(`⚠️ CA certificate file not found: ${this.config.caCertificate}`);
178
+ // Fall back to system certs if custom cert not found
179
+ ca = systemCerts || undefined;
180
+ }
181
+ }
182
+ catch (error) {
183
+ console.warn(`⚠️ Failed to read CA certificate file: ${this.config.caCertificate}`, error.message);
184
+ // Fall back to system certs if custom cert read fails
185
+ ca = systemCerts || undefined;
186
+ }
187
+ }
188
+ else {
189
+ // Use automatically detected system certificates
190
+ // On Windows, systemCerts will be undefined to let Node.js use Windows certificate store
191
+ // On Unix systems, systemCerts will contain certificates or be undefined
192
+ ca = systemCerts;
193
+ // If ca is undefined, Node.js will use its default certificate store
194
+ // This is the correct behavior, especially on Windows
195
+ }
100
196
  // Check various ways SSL validation can be disabled (in order of precedence)
101
197
  if (this.config.ignoreSslErrors) {
102
198
  // Explicit configuration option
@@ -114,6 +210,24 @@ class TestLensReporter {
114
210
  console.log('⚠️ SSL certificate validation disabled via NODE_TLS_REJECT_UNAUTHORIZED environment variable');
115
211
  }
116
212
  // Set up axios instance with retry logic and enhanced SSL handling
213
+ const httpsAgentOptions = {
214
+ rejectUnauthorized: rejectUnauthorized,
215
+ // Allow any TLS version for better compatibility
216
+ minVersion: 'TLSv1.2',
217
+ maxVersion: 'TLSv1.3'
218
+ };
219
+ // Add CA certificates if available
220
+ // On Windows, ca will be undefined to let Node.js use Windows certificate store automatically
221
+ // On Unix systems, ca will contain certificates if found, or undefined to use Node.js defaults
222
+ if (ca !== undefined) {
223
+ if (Array.isArray(ca) && ca.length > 0) {
224
+ httpsAgentOptions.ca = ca;
225
+ }
226
+ else if (typeof ca === 'string' || Buffer.isBuffer(ca)) {
227
+ httpsAgentOptions.ca = ca;
228
+ }
229
+ // If ca is undefined, we don't set it, allowing Node.js to use its default certificate store
230
+ }
117
231
  this.axiosInstance = axios_1.default.create({
118
232
  baseURL: this.config.apiEndpoint,
119
233
  timeout: this.config.timeout,
@@ -122,12 +236,7 @@ class TestLensReporter {
122
236
  ...(this.config.apiKey && { 'X-API-Key': this.config.apiKey }),
123
237
  },
124
238
  // Enhanced SSL handling with flexible TLS configuration
125
- httpsAgent: new https.Agent({
126
- rejectUnauthorized: rejectUnauthorized,
127
- // Allow any TLS version for better compatibility
128
- minVersion: 'TLSv1.2',
129
- maxVersion: 'TLSv1.3'
130
- })
239
+ httpsAgent: new https.Agent(httpsAgentOptions)
131
240
  });
132
241
  // Add retry interceptor
133
242
  this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
@@ -647,17 +756,68 @@ class TestLensReporter {
647
756
  console.error(`❌ Authentication failed: ${errorData?.error || 'Invalid API key'}`);
648
757
  }
649
758
  }
650
- else if (status !== 403) {
651
- // Log other errors (but not 403 which we handled above)
652
- console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
653
- message: error?.message || 'Unknown error',
654
- status: status,
655
- statusText: error?.response?.statusText,
656
- data: errorData,
657
- code: error?.code,
658
- url: error?.config?.url,
659
- method: error?.config?.method
660
- });
759
+ else {
760
+ // Check for SSL certificate errors
761
+ const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
762
+ error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
763
+ error?.code === 'CERT_HAS_EXPIRED' ||
764
+ error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
765
+ error?.message?.includes('certificate') ||
766
+ error?.message?.includes('SSL') ||
767
+ error?.message?.includes('TLS');
768
+ if (isSslError && status !== 403) {
769
+ console.error('\n' + '='.repeat(80));
770
+ console.error('❌ SSL Certificate Error');
771
+ console.error('='.repeat(80));
772
+ console.error('');
773
+ console.error(`Failed to send ${payload.type} event to TestLens due to SSL certificate issue.`);
774
+ console.error('');
775
+ console.error('The reporter automatically attempts to detect and use system CA certificates.');
776
+ console.error('If this error persists, it may indicate:');
777
+ console.error(' - Missing or outdated system CA certificates');
778
+ console.error(' - Corporate proxy with custom CA certificate');
779
+ console.error(' - Incomplete certificate chain from the server');
780
+ console.error('');
781
+ console.error('Error details:');
782
+ console.error(` Code: ${error?.code || 'Unknown'}`);
783
+ console.error(` Message: ${error?.message || 'Unknown error'}`);
784
+ console.error('');
785
+ console.error('Possible solutions:');
786
+ console.error('');
787
+ console.error('1. Update your system\'s CA certificate store:');
788
+ console.error(' - Windows: Update Windows root certificates via Windows Update');
789
+ console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
790
+ console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
791
+ console.error('');
792
+ console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
793
+ console.error(' you can specify it in your config (optional):');
794
+ console.error(' reporter: [');
795
+ console.error(' [\'@testlens/playwright-reporter\', {');
796
+ console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
797
+ console.error(' }]');
798
+ console.error(' ]');
799
+ console.error('');
800
+ console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
801
+ console.error(' that uses a custom CA certificate.');
802
+ console.error('');
803
+ console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
804
+ console.error(' and is insecure. Only use this as a last resort in development.');
805
+ console.error('');
806
+ console.error('='.repeat(80));
807
+ console.error('');
808
+ }
809
+ else if (status !== 403) {
810
+ // Log other errors (but not 403 which we handled above)
811
+ console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
812
+ message: error?.message || 'Unknown error',
813
+ status: status,
814
+ statusText: error?.response?.statusText,
815
+ data: errorData,
816
+ code: error?.code,
817
+ url: error?.config?.url,
818
+ method: error?.config?.method
819
+ });
820
+ }
661
821
  }
662
822
  // Don't throw error to avoid breaking test execution
663
823
  }
@@ -776,7 +936,58 @@ class TestLensReporter {
776
936
  console.log(`ℹ️ Spec code blocks already exist for: ${path.basename(specPath)} (skipped)`);
777
937
  return;
778
938
  }
779
- console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
939
+ // Check for SSL certificate errors
940
+ const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
941
+ error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
942
+ error?.code === 'CERT_HAS_EXPIRED' ||
943
+ error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
944
+ error?.message?.includes('certificate') ||
945
+ error?.message?.includes('SSL') ||
946
+ error?.message?.includes('TLS');
947
+ if (isSslError) {
948
+ console.error('\n' + '='.repeat(80));
949
+ console.error('❌ SSL Certificate Error');
950
+ console.error('='.repeat(80));
951
+ console.error('');
952
+ console.error('Failed to send spec code blocks due to SSL certificate issue.');
953
+ console.error('');
954
+ console.error('The reporter automatically attempts to detect and use system CA certificates.');
955
+ console.error('If this error persists, it may indicate:');
956
+ console.error(' - Missing or outdated system CA certificates');
957
+ console.error(' - Corporate proxy with custom CA certificate');
958
+ console.error(' - Incomplete certificate chain from the server');
959
+ console.error('');
960
+ console.error('Error details:');
961
+ console.error(` Code: ${error?.code || 'Unknown'}`);
962
+ console.error(` Message: ${error?.message || 'Unknown error'}`);
963
+ console.error('');
964
+ console.error('Possible solutions:');
965
+ console.error('');
966
+ console.error('1. Update your system\'s CA certificate store:');
967
+ console.error(' - Windows: Update Windows root certificates via Windows Update');
968
+ console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
969
+ console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
970
+ console.error('');
971
+ console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
972
+ console.error(' you can specify it in your config (optional):');
973
+ console.error(' reporter: [');
974
+ console.error(' [\'@testlens/playwright-reporter\', {');
975
+ console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
976
+ console.error(' }]');
977
+ console.error(' ]');
978
+ console.error('');
979
+ console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
980
+ console.error(' that uses a custom CA certificate.');
981
+ console.error('');
982
+ console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
983
+ console.error(' and is insecure. Only use this as a last resort in development.');
984
+ console.error('');
985
+ console.error('='.repeat(80));
986
+ console.error('');
987
+ }
988
+ else {
989
+ console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
990
+ }
780
991
  }
781
992
  }
782
993
  extractTestBlocks(filePath) {
package/index.ts CHANGED
@@ -3,6 +3,7 @@ import * as os from 'os';
3
3
  import * as path from 'path';
4
4
  import * as fs from 'fs';
5
5
  import * as https from 'https';
6
+ import * as tls from 'tls';
6
7
  import axios, { AxiosInstance } from 'axios';
7
8
  import type { Reporter, TestCase, TestResult, FullConfig, Suite } from '@playwright/test/reporter';
8
9
  import { execSync } from 'child_process';
@@ -46,6 +47,8 @@ export interface TestLensReporterConfig {
46
47
  rejectUnauthorized?: boolean;
47
48
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
48
49
  ignoreSslErrors?: boolean;
50
+ /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
51
+ caCertificate?: string;
49
52
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
50
53
  customMetadata?: Record<string, string | string[]>;
51
54
  }
@@ -77,6 +80,8 @@ export interface TestLensReporterOptions {
77
80
  rejectUnauthorized?: boolean;
78
81
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
79
82
  ignoreSslErrors?: boolean;
83
+ /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
84
+ caCertificate?: string;
80
85
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
81
86
  customMetadata?: Record<string, string | string[]>;
82
87
  }
@@ -178,6 +183,79 @@ export interface SpecData {
178
183
  export class TestLensReporter implements Reporter {
179
184
  private config: Required<TestLensReporterConfig>;
180
185
  private axiosInstance: AxiosInstance;
186
+
187
+ /**
188
+ * Automatically detect and load system CA certificates
189
+ * Uses Node.js built-in root certificates (works across all platforms)
190
+ */
191
+ private static getSystemCaCertificates(): Buffer[] | undefined {
192
+ // Use Node.js's built-in root certificates (available in Node.js 12.3.0+)
193
+ // This includes all common root CAs and works reliably across Windows, macOS, and Linux
194
+ try {
195
+ if (tls.rootCertificates && Array.isArray(tls.rootCertificates) && tls.rootCertificates.length > 0) {
196
+ // Convert string certificates to Buffer
197
+ const rootCerts = tls.rootCertificates.map(cert => Buffer.from(cert));
198
+ if (process.env.DEBUG) {
199
+ console.log(`✓ Using Node.js built-in root certificates (${rootCerts.length} certificates)`);
200
+ }
201
+ return rootCerts;
202
+ }
203
+ } catch (error) {
204
+ // Fall through to file-based detection if tls.rootCertificates is not available
205
+ if (process.env.DEBUG) {
206
+ console.log('⚠️ Node.js built-in root certificates not available, trying file-based detection');
207
+ }
208
+ }
209
+
210
+ // Fallback: Try to load from file system (for older Node.js versions or special cases)
211
+ const platform = os.platform();
212
+ const certificates: Buffer[] = [];
213
+
214
+ // Fallback: Try to load certificates from common file system locations
215
+ const certPaths: string[] = [];
216
+
217
+ if (platform === 'darwin') {
218
+ // macOS: Common certificate locations
219
+ certPaths.push(
220
+ '/etc/ssl/cert.pem',
221
+ '/usr/local/etc/openssl/cert.pem',
222
+ '/opt/homebrew/etc/openssl/cert.pem',
223
+ '/System/Library/OpenSSL/certs/cert.pem'
224
+ );
225
+ } else {
226
+ // Linux and other Unix-like systems
227
+ certPaths.push(
228
+ '/etc/ssl/certs/ca-certificates.crt',
229
+ '/etc/ssl/certs/ca-bundle.crt',
230
+ '/etc/pki/tls/certs/ca-bundle.crt',
231
+ '/etc/ssl/ca-bundle.pem',
232
+ '/usr/share/ssl/certs/ca-bundle.crt',
233
+ '/usr/local/share/certs/ca-root-nss.crt',
234
+ '/etc/ca-certificates/extracted/tls-ca-bundle.pem'
235
+ );
236
+ }
237
+
238
+ // Try to load certificates from common locations
239
+ for (const certPath of certPaths) {
240
+ try {
241
+ if (certPath && fs.existsSync(certPath)) {
242
+ const certData = fs.readFileSync(certPath);
243
+ certificates.push(certData);
244
+ // Only log in debug mode to avoid noise
245
+ if (process.env.DEBUG) {
246
+ console.log(`✓ Loaded CA certificates from: ${certPath}`);
247
+ }
248
+ }
249
+ } catch (error) {
250
+ // Silently continue if a certificate file can't be read
251
+ // This is expected as not all paths will exist on every system
252
+ }
253
+ }
254
+
255
+ // Return undefined if no certificates found (let Node.js use defaults)
256
+ // Return certificates array if we found any
257
+ return certificates.length > 0 ? certificates : undefined;
258
+ }
181
259
  private runId: string;
182
260
  private runMetadata: RunMetadata;
183
261
  private specMap: Map<string, SpecData>;
@@ -268,6 +346,42 @@ export class TestLensReporter implements Reporter {
268
346
 
269
347
  // Determine SSL validation behavior
270
348
  let rejectUnauthorized = true; // Default to secure
349
+ let ca: Buffer | string | Buffer[] | undefined = undefined;
350
+
351
+ // Automatically detect and load system CA certificates
352
+ // This helps resolve "unable to get local issuer certificate" errors
353
+ const systemCerts = TestLensReporter.getSystemCaCertificates();
354
+
355
+ // Load custom CA certificate if explicitly provided (for advanced users)
356
+ if (this.config.caCertificate) {
357
+ try {
358
+ if (fs.existsSync(this.config.caCertificate)) {
359
+ const customCert = fs.readFileSync(this.config.caCertificate);
360
+ // Combine system certs with custom cert if system certs are available
361
+ if (systemCerts && Array.isArray(systemCerts) && systemCerts.length > 0) {
362
+ ca = [...systemCerts, customCert];
363
+ } else {
364
+ ca = customCert;
365
+ }
366
+ console.log(`✓ Using custom CA certificate: ${this.config.caCertificate}`);
367
+ } else {
368
+ console.warn(`⚠️ CA certificate file not found: ${this.config.caCertificate}`);
369
+ // Fall back to system certs if custom cert not found
370
+ ca = systemCerts || undefined;
371
+ }
372
+ } catch (error) {
373
+ console.warn(`⚠️ Failed to read CA certificate file: ${this.config.caCertificate}`, (error as Error).message);
374
+ // Fall back to system certs if custom cert read fails
375
+ ca = systemCerts || undefined;
376
+ }
377
+ } else {
378
+ // Use automatically detected system certificates
379
+ // On Windows, systemCerts will be undefined to let Node.js use Windows certificate store
380
+ // On Unix systems, systemCerts will contain certificates or be undefined
381
+ ca = systemCerts;
382
+ // If ca is undefined, Node.js will use its default certificate store
383
+ // This is the correct behavior, especially on Windows
384
+ }
271
385
 
272
386
  // Check various ways SSL validation can be disabled (in order of precedence)
273
387
  if (this.config.ignoreSslErrors) {
@@ -285,6 +399,25 @@ export class TestLensReporter implements Reporter {
285
399
  }
286
400
 
287
401
  // Set up axios instance with retry logic and enhanced SSL handling
402
+ const httpsAgentOptions: https.AgentOptions = {
403
+ rejectUnauthorized: rejectUnauthorized,
404
+ // Allow any TLS version for better compatibility
405
+ minVersion: 'TLSv1.2',
406
+ maxVersion: 'TLSv1.3'
407
+ };
408
+
409
+ // Add CA certificates if available
410
+ // On Windows, ca will be undefined to let Node.js use Windows certificate store automatically
411
+ // On Unix systems, ca will contain certificates if found, or undefined to use Node.js defaults
412
+ if (ca !== undefined) {
413
+ if (Array.isArray(ca) && ca.length > 0) {
414
+ httpsAgentOptions.ca = ca;
415
+ } else if (typeof ca === 'string' || Buffer.isBuffer(ca)) {
416
+ httpsAgentOptions.ca = ca;
417
+ }
418
+ // If ca is undefined, we don't set it, allowing Node.js to use its default certificate store
419
+ }
420
+
288
421
  this.axiosInstance = axios.create({
289
422
  baseURL: this.config.apiEndpoint,
290
423
  timeout: this.config.timeout,
@@ -293,12 +426,7 @@ export class TestLensReporter implements Reporter {
293
426
  ...(this.config.apiKey && { 'X-API-Key': this.config.apiKey }),
294
427
  },
295
428
  // Enhanced SSL handling with flexible TLS configuration
296
- httpsAgent: new https.Agent({
297
- rejectUnauthorized: rejectUnauthorized,
298
- // Allow any TLS version for better compatibility
299
- minVersion: 'TLSv1.2',
300
- maxVersion: 'TLSv1.3'
301
- })
429
+ httpsAgent: new https.Agent(httpsAgentOptions)
302
430
  });
303
431
 
304
432
  // Add retry interceptor
@@ -876,17 +1004,68 @@ export class TestLensReporter implements Reporter {
876
1004
  } else {
877
1005
  console.error(`❌ Authentication failed: ${errorData?.error || 'Invalid API key'}`);
878
1006
  }
879
- } else if (status !== 403) {
880
- // Log other errors (but not 403 which we handled above)
881
- console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
882
- message: error?.message || 'Unknown error',
883
- status: status,
884
- statusText: error?.response?.statusText,
885
- data: errorData,
886
- code: error?.code,
887
- url: error?.config?.url,
888
- method: error?.config?.method
889
- });
1007
+ } else {
1008
+ // Check for SSL certificate errors
1009
+ const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
1010
+ error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
1011
+ error?.code === 'CERT_HAS_EXPIRED' ||
1012
+ error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
1013
+ error?.message?.includes('certificate') ||
1014
+ error?.message?.includes('SSL') ||
1015
+ error?.message?.includes('TLS');
1016
+
1017
+ if (isSslError && status !== 403) {
1018
+ console.error('\n' + '='.repeat(80));
1019
+ console.error('❌ SSL Certificate Error');
1020
+ console.error('='.repeat(80));
1021
+ console.error('');
1022
+ console.error(`Failed to send ${payload.type} event to TestLens due to SSL certificate issue.`);
1023
+ console.error('');
1024
+ console.error('The reporter automatically attempts to detect and use system CA certificates.');
1025
+ console.error('If this error persists, it may indicate:');
1026
+ console.error(' - Missing or outdated system CA certificates');
1027
+ console.error(' - Corporate proxy with custom CA certificate');
1028
+ console.error(' - Incomplete certificate chain from the server');
1029
+ console.error('');
1030
+ console.error('Error details:');
1031
+ console.error(` Code: ${error?.code || 'Unknown'}`);
1032
+ console.error(` Message: ${error?.message || 'Unknown error'}`);
1033
+ console.error('');
1034
+ console.error('Possible solutions:');
1035
+ console.error('');
1036
+ console.error('1. Update your system\'s CA certificate store:');
1037
+ console.error(' - Windows: Update Windows root certificates via Windows Update');
1038
+ console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
1039
+ console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
1040
+ console.error('');
1041
+ console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
1042
+ console.error(' you can specify it in your config (optional):');
1043
+ console.error(' reporter: [');
1044
+ console.error(' [\'@testlens/playwright-reporter\', {');
1045
+ console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
1046
+ console.error(' }]');
1047
+ console.error(' ]');
1048
+ console.error('');
1049
+ console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
1050
+ console.error(' that uses a custom CA certificate.');
1051
+ console.error('');
1052
+ console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
1053
+ console.error(' and is insecure. Only use this as a last resort in development.');
1054
+ console.error('');
1055
+ console.error('='.repeat(80));
1056
+ console.error('');
1057
+ } else if (status !== 403) {
1058
+ // Log other errors (but not 403 which we handled above)
1059
+ console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
1060
+ message: error?.message || 'Unknown error',
1061
+ status: status,
1062
+ statusText: error?.response?.statusText,
1063
+ data: errorData,
1064
+ code: error?.code,
1065
+ url: error?.config?.url,
1066
+ method: error?.config?.method
1067
+ });
1068
+ }
890
1069
  }
891
1070
 
892
1071
  // Don't throw error to avoid breaking test execution
@@ -1022,7 +1201,58 @@ export class TestLensReporter implements Reporter {
1022
1201
  return;
1023
1202
  }
1024
1203
 
1025
- console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1204
+ // Check for SSL certificate errors
1205
+ const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
1206
+ error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
1207
+ error?.code === 'CERT_HAS_EXPIRED' ||
1208
+ error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
1209
+ error?.message?.includes('certificate') ||
1210
+ error?.message?.includes('SSL') ||
1211
+ error?.message?.includes('TLS');
1212
+
1213
+ if (isSslError) {
1214
+ console.error('\n' + '='.repeat(80));
1215
+ console.error('❌ SSL Certificate Error');
1216
+ console.error('='.repeat(80));
1217
+ console.error('');
1218
+ console.error('Failed to send spec code blocks due to SSL certificate issue.');
1219
+ console.error('');
1220
+ console.error('The reporter automatically attempts to detect and use system CA certificates.');
1221
+ console.error('If this error persists, it may indicate:');
1222
+ console.error(' - Missing or outdated system CA certificates');
1223
+ console.error(' - Corporate proxy with custom CA certificate');
1224
+ console.error(' - Incomplete certificate chain from the server');
1225
+ console.error('');
1226
+ console.error('Error details:');
1227
+ console.error(` Code: ${error?.code || 'Unknown'}`);
1228
+ console.error(` Message: ${error?.message || 'Unknown error'}`);
1229
+ console.error('');
1230
+ console.error('Possible solutions:');
1231
+ console.error('');
1232
+ console.error('1. Update your system\'s CA certificate store:');
1233
+ console.error(' - Windows: Update Windows root certificates via Windows Update');
1234
+ console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
1235
+ console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
1236
+ console.error('');
1237
+ console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
1238
+ console.error(' you can specify it in your config (optional):');
1239
+ console.error(' reporter: [');
1240
+ console.error(' [\'@testlens/playwright-reporter\', {');
1241
+ console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
1242
+ console.error(' }]');
1243
+ console.error(' ]');
1244
+ console.error('');
1245
+ console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
1246
+ console.error(' that uses a custom CA certificate.');
1247
+ console.error('');
1248
+ console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
1249
+ console.error(' and is insecure. Only use this as a last resort in development.');
1250
+ console.error('');
1251
+ console.error('='.repeat(80));
1252
+ console.error('');
1253
+ } else {
1254
+ console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1255
+ }
1026
1256
  }
1027
1257
  }
1028
1258
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alternative-path/testlens-playwright-reporter",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Universal Playwright reporter for TestLens - works with both TypeScript and JavaScript projects",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",