@testomatio/reporter 1.1.0-beta → 1.1.0-beta-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/README.md +13 -9
- package/lib/adapter/codecept.js +59 -39
- package/lib/adapter/cucumber/current.js +95 -59
- package/lib/adapter/cucumber/legacy.js +25 -11
- package/lib/adapter/cypress-plugin/index.js +1 -1
- package/lib/adapter/jasmine.js +1 -1
- package/lib/adapter/jest.js +12 -11
- package/lib/adapter/mocha.js +22 -50
- package/lib/adapter/playwright.js +40 -21
- package/lib/adapter/webdriver.js +1 -1
- package/lib/bin/reportXml.js +1 -0
- package/lib/bin/startTest.js +25 -4
- package/lib/client.js +117 -52
- package/lib/constants.js +4 -6
- package/lib/junit-adapter/java.js +27 -9
- package/lib/pipe/csv.js +4 -1
- package/lib/pipe/github.js +24 -37
- package/lib/pipe/gitlab.js +5 -16
- package/lib/pipe/testomatio.js +138 -34
- package/lib/reporter-functions.js +43 -0
- package/lib/reporter.js +13 -5
- package/lib/storages/artifact-storage.js +70 -0
- package/lib/storages/data-storage.js +307 -0
- package/lib/storages/key-value-storage.js +58 -0
- package/lib/storages/logger.js +290 -0
- package/lib/utils/pipe_utils.js +135 -0
- package/lib/{util.js → utils/utils.js} +130 -10
- package/lib/xmlReader.js +132 -44
- package/package.json +9 -6
- package/lib/_ArtifactStorageOld.js +0 -141
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -172
- package/lib/logger.js +0 -221
package/lib/adapter/mocha.js
CHANGED
|
@@ -1,58 +1,40 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
2
|
const Mocha = require('mocha');
|
|
3
|
-
const debug = require('debug')('@testomatio/reporter:adapter:mocha');
|
|
4
3
|
const chalk = require('chalk');
|
|
5
4
|
const TestomatClient = require('../client');
|
|
6
|
-
const { STATUS } = require('../constants');
|
|
7
|
-
const { parseTest,
|
|
8
|
-
const ArtifactStorage = require('../_ArtifactStorageOld');
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
6
|
+
const { parseTest, fileSystem } = require('../utils/utils');
|
|
9
7
|
|
|
10
|
-
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
|
|
8
|
+
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
|
|
9
|
+
Mocha.Runner.constants;
|
|
11
10
|
|
|
12
11
|
function MochaReporter(runner, opts) {
|
|
13
12
|
Mocha.reporters.Base.call(this, runner);
|
|
14
|
-
let passes = 0;
|
|
15
|
-
let
|
|
13
|
+
let passes = 0;
|
|
14
|
+
let failures = 0;
|
|
15
|
+
let skipped = 0;
|
|
16
|
+
// let artifactStore;
|
|
16
17
|
|
|
17
18
|
const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
|
|
18
19
|
|
|
19
|
-
if (!apiKey) {
|
|
20
|
-
debug('TESTOMATIO key is empty, ignoring reports');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
20
|
const client = new TestomatClient({ apiKey });
|
|
24
21
|
|
|
25
22
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
26
23
|
client.createRun();
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
toFile: true
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
artifactStore = runner._workerReporter !== undefined
|
|
33
|
-
? new ArtifactStorage(params)
|
|
34
|
-
: new ArtifactStorage();
|
|
25
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
35
26
|
});
|
|
36
27
|
|
|
37
|
-
runner.on(EVENT_TEST_PASS, async
|
|
28
|
+
runner.on(EVENT_TEST_PASS, async test => {
|
|
38
29
|
passes += 1;
|
|
39
30
|
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
40
31
|
const testId = parseTest(test.title);
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
client.addTestRun(
|
|
48
|
-
STATUS.PASSED,
|
|
49
|
-
{
|
|
50
|
-
test_id: testId,
|
|
51
|
-
title: test.title,
|
|
52
|
-
time: test.duration,
|
|
53
|
-
},
|
|
54
|
-
content
|
|
55
|
-
);
|
|
33
|
+
client.addTestRun(STATUS.PASSED, {
|
|
34
|
+
test_id: testId,
|
|
35
|
+
title: test.title,
|
|
36
|
+
time: test.duration,
|
|
37
|
+
});
|
|
56
38
|
});
|
|
57
39
|
|
|
58
40
|
runner.on(EVENT_TEST_PENDING, test => {
|
|
@@ -66,33 +48,23 @@ function MochaReporter(runner, opts) {
|
|
|
66
48
|
});
|
|
67
49
|
});
|
|
68
50
|
|
|
69
|
-
runner.on(EVENT_TEST_FAIL, async(test, err) => {
|
|
51
|
+
runner.on(EVENT_TEST_FAIL, async (test, err) => {
|
|
70
52
|
failures += 1;
|
|
71
53
|
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
72
54
|
const testId = parseTest(test.title);
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
STATUS.FAILED, {
|
|
81
|
-
error: err,
|
|
82
|
-
test_id: testId,
|
|
83
|
-
title: test.title,
|
|
84
|
-
time: test.duration,
|
|
85
|
-
},
|
|
86
|
-
content
|
|
87
|
-
);
|
|
56
|
+
client.addTestRun(STATUS.FAILED, {
|
|
57
|
+
error: err,
|
|
58
|
+
test_id: testId,
|
|
59
|
+
title: test.title,
|
|
60
|
+
time: test.duration,
|
|
61
|
+
});
|
|
88
62
|
});
|
|
89
63
|
|
|
90
64
|
runner.on(EVENT_RUN_END, () => {
|
|
91
65
|
const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
92
66
|
console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
93
67
|
client.updateRunStatus(status);
|
|
94
|
-
|
|
95
|
-
artifactStore.cleanup();
|
|
96
68
|
});
|
|
97
69
|
}
|
|
98
70
|
|
|
@@ -3,14 +3,14 @@ const crypto = require('crypto');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const { APP_PREFIX, STATUS: Status } = require('../constants');
|
|
6
|
+
const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
7
7
|
const TestomatioClient = require('../client');
|
|
8
8
|
const { isArtifactsEnabled } = require('../fileUploader');
|
|
9
|
-
const { parseTest } = require('../
|
|
9
|
+
const { parseTest, fileSystem } = require('../utils/utils');
|
|
10
10
|
|
|
11
11
|
const reportTestPromises = [];
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class PlaywrightReporter {
|
|
14
14
|
constructor(config = {}) {
|
|
15
15
|
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
16
16
|
|
|
@@ -18,11 +18,19 @@ class TestomatioReporter {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
onBegin(_config, suite) {
|
|
21
|
+
// clean data storage
|
|
22
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
21
23
|
if (!this.client) return;
|
|
22
24
|
this.suite = suite;
|
|
23
25
|
this.client.createRun();
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
/*
|
|
29
|
+
onTestBegin(test) {
|
|
30
|
+
// does not work; value is not to the storage context
|
|
31
|
+
global.testTitle = test.title;
|
|
32
|
+
} */
|
|
33
|
+
|
|
26
34
|
onTestEnd(test, result) {
|
|
27
35
|
if (!this.client) return;
|
|
28
36
|
|
|
@@ -37,21 +45,34 @@ class TestomatioReporter {
|
|
|
37
45
|
for (const step of result.steps) {
|
|
38
46
|
appendStep(step, steps);
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
|
|
49
|
+
let logs = '';
|
|
50
|
+
if (result.stderr.length || result.stdout.length) {
|
|
51
|
+
logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const reportTestPromise = this.client
|
|
55
|
+
.addTestRun(checkStatus(result.status), {
|
|
56
|
+
error,
|
|
57
|
+
test_id: testId,
|
|
58
|
+
suite_title,
|
|
59
|
+
title,
|
|
60
|
+
steps: steps.join('\n'),
|
|
61
|
+
time: duration,
|
|
62
|
+
stack: logs,
|
|
53
63
|
})
|
|
54
|
-
|
|
64
|
+
.then(pipes => {
|
|
65
|
+
testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
66
|
+
|
|
67
|
+
this.uploads.push({
|
|
68
|
+
testId,
|
|
69
|
+
title,
|
|
70
|
+
suite_title,
|
|
71
|
+
files: result.attachments.filter(a => a.body || a.path),
|
|
72
|
+
});
|
|
73
|
+
// remove empty uploads
|
|
74
|
+
this.uploads = this.uploads.filter(upload => upload.files.length);
|
|
75
|
+
});
|
|
55
76
|
|
|
56
77
|
reportTestPromises.push(reportTestPromise);
|
|
57
78
|
}
|
|
@@ -67,10 +88,9 @@ class TestomatioReporter {
|
|
|
67
88
|
const promises = [];
|
|
68
89
|
|
|
69
90
|
for (const upload of this.uploads) {
|
|
70
|
-
|
|
71
91
|
const { title, testId, suite_title } = upload;
|
|
72
92
|
|
|
73
|
-
const files = upload.files.map(
|
|
93
|
+
const files = upload.files.map(attachment => {
|
|
74
94
|
if (attachment.body) {
|
|
75
95
|
const fileName = tmpFile();
|
|
76
96
|
fs.writeFileSync(fileName, attachment.body);
|
|
@@ -78,7 +98,6 @@ class TestomatioReporter {
|
|
|
78
98
|
return { path: attachment.path, title, type: attachment.contentType };
|
|
79
99
|
});
|
|
80
100
|
|
|
81
|
-
|
|
82
101
|
promises.push(
|
|
83
102
|
this.client.addTestRun(undefined, {
|
|
84
103
|
test_id: testId,
|
|
@@ -124,4 +143,4 @@ function tmpFile(prefix = 'tmp.') {
|
|
|
124
143
|
return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex'));
|
|
125
144
|
}
|
|
126
145
|
|
|
127
|
-
module.exports =
|
|
146
|
+
module.exports = PlaywrightReporter;
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
2
|
const WDIOReporter = require('@wdio/reporter').default;
|
|
3
3
|
const TestomatClient = require('../client');
|
|
4
|
-
const { parseTest } = require('../
|
|
4
|
+
const { parseTest } = require('../utils/utils');
|
|
5
5
|
|
|
6
6
|
class WebdriverReporter extends WDIOReporter {
|
|
7
7
|
constructor(options) {
|
package/lib/bin/reportXml.js
CHANGED
package/lib/bin/startTest.js
CHANGED
|
@@ -13,9 +13,11 @@ program
|
|
|
13
13
|
.option('--launch', 'Start a new run and return its ID')
|
|
14
14
|
.option('--finish', 'Finish Run by its ID')
|
|
15
15
|
.option("--env-file <envfile>", "Load environment variables from env file")
|
|
16
|
-
.
|
|
16
|
+
.option("--filter <filter>", "Additional execution filter")
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
const { launch, finish, filter } = opts;
|
|
19
|
+
let { command } = opts;
|
|
17
20
|
|
|
18
|
-
const { command, launch, finish } = opts;
|
|
19
21
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
20
22
|
|
|
21
23
|
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || process.env.TESTOMATIO;
|
|
@@ -51,6 +53,27 @@ program
|
|
|
51
53
|
|
|
52
54
|
let exitCode = 0;
|
|
53
55
|
|
|
56
|
+
const client = new TestomatClient({ apiKey, title, parallel: true });
|
|
57
|
+
|
|
58
|
+
if(filter) {
|
|
59
|
+
const [pipe, ...optsArray] = filter.split(":");
|
|
60
|
+
const pipeOptions = optsArray.join(":");
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const tests = await client.prepareRun({pipe, pipeOptions});
|
|
64
|
+
|
|
65
|
+
if(!tests || tests.length === 0) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const grep = ` --grep (${tests.join('|')})`;
|
|
70
|
+
command += grep;
|
|
71
|
+
}
|
|
72
|
+
catch(err) {
|
|
73
|
+
console.log(APP_PREFIX, err);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
54
77
|
if (!command.split) {
|
|
55
78
|
process.exitCode = 255;
|
|
56
79
|
console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
|
|
@@ -73,8 +96,6 @@ program
|
|
|
73
96
|
return;
|
|
74
97
|
}
|
|
75
98
|
|
|
76
|
-
const client = new TestomatClient({ apiKey, title, parallel: true });
|
|
77
|
-
|
|
78
99
|
client.createRun().then(() => {
|
|
79
100
|
const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
|
|
80
101
|
|
package/lib/client.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:client');
|
|
2
2
|
const createCallsiteRecord = require('callsite-record');
|
|
3
3
|
const { sep, join } = require('path');
|
|
4
|
+
const { minimatch } = require('minimatch')
|
|
4
5
|
const fs = require('fs');
|
|
5
6
|
const chalk = require('chalk');
|
|
6
7
|
const { randomUUID } = require('crypto');
|
|
7
8
|
const upload = require('./fileUploader');
|
|
8
9
|
const { APP_PREFIX } = require('./constants');
|
|
9
10
|
const pipesFactory = require('./pipe');
|
|
10
|
-
const
|
|
11
|
+
const artifactStorage = require('./storages/artifact-storage');
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @typedef {import('../types').TestData} TestData
|
|
14
15
|
* @typedef {import('../types').RunStatus} RunStatus
|
|
15
|
-
* @typedef {import('../types').PipeResult} PipeResult
|
|
16
|
+
* @typedef {import('../types').PipeResult} PipeResult
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
class Client {
|
|
@@ -22,39 +23,85 @@ class Client {
|
|
|
22
23
|
* @param {*} params
|
|
23
24
|
*/
|
|
24
25
|
constructor(params = {}) {
|
|
25
|
-
this.parallel = params.parallel;
|
|
26
26
|
const store = {};
|
|
27
27
|
this.uuid = randomUUID();
|
|
28
28
|
this.pipes = pipesFactory(params, store);
|
|
29
29
|
this.queue = Promise.resolve();
|
|
30
30
|
this.totalUploaded = 0;
|
|
31
31
|
this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
|
|
32
|
+
this.executionList = Promise.resolve();
|
|
33
|
+
|
|
32
34
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Asynchronously prepares the execution list for running tests through various pipes.
|
|
39
|
+
* Each pipe in the client is checked for enablement,
|
|
40
|
+
* and if all pipes are disabled, the function returns a resolved Promise.
|
|
41
|
+
* Otherwise, it executes the `prepareRun` method for each enabled pipe and collects the results.
|
|
42
|
+
* The results are then filtered to remove any undefined values.
|
|
43
|
+
* If no valid results are found, the function returns undefined.
|
|
44
|
+
* Otherwise, it returns the first non-empty array from the filtered results.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} params - The options for preparing the test execution list.
|
|
47
|
+
* @param {string} params.pipe - Name of the executed pipe.
|
|
48
|
+
* @param {string} params.pipeOptions - Filter option.
|
|
49
|
+
* @returns {Promise<any>} - A Promise that resolves to an
|
|
50
|
+
* array containing the prepared execution list,
|
|
51
|
+
* or resolves to undefined if no valid results are found or if all pipes are disabled.
|
|
52
|
+
*/
|
|
53
|
+
async prepareRun(params) {
|
|
54
|
+
const { pipe, pipeOptions } = params;
|
|
55
|
+
// all pipes disabled, skipping
|
|
56
|
+
if (!this.pipes.some(p => p.isEnabled)) {
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const filterPipe = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
|
|
62
|
+
|
|
63
|
+
if (!filterPipe.isEnabled) {
|
|
64
|
+
// TODO:for the future for the another pipes
|
|
65
|
+
console.warn(
|
|
66
|
+
APP_PREFIX,
|
|
67
|
+
`At the moment processing is available only for the "testomatio" key. Example: "testomatio:tag-name=xxx"`
|
|
68
|
+
);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const results = await Promise.all(this.pipes.map(async p =>
|
|
73
|
+
({ pipe: p.toString(), result: await p.prepareRun(pipeOptions) })
|
|
74
|
+
));
|
|
75
|
+
|
|
76
|
+
const result = results.filter(p => p.pipe.includes('Testomatio'))[0]?.result;
|
|
77
|
+
|
|
78
|
+
if (!result || result.length === 0) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
debug('Execution tests list', result);
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(APP_PREFIX, err);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
35
90
|
/**
|
|
36
91
|
* Used to create a new Test run
|
|
37
92
|
*
|
|
38
93
|
* @returns {Promise<any>} - resolves to Run id which should be used to update / add test
|
|
39
94
|
*/
|
|
40
95
|
createRun() {
|
|
96
|
+
debug('Creating run...');
|
|
41
97
|
// all pipes disabled, skipping
|
|
42
|
-
if (!this.pipes
|
|
43
|
-
|
|
44
|
-
const runParams = {
|
|
45
|
-
title: this.title,
|
|
46
|
-
parallel: this.parallel,
|
|
47
|
-
env: this.env,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
global.testomatioArtifacts = [];
|
|
51
|
-
// TODO: create global storage
|
|
98
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
52
99
|
|
|
53
100
|
this.queue = this.queue
|
|
54
|
-
.then(() => Promise.all(this.pipes.map(p => p.createRun(
|
|
101
|
+
.then(() => Promise.all(this.pipes.map(p => p.createRun())))
|
|
55
102
|
.catch(err => console.log(APP_PREFIX, err))
|
|
56
103
|
.then(() => undefined); // fixes return type
|
|
57
|
-
debug('Run', this.queue);
|
|
104
|
+
// debug('Run', this.queue);
|
|
58
105
|
return this.queue;
|
|
59
106
|
}
|
|
60
107
|
|
|
@@ -63,12 +110,11 @@ class Client {
|
|
|
63
110
|
*
|
|
64
111
|
* @param {string|undefined} status
|
|
65
112
|
* @param {TestData} [testData]
|
|
66
|
-
* @param {string[]} [storeArtifacts]
|
|
67
113
|
* @returns {Promise<PipeResult[]>}
|
|
68
114
|
*/
|
|
69
|
-
async addTestRun(status, testData
|
|
115
|
+
async addTestRun(status, testData) {
|
|
70
116
|
// all pipes disabled, skipping
|
|
71
|
-
if (!this.pipes
|
|
117
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) return [];
|
|
72
118
|
|
|
73
119
|
if (!testData)
|
|
74
120
|
testData = {
|
|
@@ -103,20 +149,23 @@ class Client {
|
|
|
103
149
|
stack = this.formatSteps(stack, steps);
|
|
104
150
|
}
|
|
105
151
|
|
|
152
|
+
stack += testData.stack || '';
|
|
153
|
+
|
|
154
|
+
// ATTACH LOGS from storage
|
|
155
|
+
// in some cases (e.g. using cucumber) logger instance becomes empty object, if import at the top of the file
|
|
156
|
+
const logger = require('./storages/logger');
|
|
106
157
|
const testLogs = logger.getLogs(test_id);
|
|
107
|
-
debug(`Test logs for ${test_id}:\n`, testLogs);
|
|
108
|
-
stack +=
|
|
158
|
+
// debug(`Test logs for ${test_id}:\n`, testLogs);
|
|
159
|
+
if (stack) stack += '\n\n';
|
|
160
|
+
stack += testLogs ? `${chalk.bold('Logs:')}\n${testLogs}` : '';
|
|
109
161
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
162
|
+
// GET ARTIFACTS from storage
|
|
163
|
+
const artifactFiles = artifactStorage.get(test_id);
|
|
164
|
+
if (artifactFiles) files.push(...artifactFiles);
|
|
114
165
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
global.testomatioArtifacts = [];
|
|
119
|
-
}
|
|
166
|
+
// GET KEY-VALUEs from storage
|
|
167
|
+
const keyValueStorage = require('./storages/key-value-storage');
|
|
168
|
+
const keyValues = keyValueStorage.get(test_id);
|
|
120
169
|
|
|
121
170
|
for (const file of files) {
|
|
122
171
|
uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
|
|
@@ -129,7 +178,7 @@ class Client {
|
|
|
129
178
|
|
|
130
179
|
const artifacts = await Promise.all(uploadedFiles);
|
|
131
180
|
|
|
132
|
-
global.testomatioArtifacts = [];
|
|
181
|
+
// global.testomatioArtifacts = [];
|
|
133
182
|
|
|
134
183
|
this.totalUploaded += uploadedFiles.filter(n => n).length;
|
|
135
184
|
|
|
@@ -147,19 +196,22 @@ class Client {
|
|
|
147
196
|
message,
|
|
148
197
|
run_time: parseFloat(time),
|
|
149
198
|
artifacts,
|
|
199
|
+
meta: keyValues,
|
|
150
200
|
};
|
|
151
201
|
|
|
152
|
-
this.queue = this.queue
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
202
|
+
this.queue = this.queue.then(() =>
|
|
203
|
+
Promise.all(
|
|
204
|
+
this.pipes.map(async p => {
|
|
205
|
+
try {
|
|
206
|
+
const result = await p.addTest(data);
|
|
207
|
+
return { pipe: p.toString(), result };
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.log(APP_PREFIX, p.toString(), err);
|
|
210
|
+
}
|
|
211
|
+
}),
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
|
|
163
215
|
return this.queue;
|
|
164
216
|
}
|
|
165
217
|
|
|
@@ -171,22 +223,25 @@ class Client {
|
|
|
171
223
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
172
224
|
*/
|
|
173
225
|
updateRunStatus(status, isParallel = false) {
|
|
226
|
+
debug('Updating run status...');
|
|
174
227
|
// all pipes disabled, skipping
|
|
175
|
-
if (!this.pipes
|
|
228
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
176
229
|
|
|
177
230
|
const runParams = { status, parallel: isParallel };
|
|
178
231
|
|
|
179
232
|
this.queue = this.queue
|
|
180
233
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
181
234
|
.then(() => {
|
|
182
|
-
debug('TOTAL
|
|
235
|
+
debug('TOTAL artifacts', this.totalUploaded);
|
|
236
|
+
if (this.totalUploaded && !upload.isArtifactsEnabled())
|
|
237
|
+
debug(`${this.totalUploaded} artifacts are not uploaded, because artifacts uploading is not enabled`);
|
|
183
238
|
|
|
184
|
-
if (
|
|
239
|
+
if (this.totalUploaded && upload.isArtifactsEnabled()) {
|
|
185
240
|
console.log(
|
|
186
241
|
APP_PREFIX,
|
|
187
|
-
`🗄️
|
|
242
|
+
`🗄️ ${this.totalUploaded} artifacts ${
|
|
188
243
|
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
189
|
-
} uploaded to S3 bucket
|
|
244
|
+
} uploaded to S3 bucket`,
|
|
190
245
|
);
|
|
191
246
|
}
|
|
192
247
|
})
|
|
@@ -213,17 +268,21 @@ class Client {
|
|
|
213
268
|
stack += '\n\n';
|
|
214
269
|
}
|
|
215
270
|
|
|
271
|
+
const customFilter = process.env.TESTOMATIO_STACK_IGNORE;
|
|
272
|
+
|
|
216
273
|
try {
|
|
274
|
+
let hasFrame = false;
|
|
217
275
|
const record = createCallsiteRecord({
|
|
218
276
|
forError: error,
|
|
277
|
+
isCallsiteFrame: frame => {
|
|
278
|
+
if (customFilter && minimatch(frame.getFileName(), customFilter)) return false;
|
|
279
|
+
if (hasFrame) return false;
|
|
280
|
+
if (isNotInternalFrame(frame)) hasFrame = true;
|
|
281
|
+
return hasFrame;
|
|
282
|
+
}
|
|
219
283
|
});
|
|
220
284
|
if (record && !record.filename.startsWith('http')) {
|
|
221
|
-
stack += record.renderSync({
|
|
222
|
-
stackFilter: frame =>
|
|
223
|
-
frame.getFileName().indexOf(sep) > -1 &&
|
|
224
|
-
frame.getFileName().indexOf('node_modules') < 0 &&
|
|
225
|
-
frame.getFileName().indexOf('internal') < 0,
|
|
226
|
-
});
|
|
285
|
+
stack += record.renderSync({ stackFilter: isNotInternalFrame });
|
|
227
286
|
}
|
|
228
287
|
return stack;
|
|
229
288
|
} catch (e) {
|
|
@@ -232,4 +291,10 @@ class Client {
|
|
|
232
291
|
}
|
|
233
292
|
}
|
|
234
293
|
|
|
294
|
+
function isNotInternalFrame(frame) {
|
|
295
|
+
return frame.getFileName().includes(sep) &&
|
|
296
|
+
!frame.getFileName().includes('node_modules') &&
|
|
297
|
+
!frame.getFileName().includes('internal')
|
|
298
|
+
}
|
|
299
|
+
|
|
235
300
|
module.exports = Client;
|
package/lib/constants.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
2
4
|
|
|
3
5
|
const APP_PREFIX = chalk.gray('[TESTOMATIO]');
|
|
4
|
-
const TESTOMAT_ARTIFACT_SUFFIX = "testomatio_artifact_";
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
mainDir: "testomatio_tmp",
|
|
8
|
-
}
|
|
7
|
+
const TESTOMAT_TMP_STORAGE_DIR = path.join(os.tmpdir(), 'testomatio_tmp');
|
|
9
8
|
|
|
10
9
|
const CSV_HEADERS = [
|
|
11
10
|
{ id: 'suite_title', title: 'Suite_title' },
|
|
@@ -24,8 +23,7 @@ const STATUS = {
|
|
|
24
23
|
|
|
25
24
|
module.exports = {
|
|
26
25
|
APP_PREFIX,
|
|
27
|
-
|
|
28
|
-
TESTOMAT_TMP_STORAGE,
|
|
26
|
+
TESTOMAT_TMP_STORAGE_DIR,
|
|
29
27
|
CSV_HEADERS,
|
|
30
28
|
STATUS,
|
|
31
29
|
}
|
|
@@ -10,24 +10,42 @@ class JavaAdapter extends Adapter {
|
|
|
10
10
|
|
|
11
11
|
formatTest(t) {
|
|
12
12
|
const fileParts = t.suite_title.split('.')
|
|
13
|
-
const example = t.title.match(/\[(.*)\]/)?.[1];
|
|
14
|
-
if (example) t.example = { "#": example }
|
|
15
13
|
|
|
16
14
|
t.file = namespaceToFileName(t.suite_title);
|
|
17
15
|
t.title = t.title.split('(')[0];
|
|
16
|
+
|
|
17
|
+
// detect params
|
|
18
|
+
const paramMatches = t.title.match(/\[(.*?)\]/g);
|
|
19
|
+
|
|
20
|
+
if (paramMatches) {
|
|
21
|
+
const params = paramMatches.map((_match, index) => `param${index + 1}`);
|
|
22
|
+
if (params.length === 1) params[0] = 'param';
|
|
23
|
+
let paramIndex = 0;
|
|
24
|
+
|
|
25
|
+
t.title = t.title.replace(/: \[(.*?)\]/g, () => {
|
|
26
|
+
if (params.length < 2) return `\${param}`
|
|
27
|
+
const paramName = params[paramIndex] || `param${paramIndex + 1}`;
|
|
28
|
+
paramIndex++;
|
|
29
|
+
return `\${${paramName}}`;
|
|
30
|
+
});
|
|
31
|
+
const example = {};
|
|
32
|
+
paramMatches.forEach((match, index) => { example[params[index]] = match.replace(/[[\]]/g, '') });
|
|
33
|
+
t.example = example;
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ')
|
|
19
37
|
return t;
|
|
20
38
|
}
|
|
21
39
|
|
|
22
|
-
formatStack(t) {
|
|
23
|
-
|
|
40
|
+
// formatStack(t) {
|
|
41
|
+
// const stack = super.formatStack(t);
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
// const file = t.suite_title.split('.');
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
45
|
+
// const fileLine = `at .*${file[file.length - 1]}\.java:(\\d+)` // eslint-disable-line no-useless-escape
|
|
46
|
+
// const regexp = new RegExp(fileLine,"g")
|
|
47
|
+
// return stack.replace(regexp, `${this.opts.javaTests}${path.sep}${namespaceToFileName(t.suite_title)}:$1:`);
|
|
48
|
+
// }
|
|
31
49
|
}
|
|
32
50
|
|
|
33
51
|
function namespaceToFileName(fileName) {
|
package/lib/pipe/csv.js
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const csvWriter = require('csv-writer');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
const merge = require('lodash.merge');
|
|
7
|
-
const { isSameTest, getCurrentDateTime } = require('../
|
|
7
|
+
const { isSameTest, getCurrentDateTime } = require('../utils/utils');
|
|
8
8
|
const { CSV_HEADERS } = require('../constants');
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -40,6 +40,9 @@ class CsvPipe {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// TODO: to using SET opts as argument => prepareRun(opts)
|
|
44
|
+
async prepareRun() {}
|
|
45
|
+
|
|
43
46
|
async createRun() {
|
|
44
47
|
// empty
|
|
45
48
|
}
|