@alternative-path/testlens-playwright-reporter 0.4.1 → 0.4.3

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 CHANGED
@@ -1,207 +1,192 @@
1
- import type { Reporter, TestCase, TestResult, FullConfig, Suite } from '@playwright/test/reporter';
2
- export interface TestLensReporterConfig {
3
- /** TestLens API endpoint URL */
4
- apiEndpoint?: string;
5
- /** API key for authentication - can be provided in config or via TESTLENS_API_KEY environment variable */
6
- apiKey?: string;
7
- /** Enable real-time streaming of test events */
8
- enableRealTimeStream?: boolean;
9
- /** Enable Git information collection */
10
- enableGitInfo?: boolean;
11
- /** Enable artifact processing */
12
- enableArtifacts?: boolean;
13
- /** Enable video capture - defaults to true */
14
- enableVideo?: boolean;
15
- /** Enable screenshot capture - defaults to true */
16
- enableScreenshot?: boolean;
17
- /** Batch size for API requests */
18
- batchSize?: number;
19
- /** Flush interval in milliseconds */
20
- flushInterval?: number;
21
- /** Number of retry attempts for failed API calls */
22
- retryAttempts?: number;
23
- /** Request timeout in milliseconds */
24
- timeout?: number;
25
- /** SSL certificate validation - set to false to disable SSL verification */
26
- rejectUnauthorized?: boolean;
27
- /** Alternative SSL option - set to true to ignore SSL certificate errors */
28
- ignoreSslErrors?: boolean;
29
- /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
30
- caCertificate?: string;
31
- /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
32
- customMetadata?: Record<string, string | string[]>;
33
- }
34
- export interface TestLensReporterOptions {
35
- /** TestLens API endpoint URL */
36
- apiEndpoint?: string;
37
- /** API key for authentication - can be provided in config or via TESTLENS_API_KEY environment variable */
38
- apiKey?: string;
39
- /** Enable real-time streaming of test events */
40
- enableRealTimeStream?: boolean;
41
- /** Enable Git information collection */
42
- enableGitInfo?: boolean;
43
- /** Enable artifact processing */
44
- enableArtifacts?: boolean;
45
- /** Enable video capture - defaults to true */
46
- enableVideo?: boolean;
47
- /** Enable screenshot capture - defaults to true */
48
- enableScreenshot?: boolean;
49
- /** Batch size for API requests */
50
- batchSize?: number;
51
- /** Flush interval in milliseconds */
52
- flushInterval?: number;
53
- /** Number of retry attempts for failed API calls */
54
- retryAttempts?: number;
55
- /** Request timeout in milliseconds */
56
- timeout?: number;
57
- /** SSL certificate validation - set to false to disable SSL verification */
58
- rejectUnauthorized?: boolean;
59
- /** Alternative SSL option - set to true to ignore SSL certificate errors */
60
- ignoreSslErrors?: boolean;
61
- /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
62
- caCertificate?: string;
63
- /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
64
- customMetadata?: Record<string, string | string[]>;
65
- }
66
- export interface GitInfo {
67
- branch: string;
68
- commit: string;
69
- shortCommit: string;
70
- author: string;
71
- message: string;
72
- timestamp: string;
73
- isDirty: boolean;
74
- remoteName: string;
75
- remoteUrl: string;
76
- }
77
- export interface CodeBlock {
78
- type: 'test' | 'describe';
79
- name: string;
80
- content: string;
81
- summary?: string;
82
- describe?: string;
83
- startLine?: number;
84
- endLine?: number;
85
- }
86
- export interface RunMetadata {
87
- id: string;
88
- startTime: string;
89
- endTime?: string;
90
- duration?: number;
91
- environment: string;
92
- browser: string;
93
- os: string;
94
- playwrightVersion: string;
95
- nodeVersion: string;
96
- gitInfo?: GitInfo | null;
97
- shardInfo?: {
98
- current: number;
99
- total: number;
100
- };
101
- totalTests?: number;
102
- passedTests?: number;
103
- failedTests?: number;
104
- skippedTests?: number;
105
- status?: string;
106
- testlensBuildName?: string;
107
- customMetadata?: Record<string, string | string[]>;
108
- }
109
- export interface TestError {
110
- message: string;
111
- stack?: string;
112
- location?: {
113
- file: string;
114
- line: number;
115
- column: number;
116
- };
117
- snippet?: string;
118
- expected?: string;
119
- actual?: string;
120
- diff?: string;
121
- matcherName?: string;
122
- timeout?: number;
123
- }
124
- export interface TestData {
125
- id: string;
126
- name: string;
127
- status: string;
128
- originalStatus?: string;
129
- duration: number;
130
- startTime: string;
131
- endTime: string;
132
- errorMessages: string[];
133
- errors?: TestError[];
134
- retryAttempts: number;
135
- currentRetry: number;
136
- annotations: Array<{
137
- type: string;
138
- description?: string;
139
- }>;
140
- projectName: string;
141
- workerIndex?: number;
142
- parallelIndex?: number;
143
- location?: {
144
- file: string;
145
- line: number;
146
- column: number;
147
- };
148
- }
149
- export interface SpecData {
150
- filePath: string;
151
- testSuiteName: string;
152
- tags?: string[];
153
- startTime: string;
154
- endTime?: string;
155
- status: string;
156
- }
157
- export declare class TestLensReporter implements Reporter {
158
- private config;
159
- private axiosInstance;
160
- /**
161
- * Get bundled CA certificates for TestLens
162
- * Combines custom CA bundle with Node.js root certificates
163
- * This ensures SSL works with both testlens.qa-path.com and other HTTPS endpoints
164
- */
165
- private static getBundledCaCertificates;
166
- /**
167
- * Automatically detect and load system CA certificates
168
- * Uses bundled certificates as primary source, falls back to system certificates
169
- */
170
- private static getSystemCaCertificates;
171
- private runId;
172
- private runMetadata;
173
- private specMap;
174
- private testMap;
175
- private runCreationFailed;
176
- private cliArgs;
177
- /**
178
- * Parse custom metadata from environment variables
179
- * Checks for common metadata environment variables
180
- */
181
- private static parseCustomArgs;
182
- constructor(options: TestLensReporterOptions);
183
- private initializeRunMetadata;
184
- private getPlaywrightVersion;
185
- private normalizeTestStatus;
186
- private normalizeRunStatus;
187
- onBegin(config: FullConfig, suite: Suite): Promise<void>;
188
- onTestBegin(test: TestCase, result: TestResult): Promise<void>;
189
- onTestEnd(test: TestCase, result: TestResult): Promise<void>;
190
- onEnd(result: {
191
- status: string;
192
- }): Promise<void>;
193
- private sendToApi;
194
- private processArtifacts;
195
- private sendSpecCodeBlocks;
196
- private extractTestBlocks;
197
- private collectGitInfo;
198
- private getArtifactType;
199
- private extractTags;
200
- private getTestId;
201
- private uploadArtifactToS3;
202
- private getContentType;
203
- private generateS3Key;
204
- private sanitizeForS3;
205
- private getFileSize;
206
- }
207
- export default TestLensReporter;
1
+ import type { Reporter, TestCase, TestResult, FullConfig, Suite } from '@playwright/test/reporter';
2
+ export interface TestLensReporterConfig {
3
+ /** TestLens API endpoint URL */
4
+ apiEndpoint?: string;
5
+ /** API key for authentication - can be provided in config or via TESTLENS_API_KEY environment variable */
6
+ apiKey?: string;
7
+ /** Enable real-time streaming of test events */
8
+ enableRealTimeStream?: boolean;
9
+ /** Enable Git information collection */
10
+ enableGitInfo?: boolean;
11
+ /** Enable artifact processing */
12
+ enableArtifacts?: boolean;
13
+ /** Enable video capture - defaults to true */
14
+ enableVideo?: boolean;
15
+ /** Enable screenshot capture - defaults to true */
16
+ enableScreenshot?: boolean;
17
+ /** Batch size for API requests */
18
+ batchSize?: number;
19
+ /** Flush interval in milliseconds */
20
+ flushInterval?: number;
21
+ /** Number of retry attempts for failed API calls */
22
+ retryAttempts?: number;
23
+ /** Request timeout in milliseconds */
24
+ timeout?: number;
25
+ /** SSL certificate validation - set to false to disable SSL verification */
26
+ rejectUnauthorized?: boolean;
27
+ /** Alternative SSL option - set to true to ignore SSL certificate errors */
28
+ ignoreSslErrors?: boolean;
29
+ /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
30
+ customMetadata?: Record<string, string | string[]>;
31
+ }
32
+ export interface TestLensReporterOptions {
33
+ /** TestLens API endpoint URL */
34
+ apiEndpoint?: string;
35
+ /** API key for authentication - can be provided in config or via TESTLENS_API_KEY environment variable */
36
+ apiKey?: string;
37
+ /** Enable real-time streaming of test events */
38
+ enableRealTimeStream?: boolean;
39
+ /** Enable Git information collection */
40
+ enableGitInfo?: boolean;
41
+ /** Enable artifact processing */
42
+ enableArtifacts?: boolean;
43
+ /** Enable video capture - defaults to true */
44
+ enableVideo?: boolean;
45
+ /** Enable screenshot capture - defaults to true */
46
+ enableScreenshot?: boolean;
47
+ /** Batch size for API requests */
48
+ batchSize?: number;
49
+ /** Flush interval in milliseconds */
50
+ flushInterval?: number;
51
+ /** Number of retry attempts for failed API calls */
52
+ retryAttempts?: number;
53
+ /** Request timeout in milliseconds */
54
+ timeout?: number;
55
+ /** SSL certificate validation - set to false to disable SSL verification */
56
+ rejectUnauthorized?: boolean;
57
+ /** Alternative SSL option - set to true to ignore SSL certificate errors */
58
+ ignoreSslErrors?: boolean;
59
+ /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
60
+ customMetadata?: Record<string, string | string[]>;
61
+ }
62
+ export interface GitInfo {
63
+ branch: string;
64
+ commit: string;
65
+ shortCommit: string;
66
+ author: string;
67
+ message: string;
68
+ timestamp: string;
69
+ isDirty: boolean;
70
+ remoteName: string;
71
+ remoteUrl: string;
72
+ }
73
+ export interface CodeBlock {
74
+ type: 'test' | 'describe';
75
+ name: string;
76
+ content: string;
77
+ summary?: string;
78
+ describe?: string;
79
+ startLine?: number;
80
+ endLine?: number;
81
+ }
82
+ export interface RunMetadata {
83
+ id: string;
84
+ startTime: string;
85
+ endTime?: string;
86
+ duration?: number;
87
+ environment: string;
88
+ browser: string;
89
+ os: string;
90
+ playwrightVersion: string;
91
+ nodeVersion: string;
92
+ gitInfo?: GitInfo | null;
93
+ shardInfo?: {
94
+ current: number;
95
+ total: number;
96
+ };
97
+ totalTests?: number;
98
+ passedTests?: number;
99
+ failedTests?: number;
100
+ skippedTests?: number;
101
+ status?: string;
102
+ testlensBuildName?: string;
103
+ customMetadata?: Record<string, string | string[]>;
104
+ }
105
+ export interface TestError {
106
+ message: string;
107
+ stack?: string;
108
+ location?: {
109
+ file: string;
110
+ line: number;
111
+ column: number;
112
+ };
113
+ snippet?: string;
114
+ expected?: string;
115
+ actual?: string;
116
+ diff?: string;
117
+ matcherName?: string;
118
+ timeout?: number;
119
+ }
120
+ export interface TestData {
121
+ id: string;
122
+ name: string;
123
+ status: string;
124
+ originalStatus?: string;
125
+ duration: number;
126
+ startTime: string;
127
+ endTime: string;
128
+ errorMessages: string[];
129
+ errors?: TestError[];
130
+ retryAttempts: number;
131
+ currentRetry: number;
132
+ annotations: Array<{
133
+ type: string;
134
+ description?: string;
135
+ }>;
136
+ projectName: string;
137
+ workerIndex?: number;
138
+ parallelIndex?: number;
139
+ location?: {
140
+ file: string;
141
+ line: number;
142
+ column: number;
143
+ };
144
+ }
145
+ export interface SpecData {
146
+ filePath: string;
147
+ testSuiteName: string;
148
+ tags?: string[];
149
+ startTime: string;
150
+ endTime?: string;
151
+ status: string;
152
+ }
153
+ export declare class TestLensReporter implements Reporter {
154
+ private config;
155
+ private axiosInstance;
156
+ private runId;
157
+ private runMetadata;
158
+ private specMap;
159
+ private testMap;
160
+ private runCreationFailed;
161
+ private cliArgs;
162
+ /**
163
+ * Parse custom metadata from environment variables
164
+ * Checks for common metadata environment variables
165
+ */
166
+ private static parseCustomArgs;
167
+ constructor(options: TestLensReporterOptions);
168
+ private initializeRunMetadata;
169
+ private getPlaywrightVersion;
170
+ private normalizeTestStatus;
171
+ private normalizeRunStatus;
172
+ onBegin(config: FullConfig, suite: Suite): Promise<void>;
173
+ onTestBegin(test: TestCase, result: TestResult): Promise<void>;
174
+ onTestEnd(test: TestCase, result: TestResult): Promise<void>;
175
+ onEnd(result: {
176
+ status: string;
177
+ }): Promise<void>;
178
+ private sendToApi;
179
+ private processArtifacts;
180
+ private sendSpecCodeBlocks;
181
+ private extractTestBlocks;
182
+ private collectGitInfo;
183
+ private getArtifactType;
184
+ private extractTags;
185
+ private getTestId;
186
+ private uploadArtifactToS3;
187
+ private getContentType;
188
+ private generateS3Key;
189
+ private sanitizeForS3;
190
+ private getFileSize;
191
+ }
192
+ export default TestLensReporter;
package/index.js CHANGED
@@ -7,7 +7,6 @@ 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"));
11
10
  const axios_1 = tslib_1.__importDefault(require("axios"));
