playwright-testrail-sync
Version:
TestRail Integration for Playwright with comprehensive logging and error handling
250 lines • 9.65 kB
JavaScript
"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