@testomatio/reporter 1.1.0-beta.mocha-create.9 → 1.1.0-rc.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/README.md +1 -0
- package/lib/adapter/codecept.js +81 -6
- package/lib/adapter/cucumber/current.js +15 -8
- package/lib/adapter/cucumber/legacy.js +2 -1
- package/lib/adapter/cypress-plugin/index.js +46 -26
- package/lib/adapter/jest.js +39 -0
- package/lib/adapter/mocha.js +58 -5
- package/lib/adapter/playwright.js +21 -9
- package/lib/bin/startTest.js +2 -1
- package/lib/client.js +46 -44
- package/lib/config.js +5 -0
- package/lib/data-storage.js +203 -0
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/testomatio.js +2 -1
- package/lib/reporter-functions.js +21 -9
- package/lib/reporter.js +8 -1
- package/lib/{storages/artifact-storage.js → services/artifacts.js} +17 -30
- package/lib/services/index.js +13 -0
- package/lib/{storages/key-value-storage.js → services/key-values.js} +18 -18
- package/lib/{storages → services}/logger.js +32 -20
- package/lib/utils/utils.js +13 -4
- package/lib/xmlReader.js +106 -104
- package/package.json +2 -2
- package/Changelog.md +0 -367
- package/lib/storages/data-storage.js +0 -307
package/README.md
CHANGED
package/lib/adapter/codecept.js
CHANGED
|
@@ -4,6 +4,7 @@ const TestomatClient = require('../client');
|
|
|
4
4
|
const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
5
5
|
const upload = require('../fileUploader');
|
|
6
6
|
const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
|
|
7
|
+
const { services } = require('../services');
|
|
7
8
|
|
|
8
9
|
if (!global.codeceptjs) {
|
|
9
10
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
@@ -51,21 +52,56 @@ function CodeceptReporter(config) {
|
|
|
51
52
|
|
|
52
53
|
recorder.startUnlessRunning();
|
|
53
54
|
|
|
54
|
-
global.testomatioRunningEnvironment = 'codeceptjs';
|
|
55
|
-
|
|
56
55
|
// Listening to events
|
|
57
56
|
event.dispatcher.on(event.all.before, () => {
|
|
58
57
|
// clear tmp dir
|
|
59
58
|
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
60
59
|
|
|
61
|
-
recorder.add('Creating new run', () =>
|
|
60
|
+
// recorder.add('Creating new run', () => );
|
|
61
|
+
client.createRun();
|
|
62
62
|
videos = [];
|
|
63
63
|
traces = [];
|
|
64
64
|
|
|
65
65
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
let hookSteps = [];
|
|
69
|
+
let suiteHookRunning = false;
|
|
70
|
+
|
|
71
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
72
|
+
suiteHookRunning = true;
|
|
73
|
+
hookSteps = [];
|
|
74
|
+
global.testomatioDataStore.steps = [];
|
|
75
|
+
|
|
76
|
+
services.setContext(suite.fullTitle());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
event.dispatcher.on(event.suite.after, () => {
|
|
80
|
+
services.setContext(null);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
event.dispatcher.on(event.hook.started, () => {
|
|
84
|
+
// global.testomatioDataStore.steps = [];
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
event.dispatcher.on(event.hook.passed, () => {
|
|
88
|
+
if (suiteHookRunning) {
|
|
89
|
+
hookSteps.push(...global.testomatioDataStore.steps);
|
|
90
|
+
services.setContext(null);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
event.dispatcher.on(event.hook.failed, () => {
|
|
95
|
+
if (suiteHookRunning) {
|
|
96
|
+
hookSteps.push(...global.testomatioDataStore.steps);
|
|
97
|
+
services.setContext(null);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
event.dispatcher.on(event.test.before, test => {
|
|
102
|
+
suiteHookRunning = false;
|
|
103
|
+
global.testomatioDataStore.steps = [];
|
|
104
|
+
|
|
69
105
|
recorder.add(() => {
|
|
70
106
|
currentMetaStep = [];
|
|
71
107
|
// output.reset();
|
|
@@ -76,11 +112,16 @@ function CodeceptReporter(config) {
|
|
|
76
112
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
77
113
|
// reset steps
|
|
78
114
|
global.testomatioDataStore.steps = [];
|
|
115
|
+
|
|
116
|
+
services.setContext(test.fullTitle());
|
|
79
117
|
});
|
|
80
118
|
|
|
81
119
|
event.dispatcher.on(event.test.started, test => {
|
|
120
|
+
services.setContext(test.fullTitle());
|
|
121
|
+
|
|
82
122
|
testTimeMap[test.id] = Date.now();
|
|
83
123
|
if (global.testomatioDataStore) global.testomatioDataStore.currentlyRunningTestId = getIdFromTestTitle(test.title);
|
|
124
|
+
// start logging
|
|
84
125
|
});
|
|
85
126
|
|
|
86
127
|
event.dispatcher.on(event.all.result, async () => {
|
|
@@ -104,6 +145,12 @@ function CodeceptReporter(config) {
|
|
|
104
145
|
}
|
|
105
146
|
const testId = parseTest(tags);
|
|
106
147
|
const testObj = getTestAndMessage(title);
|
|
148
|
+
|
|
149
|
+
const logs = getTestLogs(test);
|
|
150
|
+
const manuallyAttachedArtifacts = services.artifacts.get(test.fullTitle());
|
|
151
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
152
|
+
services.setContext(null);
|
|
153
|
+
|
|
107
154
|
client.addTestRun(STATUS.PASSED, {
|
|
108
155
|
...stripExampleFromTitle(title),
|
|
109
156
|
suite_title: test.parent && test.parent.title,
|
|
@@ -111,6 +158,9 @@ function CodeceptReporter(config) {
|
|
|
111
158
|
time: getDuration(test),
|
|
112
159
|
steps: global.testomatioDataStore.steps.join('\n') || null,
|
|
113
160
|
test_id: testId,
|
|
161
|
+
logs,
|
|
162
|
+
manuallyAttachedArtifacts,
|
|
163
|
+
meta: keyValues,
|
|
114
164
|
});
|
|
115
165
|
// output.stop();
|
|
116
166
|
});
|
|
@@ -152,6 +202,11 @@ function CodeceptReporter(config) {
|
|
|
152
202
|
if (artifacts.screenshot) files.push({ path: artifacts.screenshot, type: 'image/png' });
|
|
153
203
|
// todo: video must be uploaded later....
|
|
154
204
|
|
|
205
|
+
const logs = getTestLogs(test);
|
|
206
|
+
const manuallyAttachedArtifacts = services.artifacts.get(test.fullTitle());
|
|
207
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
208
|
+
services.setContext(null);
|
|
209
|
+
|
|
155
210
|
const reportTestPromise = client
|
|
156
211
|
.addTestRun(STATUS.FAILED, {
|
|
157
212
|
...stripExampleFromTitle(title),
|
|
@@ -162,6 +217,9 @@ function CodeceptReporter(config) {
|
|
|
162
217
|
time: getDuration(test),
|
|
163
218
|
files,
|
|
164
219
|
steps: global.testomatioDataStore?.steps?.join('\n') || null,
|
|
220
|
+
logs,
|
|
221
|
+
manuallyAttachedArtifacts,
|
|
222
|
+
meta: keyValues,
|
|
165
223
|
})
|
|
166
224
|
.then(pipes => {
|
|
167
225
|
testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
@@ -230,7 +288,7 @@ function CodeceptReporter(config) {
|
|
|
230
288
|
currentMetaStep = metaSteps;
|
|
231
289
|
stepShift = 2 * shift;
|
|
232
290
|
|
|
233
|
-
const durationMs = +new Date() -
|
|
291
|
+
const durationMs = +new Date() - +stepStart;
|
|
234
292
|
let duration = '';
|
|
235
293
|
if (durationMs) {
|
|
236
294
|
duration = repeat(1) + chalk.grey(`(${durationMs}ms)`);
|
|
@@ -255,7 +313,7 @@ async function uploadAttachments(client, attachments, messagePrefix, attachmentT
|
|
|
255
313
|
if (attachments.length > 0) {
|
|
256
314
|
console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType}/-s ...`);
|
|
257
315
|
|
|
258
|
-
const promises = attachments.map(async
|
|
316
|
+
const promises = attachments.map(async attachment => {
|
|
259
317
|
const { testId, title, path, type } = attachment;
|
|
260
318
|
const file = { path, type, title };
|
|
261
319
|
return client.addTestRun(undefined, {
|
|
@@ -303,4 +361,21 @@ function repeat(num) {
|
|
|
303
361
|
return ''.padStart(num, ' ');
|
|
304
362
|
}
|
|
305
363
|
|
|
364
|
+
// TODO: think about moving to some common utils
|
|
365
|
+
function getTestLogs(test) {
|
|
366
|
+
const suiteLogsArr = services.logger.getLogs(test.parent.fullTitle());
|
|
367
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
368
|
+
const testLogsArr = services.logger.getLogs(test.fullTitle());
|
|
369
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
370
|
+
|
|
371
|
+
let logs = '';
|
|
372
|
+
if (suiteLogs) {
|
|
373
|
+
logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
|
|
374
|
+
}
|
|
375
|
+
if (testLogs) {
|
|
376
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
377
|
+
}
|
|
378
|
+
return logs;
|
|
379
|
+
}
|
|
380
|
+
|
|
306
381
|
module.exports = CodeceptReporter;
|
|
@@ -4,8 +4,9 @@ const chalk = require('chalk');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
-
const logger = require('../../storages/logger');
|
|
8
7
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
8
|
+
const { TESTOMATIO } = require('../../config');
|
|
9
|
+
const { services } = require('../../services');
|
|
9
10
|
|
|
10
11
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
11
12
|
const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
|
|
@@ -29,10 +30,8 @@ class CucumberReporter extends Formatter {
|
|
|
29
30
|
this.failures = [];
|
|
30
31
|
this.cases = [];
|
|
31
32
|
|
|
32
|
-
this.client = new TestomatClient({ apiKey: options.apiKey ||
|
|
33
|
+
this.client = new TestomatClient({ apiKey: options.apiKey || TESTOMATIO });
|
|
33
34
|
this.status = STATUS.PASSED;
|
|
34
|
-
|
|
35
|
-
global.testomatioRunningEnvironment = 'cucumber:current';
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
parseEnvelope(envelope) {
|
|
@@ -49,9 +48,10 @@ class CucumberReporter extends Formatter {
|
|
|
49
48
|
|
|
50
49
|
onTestCaseStarted(testCaseStarted) {
|
|
51
50
|
const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseStarted.id);
|
|
52
|
-
const testId = getTestId(testCaseAttempt.pickle);
|
|
53
51
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
54
|
-
|
|
52
|
+
|
|
53
|
+
const testTitle = testCaseAttempt.pickle.name + testCaseAttempt.pickle.uri;
|
|
54
|
+
services.setContext(testTitle);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
onTestCaseFinished(testCaseFinished) {
|
|
@@ -109,7 +109,10 @@ class CucumberReporter extends Formatter {
|
|
|
109
109
|
|
|
110
110
|
if (!this.client) return;
|
|
111
111
|
|
|
112
|
-
const
|
|
112
|
+
const testTitle = testCaseAttempt.pickle.name + testCaseAttempt.pickle.uri;
|
|
113
|
+
const logs = services.logger.getLogs(testTitle).join('\n');
|
|
114
|
+
const artifacts = services.artifacts.get(testTitle);
|
|
115
|
+
const keyValues = services.keyValues.get(testTitle);
|
|
113
116
|
|
|
114
117
|
this.client.addTestRun(status, {
|
|
115
118
|
// error: testCaseAttempt.worstTestStepResult.message,
|
|
@@ -119,11 +122,15 @@ class CucumberReporter extends Formatter {
|
|
|
119
122
|
.join('\n')
|
|
120
123
|
.trim(),
|
|
121
124
|
example: { ...example },
|
|
122
|
-
|
|
125
|
+
logs,
|
|
126
|
+
manuallyAttachedArtifacts: artifacts,
|
|
127
|
+
meta: keyValues,
|
|
123
128
|
title: scenario,
|
|
124
129
|
test_id: testId,
|
|
125
130
|
time,
|
|
126
131
|
});
|
|
132
|
+
|
|
133
|
+
services.setContext(null);
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
onTestRunFinished(envelope) {
|
|
@@ -4,6 +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 { TESTOMATIO } = require('../../config');
|
|
7
8
|
|
|
8
9
|
const createTestomatFormatter = apiKey => {
|
|
9
10
|
if (!apiKey || apiKey === '') {
|
|
@@ -151,4 +152,4 @@ const createTestomatFormatter = apiKey => {
|
|
|
151
152
|
};
|
|
152
153
|
};
|
|
153
154
|
|
|
154
|
-
module.exports = createTestomatFormatter(
|
|
155
|
+
module.exports = createTestomatFormatter(TESTOMATIO);
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
const { STATUS } = require('../../constants');
|
|
2
2
|
const { parseTest, parseSuite } = require('../../utils/utils');
|
|
3
3
|
const TestomatClient = require('../../client');
|
|
4
|
+
const { TESTOMATIO } = require('../../config');
|
|
4
5
|
|
|
5
6
|
const testomatioReporter = on => {
|
|
6
|
-
if (!
|
|
7
|
+
if (!TESTOMATIO) {
|
|
7
8
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
8
|
-
return
|
|
9
|
+
return;
|
|
9
10
|
}
|
|
10
|
-
const client = new TestomatClient({ apiKey:
|
|
11
|
+
const client = new TestomatClient({ apiKey: TESTOMATIO });
|
|
11
12
|
|
|
12
|
-
on('before:run', async
|
|
13
|
+
on('before:run', async run => {
|
|
13
14
|
// TODO: looks like client.env does not exist
|
|
14
15
|
if (!client.env) {
|
|
15
|
-
client.env = `${run.browser.displayName},${run.system.osName}
|
|
16
|
+
client.env = `${run.browser.displayName},${run.system.osName}`;
|
|
16
17
|
}
|
|
17
18
|
await client.createRun();
|
|
18
19
|
});
|
|
@@ -26,8 +27,9 @@ const testomatioReporter = on => {
|
|
|
26
27
|
const lastAttemptIndex = test.attempts.length - 1;
|
|
27
28
|
const latestAttempt = test.attempts[lastAttemptIndex];
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
const
|
|
30
|
+
// latestAttempt.duration && latestAttempt.error were available in adapters version up to 13 JFYI
|
|
31
|
+
const time = latestAttempt.duration || latestAttempt.wallClockDuration || test.duration;
|
|
32
|
+
let error = latestAttempt.error;
|
|
31
33
|
|
|
32
34
|
let title = test.title.pop();
|
|
33
35
|
const examples = title.match(/\(example (#\d+)\)/);
|
|
@@ -40,23 +42,28 @@ const testomatioReporter = on => {
|
|
|
40
42
|
const testId = parseTest(title);
|
|
41
43
|
const suiteId = parseSuite(suiteTitle);
|
|
42
44
|
|
|
43
|
-
if (error) {
|
|
44
|
-
error
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
45
|
+
if (!error && test.displayError) {
|
|
46
|
+
error = { message: test.displayError };
|
|
47
|
+
error.inspect = function () {
|
|
48
|
+
// eslint-disable-line func-names
|
|
49
|
+
return this.message;
|
|
50
|
+
};
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
const formattedError = error
|
|
54
|
+
? {
|
|
55
|
+
message: error.message,
|
|
56
|
+
inspect:
|
|
57
|
+
error.inspect ||
|
|
58
|
+
function () {
|
|
59
|
+
return this.message;
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
: '';
|
|
63
|
+
|
|
52
64
|
const screenshots = Array.isArray(results.screenshots)
|
|
53
65
|
? results.screenshots
|
|
54
|
-
.filter(
|
|
55
|
-
screenshot =>
|
|
56
|
-
screenshot?.path &&
|
|
57
|
-
screenshot?.path.includes(title) &&
|
|
58
|
-
screenshot?.takenAt
|
|
59
|
-
)
|
|
66
|
+
.filter(screenshot => screenshot?.path && screenshot?.path.includes(title) && screenshot?.takenAt)
|
|
60
67
|
.map(screenshot => screenshot.path)
|
|
61
68
|
: [];
|
|
62
69
|
|
|
@@ -64,17 +71,30 @@ const testomatioReporter = on => {
|
|
|
64
71
|
|
|
65
72
|
let state;
|
|
66
73
|
switch (test.state) {
|
|
67
|
-
case 'passed':
|
|
68
|
-
|
|
74
|
+
case 'passed':
|
|
75
|
+
state = STATUS.PASSED;
|
|
76
|
+
break;
|
|
77
|
+
case 'failed':
|
|
78
|
+
state = STATUS.FAILED;
|
|
79
|
+
break;
|
|
69
80
|
case 'skipped':
|
|
70
|
-
case 'pending':
|
|
81
|
+
case 'pending':
|
|
71
82
|
default:
|
|
72
83
|
state = STATUS.SKIPPED;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
|
-
addSpecTestsPromises.push(
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
addSpecTestsPromises.push(
|
|
87
|
+
client.addTestRun(state, {
|
|
88
|
+
title,
|
|
89
|
+
time,
|
|
90
|
+
example,
|
|
91
|
+
error: formattedError,
|
|
92
|
+
files,
|
|
93
|
+
suite_title: suiteTitle,
|
|
94
|
+
test_id: testId,
|
|
95
|
+
suite_id: suiteId,
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
await Promise.all(addSpecTestsPromises);
|
package/lib/adapter/jest.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
1
2
|
const TestomatClient = require('../client');
|
|
2
3
|
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
3
4
|
const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
|
|
5
|
+
const { services } = require('../services');
|
|
6
|
+
const debug = require('debug')('@testomatio/reporter:adapter-jest');
|
|
4
7
|
|
|
5
8
|
class JestReporter {
|
|
6
9
|
constructor(globalConfig, options) {
|
|
@@ -16,6 +19,19 @@ class JestReporter {
|
|
|
16
19
|
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
// start of test file (including beforeAll)
|
|
23
|
+
onTestStart(testFile) {
|
|
24
|
+
debug('Start running test file:', testFile.path);
|
|
25
|
+
services.setContext(testFile.path);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// start of the test (including beforeEach)
|
|
29
|
+
onTestCaseStart(test, testCase) {
|
|
30
|
+
debug('Start running test:', testCase.fullName);
|
|
31
|
+
services.setContext(testCase.fullName);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// end of test file! (there is also onTestCaseResult listener)
|
|
19
35
|
onTestResult(test, testResult) {
|
|
20
36
|
if (!this.client) return;
|
|
21
37
|
|
|
@@ -39,6 +55,10 @@ class JestReporter {
|
|
|
39
55
|
suite_title = result.fullName.replace(result.title, '');
|
|
40
56
|
}
|
|
41
57
|
|
|
58
|
+
const logs = getTestLogs(result);
|
|
59
|
+
const artifacts = services.artifacts.get(testResult.fullName);
|
|
60
|
+
const keyValues = services.keyValues.get(testResult.fullName);
|
|
61
|
+
|
|
42
62
|
const deducedStatus = status === 'pending' ? 'skipped' : status;
|
|
43
63
|
// In jest if test is not matched with test name pattern it is considered as skipped.
|
|
44
64
|
// So adding a check if it is skipped for real or because of test pattern
|
|
@@ -50,6 +70,9 @@ class JestReporter {
|
|
|
50
70
|
steps,
|
|
51
71
|
title,
|
|
52
72
|
time: duration,
|
|
73
|
+
logs,
|
|
74
|
+
manuallyAttachedArtifacts: artifacts,
|
|
75
|
+
meta: keyValues,
|
|
53
76
|
});
|
|
54
77
|
}
|
|
55
78
|
}
|
|
@@ -64,4 +87,20 @@ class JestReporter {
|
|
|
64
87
|
}
|
|
65
88
|
}
|
|
66
89
|
|
|
90
|
+
function getTestLogs(testResult) {
|
|
91
|
+
const suiteLogsArr = services.logger.getLogs(testResult.testFilePath);
|
|
92
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
93
|
+
const testLogsArr = services.logger.getLogs(testResult.fullName);
|
|
94
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
95
|
+
|
|
96
|
+
let logs = '';
|
|
97
|
+
if (suiteLogs) {
|
|
98
|
+
logs += `${chalk.bold('\t--- Suite ---')}\n${suiteLogs}`;
|
|
99
|
+
}
|
|
100
|
+
if (testLogs) {
|
|
101
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
102
|
+
}
|
|
103
|
+
return logs;
|
|
104
|
+
}
|
|
105
|
+
|
|
67
106
|
module.exports = JestReporter;
|
package/lib/adapter/mocha.js
CHANGED
|
@@ -4,9 +4,20 @@ 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
|
-
|
|
8
|
-
const {
|
|
9
|
-
|
|
7
|
+
const { TESTOMATIO } = require('../config');
|
|
8
|
+
const { services } = require('../services');
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
EVENT_RUN_BEGIN,
|
|
12
|
+
EVENT_RUN_END,
|
|
13
|
+
EVENT_TEST_FAIL,
|
|
14
|
+
EVENT_TEST_PASS,
|
|
15
|
+
EVENT_TEST_PENDING,
|
|
16
|
+
EVENT_SUITE_BEGIN,
|
|
17
|
+
EVENT_SUITE_END,
|
|
18
|
+
EVENT_TEST_BEGIN,
|
|
19
|
+
EVENT_TEST_END,
|
|
20
|
+
} = Mocha.Runner.constants;
|
|
10
21
|
|
|
11
22
|
function MochaReporter(runner, opts) {
|
|
12
23
|
Mocha.reporters.Base.call(this, runner);
|
|
@@ -15,7 +26,7 @@ function MochaReporter(runner, opts) {
|
|
|
15
26
|
let skipped = 0;
|
|
16
27
|
// let artifactStore;
|
|
17
28
|
|
|
18
|
-
const apiKey = opts?.reporterOptions?.apiKey ||
|
|
29
|
+
const apiKey = opts?.reporterOptions?.apiKey || TESTOMATIO;
|
|
19
30
|
|
|
20
31
|
const client = new TestomatClient({ apiKey });
|
|
21
32
|
|
|
@@ -25,12 +36,32 @@ function MochaReporter(runner, opts) {
|
|
|
25
36
|
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
26
37
|
});
|
|
27
38
|
|
|
39
|
+
runner.on(EVENT_SUITE_BEGIN, async suite => {
|
|
40
|
+
services.setContext(suite.fullTitle());
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
runner.on(EVENT_SUITE_END, async () => {
|
|
44
|
+
services.setContext(null);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
runner.on(EVENT_TEST_BEGIN, async test => {
|
|
48
|
+
services.setContext(test.fullTitle());
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
runner.on(EVENT_TEST_END, async () => {
|
|
52
|
+
services.setContext(null);
|
|
53
|
+
});
|
|
54
|
+
|
|
28
55
|
runner.on(EVENT_TEST_PASS, async test => {
|
|
29
56
|
passes += 1;
|
|
30
57
|
|
|
31
58
|
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
32
59
|
const testId = parseTest(test.title);
|
|
33
60
|
|
|
61
|
+
const logs = getTestLogs(test);
|
|
62
|
+
const artifacts = services.artifacts.get(test.fullTitle());
|
|
63
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
64
|
+
|
|
34
65
|
client.addTestRun(STATUS.PASSED, {
|
|
35
66
|
test_id: testId,
|
|
36
67
|
suite_title: getSuiteTitle(test),
|
|
@@ -38,6 +69,9 @@ function MochaReporter(runner, opts) {
|
|
|
38
69
|
code: test.body.toString(),
|
|
39
70
|
file: getFile(test),
|
|
40
71
|
time: test.duration,
|
|
72
|
+
logs,
|
|
73
|
+
manuallyAttachedArtifacts: artifacts,
|
|
74
|
+
meta: keyValues,
|
|
41
75
|
});
|
|
42
76
|
});
|
|
43
77
|
|
|
@@ -60,6 +94,8 @@ function MochaReporter(runner, opts) {
|
|
|
60
94
|
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
61
95
|
const testId = parseTest(test.title);
|
|
62
96
|
|
|
97
|
+
const logs = getTestLogs(test);
|
|
98
|
+
|
|
63
99
|
client.addTestRun(STATUS.FAILED, {
|
|
64
100
|
error: err,
|
|
65
101
|
suite_title: getSuiteTitle(test),
|
|
@@ -68,6 +104,7 @@ function MochaReporter(runner, opts) {
|
|
|
68
104
|
title: getTestName(test),
|
|
69
105
|
code: test.body.toString(),
|
|
70
106
|
time: test.duration,
|
|
107
|
+
logs,
|
|
71
108
|
});
|
|
72
109
|
});
|
|
73
110
|
|
|
@@ -78,13 +115,29 @@ function MochaReporter(runner, opts) {
|
|
|
78
115
|
});
|
|
79
116
|
}
|
|
80
117
|
|
|
118
|
+
function getTestLogs(test) {
|
|
119
|
+
|
|
120
|
+
const suiteLogsArr = services.logger.getLogs(test.parent.fullTitle());
|
|
121
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
122
|
+
const testLogsArr = services.logger.getLogs(test.fullTitle());
|
|
123
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
124
|
+
|
|
125
|
+
let logs = '';
|
|
126
|
+
if (suiteLogs) {
|
|
127
|
+
logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
|
|
128
|
+
}
|
|
129
|
+
if (testLogs) {
|
|
130
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
131
|
+
}
|
|
132
|
+
return logs;
|
|
133
|
+
}
|
|
134
|
+
|
|
81
135
|
// To have this reporter "extend" a built-in reporter uncomment the following line:
|
|
82
136
|
Mocha.utils.inherits(MochaReporter, Mocha.reporters.Spec);
|
|
83
137
|
|
|
84
138
|
module.exports = MochaReporter;
|
|
85
139
|
|
|
86
140
|
function getSuiteTitle(test, pathArr = []) {
|
|
87
|
-
let root = pathArr.length === 0;
|
|
88
141
|
|
|
89
142
|
if (test.parent.parent) getSuiteTitle(test.parent, pathArr);
|
|
90
143
|
|
|
@@ -25,18 +25,13 @@ class PlaywrightReporter {
|
|
|
25
25
|
this.client.createRun();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/*
|
|
29
|
-
onTestBegin(test) {
|
|
30
|
-
// does not work; value is not to the storage context
|
|
31
|
-
global.testTitle = test.title;
|
|
32
|
-
} */
|
|
33
|
-
|
|
34
28
|
onTestEnd(test, result) {
|
|
35
29
|
if (!this.client) return;
|
|
36
30
|
|
|
37
|
-
let testId = parseTest(test.title);
|
|
38
|
-
|
|
39
31
|
const { title } = test;
|
|
32
|
+
|
|
33
|
+
let testId = parseTest(title);
|
|
34
|
+
|
|
40
35
|
const { error, duration } = result;
|
|
41
36
|
|
|
42
37
|
const suite_title = test.parent ? test.parent.title : null;
|
|
@@ -59,7 +54,8 @@ class PlaywrightReporter {
|
|
|
59
54
|
title,
|
|
60
55
|
steps: steps.join('\n'),
|
|
61
56
|
time: duration,
|
|
62
|
-
|
|
57
|
+
logs,
|
|
58
|
+
// TODO: attach artifacts
|
|
63
59
|
})
|
|
64
60
|
.then(pipes => {
|
|
65
61
|
testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
@@ -143,4 +139,20 @@ function tmpFile(prefix = 'tmp.') {
|
|
|
143
139
|
return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex'));
|
|
144
140
|
}
|
|
145
141
|
|
|
142
|
+
function initPlaywrightForStorage() {
|
|
143
|
+
try {
|
|
144
|
+
// @ts-ignore-next-line
|
|
145
|
+
// eslint-disable-next-line import/no-unresolved
|
|
146
|
+
const { test } = require('@playwright/test');
|
|
147
|
+
// eslint-disable-next-line no-empty-pattern
|
|
148
|
+
test.beforeEach(async ({}, testInfo) => {
|
|
149
|
+
const fullTestTitle = `${testInfo.file}_${testInfo.title}`;
|
|
150
|
+
global.testomatioTestTitle = fullTestTitle;
|
|
151
|
+
});
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// ignore
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
module.exports = PlaywrightReporter;
|
|
158
|
+
module.exports.initPlaywrightForStorage = initPlaywrightForStorage;
|
package/lib/bin/startTest.js
CHANGED
|
@@ -5,6 +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 { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ program
|
|
|
20
21
|
|
|
21
22
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
22
23
|
|
|
23
|
-
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] ||
|
|
24
|
+
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || TESTOMATIO;
|
|
24
25
|
const title = process.env.TESTOMATIO_TITLE;
|
|
25
26
|
|
|
26
27
|
if (launch) {
|