12
11
  const child_process_1 = require("child_process");
13
12
  // Lazy-load mime module to support ESM
@@ -21,94 +20,6 @@ async function getMime() {
21
20
  return mimeModule;
22
21
  }
23
22
  class TestLensReporter {
24
- /**
25
- * Get bundled CA certificates for TestLens
26
- * Combines custom CA bundle with Node.js root certificates
27
- * This ensures SSL works with both testlens.qa-path.com and other HTTPS endpoints
28
- */
29
- static getBundledCaCertificates() {
30
- const allCerts = [];
31
- // First, add our bundled TestLens CA certificate chain
32
- try {
33
- const certPath = path.join(__dirname, 'testlens-ca-bundle.pem');
34
- if (fs.existsSync(certPath)) {
35
- const certData = fs.readFileSync(certPath, 'utf8');
36
- // Split the bundle into individual certificates
37
- const certs = certData.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g);
38
- if (certs && certs.length > 0) {
39
- const buffers = certs.map(cert => Buffer.from(cert, 'utf8'));
40
- allCerts.push(...buffers);
41
- if (process.env.DEBUG) {
42
- console.log(`✓ Loaded bundled TestLens CA certificates (${buffers.length} certificate(s))`);
43
- }
44
- }
45
- }
46
- }
47
- catch (error) {
48
- if (process.env.DEBUG) {
49
- console.log('⚠️ Bundled CA certificate not available');
50
- }
51
- }
52
- // Then, add Node.js built-in root certificates for general SSL support
53
- try {
54
- if (tls.rootCertificates && Array.isArray(tls.rootCertificates) && tls.rootCertificates.length > 0) {
55
- const rootCerts = tls.rootCertificates.map(cert => Buffer.from(cert, 'utf8'));
56
- allCerts.push(...rootCerts);
57
- if (process.env.DEBUG) {
58
- console.log(`✓ Added Node.js built-in root certificates (${rootCerts.length} certificates)`);
59
- }
60
- }
61
- }
62
- catch (error) {
63
- if (process.env.DEBUG) {
64
- console.log('⚠️ Node.js built-in root certificates not available');
65
- }
66
- }
67
- return allCerts.length > 0 ? allCerts : undefined;
68
- }
69
- /**
70
- * Automatically detect and load system CA certificates
71
- * Uses bundled certificates as primary source, falls back to system certificates
72
- */
73
- static getSystemCaCertificates() {
74
- // First, try to use our bundled certificates (Node.js rootCertificates)
75
- const bundledCerts = TestLensReporter.getBundledCaCertificates();
76
- if (bundledCerts && bundledCerts.length > 0) {
77
- return bundledCerts;
78
- }
79
- // Fallback: Try to load from file system (for older Node.js versions or special cases)
80
- const platform = os.platform();
81
- const certificates = [];
82
- const certPaths = [];
83
- if (platform === 'darwin') {
84
- // macOS: Common certificate locations
85
- certPaths.push('/etc/ssl/cert.pem', '/usr/local/etc/openssl/cert.pem', '/opt/homebrew/etc/openssl/cert.pem', '/System/Library/OpenSSL/certs/cert.pem');
86
- }
87
- else {
88
- // Linux and other Unix-like systems
89
- 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');
90
- }
91
- // Try to load certificates from common locations
92
- for (const certPath of certPaths) {
93
- try {
94
- if (certPath && fs.existsSync(certPath)) {
95
- const certData = fs.readFileSync(certPath);
96
- certificates.push(certData);
97
- // Only log in debug mode to avoid noise
98
- if (process.env.DEBUG) {
99
- console.log(`✓ Loaded CA certificates from: ${certPath}`);
100
- }
101
- }
102
- }
103
- catch (error) {
104
- // Silently continue if a certificate file can't be read
105
- // This is expected as not all paths will exist on every system
106
- }
107
- }
108
- // Return undefined if no certificates found (let Node.js use defaults)
109
- // Return certificates array if we found any
110
- return certificates.length > 0 ? certificates : undefined;
111
- }
112
23
  /**
113
24
  * Parse custom metadata from environment variables
114
25
  * Checks for common metadata environment variables
@@ -186,46 +97,6 @@ class TestLensReporter {
186
97
  }
187
98
  // Determine SSL validation behavior
188
99
  let rejectUnauthorized = true; // Default to secure
189
- let ca = undefined;
190
- // Use bundled CA certificates as primary source (Node.js rootCertificates)
191
- // This ensures consistent behavior across all platforms
192
- const bundledCerts = TestLensReporter.getBundledCaCertificates();
193
- // Load custom CA certificate if explicitly provided (for advanced users)
194
- // Custom certificates will be combined with bundled certificates
195
- if (this.config.caCertificate) {
196
- try {
197
- if (fs.existsSync(this.config.caCertificate)) {
198
- const customCert = fs.readFileSync(this.config.caCertificate);
199
- // Combine bundled certs with custom cert
200
- if (bundledCerts && Array.isArray(bundledCerts) && bundledCerts.length > 0) {
201
- ca = [...bundledCerts, customCert];
202
- console.log(`✓ Using bundled CA certificates + custom certificate: ${this.config.caCertificate}`);
203
- }
204
- else {
205
- ca = customCert;
206
- console.log(`✓ Using custom CA certificate: ${this.config.caCertificate}`);
207
- }
208
- }
209
- else {
210
- console.warn(`⚠️ CA certificate file not found: ${this.config.caCertificate}`);
211
- // Fall back to bundled certs if custom cert not found
212
- ca = bundledCerts || undefined;
213
- }
214
- }
215
- catch (error) {
216
- console.warn(`⚠️ Failed to read CA certificate file: ${this.config.caCertificate}`, error.message);
217
- // Fall back to bundled certs if custom cert read fails
218
- ca = bundledCerts || undefined;
219
- }
220
- }
221
- else {
222
- // Use bundled certificates as primary source
223
- // This works reliably across all platforms (Windows, macOS, Linux)
224
- ca = bundledCerts || undefined;
225
- if (ca && process.env.DEBUG) {
226
- console.log(`✓ Using bundled CA certificates (${Array.isArray(ca) ? ca.length : 1} certificates)`);
227
- }
228
- }
229
100
  // Check various ways SSL validation can be disabled (in order of precedence)
230
101
  if (this.config.ignoreSslErrors) {
231
102
  // Explicit configuration option
@@ -243,24 +114,6 @@ class TestLensReporter {
243
114
  console.log('⚠️ SSL certificate validation disabled via NODE_TLS_REJECT_UNAUTHORIZED environment variable');
244
115
  }
245
116
  // Set up axios instance with retry logic and enhanced SSL handling
246
- const httpsAgentOptions = {
247
- rejectUnauthorized: rejectUnauthorized,
248
- // Allow any TLS version for better compatibility
249
- minVersion: 'TLSv1.2',
250
- maxVersion: 'TLSv1.3'
251
- };
252
- // Add CA certificates if available
253
- // On Windows, ca will be undefined to let Node.js use Windows certificate store automatically
254
- // On Unix systems, ca will contain certificates if found, or undefined to use Node.js defaults
255
- if (ca !== undefined) {
256
- if (Array.isArray(ca) && ca.length > 0) {
257
- httpsAgentOptions.ca = ca;
258
- }
259
- else if (typeof ca === 'string' || Buffer.isBuffer(ca)) {
260
- httpsAgentOptions.ca = ca;
261
- }
262
- // If ca is undefined, we don't set it, allowing Node.js to use its default certificate store
263
- }
264
117
  this.axiosInstance = axios_1.default.create({
265
118
  baseURL: this.config.apiEndpoint,
266
119
  timeout: this.config.timeout,
@@ -269,7 +122,12 @@ class TestLensReporter {
269
122
  ...(this.config.apiKey && { 'X-API-Key': this.config.apiKey }),
270
123
  },
271
124
  // Enhanced SSL handling with flexible TLS configuration
272
- httpsAgent: new https.Agent(httpsAgentOptions)
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
+ })
273
131
  });
274
132
  // Add retry interceptor
275
133
  this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
@@ -789,68 +647,17 @@ class TestLensReporter {
789
647
  console.error(`❌ Authentication failed: ${errorData?.error || 'Invalid API key'}`);
790
648
  }
791
649
  }
792
- else {
793
- // Check for SSL certificate errors
794
- const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
795
- error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
796
- error?.code === 'CERT_HAS_EXPIRED' ||
797
- error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
798
- error?.message?.includes('certificate') ||
799
- error?.message?.includes('SSL') ||
800
- error?.message?.includes('TLS');
801
- if (isSslError && status !== 403) {
802
- console.error('\n' + '='.repeat(80));
803
- console.error('❌ SSL Certificate Error');
804
- console.error('='.repeat(80));
805
- console.error('');
806
- console.error(`Failed to send ${payload.type} event to TestLens due to SSL certificate issue.`);
807
- console.error('');
808
- console.error('The reporter automatically attempts to detect and use system CA certificates.');
809
- console.error('If this error persists, it may indicate:');
810
- console.error(' - Missing or outdated system CA certificates');
811
- console.error(' - Corporate proxy with custom CA certificate');
812
- console.error(' - Incomplete certificate chain from the server');
813
- console.error('');
814
- console.error('Error details:');
815
- console.error(` Code: ${error?.code || 'Unknown'}`);
816
- console.error(` Message: ${error?.message || 'Unknown error'}`);
817
- console.error('');
818
- console.error('Possible solutions:');
819
- console.error('');
820
- console.error('1. Update your system\'s CA certificate store:');
821
- console.error(' - Windows: Update Windows root certificates via Windows Update');
822
- console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
823
- console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
824
- console.error('');
825
- console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
826
- console.error(' you can specify it in your config (optional):');
827
- console.error(' reporter: [');
828
- console.error(' [\'@testlens/playwright-reporter\', {');
829
- console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
830
- console.error(' }]');
831
- console.error(' ]');
832
- console.error('');
833
- console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
834
- console.error(' that uses a custom CA certificate.');
835
- console.error('');
836
- console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
837
- console.error(' and is insecure. Only use this as a last resort in development.');
838
- console.error('');
839
- console.error('='.repeat(80));
840
- console.error('');
841
- }
842
- else if (status !== 403) {
843
- // Log other errors (but not 403 which we handled above)
844
- console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
845
- message: error?.message || 'Unknown error',
846
- status: status,
847
- statusText: error?.response?.statusText,
848
- data: errorData,
849
- code: error?.code,
850
- url: error?.config?.url,
851
- method: error?.config?.method
852
- });
853
- }
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
+ });
854
661
  }
855
662
  // Don't throw error to avoid breaking test execution
856
663
  }
@@ -969,58 +776,7 @@ class TestLensReporter {
969
776
  console.log(`ℹ️ Spec code blocks already exist for: ${path.basename(specPath)} (skipped)`);
970
777
  return;
971
778
  }
972
- // Check for SSL certificate errors
973
- const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
974
- error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
975
- error?.code === 'CERT_HAS_EXPIRED' ||
976
- error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
977
- error?.message?.includes('certificate') ||
978
- error?.message?.includes('SSL') ||
979
- error?.message?.includes('TLS');
980
- if (isSslError) {
981
- console.error('\n' + '='.repeat(80));
982
- console.error('❌ SSL Certificate Error');
983
- console.error('='.repeat(80));
984
- console.error('');
985
- console.error('Failed to send spec code blocks due to SSL certificate issue.');
986
- console.error('');
987
- console.error('The reporter automatically attempts to detect and use system CA certificates.');
988
- console.error('If this error persists, it may indicate:');
989
- console.error(' - Missing or outdated system CA certificates');
990
- console.error(' - Corporate proxy with custom CA certificate');
991
- console.error(' - Incomplete certificate chain from the server');
992
- console.error('');
993
- console.error('Error details:');
994
- console.error(` Code: ${error?.code || 'Unknown'}`);
995
- console.error(` Message: ${error?.message || 'Unknown error'}`);
996
- console.error('');
997
- console.error('Possible solutions:');
998
- console.error('');
999
- console.error('1. Update your system\'s CA certificate store:');
1000
- console.error(' - Windows: Update Windows root certificates via Windows Update');
1001
- console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
1002
- console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
1003
- console.error('');
1004
- console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
1005
- console.error(' you can specify it in your config (optional):');
1006
- console.error(' reporter: [');
1007
- console.error(' [\'@testlens/playwright-reporter\', {');
1008
- console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
1009
- console.error(' }]');
1010
- console.error(' ]');
1011
- console.error('');
1012
- console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
1013
- console.error(' that uses a custom CA certificate.');
1014
- console.error('');
1015
- console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
1016
- console.error(' and is insecure. Only use this as a last resort in development.');
1017
- console.error('');
1018
- console.error('='.repeat(80));
1019
- console.error('');
1020
- }
1021
- else {
1022
- console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1023
- }
779
+ console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1024
780
  }
1025
781
  }
1026
782
  extractTestBlocks(filePath) {
package/index.ts CHANGED
@@ -3,7 +3,6 @@ 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';
7
6
  import axios, { AxiosInstance } from 'axios';
8
7
  import type { Reporter, TestCase, TestResult, FullConfig, Suite } from '@playwright/test/reporter';
9
8
  import { execSync } from 'child_process';
@@ -47,8 +46,6 @@ export interface TestLensReporterConfig {
47
46
  rejectUnauthorized?: boolean;
48
47
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
49
48
  ignoreSslErrors?: boolean;
50
- /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
51
- caCertificate?: string;
52
49
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
53
50
  customMetadata?: Record<string, string | string[]>;
54
51
  }
@@ -80,8 +77,6 @@ export interface TestLensReporterOptions {
80
77
  rejectUnauthorized?: boolean;
81
78
  /** Alternative SSL option - set to true to ignore SSL certificate errors */
