@testomatio/reporter 1.1.1 → 1.1.2-beta-fix-config-2
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/cucumber/current.js +2 -2
- package/lib/adapter/cucumber/legacy.js +2 -2
- package/lib/adapter/cypress-plugin/index.js +3 -3
- package/lib/adapter/jest.js +2 -2
- package/lib/adapter/mocha.js +2 -2
- package/lib/adapter/playwright.js +10 -11
- package/lib/bin/startTest.js +2 -2
- package/lib/client.js +2 -3
- package/lib/config.js +14 -7
- package/lib/constants.js +5 -1
- package/lib/data-storage.js +20 -20
- package/lib/pipe/testomatio.js +67 -42
- package/lib/services/logger.js +24 -15
- package/lib/utils/utils.js +5 -2
- package/lib/xmlReader.js +2 -2
- package/package.json +6 -5
|
@@ -5,7 +5,7 @@ const fs = require('fs');
|
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
7
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
8
|
-
const
|
|
8
|
+
const config = require('../../config');
|
|
9
9
|
const { services } = require('../../services');
|
|
10
10
|
|
|
11
11
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
@@ -30,7 +30,7 @@ class CucumberReporter extends Formatter {
|
|
|
30
30
|
this.failures = [];
|
|
31
31
|
this.cases = [];
|
|
32
32
|
|
|
33
|
-
this.client = new TestomatClient({ apiKey: options.apiKey || TESTOMATIO });
|
|
33
|
+
this.client = new TestomatClient({ apiKey: options.apiKey || config.TESTOMATIO });
|
|
34
34
|
this.status = STATUS.PASSED;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -4,7 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
-
const
|
|
7
|
+
const config = require('../../config');
|
|
8
8
|
|
|
9
9
|
const createTestomatFormatter = apiKey => {
|
|
10
10
|
if (!apiKey || apiKey === '') {
|
|
@@ -152,4 +152,4 @@ const createTestomatFormatter = apiKey => {
|
|
|
152
152
|
};
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
module.exports = createTestomatFormatter(TESTOMATIO);
|
|
155
|
+
module.exports = createTestomatFormatter(config.TESTOMATIO);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const { STATUS } = require('../../constants');
|
|
2
2
|
const { parseTest, parseSuite } = require('../../utils/utils');
|
|
3
3
|
const TestomatClient = require('../../client');
|
|
4
|
-
const
|
|
4
|
+
const config = require('../../config');
|
|
5
5
|
|
|
6
6
|
const testomatioReporter = on => {
|
|
7
|
-
if (!TESTOMATIO) {
|
|
7
|
+
if (!config.TESTOMATIO) {
|
|
8
8
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
const client = new TestomatClient({ apiKey: TESTOMATIO });
|
|
11
|
+
const client = new TestomatClient({ apiKey: config.TESTOMATIO });
|
|
12
12
|
|
|
13
13
|
on('before:run', async run => {
|
|
14
14
|
// TODO: looks like client.env does not exist
|
package/lib/adapter/jest.js
CHANGED
|
@@ -55,8 +55,8 @@ class JestReporter {
|
|
|
55
55
|
if (!fullSuiteTitle && testResult.testFilePath) fullSuiteTitle = path.basename(testResult.testFilePath);
|
|
56
56
|
|
|
57
57
|
const logs = getTestLogs(result);
|
|
58
|
-
const artifacts = services.artifacts.get(
|
|
59
|
-
const keyValues = services.keyValues.get(
|
|
58
|
+
const artifacts = services.artifacts.get(result.fullName);
|
|
59
|
+
const keyValues = services.keyValues.get(result.fullName);
|
|
60
60
|
|
|
61
61
|
const deducedStatus = status === 'pending' ? 'skipped' : status;
|
|
62
62
|
// In jest if test is not matched with test name pattern it is considered as skipped.
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -4,7 +4,7 @@ const chalk = require('chalk');
|
|
|
4
4
|
const TestomatClient = require('../client');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
6
6
|
const { parseTest, fileSystem } = require('../utils/utils');
|
|
7
|
-
const
|
|
7
|
+
const config = require('../config');
|
|
8
8
|
const { services } = require('../services');
|
|
9
9
|
|
|
10
10
|
const {
|
|
@@ -26,7 +26,7 @@ function MochaReporter(runner, opts) {
|
|
|
26
26
|
let skipped = 0;
|
|
27
27
|
// let artifactStore;
|
|
28
28
|
|
|
29
|
-
const apiKey = opts?.reporterOptions?.apiKey || TESTOMATIO;
|
|
29
|
+
const apiKey = opts?.reporterOptions?.apiKey || config.TESTOMATIO;
|
|
30
30
|
|
|
31
31
|
const client = new TestomatClient({ apiKey });
|
|
32
32
|
|
|
@@ -42,9 +42,7 @@ class PlaywrightReporter {
|
|
|
42
42
|
|
|
43
43
|
const { error, duration } = result;
|
|
44
44
|
|
|
45
|
-
const suite_title = test.parent
|
|
46
|
-
? test.parent?.title
|
|
47
|
-
: path.basename(test?.location?.file);
|
|
45
|
+
const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
|
|
48
46
|
|
|
49
47
|
const steps = [];
|
|
50
48
|
for (const step of result.steps) {
|
|
@@ -87,12 +85,11 @@ class PlaywrightReporter {
|
|
|
87
85
|
reportTestPromises.push(reportTestPromise);
|
|
88
86
|
}
|
|
89
87
|
|
|
90
|
-
|
|
91
88
|
#getArtifactPath(artifact) {
|
|
92
89
|
if (artifact.path) {
|
|
93
90
|
if (path.isAbsolute(artifact.path)) return artifact.path;
|
|
94
91
|
|
|
95
|
-
return path.join(this.config.outputDir || this.config.projects[0].outputDir, artifact.path);
|
|
92
|
+
return path.join(this.config.outputDir || this.config.projects[0].outputDir, artifact.path);
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
if (artifact.body) {
|
|
@@ -104,7 +101,7 @@ class PlaywrightReporter {
|
|
|
104
101
|
return null;
|
|
105
102
|
}
|
|
106
103
|
|
|
107
|
-
async onEnd(result
|
|
104
|
+
async onEnd(result) {
|
|
108
105
|
if (!this.client) return;
|
|
109
106
|
|
|
110
107
|
await Promise.all(reportTestPromises);
|
|
@@ -117,9 +114,11 @@ class PlaywrightReporter {
|
|
|
117
114
|
for (const upload of this.uploads) {
|
|
118
115
|
const { title, testId, suite_title } = upload;
|
|
119
116
|
|
|
120
|
-
const files = upload.files.map(attachment => {
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const files = upload.files.map(attachment => ({
|
|
118
|
+
path: this.#getArtifactPath(attachment),
|
|
119
|
+
title,
|
|
120
|
+
type: attachment.contentType
|
|
121
|
+
}));
|
|
123
122
|
|
|
124
123
|
promises.push(
|
|
125
124
|
this.client.addTestRun(undefined, {
|
|
@@ -169,7 +168,7 @@ function tmpFile(prefix = 'tmp.') {
|
|
|
169
168
|
/**
|
|
170
169
|
* Returns filename + test title
|
|
171
170
|
* @param {*} test - testInfo object from Playwright
|
|
172
|
-
* @returns
|
|
171
|
+
* @returns
|
|
173
172
|
*/
|
|
174
173
|
function getTestContextName(test) {
|
|
175
174
|
return `${test._requireFile || ''}_${test.title}`;
|
|
@@ -186,7 +185,7 @@ function initPlaywrightForStorage() {
|
|
|
186
185
|
});
|
|
187
186
|
} catch (e) {
|
|
188
187
|
// ignore
|
|
189
|
-
}
|
|
188
|
+
}
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
module.exports = PlaywrightReporter;
|
package/lib/bin/startTest.js
CHANGED
|
@@ -5,7 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
|
-
const
|
|
8
|
+
const config = require('../config');
|
|
9
9
|
|
|
10
10
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ program
|
|
|
21
21
|
|
|
22
22
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
23
23
|
|
|
24
|
-
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || TESTOMATIO;
|
|
24
|
+
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
|
|
25
25
|
const title = process.env.TESTOMATIO_TITLE;
|
|
26
26
|
|
|
27
27
|
if (launch) {
|
package/lib/client.js
CHANGED
|
@@ -140,7 +140,6 @@ class Client {
|
|
|
140
140
|
} = testData;
|
|
141
141
|
let { message = '' } = testData;
|
|
142
142
|
|
|
143
|
-
|
|
144
143
|
let errorFormatted = '';
|
|
145
144
|
if (error) {
|
|
146
145
|
errorFormatted += this.formatError(error) || '';
|
|
@@ -242,11 +241,11 @@ class Client {
|
|
|
242
241
|
console.log(
|
|
243
242
|
APP_PREFIX,
|
|
244
243
|
chalk.yellow(
|
|
245
|
-
`Some artifacts were not uploaded. ${this.failedToUpload} artifacts could not be uploaded.
|
|
244
|
+
`Some artifacts were not uploaded. ${this.failedToUpload} artifacts could not be uploaded.
|
|
245
|
+
Run tests with DEBUG="@testomatio/reporter:file-uploader" to see details"`,
|
|
246
246
|
),
|
|
247
247
|
);
|
|
248
248
|
}
|
|
249
|
-
|
|
250
249
|
}
|
|
251
250
|
})
|
|
252
251
|
.catch(err => console.log(APP_PREFIX, err));
|
package/lib/config.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
// This file is used to read environment variables from .env file
|
|
1
|
+
// This file is used to read environment variables from .env file
|
|
2
|
+
|
|
3
|
+
if (process.env.TESTOMATIO_ENV_FILE_PATH) {
|
|
4
|
+
require('dotenv').config({ path: process.env.TESTOMATIO_ENV_FILE_PATH });
|
|
5
|
+
}
|
|
2
6
|
|
|
3
|
-
// ! uncommenting next line leads ro reading vars from .env file
|
|
4
|
-
// require('dotenv').config();
|
|
5
7
|
const debug = require('debug')('@testomatio/reporter:config');
|
|
6
8
|
|
|
7
9
|
/* for possibility to use multiple env files (reading different paths)
|
|
8
10
|
const dotenv = require('dotenv');
|
|
9
11
|
const envFileVars = dotenv.config({ path: '.env' }).parsed; */
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
if (process.env.TESTOMATIO_API_KEY) {
|
|
14
|
+
process.env.TESTOMATIO = process.env.TESTOMATIO_API_KEY;
|
|
15
|
+
}
|
|
16
|
+
if (process.env.TESTOMATIO_TOKEN) {
|
|
17
|
+
process.env.TESTOMATIO = process.env.TESTOMATIO_TOKEN;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (process.env.TESTOMATIO === 'undefined')
|
|
21
|
+
console.error('TESTOMATIO is "undefined". Something went wrong. Contact dev team.');
|
|
14
22
|
|
|
15
23
|
// select only TESTOMATIO related variables (only to print them in debug)
|
|
16
24
|
const testomatioEnvVars =
|
|
@@ -26,4 +34,3 @@ debug('TESTOMATIO variables:', testomatioEnvVars);
|
|
|
26
34
|
const config = process.env;
|
|
27
35
|
|
|
28
36
|
module.exports = config;
|
|
29
|
-
module.exports.TESTOMATIO = TESTOMATIO;
|
package/lib/constants.js
CHANGED
|
@@ -3,6 +3,8 @@ const os = require('os');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
|
|
5
5
|
const APP_PREFIX = chalk.gray('[TESTOMATIO]');
|
|
6
|
+
const AXIOS_TIMEOUT = 20 * 1000; // sum = 20sec
|
|
7
|
+
const AXIOS_RETRY_TIMEOUT = 5 * 1000; // sum = 5sec
|
|
6
8
|
|
|
7
9
|
const TESTOMAT_TMP_STORAGE_DIR = path.join(os.tmpdir(), 'testomatio_tmp');
|
|
8
10
|
|
|
@@ -32,5 +34,7 @@ module.exports = {
|
|
|
32
34
|
TESTOMAT_TMP_STORAGE_DIR,
|
|
33
35
|
CSV_HEADERS,
|
|
34
36
|
STATUS,
|
|
35
|
-
HTML_REPORT
|
|
37
|
+
HTML_REPORT,
|
|
38
|
+
AXIOS_TIMEOUT,
|
|
39
|
+
AXIOS_RETRY_TIMEOUT
|
|
36
40
|
}
|
package/lib/data-storage.js
CHANGED
|
@@ -4,7 +4,8 @@ const path = require('path');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const { join } = require('path');
|
|
6
6
|
const { TESTOMAT_TMP_STORAGE_DIR } = require('./constants');
|
|
7
|
-
const { fileSystem,
|
|
7
|
+
const { fileSystem, testRunnerHelper } = require('./utils/utils');
|
|
8
|
+
const crypto = require('crypto');
|
|
8
9
|
|
|
9
10
|
class DataStorage {
|
|
10
11
|
static #instance;
|
|
@@ -37,16 +38,6 @@ class DataStorage {
|
|
|
37
38
|
this.isFileStorage = true;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
#stringToFilename(str) {
|
|
41
|
-
// TODO: use md5 hash later
|
|
42
|
-
const validFilenameRegex = /[^a-zA-Z0-9_.-]/g;
|
|
43
|
-
// replace all characters not in the regex above with underscore, then duplicate underscores removed
|
|
44
|
-
let filename = str.replace(validFilenameRegex, '_').replace(/_{2,}/g, '_').substring(0, 255); // max filename length
|
|
45
|
-
// remove leading and trailing underscores
|
|
46
|
-
filename = filename.replace(/^_+|_+$/g, '');
|
|
47
|
-
return filename;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
41
|
/**
|
|
51
42
|
* Puts any data to storage (file or global variable).
|
|
52
43
|
* If file: stores data as text, if global variable – stores as array of data.
|
|
@@ -59,19 +50,19 @@ class DataStorage {
|
|
|
59
50
|
putData(dataType, data, context = null) {
|
|
60
51
|
if (!dataType || !data) return;
|
|
61
52
|
|
|
62
|
-
context = context || this.context ||
|
|
53
|
+
context = context || this.context || testRunnerHelper.getNameOfCurrentlyRunningTest();
|
|
63
54
|
if (!context) {
|
|
64
55
|
debug(`No context provided for "${dataType}" data:`, data);
|
|
65
56
|
return;
|
|
66
57
|
}
|
|
67
|
-
|
|
58
|
+
const contextHash = stringToMD5Hash(context);
|
|
68
59
|
|
|
69
60
|
if (this.isFileStorage) {
|
|
70
61
|
const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
|
|
71
62
|
fileSystem.createDir(dataDirPath);
|
|
72
|
-
this.#putDataToFile(dataType, data,
|
|
63
|
+
this.#putDataToFile(dataType, data, contextHash);
|
|
73
64
|
} else {
|
|
74
|
-
this.#putDataToGlobalVar(dataType, data,
|
|
65
|
+
this.#putDataToGlobalVar(dataType, data, contextHash);
|
|
75
66
|
}
|
|
76
67
|
}
|
|
77
68
|
|
|
@@ -85,32 +76,32 @@ class DataStorage {
|
|
|
85
76
|
*/
|
|
86
77
|
getData(dataType, context) {
|
|
87
78
|
// TODO: think if it could be useful
|
|
88
|
-
// context = context || this.context ||
|
|
79
|
+
// context = context || this.context || testRunnerHelper.getNameOfCurrentlyRunningTest();
|
|
89
80
|
|
|
90
81
|
if (!context) {
|
|
91
82
|
debug(`Trying to get "${dataType}" data without context`);
|
|
92
83
|
return null;
|
|
93
84
|
}
|
|
94
85
|
|
|
95
|
-
|
|
86
|
+
const contextHash = stringToMD5Hash(context);
|
|
96
87
|
|
|
97
88
|
let testDataFromFile = [];
|
|
98
89
|
let testDataFromGlobalVar = [];
|
|
99
90
|
|
|
100
91
|
if (global?.testomatioDataStore) {
|
|
101
|
-
testDataFromGlobalVar = this.#getDataFromGlobalVar(dataType,
|
|
92
|
+
testDataFromGlobalVar = this.#getDataFromGlobalVar(dataType, contextHash);
|
|
102
93
|
if (testDataFromGlobalVar) {
|
|
103
94
|
if (testDataFromGlobalVar.length) return testDataFromGlobalVar;
|
|
104
95
|
}
|
|
105
96
|
// don't return nothing if no data in global variable
|
|
106
97
|
}
|
|
107
98
|
|
|
108
|
-
testDataFromFile = this.#getDataFromFile(dataType,
|
|
99
|
+
testDataFromFile = this.#getDataFromFile(dataType, contextHash);
|
|
109
100
|
|
|
110
101
|
if (testDataFromFile.length) {
|
|
111
102
|
return testDataFromFile;
|
|
112
103
|
}
|
|
113
|
-
debug(`No "${dataType}" data for context "${
|
|
104
|
+
debug(`No "${dataType}" data for context "${contextHash}" in both file and global variable`);
|
|
114
105
|
|
|
115
106
|
// in case no data found for context
|
|
116
107
|
return null;
|
|
@@ -197,7 +188,16 @@ class DataStorage {
|
|
|
197
188
|
}
|
|
198
189
|
}
|
|
199
190
|
|
|
191
|
+
function stringToMD5Hash(str) {
|
|
192
|
+
const md5 = crypto.createHash('md5');
|
|
193
|
+
md5.update(str);
|
|
194
|
+
const hash = md5.digest('hex');
|
|
195
|
+
|
|
196
|
+
return hash;
|
|
197
|
+
}
|
|
198
|
+
|
|
200
199
|
module.exports.dataStorage = DataStorage.getInstance();
|
|
200
|
+
module.exports.stringToMD5Hash = stringToMD5Hash;
|
|
201
201
|
|
|
202
202
|
// TODO: consider using fs promises instead of writeSync/appendFileSync to
|
|
203
203
|
// prevent blocking and improve performance (probably queue usage will be required)
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:pipe:testomatio');
|
|
2
2
|
const chalk = require('chalk');
|
|
3
|
+
// Retry interceptor function
|
|
4
|
+
const axiosRetry = require('axios-retry');
|
|
5
|
+
// Default axios instance
|
|
3
6
|
const axios = require('axios');
|
|
4
7
|
const JsonCycle = require('json-cycle');
|
|
5
|
-
|
|
8
|
+
|
|
9
|
+
const { APP_PREFIX, STATUS, AXIOS_TIMEOUT, AXIOS_RETRY_TIMEOUT } = require('../constants');
|
|
6
10
|
const { isValidUrl, foundedTestLog } = require('../utils/utils');
|
|
7
|
-
const { parseFilterParams, generateFilterRequestParams, setS3Credentials
|
|
8
|
-
const
|
|
11
|
+
const { parseFilterParams, generateFilterRequestParams, setS3Credentials } = require('../utils/pipe_utils');
|
|
12
|
+
const config = require('../config');
|
|
9
13
|
|
|
10
14
|
if (process.env.TESTOMATIO_RUN) {
|
|
11
15
|
process.env.runId = process.env.TESTOMATIO_RUN;
|
|
@@ -21,7 +25,7 @@ class TestomatioPipe {
|
|
|
21
25
|
constructor(params, store) {
|
|
22
26
|
this.isEnabled = false;
|
|
23
27
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
24
|
-
this.apiKey = params.apiKey || TESTOMATIO;
|
|
28
|
+
this.apiKey = params.apiKey || config.TESTOMATIO;
|
|
25
29
|
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
26
30
|
if (!this.apiKey) {
|
|
27
31
|
return;
|
|
@@ -34,10 +38,31 @@ class TestomatioPipe {
|
|
|
34
38
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
35
39
|
this.env = process.env.TESTOMATIO_ENV;
|
|
36
40
|
this.label = process.env.TESTOMATIO_LABEL;
|
|
37
|
-
|
|
41
|
+
// Create a new instance of axios with a custom config
|
|
38
42
|
this.axios = axios.create({
|
|
39
43
|
baseURL: `${this.url.trim()}`,
|
|
40
|
-
timeout:
|
|
44
|
+
timeout: AXIOS_TIMEOUT,
|
|
45
|
+
});
|
|
46
|
+
// Pass the axios instance to the retry function
|
|
47
|
+
axiosRetry(this.axios, {
|
|
48
|
+
retries: 3, // Number of retries (Defaults to 3)
|
|
49
|
+
shouldResetTimeout: true,
|
|
50
|
+
retryCondition: error => {
|
|
51
|
+
// Conditional check the error status code
|
|
52
|
+
switch (error.response.status) {
|
|
53
|
+
case 409:
|
|
54
|
+
case 429:
|
|
55
|
+
case 502:
|
|
56
|
+
case 503:
|
|
57
|
+
return true; // Retry request with response status code 409, 429, 502, 503
|
|
58
|
+
default:
|
|
59
|
+
return false; // Do not retry the others
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
retryDelay: retryCount => retryCount * AXIOS_RETRY_TIMEOUT, // sum = 15sec
|
|
63
|
+
onRetry: retryCount => {
|
|
64
|
+
debug(`Retry attempt #${retryCount} failed. Retrying again...`);
|
|
65
|
+
},
|
|
41
66
|
});
|
|
42
67
|
|
|
43
68
|
this.isEnabled = true;
|
|
@@ -57,7 +82,7 @@ class TestomatioPipe {
|
|
|
57
82
|
/**
|
|
58
83
|
* Asynchronously prepares and retrieves the Testomat.io test grepList based on the provided options.
|
|
59
84
|
* @param {Object} opts - The options for preparing the test grepList.
|
|
60
|
-
* @returns {Promise<string[]>} - An array containing the retrieved
|
|
85
|
+
* @returns {Promise<string[]>} - An array containing the retrieved
|
|
61
86
|
* test grepList, or an empty array if no tests are found or the request is disabled.
|
|
62
87
|
* @throws {Error} - Throws an error if there was a problem while making the request.
|
|
63
88
|
*/
|
|
@@ -70,7 +95,7 @@ class TestomatioPipe {
|
|
|
70
95
|
const q = generateFilterRequestParams({
|
|
71
96
|
type,
|
|
72
97
|
id,
|
|
73
|
-
apiKey: this.apiKey.trim()
|
|
98
|
+
apiKey: this.apiKey.trim(),
|
|
74
99
|
});
|
|
75
100
|
|
|
76
101
|
if (!q) {
|
|
@@ -84,14 +109,10 @@ class TestomatioPipe {
|
|
|
84
109
|
foundedTestLog(APP_PREFIX, data.tests);
|
|
85
110
|
return data.tests;
|
|
86
111
|
}
|
|
87
|
-
|
|
112
|
+
|
|
88
113
|
console.log(APP_PREFIX, `⛔ No tests found for your --filter --> ${type}=${id}`);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.error(
|
|
92
|
-
APP_PREFIX,
|
|
93
|
-
`🚩 Error getting Testomat.io test grepList: ${err}`,
|
|
94
|
-
);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.error(APP_PREFIX, `🚩 Error getting Testomat.io test grepList: ${err}`);
|
|
95
116
|
}
|
|
96
117
|
}
|
|
97
118
|
|
|
@@ -150,10 +171,13 @@ class TestomatioPipe {
|
|
|
150
171
|
maxContentLength: Infinity,
|
|
151
172
|
maxBodyLength: Infinity,
|
|
152
173
|
});
|
|
174
|
+
|
|
153
175
|
this.runId = resp.data.uid;
|
|
154
176
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
155
177
|
this.runPublicUrl = resp.data.public_url;
|
|
178
|
+
|
|
156
179
|
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
180
|
+
|
|
157
181
|
this.store.runUrl = this.runUrl;
|
|
158
182
|
this.store.runPublicUrl = this.runPublicUrl;
|
|
159
183
|
this.store.runId = this.runId;
|
|
@@ -184,37 +208,38 @@ class TestomatioPipe {
|
|
|
184
208
|
|
|
185
209
|
debug('Adding test', json);
|
|
186
210
|
|
|
187
|
-
return this.axios
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (err.response
|
|
198
|
-
|
|
211
|
+
return this.axios
|
|
212
|
+
.post(`/api/reporter/${this.runId}/testrun`, json, {
|
|
213
|
+
maxContentLength: Infinity,
|
|
214
|
+
maxBodyLength: Infinity,
|
|
215
|
+
headers: {
|
|
216
|
+
// Overwrite Axios's automatically set Content-Type
|
|
217
|
+
'Content-Type': 'application/json',
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
.catch(err => {
|
|
221
|
+
if (err.response) {
|
|
222
|
+
if (err.response.status >= 400) {
|
|
223
|
+
const responseData = err.response.data || { message: '' };
|
|
224
|
+
console.log(
|
|
225
|
+
APP_PREFIX,
|
|
226
|
+
chalk.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
227
|
+
chalk.grey(data?.title || ''),
|
|
228
|
+
);
|
|
229
|
+
if (err.response.data.message.includes('could not be matched')) {
|
|
230
|
+
this.hasUnmatchedTests = true;
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
199
234
|
console.log(
|
|
200
235
|
APP_PREFIX,
|
|
201
|
-
chalk.yellow(`Warning: ${
|
|
202
|
-
|
|
236
|
+
chalk.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
|
|
237
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
203
238
|
);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
return;
|
|
239
|
+
} else {
|
|
240
|
+
console.log(APP_PREFIX, chalk.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
208
241
|
}
|
|
209
|
-
|
|
210
|
-
APP_PREFIX,
|
|
211
|
-
chalk.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
|
|
212
|
-
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
213
|
-
);
|
|
214
|
-
} else {
|
|
215
|
-
console.log(APP_PREFIX, chalk.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
216
|
-
}
|
|
217
|
-
});
|
|
242
|
+
});
|
|
218
243
|
}
|
|
219
244
|
|
|
220
245
|
/**
|
package/lib/services/logger.js
CHANGED
|
@@ -26,7 +26,7 @@ class Logger {
|
|
|
26
26
|
// set default logger to be used in log, warn, error, etc methods
|
|
27
27
|
#originalUserLogger = { ...console };
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
#userLoggerWithOverridenMethods;
|
|
30
30
|
|
|
31
31
|
static #instance;
|
|
32
32
|
|
|
@@ -197,16 +197,18 @@ class Logger {
|
|
|
197
197
|
* @param {*} userLogger
|
|
198
198
|
*/
|
|
199
199
|
intercept(userLogger) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return;
|
|
200
|
+
// STEP 1: reset previously intercepted logger methods to original
|
|
201
|
+
if (this.#userLoggerWithOverridenMethods) {
|
|
202
|
+
for (const method of LOG_METHODS) {
|
|
203
|
+
this.#userLoggerWithOverridenMethods[method] = this.#originalUserLogger[method];
|
|
204
|
+
}
|
|
206
205
|
}
|
|
207
206
|
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
// STEP 2: intercept new logger
|
|
208
|
+
this.#originalUserLogger = { ...userLogger };
|
|
209
|
+
|
|
210
|
+
const isUserLoggerConsole = userLogger.toString?.().toLowerCase() === '[object console]';
|
|
211
|
+
debug(`Intercepting ${isUserLoggerConsole ? 'console' : 'some user'} logger}`);
|
|
210
212
|
|
|
211
213
|
// override user logger (any, e.g. console) methods to intercept log messages
|
|
212
214
|
for (const method of LOG_METHODS) {
|
|
@@ -219,10 +221,7 @@ class Logger {
|
|
|
219
221
|
userLogger[method] = (...args) => this[method](...args);
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
|
|
223
|
-
Playwright intercepts console messages by default. Thus when we intercept any logger and provide output by console,
|
|
224
|
-
messages are intercepted by Playwright.
|
|
225
|
-
*/
|
|
224
|
+
this.#userLoggerWithOverridenMethods = userLogger;
|
|
226
225
|
|
|
227
226
|
/*
|
|
228
227
|
Initial idea was to intercept any logger (tracer, pino, etc),
|
|
@@ -232,11 +231,22 @@ class Logger {
|
|
|
232
231
|
Also its difficult to understand (actually did not find the way to do it) if logger was already intercepted or not.
|
|
233
232
|
Thus, decided to intercept only console by default and provide output by default console.
|
|
234
233
|
It means, if user uses his own logger, its messages will be intercepted,
|
|
235
|
-
but the output will be provided by console.
|
|
234
|
+
but the output will be always provided by console.
|
|
236
235
|
TODO: try to implement the providing output to terminal by user logger
|
|
237
236
|
*/
|
|
238
237
|
}
|
|
239
238
|
|
|
239
|
+
stopInterception() {
|
|
240
|
+
debug('Stop ntercepting logs');
|
|
241
|
+
|
|
242
|
+
// restore original user logger
|
|
243
|
+
if (this.#userLoggerWithOverridenMethods) {
|
|
244
|
+
for (const method of LOG_METHODS) {
|
|
245
|
+
this.#userLoggerWithOverridenMethods[method] = this.#originalUserLogger[method];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
240
250
|
/**
|
|
241
251
|
* Allows to configure logger. Make sure you do it before the logger usage in your code.
|
|
242
252
|
*
|
|
@@ -269,7 +279,6 @@ I found the only ability to get any logs using .task('log', 'message') (this is
|
|
|
269
279
|
and then intercept it with:
|
|
270
280
|
on('task', {
|
|
271
281
|
log (message) {
|
|
272
|
-
console.log(message)
|
|
273
282
|
return null
|
|
274
283
|
}
|
|
275
284
|
})
|
package/lib/utils/utils.js
CHANGED
|
@@ -298,8 +298,11 @@ function removeColorCodes(input) {
|
|
|
298
298
|
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
const
|
|
301
|
+
const testRunnerHelper = {
|
|
302
|
+
// for Jest
|
|
302
303
|
getNameOfCurrentlyRunningTest: () => {
|
|
304
|
+
if (global.testomatioTestTitle) return global.testomatioTestTitle;
|
|
305
|
+
|
|
303
306
|
if (!process.env.JEST_WORKER_ID) return null;
|
|
304
307
|
try {
|
|
305
308
|
// TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
|
|
@@ -329,5 +332,5 @@ module.exports = {
|
|
|
329
332
|
humanize,
|
|
330
333
|
removeColorCodes,
|
|
331
334
|
foundedTestLog,
|
|
332
|
-
|
|
335
|
+
testRunnerHelper,
|
|
333
336
|
};
|
package/lib/xmlReader.js
CHANGED
|
@@ -15,7 +15,7 @@ const {
|
|
|
15
15
|
const upload = require('./fileUploader');
|
|
16
16
|
const pipesFactory = require('./pipe');
|
|
17
17
|
const adapterFactory = require('./junit-adapter');
|
|
18
|
-
const
|
|
18
|
+
const config = require('./config');
|
|
19
19
|
|
|
20
20
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
21
21
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
@@ -33,7 +33,7 @@ const reduceOptions = {};
|
|
|
33
33
|
class XmlReader {
|
|
34
34
|
constructor(opts = {}) {
|
|
35
35
|
this.requestParams = {
|
|
36
|
-
apiKey: opts.apiKey || TESTOMATIO,
|
|
36
|
+
apiKey: opts.apiKey || config.TESTOMATIO,
|
|
37
37
|
url: opts.url || TESTOMATIO_URL,
|
|
38
38
|
title: TESTOMATIO_TITLE,
|
|
39
39
|
env: TESTOMATIO_ENV,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2-beta-fix-config-2",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"typings": "typings/index.d.ts",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
13
13
|
"@octokit/rest": "^19.0.5",
|
|
14
14
|
"aws-sdk": "^2.1072.0",
|
|
15
|
-
"axios": "^
|
|
15
|
+
"axios": "^1.6.2",
|
|
16
|
+
"axios-retry": "^3.9.1",
|
|
16
17
|
"callsite-record": "^4.1.4",
|
|
17
18
|
"chalk": "^4.1.0",
|
|
18
19
|
"commander": "^4.1.1",
|
|
@@ -30,8 +31,8 @@
|
|
|
30
31
|
"lodash.memoize": "^4.1.2",
|
|
31
32
|
"lodash.merge": "^4.6.2",
|
|
32
33
|
"minimatch": "^9.0.3",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
34
|
+
"promise-retry": "^2.0.1",
|
|
35
|
+
"uuid": "^9.0.0"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"bin",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
53
54
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
54
55
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js",
|
|
55
|
-
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/reporter-functions.test.js"
|
|
56
|
+
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@cucumber/cucumber": "^9.3.0",
|