UNPKG

playwright-testrail-sync

Version:

TestRail Integration for Playwright with comprehensive logging and error handling

250 lines 9.65 kB
"use strict"; /** * TestRail HTTP Client * Core HTTP client for TestRail API operations */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestRailApiClient = void 0; const axios_1 = __importDefault(require("axios")); const error_handler_1 = require("./error-handler"); const attachment_handler_1 = require("./attachment-handler"); /** * TestRail HTTP client implementation */ class TestRailApiClient { constructor(config, logger) { this.config = config; this.logger = logger; this.axios = this.createAxiosInstance(); } createAxiosInstance() { const instance = axios_1.default.create({ baseURL: `${this.config.host}/index.php?/api/v2`, timeout: 30000, // 30 seconds headers: { "Content-Type": "application/json", "User-Agent": "PlaywrightTestRailSync/1.0.0", }, auth: { username: this.config.username, password: this.config.password, }, }); // Request interceptor for logging instance.interceptors.request.use(config => { this.logger.logApiRequest(config.method?.toUpperCase() || "UNKNOWN", config.url || "", config.headers, config.data); return config; }, error => { this.logger.error("Request interceptor error", error); return Promise.reject(error); }); // Response interceptor for logging instance.interceptors.response.use(response => { this.logger.logApiResponse(response.status, response.statusText, response.data, response.config.metadata?.duration); // Additional logging for attachment uploads if (response.config.url?.includes("add_attachment_to_result")) { this.logger.verboseApiCall("TestRail attachment upload response", { status: response.status, statusText: response.statusText, url: response.config.url, data: response.data, }); } return response; }, error => { // Don't log internal API errors - they're handled by the caller return Promise.reject((0, error_handler_1.handleApiError)(error)); }); return instance; } async get(url, headers) { const config = { method: "GET", url, headers: { ...headers }, }; const response = await this.axios.request(config); return response.data; } async post(url, data, headers) { const config = { method: "POST", url, data, headers: { ...headers }, }; const response = await this.axios.request(config); return response.data; } async postFormData(url, formData, headers) { const config = { method: "POST", url, data: formData, headers: { ...formData.getHeaders(), ...headers, }, }; const response = await this.axios.request(config); return response.data; } /** * Create a new TestRail run */ async createRun(config) { this.logger.verboseApiCall("TestRail Creating run", { projectId: config.projectId, suiteId: config.suiteId, name: config.testRunName, }); const runData = { name: config.testRunName && config.testRunName.trim() !== "" ? config.testRunName : `Playwright Test Run - ${new Date().toISOString()}`, description: "Automated test run created by TestRail Integration for Playwright", suite_id: config.suiteId, include_all: true, case_ids: config.caseIds, milestone_id: config.milestoneId, assignedto_id: config.assignedToId, refs: config.refs, }; try { const run = await this.post(`/add_run/${config.projectId}`, runData); this.logger.verboseApiCall("TestRail Run created successfully", { runId: run.id, name: run.name, url: `${this.config.host}/index.php?/runs/view/${run.id}`, }); return run; } catch (error) { this.logger.verboseApiCall("Failed to create TestRail run", error); // Provide more specific error messages for common issues const errorMessage = error.message || error.error || error.response?.data?.error || ""; if (errorMessage.includes("suite") || errorMessage.includes("Suite")) { throw new Error(`Invalid TestRail suite ID: ${config.suiteId}. Please check if the suite exists in your TestRail project.`); } else if (errorMessage.includes("project") || errorMessage.includes("Project")) { throw new Error(`Invalid TestRail project ID: ${config.projectId}. Please check if the project exists in your TestRail instance.`); } else if (errorMessage.includes("assigned") || errorMessage.includes("user") || errorMessage.includes("User")) { throw new Error(`Invalid TestRail assigned user ID: ${config.assignedToId}. Please check if the user exists in your TestRail instance.`); } else if (errorMessage.includes("milestone") || errorMessage.includes("Milestone")) { throw new Error(`Invalid TestRail milestone ID: ${config.milestoneId}. Please check if the milestone exists in your TestRail project.`); } throw error; } } /** * Get an existing TestRail run */ async getRun(runId) { this.logger.verboseApiCall("TestRail Getting run", { runId }); try { const run = await this.get(`/get_run/${runId}`); this.logger.verboseApiCall("TestRail Run retrieved successfully", { runId: run.id, name: run.name, status: run.is_completed ? "completed" : "active", }); return run; } catch (error) { this.logger.verboseApiCall("Failed to get TestRail run", error); throw error; } } /** * Add test results for multiple cases */ async addResultsForCases(runId, results) { this.logger.verboseApiCall("TestRail Adding results for cases", { runId, resultCount: results.length, }); try { const response = await this.post(`/add_results_for_cases/${runId}`, { results, }); this.logger.verboseApiCall("TestRail Results added successfully", { runId, resultCount: response.length, }); return response; } catch (error) { this.logger.verboseApiCall("Failed to add results for cases", error); throw error; } } /** * Add attachment to a test result */ async addAttachmentToResult(resultId, filePath, fileName) { return (0, attachment_handler_1.addAttachmentToResult)(resultId, filePath, fileName, this, this.logger); } /** * Add attachment to a test case */ async addAttachmentToCase(caseId, filePath, fileName) { return (0, attachment_handler_1.addAttachmentToCase)(caseId, filePath, fileName, this, this.logger); } /** * Get a test case */ async getCase(caseId) { this.logger.verboseApiCall("TestRail Getting case", { caseId }); try { const testCase = await this.get(`/get_case/${caseId}`); this.logger.verboseApiCall("TestRail Case retrieved successfully", { caseId: testCase.id, title: testCase.title, }); return testCase; } catch (error) { // Don't log internal API errors - they're handled by the caller throw error; } } /** * Validate connection to TestRail */ async validateConnection() { this.logger.verboseApiCall("TestRail Validating connection", { host: this.config.host, username: this.config.username, }); try { // Try to get project info to validate connection await this.get(`/get_project/${this.config.projectId}`); this.logger.verboseApiCall("TestRail Connection validated successfully", {}); return true; } catch (error) { // Extract meaningful error information without circular references const errorMessage = error?.message || error?.toString() || "Unknown error"; const statusCode = error?.response?.status || error?.status; const statusText = error?.response?.statusText || error?.statusText; if (statusCode) { this.logger.error(`Connection validation failed: ${statusCode} ${statusText}`); } else { this.logger.error(`Connection validation failed: ${errorMessage}`); } return false; } } } exports.TestRailApiClient = TestRailApiClient; //# sourceMappingURL=testrail-client.js.map