82
79
  ignoreSslErrors?: boolean;
83
- /** Path to a custom CA certificate file (PEM format) to use for SSL verification */
84
- caCertificate?: string;
85
80
  /** Custom metadata from CLI arguments (automatically parsed from --key=value arguments) */
86
81
  customMetadata?: Record<string, string | string[]>;
87
82
  }
@@ -183,112 +178,6 @@ export interface SpecData {
183
178
  export class TestLensReporter implements Reporter {
184
179
  private config: Required<TestLensReporterConfig>;
185
180
  private axiosInstance: AxiosInstance;
186
-
187
- /**
188
- * Get bundled CA certificates for TestLens
189
- * Combines custom CA bundle with Node.js root certificates
190
- * This ensures SSL works with both testlens.qa-path.com and other HTTPS endpoints
191
- */
192
- private static getBundledCaCertificates(): Buffer[] | undefined {
193
- const allCerts: Buffer[] = [];
194
-
195
- // First, add our bundled TestLens CA certificate chain
196
- try {
197
- const certPath = path.join(__dirname, 'testlens-ca-bundle.pem');
198
- if (fs.existsSync(certPath)) {
199
- const certData = fs.readFileSync(certPath, 'utf8');
200
- // Split the bundle into individual certificates
201
- const certs = certData.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g);
202
- if (certs && certs.length > 0) {
203
- const buffers = certs.map(cert => Buffer.from(cert, 'utf8'));
204
- allCerts.push(...buffers);
205
- if (process.env.DEBUG) {
206
- console.log(`✓ Loaded bundled TestLens CA certificates (${buffers.length} certificate(s))`);
207
- }
208
- }
209
- }
210
- } catch (error) {
211
- if (process.env.DEBUG) {
212
- console.log('⚠️ Bundled CA certificate not available');
213
- }
214
- }
215
-
216
- // Then, add Node.js built-in root certificates for general SSL support
217
- try {
218
- if (tls.rootCertificates && Array.isArray(tls.rootCertificates) && tls.rootCertificates.length > 0) {
219
- const rootCerts = tls.rootCertificates.map(cert => Buffer.from(cert, 'utf8'));
220
- allCerts.push(...rootCerts);
221
- if (process.env.DEBUG) {
222
- console.log(`✓ Added Node.js built-in root certificates (${rootCerts.length} certificates)`);
223
- }
224
- }
225
- } catch (error) {
226
- if (process.env.DEBUG) {
227
- console.log('⚠️ Node.js built-in root certificates not available');
228
- }
229
- }
230
-
231
- return allCerts.length > 0 ? allCerts : undefined;
232
- }
233
-
234
- /**
235
- * Automatically detect and load system CA certificates
236
- * Uses bundled certificates as primary source, falls back to system certificates
237
- */
238
- private static getSystemCaCertificates(): Buffer[] | undefined {
239
- // First, try to use our bundled certificates (Node.js rootCertificates)
240
- const bundledCerts = TestLensReporter.getBundledCaCertificates();
241
- if (bundledCerts && bundledCerts.length > 0) {
242
- return bundledCerts;
243
- }
244
-
245
- // Fallback: Try to load from file system (for older Node.js versions or special cases)
246
- const platform = os.platform();
247
- const certificates: Buffer[] = [];
248
- const certPaths: string[] = [];
249
-
250
- if (platform === 'darwin') {
251
- // macOS: Common certificate locations
252
- certPaths.push(
253
- '/etc/ssl/cert.pem',
254
- '/usr/local/etc/openssl/cert.pem',
255
- '/opt/homebrew/etc/openssl/cert.pem',
256
- '/System/Library/OpenSSL/certs/cert.pem'
257
- );
258
- } else {
259
- // Linux and other Unix-like systems
260
- certPaths.push(
261
- '/etc/ssl/certs/ca-certificates.crt',
262
- '/etc/ssl/certs/ca-bundle.crt',
263
- '/etc/pki/tls/certs/ca-bundle.crt',
264
- '/etc/ssl/ca-bundle.pem',
265
- '/usr/share/ssl/certs/ca-bundle.crt',
266
- '/usr/local/share/certs/ca-root-nss.crt',
267
- '/etc/ca-certificates/extracted/tls-ca-bundle.pem'
268
- );
269
- }
270
-
271
- // Try to load certificates from common locations
272
- for (const certPath of certPaths) {
273
- try {
274
- if (certPath && fs.existsSync(certPath)) {
275
- const certData = fs.readFileSync(certPath);
276
- certificates.push(certData);
277
- // Only log in debug mode to avoid noise
278
- if (process.env.DEBUG) {
279
- console.log(`✓ Loaded CA certificates from: ${certPath}`);
280
- }
281
- }
282
- } catch (error) {
283
- // Silently continue if a certificate file can't be read
284
- // This is expected as not all paths will exist on every system
285
- }
286
- }
287
-
288
- // Return undefined if no certificates found (let Node.js use defaults)
289
- // Return certificates array if we found any
290
- return certificates.length > 0 ? certificates : undefined;
291
- }
292
181
  private runId: string;
293
182
  private runMetadata: RunMetadata;
294
183
  private specMap: Map<string, SpecData>;
@@ -379,44 +268,6 @@ export class TestLensReporter implements Reporter {
379
268
 
380
269
  // Determine SSL validation behavior
381
270
  let rejectUnauthorized = true; // Default to secure
382
- let ca: Buffer | string | Buffer[] | undefined = undefined;
383
-
384
- // Use bundled CA certificates as primary source (Node.js rootCertificates)
385
- // This ensures consistent behavior across all platforms
386
- const bundledCerts = TestLensReporter.getBundledCaCertificates();
387
-
388
- // Load custom CA certificate if explicitly provided (for advanced users)
389
- // Custom certificates will be combined with bundled certificates
390
- if (this.config.caCertificate) {
391
- try {
392
- if (fs.existsSync(this.config.caCertificate)) {
393
- const customCert = fs.readFileSync(this.config.caCertificate);
394
- // Combine bundled certs with custom cert
395
- if (bundledCerts && Array.isArray(bundledCerts) && bundledCerts.length > 0) {
396
- ca = [...bundledCerts, customCert];
397
- console.log(`✓ Using bundled CA certificates + custom certificate: ${this.config.caCertificate}`);
398
- } else {
399
- ca = customCert;
400
- console.log(`✓ Using custom CA certificate: ${this.config.caCertificate}`);
401
- }
402
- } else {
403
- console.warn(`⚠️ CA certificate file not found: ${this.config.caCertificate}`);
404
- // Fall back to bundled certs if custom cert not found
405
- ca = bundledCerts || undefined;
406
- }
407
- } catch (error) {
408
- console.warn(`⚠️ Failed to read CA certificate file: ${this.config.caCertificate}`, (error as Error).message);
409
- // Fall back to bundled certs if custom cert read fails
410
- ca = bundledCerts || undefined;
411
- }
412
- } else {
413
- // Use bundled certificates as primary source
414
- // This works reliably across all platforms (Windows, macOS, Linux)
415
- ca = bundledCerts || undefined;
416
- if (ca && process.env.DEBUG) {
417
- console.log(`✓ Using bundled CA certificates (${Array.isArray(ca) ? ca.length : 1} certificates)`);
418
- }
419
- }
420
271
 
421
272
  // Check various ways SSL validation can be disabled (in order of precedence)
422
273
  if (this.config.ignoreSslErrors) {
@@ -434,25 +285,6 @@ export class TestLensReporter implements Reporter {
434
285
  }
435
286
 
436
287
  // Set up axios instance with retry logic and enhanced SSL handling
437
- const httpsAgentOptions: https.AgentOptions = {
438
- rejectUnauthorized: rejectUnauthorized,
439
- // Allow any TLS version for better compatibility
440
- minVersion: 'TLSv1.2',
441
- maxVersion: 'TLSv1.3'
442
- };
443
-
444
- // Add CA certificates if available
445
- // On Windows, ca will be undefined to let Node.js use Windows certificate store automatically
446
- // On Unix systems, ca will contain certificates if found, or undefined to use Node.js defaults
447
- if (ca !== undefined) {
448
- if (Array.isArray(ca) && ca.length > 0) {
449
- httpsAgentOptions.ca = ca;
450
- } else if (typeof ca === 'string' || Buffer.isBuffer(ca)) {
451
- httpsAgentOptions.ca = ca;
452
- }
453
- // If ca is undefined, we don't set it, allowing Node.js to use its default certificate store
454
- }
455
-
456
288
  this.axiosInstance = axios.create({
457
289
  baseURL: this.config.apiEndpoint,
458
290
  timeout: this.config.timeout,
@@ -461,7 +293,12 @@ export class TestLensReporter implements Reporter {
461
293
  ...(this.config.apiKey && { 'X-API-Key': this.config.apiKey }),
462
294
  },
463
295
  // Enhanced SSL handling with flexible TLS configuration
464
- httpsAgent: new https.Agent(httpsAgentOptions)
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
+ })
465
302
  });
466
303
 
467
304
  // Add retry interceptor
@@ -1039,68 +876,17 @@ export class TestLensReporter implements Reporter {
1039
876
  } else {
1040
877
  console.error(`❌ Authentication failed: ${errorData?.error || 'Invalid API key'}`);
1041
878
  }
1042
- } else {
1043
- // Check for SSL certificate errors
1044
- const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
1045
- error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
1046
- error?.code === 'CERT_HAS_EXPIRED' ||
1047
- error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
1048
- error?.message?.includes('certificate') ||
1049
- error?.message?.includes('SSL') ||
1050
- error?.message?.includes('TLS');
1051
-
1052
- if (isSslError && status !== 403) {
1053
- console.error('\n' + '='.repeat(80));
1054
- console.error('❌ SSL Certificate Error');
1055
- console.error('='.repeat(80));
1056
- console.error('');
1057
- console.error(`Failed to send ${payload.type} event to TestLens due to SSL certificate issue.`);
1058
- console.error('');
1059
- console.error('The reporter automatically attempts to detect and use system CA certificates.');
1060
- console.error('If this error persists, it may indicate:');
1061
- console.error(' - Missing or outdated system CA certificates');
1062
- console.error(' - Corporate proxy with custom CA certificate');
1063
- console.error(' - Incomplete certificate chain from the server');
1064
- console.error('');
1065
- console.error('Error details:');
1066
- console.error(` Code: ${error?.code || 'Unknown'}`);
1067
- console.error(` Message: ${error?.message || 'Unknown error'}`);
1068
- console.error('');
1069
- console.error('Possible solutions:');
1070
- console.error('');
1071
- console.error('1. Update your system\'s CA certificate store:');
1072
- console.error(' - Windows: Update Windows root certificates via Windows Update');
1073
- console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
1074
- console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
1075
- console.error('');
1076
- console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
1077
- console.error(' you can specify it in your config (optional):');
1078
- console.error(' reporter: [');
1079
- console.error(' [\'@testlens/playwright-reporter\', {');
1080
- console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
1081
- console.error(' }]');
1082
- console.error(' ]');
1083
- console.error('');
1084
- console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
1085
- console.error(' that uses a custom CA certificate.');
1086
- console.error('');
1087
- console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
1088
- console.error(' and is insecure. Only use this as a last resort in development.');
1089
- console.error('');
1090
- console.error('='.repeat(80));
1091
- console.error('');
1092
- } else if (status !== 403) {
1093
- // Log other errors (but not 403 which we handled above)
1094
- console.error(`❌ Failed to send ${payload.type} event to TestLens:`, {
1095
- message: error?.message || 'Unknown error',
1096
- status: status,
1097
- statusText: error?.response?.statusText,
1098
- data: errorData,
1099
- code: error?.code,
1100
- url: error?.config?.url,
1101
- method: error?.config?.method
1102
- });
1103
- }
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
+ });
1104
890
  }
