UNPKG

azure-devops-test-helper

Version:

Import JSON result with attachments into Azure Test Plan

132 lines (131 loc) 6.57 kB
import { AzureTestRunStatus } from "./constants.js"; import path from "node:path"; import url from "node:url"; import fs from "node:fs"; import Ajv from "ajv"; import addFormats from "ajv-formats"; import axios from "axios"; import { createTestRun, setRunStatus } from "./run.js"; import { getUpdatingTestResult, uploadScreenshots } from "./result.js"; import { getTestCasesByPlanID } from "./plan.js"; import { filterExecutedTestCase } from "./case.js"; import { createAzureAPIClients } from "./client.js"; class AzureDevopsResultImporter { azureApiClients; axiosClient; ajv; testReportSchema; configSchema; constructor() { this.ajv = new Ajv(); addFormats(this.ajv); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const dirname = url.fileURLToPath(new URL(".", import.meta.url)) ?? __dirname; this.testReportSchema = JSON.parse(fs.readFileSync(path.join(dirname, "./schema/TestReportSchema.json")).toString()); this.configSchema = JSON.parse(fs.readFileSync(path.join(dirname, "./schema/AzureResultImporterConfigSchema.json")).toString()); } async importReportFilesToTestRun(reportAbsoluteDir, config) { if (!this.ajv.validate(this.configSchema, config)) { throw new Error("Invalid configuration options!!!"); } if (!fs.existsSync(reportAbsoluteDir) || !path.isAbsolute(reportAbsoluteDir)) { throw new Error("Report directory does not exist or is not an absolute path!!!"); } const reportFiles = fs .readdirSync(reportAbsoluteDir) .filter((file) => path.extname(file) === ".json"); const reports = []; reportFiles.forEach((file) => { const fileData = fs.readFileSync(path.join(reportAbsoluteDir, file)); reports.push(JSON.parse(fileData.toString())); }); const results = await this.importTestResultToTestRun(reports, config); console.log("Report uploading process successfully!!"); const resultPath = path.join(reportAbsoluteDir, "uploadedData.json"); fs.writeFile(resultPath, JSON.stringify(results, null, 2), "utf-8", () => { console.log(`Uploaded data can be found at ${resultPath}`); }); return results; } async importTestResultToTestRun(testReports, config) { const validFormatReports = testReports.filter((report) => this.ajv.validate(this.testReportSchema, report)); if (validFormatReports.length > 0) { const configurationMergedReports = await this.mergeReportByConfigId(validFormatReports); const azureApiClients = await this.getAzureAPIClients(config); const azureTestApiClient = azureApiClients.testAPIClient; const axiosClient = await this.getAxiosClient(config); const executedConfigurationIds = configurationMergedReports.map((report) => Number(report.azureConfigurationId)); const executedTestCaseIds = testReports .map((report) => report.testResults) .flat() .filter((result) => result) .map((result) => result.testCase?.id ?? ""); const testCasesOnPlan = await getTestCasesByPlanID(axiosClient, config.planId, config.suiteId); const updatingTestCases = config.override ? testCasesOnPlan : filterExecutedTestCase(testCasesOnPlan, executedTestCaseIds); const testRun = await createTestRun(azureTestApiClient, updatingTestCases, config, executedConfigurationIds); if (!testRun.id) { throw new Error("Failed to create test Run!"); } await setRunStatus(azureTestApiClient, config, testRun.id, AzureTestRunStatus.INPROGRESS); const updatingResult = await getUpdatingTestResult(azureApiClients, configurationMergedReports, config.project, testRun.id, updatingTestCases); let importedTestResults = []; try { importedTestResults = await azureTestApiClient.updateTestResults(updatingResult.testCaseResults, config.project, testRun.id); } catch (error) { await setRunStatus(azureTestApiClient, config, testRun.id, AzureTestRunStatus.ABORTED); throw new Error("Failed to import result into the created Test Run! with the following error: \n" + error); } await setRunStatus(azureTestApiClient, config, testRun.id, AzureTestRunStatus.COMPLETED); let uploadedScreenshot = []; try { uploadedScreenshot = await uploadScreenshots(azureTestApiClient, config.project, updatingResult.screenshots, testRun.id); } catch (error) { throw new Error("Failed to upload screenshot(s) into the created Test Run! with the following error: \n" + error); } return { testCaseResults: importedTestResults, attachments: uploadedScreenshot }; } return { testCaseResults: [], attachments: [] }; } async getAzureAPIClients(config) { if (!this.azureApiClients) { this.azureApiClients = await createAzureAPIClients(config.pat, config.organizationUrl); } return this.azureApiClients; } async getAxiosClient(config) { if (!this.axiosClient) { this.axiosClient = axios.create({ headers: { Authorization: "Basic " + Buffer.from(":" + config.pat).toString("base64"), }, params: { Authorization: "Basic " + config.pat, }, baseURL: `${config.organizationUrl}/${config.project}/_apis`, }); } return this.axiosClient; } async mergeReportByConfigId(reports) { const mergedReports = []; reports.forEach((report) => { if (report.azureConfigurationId) { const mergedReport = mergedReports.find((mergedReport) => mergedReport.azureConfigurationId === report.azureConfigurationId); if (mergedReport) { mergedReport.testResults.push(...report.testResults); mergedReport.screenshots.push(...report.screenshots); } else { mergedReports.push(report); } } }); return mergedReports; } } export default new AzureDevopsResultImporter();