UNPKG

tdpw

Version:

CLI tool for uploading Playwright test reports to TestDino platform with Azure storage support

159 lines 5.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApiClient = void 0; const types_1 = require("../types"); // Upload endpoint relative to base API URL const UPLOAD_ENDPOINT = '/api/reports/playwright'; /** * Client for communicating with the TestDino API */ class ApiClient { baseUrl; apiKey; constructor(config) { this.baseUrl = config.apiUrl; this.apiKey = config.token; } /** * Headers to include on every API request */ getHeaders() { return { 'Content-Type': 'application/json', 'User-Agent': 'tdpw/1.0.0', 'X-API-Key': this.apiKey, }; } /** * Upload a JSON payload to the TestDino API with enhanced error handling * @param payload The data to upload (report + metadata) */ async uploadReport(payload) { const url = `${this.baseUrl}${UPLOAD_ENDPOINT}`; let response; try { response = await fetch(url, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(payload), }); } catch (error) { // Handle network-level errors (DNS, connection refused, etc.) const errorMessage = error instanceof Error ? error.message : 'Unknown network error'; throw new types_1.NetworkError(`Failed to connect to TestDino API: ${errorMessage}`); } // Handle HTTP error responses if (!response.ok) { await this.handleHttpError(response); } // Parse JSON response let json; try { json = await response.json(); } catch (error) { throw new types_1.NetworkError(`Invalid JSON response from API: ${error instanceof Error ? error.message : 'Parse error'}`); } // Extract response data const result = this.parseUploadResponse(json); return result; } /** * Handle HTTP error responses with detailed error messages */ async handleHttpError(response) { let errorBody; try { errorBody = await response.text(); } catch { errorBody = 'Unable to read error response'; } switch (response.status) { case 401: throw new types_1.AuthenticationError('Invalid API key or unauthorized access'); case 403: throw new types_1.AuthenticationError('API key does not have permission to upload reports'); case 400: throw new types_1.NetworkError(`Bad request - Invalid data format: ${errorBody}`); case 413: throw new types_1.NetworkError('Report payload too large - consider uploading without HTML/traces'); case 429: throw new types_1.NetworkError('Rate limit exceeded - please wait before retrying'); case 500: case 502: case 503: case 504: throw new types_1.NetworkError(`TestDino API server error (${response.status}) - please try again later`); default: throw new types_1.NetworkError(`HTTP ${response.status}: ${errorBody || response.statusText}`); } } /** * Parse and validate the upload response */ parseUploadResponse(json) { if (!json || typeof json !== 'object') { throw new types_1.NetworkError('Invalid response format - expected JSON object'); } // Handle different response structures let responseData; // Check for direct response if ('testRunId' in json) { responseData = json; } // Check for wrapped response else if ('data' in json && json.data && typeof json.data === 'object' && 'testRunId' in json.data) { responseData = json.data; } // Check for success wrapper else if ('success' in json && 'result' in json && json.result && typeof json.result === 'object' && 'testRunId' in json.result) { responseData = json.result; } else { throw new types_1.NetworkError(`Unexpected API response structure: ${JSON.stringify(json)}`); } // Validate required fields if (!responseData.testRunId || typeof responseData.testRunId !== 'string') { throw new types_1.NetworkError('API response missing required testRunId field'); } // Build response object const result = { testRunId: responseData.testRunId, }; // Add optional fields if present if (responseData.viewUrl && typeof responseData.viewUrl === 'string') { result.viewUrl = responseData.viewUrl; } if (responseData.status && typeof responseData.status === 'string') { result.status = responseData.status; } // Include any additional fields Object.keys(responseData).forEach(key => { if (!['testRunId', 'viewUrl', 'status'].includes(key)) { result[key] = responseData[key]; } }); return result; } /** * Health check endpoint to verify API connectivity */ async healthCheck() { try { const response = await fetch(`${this.baseUrl}/api/health`, { method: 'GET', headers: { 'User-Agent': 'tdpw/1.0.0', }, }); return response.ok; } catch { return false; } } } exports.ApiClient = ApiClient; //# sourceMappingURL=api.js.map