Skip to content

Commit

Permalink
upload attachments (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
ASaiAnudeep authored Jun 1, 2024
1 parent bcf78a9 commit baeb72f
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 39 deletions.
63 changes: 30 additions & 33 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "testbeats",
"version": "2.0.2",
"version": "2.0.3",
"description": "Publish test results to Microsoft Teams, Google Chat, Slack and InfluxDB",
"main": "src/index.js",
"types": "./src/index.d.ts",
Expand Down Expand Up @@ -48,6 +48,7 @@
"dependencies": {
"async-retry": "^1.3.3",
"dotenv": "^14.3.2",
"form-data-lite": "^1.0.3",
"influxdb-lite": "^1.0.0",
"performance-results-parser": "latest",
"phin-retry": "^1.0.3",
Expand Down
11 changes: 11 additions & 0 deletions src/beats/beats.api.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ class BeatsApi {
});
}

uploadAttachments(headers, payload) {
return request.post({
url: `${this.getBaseUrl()}/api/core/v1/test-cases/attachments`,
headers: {
'x-api-key': this.config.api_key,
...headers
},
body: payload
});
}

getBaseUrl() {
return process.env.TEST_BEATS_URL || "https://app.testbeats.com";
}
Expand Down
100 changes: 100 additions & 0 deletions src/beats/beats.attachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const fs = require('fs');
const path = require('path');
const FormData = require('form-data-lite');
const TestResult = require('test-results-parser/src/models/TestResult');
const { BeatsApi } = require('./beats.api');
const logger = require('../utils/logger');

const MAX_ATTACHMENTS_PER_REQUEST = 5;
const MAX_ATTACHMENTS_PER_RUN = 20;
const MAX_ATTACHMENT_SIZE = 1024 * 1024;

class BeatsAttachments {

/**
* @param {import('../index').PublishReport} config
* @param {TestResult} result
*/
constructor(config, result, test_run_id) {
this.config = config;
this.result = result;
this.api = new BeatsApi(config);
this.test_run_id = test_run_id;
this.failed_test_cases = [];
this.attachments = [];
}

async upload() {
this.#setAllFailedTestCases();
this.#setAttachments();
await this.#uploadAttachments();
}

#setAllFailedTestCases() {
for (const suite of this.result.suites) {
for (const test of suite.cases) {
if (test.status === 'FAIL') {
this.failed_test_cases.push(test);
}
}
}
}

#setAttachments() {
for (const test_case of this.failed_test_cases) {
for (const attachment of test_case.attachments) {
this.attachments.push(attachment);
}
}
}

async #uploadAttachments() {
if (this.attachments.length === 0) {
return;
}
logger.info(`⏳ Uploading ${this.attachments.length} attachments...`);
const result_file = this.config.results[0].files[0];
const result_file_dir = path.dirname(result_file);
try {
let count = 0;
const size = MAX_ATTACHMENTS_PER_REQUEST;
for (let i = 0; i < this.attachments.length; i += size) {
if (count >= MAX_ATTACHMENTS_PER_RUN) {
logger.warn('⚠️ Maximum number of attachments per run reached. Skipping remaining attachments.');
break;
}
const attachments_subset = this.attachments.slice(i, i + size);
const form = new FormData();
const file_images = []
for (const attachment of attachments_subset) {
const attachment_path = path.join(result_file_dir, attachment.path);
const stats = fs.statSync(attachment_path);
if (stats.size > MAX_ATTACHMENT_SIZE) {
logger.warn(`⚠️ Attachment ${attachment.path} is too big (${stats.size} bytes). Allowed size is ${MAX_ATTACHMENT_SIZE} bytes.`);
continue;
}
form.append('images', fs.readFileSync(attachment_path));
form.append('test_run_id', this.test_run_id);
file_images.push({
file_name: attachment.name,
file_path: attachment.path,
});
count += 1;
}
if (file_images.length === 0) {
return;
}
form.append('file_images', JSON.stringify(file_images));
await this.api.uploadAttachments(form.getHeaders(), form.getBuffer());
logger.info(`🏞️ Uploaded ${count} attachments`);
}
} catch (error) {
logger.error(`❌ Unable to upload attachments: ${error.message}`, error);
}
}



}

module.exports = { BeatsAttachments }
30 changes: 30 additions & 0 deletions src/beats/beats.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { getCIInformation } = require('../helpers/ci');
const logger = require('../utils/logger');
const { BeatsApi } = require('./beats.api');
const { HOOK } = require('../helpers/constants');
const TestResult = require('test-results-parser/src/models/TestResult');
const { BeatsAttachments } = require('./beats.attachments');

class Beats {

Expand All @@ -22,6 +24,7 @@ class Beats {
this.#setRunName();
this.#setApiKey();
await this.#publishTestResults();
await this.#uploadAttachments();
this.#updateTitleLink();
await this.#attachFailureSummary();
}
Expand Down Expand Up @@ -69,6 +72,33 @@ class Beats {
return payload;
}

async #uploadAttachments() {
if (!this.test_run_id) {
return;
}
if (this.result.status !== 'FAIL') {
return;
}
try {
const attachments = new BeatsAttachments(this.config, this.result, this.test_run_id);
await attachments.upload();
} catch (error) {
logger.error(`❌ Unable to upload attachments: ${error.message}`, error);
}
}

#getAllFailedTestCases() {
const test_cases = [];
for (const suite of this.result.suites) {
for (const test of suite.cases) {
if (test.status === 'FAIL') {
test_cases.push(test);
}
}
}
return test_cases;
}

#updateTitleLink() {
if (!this.test_run_id) {
return;
Expand Down
Loading

0 comments on commit baeb72f

Please sign in to comment.