1105
891
 
1106
892
  // Don't throw error to avoid breaking test execution
@@ -1236,58 +1022,7 @@ export class TestLensReporter implements Reporter {
1236
1022
  return;
1237
1023
  }
1238
1024
 
1239
- // Check for SSL certificate errors
1240
- const isSslError = error?.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' ||
1241
- error?.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' ||
1242
- error?.code === 'CERT_HAS_EXPIRED' ||
1243
- error?.code === 'SELF_SIGNED_CERT_IN_CHAIN' ||
1244
- error?.message?.includes('certificate') ||
1245
- error?.message?.includes('SSL') ||
1246
- error?.message?.includes('TLS');
1247
-
1248
- if (isSslError) {
1249
- console.error('\n' + '='.repeat(80));
1250
- console.error('❌ SSL Certificate Error');
1251
- console.error('='.repeat(80));
1252
- console.error('');
1253
- console.error('Failed to send spec code blocks due to SSL certificate issue.');
1254
- console.error('');
1255
- console.error('The reporter automatically attempts to detect and use system CA certificates.');
1256
- console.error('If this error persists, it may indicate:');
1257
- console.error(' - Missing or outdated system CA certificates');
1258
- console.error(' - Corporate proxy with custom CA certificate');
1259
- console.error(' - Incomplete certificate chain from the server');
1260
- console.error('');
1261
- console.error('Error details:');
1262
- console.error(` Code: ${error?.code || 'Unknown'}`);
1263
- console.error(` Message: ${error?.message || 'Unknown error'}`);
1264
- console.error('');
1265
- console.error('Possible solutions:');
1266
- console.error('');
1267
- console.error('1. Update your system\'s CA certificate store:');
1268
- console.error(' - Windows: Update Windows root certificates via Windows Update');
1269
- console.error(' - macOS: Run: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain <certificate.pem>');
1270
- console.error(' - Linux: Update ca-certificates package: sudo apt-get update && sudo apt-get install ca-certificates');
1271
- console.error('');
1272
- console.error('2. If you have a custom CA certificate file (e.g., from corporate proxy),');
1273
- console.error(' you can specify it in your config (optional):');
1274
- console.error(' reporter: [');
1275
- console.error(' [\'@testlens/playwright-reporter\', {');
1276
- console.error(' caCertificate: \'/path/to/your/ca-certificate.pem\'');
1277
- console.error(' }]');
1278
- console.error(' ]');
1279
- console.error('');
1280
- console.error('3. Contact your network administrator if you\'re behind a corporate proxy');
1281
- console.error(' that uses a custom CA certificate.');
1282
- console.error('');
1283
- console.error('⚠️ WARNING: Setting NODE_TLS_REJECT_UNAUTHORIZED=0 disables SSL verification');
1284
- console.error(' and is insecure. Only use this as a last resort in development.');
1285
- console.error('');
1286
- console.error('='.repeat(80));
1287
- console.error('');
1288
- } else {
1289
- console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1290
- }
1025
+ console.error('Failed to send spec code blocks:', errorData || error?.message || 'Unknown error');
1291
1026
  }
