@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.
- package/index.d.ts +9 -0
- package/index.js +229 -18
- package/index.ts +248 -18
- 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
|
|
651
|
-
//
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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
|
-
|
|
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
|
|
880
|
-
//
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|