@testomatio/reporter 1.1.0-beta-2 → 1.2.0-beta-1
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/_ArtifactStorageOld.js +142 -0
- package/lib/adapter/codecept.js +2 -2
- package/lib/adapter/cucumber/current.js +3 -3
- package/lib/adapter/cucumber/legacy.js +2 -2
- package/lib/adapter/jest.js +16 -2
- package/lib/adapter/mocha.js +43 -15
- package/lib/adapter/playwright.js +21 -33
- package/lib/artifactStorage.js +25 -0
- package/lib/client.js +21 -18
- package/lib/constants.js +13 -4
- package/lib/{storages/data-storage.js → dataStorage.js} +47 -122
- package/lib/{storages/logger.js → logger.js} +33 -4
- package/lib/pipe/html.js +187 -0
- package/lib/pipe/index.js +2 -0
- package/lib/reporter.js +4 -9
- package/lib/template/real-data-example.js +47 -0
- package/lib/template/template.hbs +249 -0
- package/lib/template/testomatio.hbs +720 -0
- package/lib/utils/utils.js +0 -14
- package/package.json +2 -1
- package/lib/reporter-functions.js +0 -43
- package/lib/storages/artifact-storage.js +0 -70
- package/lib/storages/key-value-storage.js +0 -58
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:storage');
|
|
2
|
+
const { join, resolve } = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const uuid = require('uuid');
|
|
6
|
+
const { TESTOMAT_ARTIFACT_SUFFIX } = require('./constants');
|
|
7
|
+
const { specificTestInfo } = require('./utils/utils');
|
|
8
|
+
|
|
9
|
+
class ArtifactStorage {
|
|
10
|
+
|
|
11
|
+
constructor(params) {
|
|
12
|
+
this.isFile = false;
|
|
13
|
+
this.isMemory = true;
|
|
14
|
+
this.storage = params?.toFile;
|
|
15
|
+
|
|
16
|
+
if (this.storage) {
|
|
17
|
+
// this is fine to use when you start saving artifacts;
|
|
18
|
+
// but when you need to retrieve them, you will not know if there is "isFile" param,
|
|
19
|
+
// because you need to create a new instance of ArtifactStorage inside client
|
|
20
|
+
// so the solution is make it opposite to isMemory (which will be retrieved from global)
|
|
21
|
+
this.isFile = true;
|
|
22
|
+
this.isMemory = false;
|
|
23
|
+
this.tmpDirFullpath = this.createTestomatTmpDir(ArtifactStorage._tmpPrefix);
|
|
24
|
+
debug('SAVE to tmp folder mode enabled!');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static async storeToFile(tmpDirName, artifact, testSuffix = "test") {
|
|
29
|
+
// this assumes to use multiple files for each test. do we really want this?
|
|
30
|
+
const suffix = TESTOMAT_ARTIFACT_SUFFIX + uuid.v4() + testSuffix;
|
|
31
|
+
const dirpath = join(os.tmpdir(), tmpDirName);
|
|
32
|
+
// why json?
|
|
33
|
+
const filepath = resolve(dirpath, `${suffix}.json`);
|
|
34
|
+
|
|
35
|
+
return fs.promises.appendFile(filepath, JSON.stringify(artifact));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static async artifact(artifact, context) {
|
|
39
|
+
// TODO: mode for different pre-hooks. As variant - "all-tests" || "only_fail"
|
|
40
|
+
// const mode = process.env.TESTOMAT_ARTIFACTS || "only_fail";
|
|
41
|
+
|
|
42
|
+
// you save the artifact without specifying its source - test id
|
|
43
|
+
if (Array.isArray(global.testomatioArtifacts)) {
|
|
44
|
+
debug("Saving artifacts to global storage");
|
|
45
|
+
|
|
46
|
+
global.testomatioArtifacts.push(artifact);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (global?.testomatioArtifacts === undefined) {
|
|
50
|
+
const tmpDirNames = ArtifactStorage.tmpTestomatDirNames();
|
|
51
|
+
|
|
52
|
+
const testSuffix = specificTestInfo(context.test);
|
|
53
|
+
|
|
54
|
+
if (Array.isArray(tmpDirNames) && tmpDirNames.length) {
|
|
55
|
+
debug("Saving artifacts to memory tmp folder");
|
|
56
|
+
|
|
57
|
+
return ArtifactStorage.storeToFile(tmpDirNames[tmpDirNames.length - 1], artifact, testSuffix);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async artifactByTestName(test) {
|
|
63
|
+
const list = [];
|
|
64
|
+
|
|
65
|
+
if (this.isFile && this.tmpDirFullpath) {
|
|
66
|
+
const files = fs.readdirSync(this.tmpDirFullpath);
|
|
67
|
+
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
if (file.includes(test)) {
|
|
70
|
+
const buff = await fs.promises.readFile(`${this.tmpDirFullpath }/${ file}`);
|
|
71
|
+
|
|
72
|
+
list.push(JSON.parse(buff.toString()));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return list;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// returns all the content from all files; do we really need it?
|
|
81
|
+
async tmpContents() {
|
|
82
|
+
const contents = [];
|
|
83
|
+
|
|
84
|
+
if (this.isFile && this.tmpDirFullpath) {
|
|
85
|
+
const files = fs.readdirSync(this.tmpDirFullpath);
|
|
86
|
+
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
const buff = await fs.promises.readFile(`${this.tmpDirFullpath }/${ file}`);
|
|
89
|
+
const content = buff.toString();
|
|
90
|
+
|
|
91
|
+
contents.push(content);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return contents;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
createTestomatTmpDir(clientPrefix) {
|
|
99
|
+
return fs.mkdtempSync(join(os.tmpdir(), clientPrefix));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
clearTmpDirByName(name) {
|
|
103
|
+
const tmpDirPath = join(os.tmpdir(), name);
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(tmpDirPath)) {
|
|
106
|
+
fs.rmSync(tmpDirPath, { recursive: true });
|
|
107
|
+
debug(` Testomat tmpDir = ${tmpDirPath} was deleted successfully!`);
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// no need to use multiple dirs; we can implement it later if required
|
|
114
|
+
static tmpTestomatDirNames() {
|
|
115
|
+
const subname = ArtifactStorage._tmpPrefix || "tsmt_reporter";
|
|
116
|
+
|
|
117
|
+
return fs.readdirSync(os.tmpdir(), { withFileTypes: true })
|
|
118
|
+
.filter((item) => item.isDirectory())
|
|
119
|
+
.map((item) => item.name)
|
|
120
|
+
.filter((name) => name.includes(subname));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
cleanup() {
|
|
124
|
+
// when you do a cleanup, I know nothing about isFile param
|
|
125
|
+
if (this.isFile && this.tmpDirFullpath) {
|
|
126
|
+
const tmpDirNames = ArtifactStorage.tmpTestomatDirNames();
|
|
127
|
+
|
|
128
|
+
if (Array.isArray(tmpDirNames) && tmpDirNames.length) {
|
|
129
|
+
for (const name of tmpDirNames) {
|
|
130
|
+
this.clearTmpDirByName(name);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
debug("The tmp folder has not been created! Nothing to delete");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ArtifactStorage._tmpPrefix = "tsmt_reporter";
|
|
141
|
+
|
|
142
|
+
module.exports = ArtifactStorage;
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:adapter:codeceptjs');
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const TestomatClient = require('../client');
|
|
4
|
-
const { STATUS, APP_PREFIX,
|
|
4
|
+
const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE } = require('../constants');
|
|
5
5
|
const upload = require('../fileUploader');
|
|
6
6
|
const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
|
|
7
7
|
|
|
@@ -56,7 +56,7 @@ function CodeceptReporter(config) {
|
|
|
56
56
|
// Listening to events
|
|
57
57
|
event.dispatcher.on(event.all.before, () => {
|
|
58
58
|
// clear tmp dir
|
|
59
|
-
fileSystem.clearDir(
|
|
59
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
60
60
|
|
|
61
61
|
recorder.add('Creating new run', () => client.createRun());
|
|
62
62
|
videos = [];
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
const { Formatter, formatterHelpers } = require('@cucumber/cucumber');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const { STATUS,
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
-
const logger = require('../../
|
|
7
|
+
const logger = require('../../logger');
|
|
8
8
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
9
9
|
|
|
10
10
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
@@ -37,7 +37,7 @@ class CucumberReporter extends Formatter {
|
|
|
37
37
|
|
|
38
38
|
parseEnvelope(envelope) {
|
|
39
39
|
if (envelope.testRunStarted) {
|
|
40
|
-
fileSystem.clearDir(
|
|
40
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
41
41
|
}
|
|
42
42
|
if (envelope.testCaseStarted && this.client) {
|
|
43
43
|
this.client.createRun();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const { Formatter } = require('cucumber');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
5
|
-
const { STATUS,
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
7
|
|
|
8
8
|
const createTestomatFormatter = apiKey => {
|
|
@@ -97,7 +97,7 @@ const createTestomatFormatter = apiKey => {
|
|
|
97
97
|
|
|
98
98
|
options.eventBroadcaster.on('gherkin-document', addDocument);
|
|
99
99
|
options.eventBroadcaster.on('test-run-started', () => {
|
|
100
|
-
fileSystem.clearDir(
|
|
100
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
101
101
|
this?.client?.createRun();
|
|
102
102
|
});
|
|
103
103
|
options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
|
package/lib/adapter/jest.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const TestomatClient = require('../client');
|
|
2
|
-
const { STATUS,
|
|
2
|
+
const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
|
|
3
3
|
const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
|
|
4
4
|
|
|
5
5
|
class JestReporter {
|
|
@@ -11,9 +11,20 @@ class JestReporter {
|
|
|
11
11
|
this.client.createRun();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
static getIdOfCurrentlyRunningTest() {
|
|
15
|
+
if (!process.env.JEST_WORKER_ID) return null;
|
|
16
|
+
try {
|
|
17
|
+
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
18
|
+
// eslint-disable-next-line no-undef
|
|
19
|
+
if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
14
25
|
onRunStart() {
|
|
15
26
|
// clear tmp dir
|
|
16
|
-
fileSystem.clearDir(
|
|
27
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
onTestResult(test, testResult) {
|
|
@@ -61,6 +72,9 @@ class JestReporter {
|
|
|
61
72
|
const { numFailedTests } = results;
|
|
62
73
|
const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
63
74
|
this.client.updateRunStatus(status);
|
|
75
|
+
|
|
76
|
+
// clear tmp dir
|
|
77
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
64
78
|
}
|
|
65
79
|
}
|
|
66
80
|
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
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');
|
|
3
4
|
const chalk = require('chalk');
|
|
4
5
|
const TestomatClient = require('../client');
|
|
5
|
-
const { STATUS,
|
|
6
|
-
const { parseTest, fileSystem } = require('../utils/utils');
|
|
6
|
+
const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
|
|
7
|
+
const { parseTest, specificTestInfo, fileSystem } = require('../utils/utils');
|
|
8
|
+
const ArtifactStorage = require('../_ArtifactStorageOld');
|
|
7
9
|
|
|
8
10
|
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
|
|
9
11
|
Mocha.Runner.constants;
|
|
@@ -13,7 +15,7 @@ function MochaReporter(runner, opts) {
|
|
|
13
15
|
let passes = 0;
|
|
14
16
|
let failures = 0;
|
|
15
17
|
let skipped = 0;
|
|
16
|
-
|
|
18
|
+
let artifactStore;
|
|
17
19
|
|
|
18
20
|
const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
|
|
19
21
|
|
|
@@ -22,7 +24,13 @@ function MochaReporter(runner, opts) {
|
|
|
22
24
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
23
25
|
client.createRun();
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
const params = {
|
|
28
|
+
toFile: true,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
artifactStore = runner._workerReporter !== undefined ? new ArtifactStorage(params) : new ArtifactStorage();
|
|
32
|
+
|
|
33
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
26
34
|
});
|
|
27
35
|
|
|
28
36
|
runner.on(EVENT_TEST_PASS, async test => {
|
|
@@ -30,11 +38,20 @@ function MochaReporter(runner, opts) {
|
|
|
30
38
|
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
31
39
|
const testId = parseTest(test.title);
|
|
32
40
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
const specificTest = specificTestInfo(test);
|
|
42
|
+
const content = await artifactStore.artifactByTestName(specificTest);
|
|
43
|
+
|
|
44
|
+
debug(`test=${specificTest} content = `, content);
|
|
45
|
+
|
|
46
|
+
client.addTestRun(
|
|
47
|
+
STATUS.PASSED,
|
|
48
|
+
{
|
|
49
|
+
test_id: testId,
|
|
50
|
+
title: test.title,
|
|
51
|
+
time: test.duration,
|
|
52
|
+
},
|
|
53
|
+
content,
|
|
54
|
+
);
|
|
38
55
|
});
|
|
39
56
|
|
|
40
57
|
runner.on(EVENT_TEST_PENDING, test => {
|
|
@@ -53,18 +70,29 @@ function MochaReporter(runner, opts) {
|
|
|
53
70
|
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
54
71
|
const testId = parseTest(test.title);
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
73
|
+
const specificTest = specificTestInfo(test);
|
|
74
|
+
const content = await artifactStore.artifactByTestName(specificTest);
|
|
75
|
+
|
|
76
|
+
debug(`fail test=${specificTest} content = `, content);
|
|
77
|
+
|
|
78
|
+
client.addTestRun(
|
|
79
|
+
STATUS.FAILED,
|
|
80
|
+
{
|
|
81
|
+
error: err,
|
|
82
|
+
test_id: testId,
|
|
83
|
+
title: test.title,
|
|
84
|
+
time: test.duration,
|
|
85
|
+
},
|
|
86
|
+
content,
|
|
87
|
+
);
|
|
62
88
|
});
|
|
63
89
|
|
|
64
90
|
runner.on(EVENT_RUN_END, () => {
|
|
65
91
|
const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
66
92
|
console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
67
93
|
client.updateRunStatus(status);
|
|
94
|
+
|
|
95
|
+
artifactStore.cleanup();
|
|
68
96
|
});
|
|
69
97
|
}
|
|
70
98
|
|
|
@@ -3,7 +3,7 @@ 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,
|
|
6
|
+
const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE } = require('../constants');
|
|
7
7
|
const TestomatioClient = require('../client');
|
|
8
8
|
const { isArtifactsEnabled } = require('../fileUploader');
|
|
9
9
|
const { parseTest, fileSystem } = require('../utils/utils');
|
|
@@ -19,17 +19,15 @@ class PlaywrightReporter {
|
|
|
19
19
|
|
|
20
20
|
onBegin(_config, suite) {
|
|
21
21
|
// clean data storage
|
|
22
|
-
fileSystem.clearDir(
|
|
22
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
|
|
23
23
|
if (!this.client) return;
|
|
24
24
|
this.suite = suite;
|
|
25
25
|
this.client.createRun();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
global.testTitle = test.title;
|
|
32
|
-
} */
|
|
28
|
+
// onTestBegin(test) {
|
|
29
|
+
// const testId = parseTest(test.title);
|
|
30
|
+
// }
|
|
33
31
|
|
|
34
32
|
onTestEnd(test, result) {
|
|
35
33
|
if (!this.client) return;
|
|
@@ -46,33 +44,23 @@ class PlaywrightReporter {
|
|
|
46
44
|
appendStep(step, steps);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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);
|
|
47
|
+
const logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
|
|
48
|
+
|
|
49
|
+
const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
|
|
50
|
+
error,
|
|
51
|
+
test_id: testId,
|
|
52
|
+
suite_title,
|
|
53
|
+
title,
|
|
54
|
+
steps: steps.join('\n'),
|
|
55
|
+
time: duration,
|
|
56
|
+
stack: logs,
|
|
57
|
+
}).then(pipes => {
|
|
58
|
+
testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
59
|
+
|
|
60
|
+
this.uploads.push({
|
|
61
|
+
testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
|
|
75
62
|
});
|
|
63
|
+
});
|
|
76
64
|
|
|
77
65
|
reportTestPromises.push(reportTestPromise);
|
|
78
66
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// const debug = require('debug')('@testomatio/reporter:logger');
|
|
2
|
+
const { DataStorage } = require('./dataStorage');
|
|
3
|
+
|
|
4
|
+
class ArtifactStorage {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.dataStorage = new DataStorage('artifact');
|
|
7
|
+
|
|
8
|
+
// singleton
|
|
9
|
+
if (!ArtifactStorage.instance) {
|
|
10
|
+
ArtifactStorage.instance = this;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
save(data, context = null) {
|
|
15
|
+
this.dataStorage.putData(data, context);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get(context) {
|
|
19
|
+
this.dataStorage.getData(context);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ArtifactStorage.instance = null;
|
|
24
|
+
|
|
25
|
+
module.exports = new ArtifactStorage();
|
package/lib/client.js
CHANGED
|
@@ -8,7 +8,6 @@ const { randomUUID } = require('crypto');
|
|
|
8
8
|
const upload = require('./fileUploader');
|
|
9
9
|
const { APP_PREFIX } = require('./constants');
|
|
10
10
|
const pipesFactory = require('./pipe');
|
|
11
|
-
const artifactStorage = require('./storages/artifact-storage');
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* @typedef {import('../types').TestData} TestData
|
|
@@ -97,6 +96,9 @@ class Client {
|
|
|
97
96
|
// all pipes disabled, skipping
|
|
98
97
|
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
99
98
|
|
|
99
|
+
global.testomatioArtifacts = [];
|
|
100
|
+
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
101
|
+
|
|
100
102
|
this.queue = this.queue
|
|
101
103
|
.then(() => Promise.all(this.pipes.map(p => p.createRun())))
|
|
102
104
|
.catch(err => console.log(APP_PREFIX, err))
|
|
@@ -110,9 +112,11 @@ class Client {
|
|
|
110
112
|
*
|
|
111
113
|
* @param {string|undefined} status
|
|
112
114
|
* @param {TestData} [testData]
|
|
115
|
+
* @param {string[]} [storeArtifacts]
|
|
113
116
|
* @returns {Promise<PipeResult[]>}
|
|
114
117
|
*/
|
|
115
|
-
async addTestRun(status, testData) {
|
|
118
|
+
async addTestRun(status, testData, storeArtifacts = []) {
|
|
119
|
+
debug('Adding test run for test', testData?.test_id || 'unknown test');
|
|
116
120
|
// all pipes disabled, skipping
|
|
117
121
|
if (!this.pipes?.filter(p => p.isEnabled).length) return [];
|
|
118
122
|
|
|
@@ -151,21 +155,23 @@ class Client {
|
|
|
151
155
|
|
|
152
156
|
stack += testData.stack || '';
|
|
153
157
|
|
|
154
|
-
// ATTACH LOGS from storage
|
|
155
158
|
// in some cases (e.g. using cucumber) logger instance becomes empty object, if import at the top of the file
|
|
156
|
-
const logger = require('./
|
|
159
|
+
const logger = require('./logger');
|
|
157
160
|
const testLogs = logger.getLogs(test_id);
|
|
158
161
|
// debug(`Test logs for ${test_id}:\n`, testLogs);
|
|
159
162
|
if (stack) stack += '\n\n';
|
|
160
163
|
stack += testLogs ? `${chalk.bold('Logs:')}\n${testLogs}` : '';
|
|
161
164
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
|
|
166
|
+
debug('CLIENT storeArtifact', storeArtifacts);
|
|
167
|
+
files.push(...storeArtifacts);
|
|
168
|
+
}
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
if (Array.isArray(global.testomatioArtifacts)) {
|
|
171
|
+
debug('CLIENT global[testomatioArtifacts]', global.testomatioArtifacts);
|
|
172
|
+
files.push(...global.testomatioArtifacts);
|
|
173
|
+
global.testomatioArtifacts = [];
|
|
174
|
+
}
|
|
169
175
|
|
|
170
176
|
for (const file of files) {
|
|
171
177
|
uploadedFiles.push(upload.uploadFileByPath(file, this.uuid));
|
|
@@ -178,7 +184,7 @@ class Client {
|
|
|
178
184
|
|
|
179
185
|
const artifacts = await Promise.all(uploadedFiles);
|
|
180
186
|
|
|
181
|
-
|
|
187
|
+
global.testomatioArtifacts = [];
|
|
182
188
|
|
|
183
189
|
this.totalUploaded += uploadedFiles.filter(n => n).length;
|
|
184
190
|
|
|
@@ -196,7 +202,6 @@ class Client {
|
|
|
196
202
|
message,
|
|
197
203
|
run_time: parseFloat(time),
|
|
198
204
|
artifacts,
|
|
199
|
-
meta: keyValues,
|
|
200
205
|
};
|
|
201
206
|
|
|
202
207
|
this.queue = this.queue.then(() =>
|
|
@@ -232,16 +237,14 @@ class Client {
|
|
|
232
237
|
this.queue = this.queue
|
|
233
238
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
234
239
|
.then(() => {
|
|
235
|
-
debug('TOTAL
|
|
236
|
-
if (this.totalUploaded && !upload.isArtifactsEnabled())
|
|
237
|
-
debug(`${this.totalUploaded} artifacts are not uploaded, because artifacts uploading is not enabled`);
|
|
240
|
+
debug('TOTAL uploaded files', this.totalUploaded);
|
|
238
241
|
|
|
239
|
-
if (this.totalUploaded
|
|
242
|
+
if (upload.isArtifactsEnabled() && this.totalUploaded > 0) {
|
|
240
243
|
console.log(
|
|
241
244
|
APP_PREFIX,
|
|
242
|
-
`🗄️ ${this.totalUploaded} artifacts ${
|
|
245
|
+
`🗄️ Total ${this.totalUploaded} artifacts ${
|
|
243
246
|
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
244
|
-
} uploaded to S3 bucket`,
|
|
247
|
+
} uploaded to S3 bucket `,
|
|
245
248
|
);
|
|
246
249
|
}
|
|
247
250
|
})
|
package/lib/constants.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
-
const os = require('os');
|
|
3
|
-
const path = require('path');
|
|
4
2
|
|
|
5
3
|
const APP_PREFIX = chalk.gray('[TESTOMATIO]');
|
|
4
|
+
const TESTOMAT_ARTIFACT_SUFFIX = "testomatio_artifact_";
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const TESTOMAT_TMP_STORAGE = {
|
|
7
|
+
mainDir: "testomatio_tmp",
|
|
8
|
+
}
|
|
8
9
|
|
|
9
10
|
const CSV_HEADERS = [
|
|
10
11
|
{ id: 'suite_title', title: 'Suite_title' },
|
|
@@ -20,10 +21,18 @@ const STATUS = {
|
|
|
20
21
|
SKIPPED: 'skipped',
|
|
21
22
|
FINISHED: 'finished',
|
|
22
23
|
};
|
|
24
|
+
// html pipe var
|
|
25
|
+
const HTML_REPORT = {
|
|
26
|
+
FOLDER: "html-report",
|
|
27
|
+
REPORT_DEFAULT_NAME: "testomatio-report.html",
|
|
28
|
+
TEMPLATE_NAME: 'testomatio.hbs'
|
|
29
|
+
};
|
|
23
30
|
|
|
24
31
|
module.exports = {
|
|
25
32
|
APP_PREFIX,
|
|
26
|
-
|
|
33
|
+
TESTOMAT_ARTIFACT_SUFFIX,
|
|
34
|
+
TESTOMAT_TMP_STORAGE,
|
|
27
35
|
CSV_HEADERS,
|
|
28
36
|
STATUS,
|
|
37
|
+
HTML_REPORT
|
|
29
38
|
}
|