1292
1027
  }
1293
1028
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alternative-path/testlens-playwright-reporter",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
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",
@@ -14,7 +14,6 @@
14
14
  "lib/",
15
15
  "postinstall.js",
16
16
  "cross-env.js",
17
- "testlens-ca-bundle.pem",
18
17
  "README.md",
19
18
  "CHANGELOG.md"
20
19
  ],
@@ -1,88 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIFvDCCBKSgAwIBAgIQCCpc5jpiqFvmHWmaKaa8zTANBgkqhkiG9w0BAQsFADA8
3
- MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
4
- UlNBIDIwNDggTTA0MB4XDTI1MTExNDAwMDAwMFoXDTI2MTIxMzIzNTk1OVowGDEW
5
- MBQGA1UEAwwNKi5xYS1wYXRoLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
6
- AQoCggEBAMFsdDGpKLNQ9GayoFXQpE9VLNxEZ/76AaiJMj30Nkuf245KxcPdmPMQ
7
- rklyBsKv2r4vGwqIrIh4ey59Zpi5z/GMtSP6DwPU/MGGRQGJ9Mpc2dhpxNvka7hW
8
- 9+t/uFAMbwvAhAe82fahsd6q/jtRMChkKQ2Ndln41PPnTPzqqoc2GWdYJO69W1x0
9
- 5CbovMOOoGKkskrS2TNe5+vYp8c8AWR6ga3zR2lxMVDAAXKcD/ejMq/FMR8TD9rf
10
- 6pWnsalQE7UAIgSltu9hrJRttCnyC6343WIQKspd3x+fZ6WdqtR4YywLij9DMBgJ
11
- KwYUHMjoY4lfkSgowhgnuCUFeqVhBrsCAwEAAaOCAtwwggLYMB8GA1UdIwQYMBaA
12
- FB9SkmFWglR/gWbYHT0KqjJch90IMB0GA1UdDgQWBBSsbNiBXRvG7yzfgQnHA4Fm
13
- Gvve1TAYBgNVHREEETAPgg0qLnFhLXBhdGguY29tMBMGA1UdIAQMMAowCAYGZ4EM
14
- AQIBMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATA7BgNVHR8E
15
- NDAyMDCgLqAshipodHRwOi8vY3JsLnIybTA0LmFtYXpvbnRydXN0LmNvbS9yMm0w
16
- NC5jcmwwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5y
17
- Mm0wNC5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQucjJt
18
- MDQuYW1hem9udHJ1c3QuY29tL3IybTA0LmNlcjAMBgNVHRMBAf8EAjAAMIIBfgYK
19
- KwYBBAHWeQIEAgSCAW4EggFqAWgAdgDYCVU7lE96/8gWGW+UT4WrsPj8XodVJg8V
20
- 0S5yu0VLFAAAAZqBmF/qAAAEAwBHMEUCIHg3mktTsuS8r57mR014haS8fqwcbqa1
21
- P3so9Qd02JFBAiEAsHQf6eH9AL0HoG5jEVfz4aZdNp/Y8unnDO+ZWSfW/wcAdgDC
22
- MX5XRRmjRe5/ON6ykEHrx8IhWiK/f9W1rXaa2Q5SzQAAAZqBmF/lAAAEAwBHMEUC
23
- IQCZhIaG3vpxTThIdHhvNCymkHGKFLggUBcPCqYMdYVFpAIgW+BszH9Kdon3IKNJ
24
- xKx5e4K+XZ+NhfXqqnCmPYeCwzQAdgCUTkOH+uzB74HzGSQmqBhlAcfTXzgCAT9y
25
- Z31VNy4Z2AAAAZqBmF/1AAAEAwBHMEUCIQDGh+jKNI6NO30js2yl0ItBltCDGA5d
26
- 3A1qoPf81FCF8wIgFTxoXXotS8nhDzOeyJh1wL6wKMxcwKXUUMcOKEjFsH4wDQYJ
27
- KoZIhvcNAQELBQADggEBAMY6zXuf7YSu4s/nHd4qSjOq7mAPvWKSumPMXcRp4FfX
28
- 16dAyqaiasX2B7dXccks4QeCUyNxCjh89irTYwgDcde8J05PEM9dpxUglyWE6gEB
29
- r+pp8xJzT9hP2TmLhkNsFKhiT8aR+wz9fiALVDeQLyUS1GOSmnAXbH8MjZkm5Cxb
30
- Taqb+N8alKrMf7Y9Cwf77KRFpKqm+b1e6h/EuVIJWAFau1jKVS5Qd6KTjyf0sRys
31
- 7nvF3JqKVIb6BlTimNUq1wYMJKX6mOb/MlHo8juzKMb+MYUA5ZHrO36u3nOgThiI
32
- oOJavnwzLhk7AWp8urdy9yTj8P3b/3H3VtCfYMeAabE=
33
- -----END CERTIFICATE-----
34
-
35
- -----BEGIN CERTIFICATE-----
36
- MIIEXjCCA0agAwIBAgITB3MSTyqVLj7Rili9uF0bwM5fJzANBgkqhkiG9w0BAQsF
37
- ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
38
- b24gUm9vdCBDQSAxMB4XDTIyMDgyMzIyMjYzNVoXDTMwMDgyMzIyMjYzNVowPDEL
39
- MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEcMBoGA1UEAxMTQW1hem9uIFJT
40
- QSAyMDQ4IE0wNDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM3pVR6A
41
- lQOp4xe776FdePXyejgA38mYx1ou9/jrpV6Sfn+/oqBKgwhY6ePsQHHQayWBJdBn
42
- v4Wz363qRI4XUh9swBFJ11TnZ3LqOMvHmWq2+loA0QPtOfXdJ2fHBLrBrngtJ/GB
43
- 0p5olAVYrSZgvQGP16Rf8ddtNyxEEhYm3HuhmNi+vSeAq1tLYJPAvRCXonTpWdSD
44
- xY6hvdmxlqTYi82AtBXSfpGQ58HHM0hw0C6aQakghrwWi5fGslLOqzpimNMIsT7c
45
- qa0GJx6JfKqJqmQQNplO2h8n9ZsFJgBowof01ppdoLAWg6caMOM0om/VILKaa30F
46
- 9W/r8Qjah7ltGVkCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD
47
- VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNV
48
- HQ4EFgQUH1KSYVaCVH+BZtgdPQqqMlyH3QgwHwYDVR0jBBgwFoAUhBjMhTTsvAyU
49
- lC4IWZzHshBOCggwewYIKwYBBQUHAQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8v
50
- b2NzcC5yb290Y2ExLmFtYXpvbnRydXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDov
51
- L2NydC5yb290Y2ExLmFtYXpvbnRydXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8E
52
- ODA2MDSgMqAwhi5odHRwOi8vY3JsLnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jv
53
- b3RjYTEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IB
54
- AQA+1O5UsAaNuW3lHzJtpNGwBnZd9QEYFtxpiAnIaV4qApnGS9OCw5ZPwie7YSlD
55
- ZF5yyFPsFhUC2Q9uJHY/CRV1b5hIiGH0+6+w5PgKiY1MWuWT8VAaJjFxvuhM7a/e
56
- fN2TIw1Wd6WCl6YRisunjQOrSP+unqC8A540JNyZ1JOE3jVqat3OZBGgMvihdj2w
57
- Y23EpwesrKiQzkHzmvSH67PVW4ycbPy08HVZnBxZ5NrlGG9bwXR3fNTaz+c+Ej6c
58
- 5AnwI3qkOFgSkg3Y75cdFz6pO/olK+e3AqygAcv0WjzmkDPuBjssuZjCHMC56oH3
59
- GJkV29Di2j5prHJbwZjG1inU
60
- -----END CERTIFICATE-----
61
-
62
- -----BEGIN CERTIFICATE-----
63
- MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF
64
- ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj
65
- b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x
66
- OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1
67
- dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL
68
- MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
69
- b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
70
- ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
71
- 9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
72
- IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
73
- VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
74
- 93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
75
- jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
76
- BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW
77
- gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH
78
- MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH
79
- MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy
80
- MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0
81
- LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF
82
- AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW
83
- MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma
84
- eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK
85
- bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN
86
- 0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U
87
- akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA==
88
- -----END CERTIFICATE-----