@testomatio/reporter 2.0.0-beta-esm → 2.0.1-beta-esm
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/adapter/codecept.d.ts +2 -0
- package/lib/adapter/codecept.js +31 -24
- package/lib/adapter/cucumber/current.d.ts +14 -0
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.js +11 -9
- package/lib/adapter/jasmine.d.ts +11 -0
- package/lib/adapter/jest.d.ts +13 -0
- package/lib/adapter/mocha.d.ts +2 -0
- package/lib/adapter/mocha.js +4 -3
- package/lib/adapter/playwright.d.ts +14 -0
- package/lib/adapter/playwright.js +58 -33
- package/lib/adapter/vitest.d.ts +35 -0
- package/lib/adapter/vitest.js +6 -6
- package/lib/adapter/webdriver.d.ts +24 -0
- package/lib/adapter/webdriver.js +34 -6
- package/lib/bin/cli.d.ts +2 -0
- package/lib/bin/cli.js +228 -0
- package/lib/bin/reportXml.d.ts +2 -0
- package/lib/bin/reportXml.js +11 -9
- package/lib/bin/startTest.d.ts +2 -0
- package/lib/bin/startTest.js +9 -5
- package/lib/bin/uploadArtifacts.d.ts +2 -0
- package/lib/bin/uploadArtifacts.js +81 -0
- package/lib/client.d.ts +76 -0
- package/lib/client.js +111 -45
- package/lib/config.d.ts +1 -0
- package/lib/constants.d.ts +25 -0
- package/lib/constants.js +5 -1
- package/lib/data-storage.d.ts +34 -0
- package/lib/data-storage.js +2 -2
- package/lib/junit-adapter/adapter.d.ts +9 -0
- package/lib/junit-adapter/csharp.d.ts +4 -0
- package/lib/junit-adapter/index.d.ts +3 -0
- package/lib/junit-adapter/java.d.ts +5 -0
- package/lib/junit-adapter/javascript.d.ts +4 -0
- package/lib/junit-adapter/python.d.ts +5 -0
- package/lib/junit-adapter/ruby.d.ts +4 -0
- package/lib/output.d.ts +11 -0
- package/lib/package.json +3 -1
- package/lib/pipe/bitbucket.d.ts +23 -0
- package/lib/pipe/bitbucket.js +2 -2
- package/lib/pipe/csv.d.ts +47 -0
- package/lib/pipe/csv.js +2 -2
- package/lib/pipe/debug.d.ts +29 -0
- package/lib/pipe/debug.js +108 -0
- package/lib/pipe/github.d.ts +30 -0
- package/lib/pipe/github.js +2 -2
- package/lib/pipe/gitlab.d.ts +23 -0
- package/lib/pipe/gitlab.js +2 -2
- package/lib/pipe/html.d.ts +34 -0
- package/lib/pipe/html.js +8 -1
- package/lib/pipe/index.d.ts +1 -0
- package/lib/pipe/index.js +3 -3
- package/lib/pipe/testomatio.d.ts +70 -0
- package/lib/pipe/testomatio.js +50 -30
- package/lib/reporter-functions.d.ts +34 -0
- package/lib/reporter-functions.js +17 -7
- package/lib/reporter.d.ts +232 -0
- package/lib/reporter.js +19 -33
- package/lib/services/artifacts.d.ts +33 -0
- package/lib/services/index.d.ts +9 -0
- package/lib/services/key-values.d.ts +27 -0
- package/lib/services/key-values.js +1 -1
- package/lib/services/logger.d.ts +64 -0
- package/lib/template/testomatio.hbs +651 -1366
- package/lib/uploader.d.ts +60 -0
- package/lib/uploader.js +312 -0
- package/lib/utils/pipe_utils.d.ts +41 -0
- package/lib/utils/pipe_utils.js +3 -5
- package/lib/utils/utils.d.ts +45 -0
- package/lib/utils/utils.js +69 -2
- package/lib/xmlReader.d.ts +92 -0
- package/lib/xmlReader.js +22 -12
- package/package.json +15 -9
- package/src/adapter/codecept.js +30 -24
- package/src/adapter/cypress-plugin/index.js +5 -3
- package/src/adapter/mocha.cjs +1 -1
- package/src/adapter/mocha.js +4 -3
- package/src/adapter/playwright.js +59 -31
- package/src/adapter/vitest.js +6 -6
- package/src/adapter/webdriver.js +41 -10
- package/src/bin/cli.js +280 -0
- package/src/bin/reportXml.js +15 -8
- package/src/bin/startTest.js +7 -3
- package/src/bin/uploadArtifacts.js +90 -0
- package/src/client.js +137 -56
- package/src/constants.js +5 -1
- package/src/data-storage.js +2 -2
- package/src/pipe/bitbucket.js +2 -2
- package/src/pipe/csv.js +3 -3
- package/src/pipe/debug.js +104 -0
- package/src/pipe/github.js +2 -3
- package/src/pipe/gitlab.js +6 -6
- package/src/pipe/html.js +11 -3
- package/src/pipe/index.js +5 -7
- package/src/pipe/testomatio.js +72 -67
- package/src/reporter-functions.js +18 -7
- package/src/reporter.cjs_decprecated +21 -0
- package/src/reporter.js +20 -11
- package/src/services/key-values.js +1 -1
- package/src/services/logger.js +4 -2
- package/src/template/testomatio.hbs +651 -1366
- package/src/uploader.js +371 -0
- package/src/utils/pipe_utils.js +4 -12
- package/src/utils/utils.js +48 -6
- package/src/xmlReader.js +26 -15
- package/lib/adapter/jasmine/jasmine.js +0 -63
- package/lib/adapter/mocha/mocha.js +0 -125
- package/lib/fileUploader.js +0 -245
- package/lib/utils/chalk.js +0 -10
- package/src/fileUploader.js +0 -307
- package/src/reporter.cjs +0 -22
- package/src/utils/chalk.js +0 -13
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
7
|
-
const mocha_1 = __importDefault(require("mocha"));
|
|
8
|
-
const client_js_1 = __importDefault(require("../../client.js"));
|
|
9
|
-
const constants_js_1 = require("../../constants.js");
|
|
10
|
-
const utils_js_1 = require("../../utils/utils.js");
|
|
11
|
-
const config_js_1 = require("../../config.js");
|
|
12
|
-
const index_js_1 = require("../../services/index.js");
|
|
13
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
14
|
-
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_SUITE_BEGIN, EVENT_SUITE_END, EVENT_TEST_BEGIN, EVENT_TEST_END, } = mocha_1.default.Runner.constants;
|
|
15
|
-
function MochaReporter(runner, opts) {
|
|
16
|
-
mocha_1.default.reporters.Base.call(this, runner);
|
|
17
|
-
let passes = 0;
|
|
18
|
-
let failures = 0;
|
|
19
|
-
let skipped = 0;
|
|
20
|
-
// let artifactStore;
|
|
21
|
-
const apiKey = opts?.reporterOptions?.apiKey || config_js_1.config.TESTOMATIO;
|
|
22
|
-
const client = new client_js_1.default({ apiKey });
|
|
23
|
-
runner.on(EVENT_RUN_BEGIN, () => {
|
|
24
|
-
client.createRun();
|
|
25
|
-
utils_js_1.fileSystem.clearDir(constants_js_1.TESTOMAT_TMP_STORAGE_DIR);
|
|
26
|
-
});
|
|
27
|
-
runner.on(EVENT_SUITE_BEGIN, async (suite) => {
|
|
28
|
-
index_js_1.services.setContext(suite.fullTitle());
|
|
29
|
-
});
|
|
30
|
-
runner.on(EVENT_SUITE_END, async () => {
|
|
31
|
-
index_js_1.services.setContext(null);
|
|
32
|
-
});
|
|
33
|
-
runner.on(EVENT_TEST_BEGIN, async (test) => {
|
|
34
|
-
index_js_1.services.setContext(test.fullTitle());
|
|
35
|
-
});
|
|
36
|
-
runner.on(EVENT_TEST_END, async () => {
|
|
37
|
-
index_js_1.services.setContext(null);
|
|
38
|
-
});
|
|
39
|
-
runner.on(EVENT_TEST_PASS, async (test) => {
|
|
40
|
-
passes += 1;
|
|
41
|
-
console.log(picocolors_1.default.bold(picocolors_1.default.green('✔')), test.fullTitle());
|
|
42
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
43
|
-
const logs = getTestLogs(test);
|
|
44
|
-
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
45
|
-
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
46
|
-
client.addTestRun(constants_js_1.STATUS.PASSED, {
|
|
47
|
-
test_id: testId,
|
|
48
|
-
suite_title: getSuiteTitle(test),
|
|
49
|
-
title: getTestName(test),
|
|
50
|
-
code: test.body.toString(),
|
|
51
|
-
file: getFile(test),
|
|
52
|
-
time: test.duration,
|
|
53
|
-
logs,
|
|
54
|
-
manuallyAttachedArtifacts: artifacts,
|
|
55
|
-
meta: keyValues,
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
runner.on(EVENT_TEST_PENDING, test => {
|
|
59
|
-
skipped += 1;
|
|
60
|
-
console.log('skip: %s', test.fullTitle());
|
|
61
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
62
|
-
client.addTestRun(constants_js_1.STATUS.SKIPPED, {
|
|
63
|
-
title: getTestName(test),
|
|
64
|
-
suite_title: getSuiteTitle(test),
|
|
65
|
-
code: test.body.toString(),
|
|
66
|
-
file: getFile(test),
|
|
67
|
-
test_id: testId,
|
|
68
|
-
time: test.duration,
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
runner.on(EVENT_TEST_FAIL, async (test, err) => {
|
|
72
|
-
failures += 1;
|
|
73
|
-
console.log(picocolors_1.default.bold(picocolors_1.default.red('✖')), test.fullTitle(), picocolors_1.default.gray(err.message));
|
|
74
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
75
|
-
const logs = getTestLogs(test);
|
|
76
|
-
client.addTestRun(constants_js_1.STATUS.FAILED, {
|
|
77
|
-
error: err,
|
|
78
|
-
suite_title: getSuiteTitle(test),
|
|
79
|
-
file: getFile(test),
|
|
80
|
-
test_id: testId,
|
|
81
|
-
title: getTestName(test),
|
|
82
|
-
code: test.body.toString(),
|
|
83
|
-
time: test.duration,
|
|
84
|
-
logs,
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
runner.on(EVENT_RUN_END, () => {
|
|
88
|
-
const status = failures === 0 ? constants_js_1.STATUS.PASSED : constants_js_1.STATUS.FAILED;
|
|
89
|
-
console.log(picocolors_1.default.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
client.updateRunStatus(status);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
function getTestLogs(test) {
|
|
95
|
-
const suiteLogsArr = index_js_1.services.logger.getLogs(test.parent.fullTitle());
|
|
96
|
-
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
97
|
-
const testLogsArr = index_js_1.services.logger.getLogs(test.fullTitle());
|
|
98
|
-
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
99
|
-
let logs = '';
|
|
100
|
-
if (suiteLogs) {
|
|
101
|
-
logs += `${picocolors_1.default.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
|
|
102
|
-
}
|
|
103
|
-
if (testLogs) {
|
|
104
|
-
logs += `\n${picocolors_1.default.bold('\t--- Test ---')}\n${testLogs}`;
|
|
105
|
-
}
|
|
106
|
-
return logs;
|
|
107
|
-
}
|
|
108
|
-
function getSuiteTitle(test, pathArr = []) {
|
|
109
|
-
if (test.parent.parent)
|
|
110
|
-
getSuiteTitle(test.parent, pathArr);
|
|
111
|
-
pathArr.push(test.parent.title);
|
|
112
|
-
return pathArr.filter(t => !!t)[0];
|
|
113
|
-
}
|
|
114
|
-
function getFile(test) {
|
|
115
|
-
return test.parent.file?.replace(process.cwd(), '');
|
|
116
|
-
}
|
|
117
|
-
function getTestName(test) {
|
|
118
|
-
if (process.env.TESTOMATIO_CREATE === 'fulltitle')
|
|
119
|
-
return test.fullTitle();
|
|
120
|
-
return test.title;
|
|
121
|
-
}
|
|
122
|
-
// To have this reporter "extend" a built-in reporter uncomment the following line:
|
|
123
|
-
// @ts-ignore
|
|
124
|
-
mocha_1.default.utils.inherits(MochaReporter, mocha_1.default.reporters.Spec);
|
|
125
|
-
module.exports = MochaReporter;
|
package/lib/fileUploader.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.upload = void 0;
|
|
7
|
-
const debug_1 = __importDefault(require("debug"));
|
|
8
|
-
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
9
|
-
const lib_storage_1 = require("@aws-sdk/lib-storage");
|
|
10
|
-
const fs_1 = __importDefault(require("fs"));
|
|
11
|
-
const util_1 = __importDefault(require("util"));
|
|
12
|
-
const path_1 = __importDefault(require("path"));
|
|
13
|
-
const promise_retry_1 = __importDefault(require("promise-retry"));
|
|
14
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
15
|
-
const crypto_1 = require("crypto");
|
|
16
|
-
const constants_js_1 = require("./constants.js");
|
|
17
|
-
const readFile = util_1.default.promisify(fs_1.default.readFile);
|
|
18
|
-
const stat = util_1.default.promisify(fs_1.default.stat);
|
|
19
|
-
const debug = (0, debug_1.default)('@testomatio/reporter:file-uploader');
|
|
20
|
-
const keys = [
|
|
21
|
-
'S3_ENDPOINT',
|
|
22
|
-
'S3_REGION',
|
|
23
|
-
'S3_BUCKET',
|
|
24
|
-
'S3_ACCESS_KEY_ID',
|
|
25
|
-
'S3_SECRET_ACCESS_KEY',
|
|
26
|
-
'S3_SESSION_TOKEN',
|
|
27
|
-
'TESTOMATIO_DISABLE_ARTIFACTS',
|
|
28
|
-
'TESTOMATIO_PRIVATE_ARTIFACTS',
|
|
29
|
-
'S3_FORCE_PATH_STYLE',
|
|
30
|
-
];
|
|
31
|
-
let config;
|
|
32
|
-
function resetConfig() {
|
|
33
|
-
config = undefined;
|
|
34
|
-
isEnabled = undefined;
|
|
35
|
-
}
|
|
36
|
-
function getConfig() {
|
|
37
|
-
if (config)
|
|
38
|
-
return config;
|
|
39
|
-
config = keys.reduce((acc, key) => {
|
|
40
|
-
acc[key] = process.env[key];
|
|
41
|
-
return acc;
|
|
42
|
-
}, {});
|
|
43
|
-
return config;
|
|
44
|
-
}
|
|
45
|
-
function getMaskedConfig() {
|
|
46
|
-
return Object.fromEntries(Object.entries(getConfig()).map(([key, value]) => [
|
|
47
|
-
key,
|
|
48
|
-
key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value,
|
|
49
|
-
]));
|
|
50
|
-
}
|
|
51
|
-
let isEnabled;
|
|
52
|
-
const isArtifactsEnabled = () => {
|
|
53
|
-
if (isEnabled !== undefined)
|
|
54
|
-
return isEnabled;
|
|
55
|
-
const { S3_BUCKET, TESTOMATIO_DISABLE_ARTIFACTS } = getConfig();
|
|
56
|
-
isEnabled = !!(S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS);
|
|
57
|
-
debug(`Upload is ${isEnabled ? 'enabled' : 'disabled'}`);
|
|
58
|
-
return isEnabled;
|
|
59
|
-
};
|
|
60
|
-
const _getFileExtBase64 = str => {
|
|
61
|
-
const type = str.charAt(0);
|
|
62
|
-
return ({
|
|
63
|
-
'/': '.jpg',
|
|
64
|
-
i: '.png',
|
|
65
|
-
R: '.gif',
|
|
66
|
-
U: '.webp',
|
|
67
|
-
}[type] || '');
|
|
68
|
-
};
|
|
69
|
-
const _getS3Config = () => {
|
|
70
|
-
const { S3_REGION, S3_SESSION_TOKEN, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } = getConfig();
|
|
71
|
-
const cfg = {
|
|
72
|
-
region: S3_REGION,
|
|
73
|
-
credentials: {
|
|
74
|
-
accessKeyId: S3_ACCESS_KEY_ID,
|
|
75
|
-
secretAccessKey: S3_SECRET_ACCESS_KEY,
|
|
76
|
-
s3ForcePathStyle: S3_FORCE_PATH_STYLE,
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
if (S3_SESSION_TOKEN) {
|
|
80
|
-
cfg.credentials.sessionToken = S3_SESSION_TOKEN;
|
|
81
|
-
}
|
|
82
|
-
if (S3_ENDPOINT) {
|
|
83
|
-
cfg.endpoint = S3_ENDPOINT;
|
|
84
|
-
}
|
|
85
|
-
return cfg;
|
|
86
|
-
};
|
|
87
|
-
const uploadUsingS3 = async (filePath, runId) => {
|
|
88
|
-
let ContentType;
|
|
89
|
-
let Key;
|
|
90
|
-
if (typeof filePath === 'object') {
|
|
91
|
-
ContentType = filePath?.type;
|
|
92
|
-
filePath = filePath?.path;
|
|
93
|
-
Key = filePath?.name;
|
|
94
|
-
}
|
|
95
|
-
const { TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET } = getConfig();
|
|
96
|
-
try {
|
|
97
|
-
debug('S3 config', getMaskedConfig());
|
|
98
|
-
debug('Started upload', filePath, 'to ', S3_BUCKET);
|
|
99
|
-
// Verification that the file was actually created: 20 attempts of 0.5 second => 10sec
|
|
100
|
-
const isFileExist = await checkFileExists(filePath, 20, 500);
|
|
101
|
-
if (!isFileExist) {
|
|
102
|
-
console.error(picocolors_1.default.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
debug('File: ', filePath, ' exists');
|
|
106
|
-
const fileData = await readFile(filePath);
|
|
107
|
-
Key = `${runId}/${(0, crypto_1.randomUUID)()}-${Key || path_1.default.basename(filePath)}`;
|
|
108
|
-
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
109
|
-
if (!S3_BUCKET || !fileData) {
|
|
110
|
-
console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold(picocolors_1.default.red(`Failed uploading '${Key}'. Please check S3 credentials`)), getMaskedConfig());
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
const s3 = new client_s3_1.S3(_getS3Config());
|
|
114
|
-
/**
|
|
115
|
-
* @type {import('@aws-sdk/client-s3').PutObjectCommandInput}
|
|
116
|
-
*/
|
|
117
|
-
const params = {
|
|
118
|
-
Bucket: S3_BUCKET,
|
|
119
|
-
Key,
|
|
120
|
-
Body: fileData,
|
|
121
|
-
ContentType,
|
|
122
|
-
ACL,
|
|
123
|
-
};
|
|
124
|
-
const out = new lib_storage_1.Upload({
|
|
125
|
-
client: s3,
|
|
126
|
-
params,
|
|
127
|
-
});
|
|
128
|
-
const link = await getS3LocationLink(out);
|
|
129
|
-
debug(`Succesfully uploaded ${filePath} => ${S3_BUCKET}/${Key} | URL: ${link}`);
|
|
130
|
-
return link;
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
debug('S3 file uploading error: ', e);
|
|
134
|
-
console.log(constants_js_1.APP_PREFIX, `To ${picocolors_1.default.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
135
|
-
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
136
|
-
console.log(constants_js_1.APP_PREFIX, `To enable ${picocolors_1.default.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.log(constants_js_1.APP_PREFIX, `To enable ${picocolors_1.default.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`);
|
|
140
|
-
}
|
|
141
|
-
console.log(constants_js_1.APP_PREFIX, '---------------');
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
|
|
145
|
-
const { S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET } = getConfig();
|
|
146
|
-
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
147
|
-
const fileExtension = _getFileExtBase64(buffer.toString('base64'));
|
|
148
|
-
const Key = `${runId}/${fileName}${fileExtension}`;
|
|
149
|
-
if (!S3_BUCKET || !buffer) {
|
|
150
|
-
console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold(picocolors_1.default.red(`Failed uploading '${Key}'. Please check S3 credentials`)), {
|
|
151
|
-
accessKeyId: S3_ACCESS_KEY_ID,
|
|
152
|
-
secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
|
|
153
|
-
region: S3_REGION,
|
|
154
|
-
bucket: S3_BUCKET,
|
|
155
|
-
acl: ACL,
|
|
156
|
-
endpoint: S3_ENDPOINT,
|
|
157
|
-
});
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
const s3 = new client_s3_1.S3(_getS3Config());
|
|
161
|
-
try {
|
|
162
|
-
const out = new lib_storage_1.Upload({
|
|
163
|
-
client: s3,
|
|
164
|
-
params: {
|
|
165
|
-
Bucket: S3_BUCKET,
|
|
166
|
-
Key,
|
|
167
|
-
Body: buffer,
|
|
168
|
-
ACL,
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
return await getS3LocationLink(out);
|
|
172
|
-
}
|
|
173
|
-
catch (e) {
|
|
174
|
-
debug('S3 buffer uploading error: ', e);
|
|
175
|
-
console.log(constants_js_1.APP_PREFIX, `To ${picocolors_1.default.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
176
|
-
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
177
|
-
console.log(constants_js_1.APP_PREFIX, `To enable ${picocolors_1.default.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
console.log(constants_js_1.APP_PREFIX, `To enable ${picocolors_1.default.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`);
|
|
181
|
-
}
|
|
182
|
-
console.log(constants_js_1.APP_PREFIX, '---------------');
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const uploadFileByPath = async (filePath, runId) => {
|
|
186
|
-
try {
|
|
187
|
-
if (isArtifactsEnabled()) {
|
|
188
|
-
return uploadUsingS3(filePath, runId);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
catch (e) {
|
|
192
|
-
debug(e);
|
|
193
|
-
console.error(picocolors_1.default.red('Error occurred while uploading artifacts! '), e);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const uploadFileAsBuffer = async (buffer, fileName, runId) => {
|
|
197
|
-
try {
|
|
198
|
-
if (isArtifactsEnabled()) {
|
|
199
|
-
return uploadUsingS3AsBuffer(buffer, fileName, runId);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
catch (e) {
|
|
203
|
-
debug(e);
|
|
204
|
-
console.error(picocolors_1.default.red('Error occurred while uploading artifacts! '), e);
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
const checkFileExists = async (filePath, attempts = 5, intervalMs = 500) => {
|
|
208
|
-
const checkFile = async () => {
|
|
209
|
-
const fileStats = await stat(filePath);
|
|
210
|
-
if (fileStats.isFile()) {
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
throw new Error('File not found');
|
|
214
|
-
};
|
|
215
|
-
try {
|
|
216
|
-
await (0, promise_retry_1.default)({
|
|
217
|
-
retries: attempts,
|
|
218
|
-
minTimeout: intervalMs,
|
|
219
|
-
}, checkFile);
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
console.error(picocolors_1.default.yellow(`File ${filePath} was not found or did not have time to be generated...`));
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
const getS3LocationLink = async (out) => {
|
|
228
|
-
const response = await out.done();
|
|
229
|
-
let s3Location = response?.Location;
|
|
230
|
-
if (!s3Location) {
|
|
231
|
-
// TODO: out: a fallback case - remove after deeper testing
|
|
232
|
-
s3Location = out?.singleUploadResult?.Location;
|
|
233
|
-
debug('Uploaded singleUploadResult.Location', s3Location);
|
|
234
|
-
if (!s3Location) {
|
|
235
|
-
throw new Error("Problems getting the S3 artifact's link. Please check S3 permissions!");
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return s3Location;
|
|
239
|
-
};
|
|
240
|
-
exports.upload = {
|
|
241
|
-
uploadFileByPath,
|
|
242
|
-
uploadFileAsBuffer,
|
|
243
|
-
isArtifactsEnabled,
|
|
244
|
-
resetConfig,
|
|
245
|
-
};
|
package/lib/utils/chalk.js
DELETED
package/src/fileUploader.js
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import createDebugMessages from 'debug';
|
|
2
|
-
import { S3 } from '@aws-sdk/client-s3';
|
|
3
|
-
import { Upload } from '@aws-sdk/lib-storage';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import util from 'util';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import promiseRetry from 'promise-retry';
|
|
8
|
-
import pc from 'picocolors';
|
|
9
|
-
import { randomUUID } from 'crypto';
|
|
10
|
-
import { APP_PREFIX } from './constants.js';
|
|
11
|
-
|
|
12
|
-
const readFile = util.promisify(fs.readFile);
|
|
13
|
-
const stat = util.promisify(fs.stat);
|
|
14
|
-
const debug = createDebugMessages('@testomatio/reporter:file-uploader');
|
|
15
|
-
const keys = [
|
|
16
|
-
'S3_ENDPOINT',
|
|
17
|
-
'S3_REGION',
|
|
18
|
-
'S3_BUCKET',
|
|
19
|
-
'S3_ACCESS_KEY_ID',
|
|
20
|
-
'S3_SECRET_ACCESS_KEY',
|
|
21
|
-
'S3_SESSION_TOKEN',
|
|
22
|
-
'TESTOMATIO_DISABLE_ARTIFACTS',
|
|
23
|
-
'TESTOMATIO_PRIVATE_ARTIFACTS',
|
|
24
|
-
'S3_FORCE_PATH_STYLE',
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
let config;
|
|
28
|
-
|
|
29
|
-
function resetConfig() {
|
|
30
|
-
config = undefined;
|
|
31
|
-
isEnabled = undefined;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getConfig() {
|
|
35
|
-
if (config) return config;
|
|
36
|
-
config = keys.reduce((acc, key) => {
|
|
37
|
-
acc[key] = process.env[key];
|
|
38
|
-
return acc;
|
|
39
|
-
}, {});
|
|
40
|
-
return config;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getMaskedConfig() {
|
|
44
|
-
return Object.fromEntries(
|
|
45
|
-
Object.entries(getConfig()).map(([key, value]) => [
|
|
46
|
-
key,
|
|
47
|
-
key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value,
|
|
48
|
-
]),
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let isEnabled;
|
|
53
|
-
|
|
54
|
-
const isArtifactsEnabled = () => {
|
|
55
|
-
if (isEnabled !== undefined) return isEnabled;
|
|
56
|
-
const { S3_BUCKET, TESTOMATIO_DISABLE_ARTIFACTS } = getConfig();
|
|
57
|
-
isEnabled = !!(S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS);
|
|
58
|
-
debug(`Upload is ${isEnabled ? 'enabled' : 'disabled'}`);
|
|
59
|
-
return isEnabled;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const _getFileExtBase64 = str => {
|
|
63
|
-
const type = str.charAt(0);
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
{
|
|
67
|
-
'/': '.jpg',
|
|
68
|
-
i: '.png',
|
|
69
|
-
R: '.gif',
|
|
70
|
-
U: '.webp',
|
|
71
|
-
}[type] || ''
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const _getS3Config = () => {
|
|
76
|
-
const { S3_REGION, S3_SESSION_TOKEN, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } =
|
|
77
|
-
getConfig();
|
|
78
|
-
|
|
79
|
-
const cfg = {
|
|
80
|
-
region: S3_REGION,
|
|
81
|
-
credentials: {
|
|
82
|
-
accessKeyId: S3_ACCESS_KEY_ID,
|
|
83
|
-
secretAccessKey: S3_SECRET_ACCESS_KEY,
|
|
84
|
-
s3ForcePathStyle: S3_FORCE_PATH_STYLE,
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
if (S3_SESSION_TOKEN) {
|
|
89
|
-
cfg.credentials.sessionToken = S3_SESSION_TOKEN;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (S3_ENDPOINT) {
|
|
93
|
-
cfg.endpoint = S3_ENDPOINT;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return cfg;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const uploadUsingS3 = async (filePath, runId) => {
|
|
100
|
-
let ContentType;
|
|
101
|
-
let Key;
|
|
102
|
-
|
|
103
|
-
if (typeof filePath === 'object') {
|
|
104
|
-
ContentType = filePath?.type;
|
|
105
|
-
filePath = filePath?.path;
|
|
106
|
-
Key = filePath?.name;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const { TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET } = getConfig();
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
debug('S3 config', getMaskedConfig());
|
|
113
|
-
debug('Started upload', filePath, 'to ', S3_BUCKET);
|
|
114
|
-
|
|
115
|
-
// Verification that the file was actually created: 20 attempts of 0.5 second => 10sec
|
|
116
|
-
const isFileExist = await checkFileExists(filePath, 20, 500);
|
|
117
|
-
|
|
118
|
-
if (!isFileExist) {
|
|
119
|
-
console.error(pc.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
debug('File: ', filePath, ' exists');
|
|
124
|
-
|
|
125
|
-
const fileData = await readFile(filePath);
|
|
126
|
-
|
|
127
|
-
Key = `${runId}/${randomUUID()}-${Key || path.basename(filePath)}`;
|
|
128
|
-
|
|
129
|
-
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
130
|
-
|
|
131
|
-
if (!S3_BUCKET || !fileData) {
|
|
132
|
-
console.log(
|
|
133
|
-
APP_PREFIX,
|
|
134
|
-
pc.bold(pc.red(`Failed uploading '${Key}'. Please check S3 credentials`)),
|
|
135
|
-
getMaskedConfig(),
|
|
136
|
-
);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const s3 = new S3(_getS3Config());
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* @type {import('@aws-sdk/client-s3').PutObjectCommandInput}
|
|
144
|
-
*/
|
|
145
|
-
const params = {
|
|
146
|
-
Bucket: S3_BUCKET,
|
|
147
|
-
Key,
|
|
148
|
-
Body: fileData,
|
|
149
|
-
ContentType,
|
|
150
|
-
ACL,
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const out = new Upload({
|
|
154
|
-
client: s3,
|
|
155
|
-
params,
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
const link = await getS3LocationLink(out);
|
|
159
|
-
|
|
160
|
-
debug(`Succesfully uploaded ${filePath} => ${S3_BUCKET}/${Key} | URL: ${link}`);
|
|
161
|
-
|
|
162
|
-
return link;
|
|
163
|
-
} catch (e) {
|
|
164
|
-
debug('S3 file uploading error: ', e);
|
|
165
|
-
|
|
166
|
-
console.log(APP_PREFIX, `To ${pc.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
167
|
-
|
|
168
|
-
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
169
|
-
console.log(APP_PREFIX, `To enable ${pc.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
170
|
-
} else {
|
|
171
|
-
console.log(
|
|
172
|
-
APP_PREFIX,
|
|
173
|
-
`To enable ${pc.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
console.log(APP_PREFIX, '---------------');
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
|
|
181
|
-
const { S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET } =
|
|
182
|
-
getConfig();
|
|
183
|
-
|
|
184
|
-
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
185
|
-
|
|
186
|
-
const fileExtension = _getFileExtBase64(buffer.toString('base64'));
|
|
187
|
-
const Key = `${runId}/${fileName}${fileExtension}`;
|
|
188
|
-
|
|
189
|
-
if (!S3_BUCKET || !buffer) {
|
|
190
|
-
console.log(APP_PREFIX, pc.bold(pc.red(`Failed uploading '${Key}'. Please check S3 credentials`)), {
|
|
191
|
-
accessKeyId: S3_ACCESS_KEY_ID,
|
|
192
|
-
secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
|
|
193
|
-
region: S3_REGION,
|
|
194
|
-
bucket: S3_BUCKET,
|
|
195
|
-
acl: ACL,
|
|
196
|
-
endpoint: S3_ENDPOINT,
|
|
197
|
-
});
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const s3 = new S3(_getS3Config());
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
const out = new Upload({
|
|
205
|
-
client: s3,
|
|
206
|
-
|
|
207
|
-
params: {
|
|
208
|
-
Bucket: S3_BUCKET,
|
|
209
|
-
Key,
|
|
210
|
-
Body: buffer,
|
|
211
|
-
ACL,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
return await getS3LocationLink(out);
|
|
216
|
-
} catch (e) {
|
|
217
|
-
debug('S3 buffer uploading error: ', e);
|
|
218
|
-
|
|
219
|
-
console.log(APP_PREFIX, `To ${pc.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
220
|
-
|
|
221
|
-
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
222
|
-
console.log(APP_PREFIX, `To enable ${pc.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
223
|
-
} else {
|
|
224
|
-
console.log(
|
|
225
|
-
APP_PREFIX,
|
|
226
|
-
`To enable ${pc.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
console.log(APP_PREFIX, '---------------');
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const uploadFileByPath = async (filePath, runId) => {
|
|
234
|
-
try {
|
|
235
|
-
if (isArtifactsEnabled()) {
|
|
236
|
-
return uploadUsingS3(filePath, runId);
|
|
237
|
-
}
|
|
238
|
-
} catch (e) {
|
|
239
|
-
debug(e);
|
|
240
|
-
|
|
241
|
-
console.error(pc.red('Error occurred while uploading artifacts! '), e);
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const uploadFileAsBuffer = async (buffer, fileName, runId) => {
|
|
246
|
-
try {
|
|
247
|
-
if (isArtifactsEnabled()) {
|
|
248
|
-
return uploadUsingS3AsBuffer(buffer, fileName, runId);
|
|
249
|
-
}
|
|
250
|
-
} catch (e) {
|
|
251
|
-
debug(e);
|
|
252
|
-
|
|
253
|
-
console.error(pc.red('Error occurred while uploading artifacts! '), e);
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const checkFileExists = async (filePath, attempts = 5, intervalMs = 500) => {
|
|
258
|
-
const checkFile = async () => {
|
|
259
|
-
const fileStats = await stat(filePath);
|
|
260
|
-
if (fileStats.isFile()) {
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
throw new Error('File not found');
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
await promiseRetry(
|
|
269
|
-
{
|
|
270
|
-
retries: attempts,
|
|
271
|
-
minTimeout: intervalMs,
|
|
272
|
-
},
|
|
273
|
-
checkFile,
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
return true;
|
|
277
|
-
} catch (err) {
|
|
278
|
-
console.error(pc.yellow(`File ${filePath} was not found or did not have time to be generated...`));
|
|
279
|
-
|
|
280
|
-
return false;
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const getS3LocationLink = async out => {
|
|
285
|
-
const response = await out.done();
|
|
286
|
-
|
|
287
|
-
let s3Location = response?.Location;
|
|
288
|
-
|
|
289
|
-
if (!s3Location) {
|
|
290
|
-
// TODO: out: a fallback case - remove after deeper testing
|
|
291
|
-
s3Location = out?.singleUploadResult?.Location;
|
|
292
|
-
debug('Uploaded singleUploadResult.Location', s3Location);
|
|
293
|
-
|
|
294
|
-
if (!s3Location) {
|
|
295
|
-
throw new Error("Problems getting the S3 artifact's link. Please check S3 permissions!");
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return s3Location;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
export const upload = {
|
|
303
|
-
uploadFileByPath,
|
|
304
|
-
uploadFileAsBuffer,
|
|
305
|
-
isArtifactsEnabled,
|
|
306
|
-
resetConfig,
|
|
307
|
-
};
|