@testomatio/reporter 0.6.9 → 0.7.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/Changelog.md +4 -0
- package/lib/adapter/cucumber/current.js +187 -0
- package/lib/adapter/cucumber/legacy.js +136 -0
- package/lib/adapter/cucumber.js +11 -134
- package/lib/fileUploader.js +2 -1
- package/lib/xmlReader.js +0 -1
- package/package.json +4 -3
- package/tests/adapter/examples/cucumber/cucumber.js +4 -0
- package/tests/adapter/examples/cucumber/features/dummy.feature +9 -0
- package/tests/adapter/examples/cucumber/features/steps/steps.js +9 -0
- package/tests/adapter/index.test.js +6 -0
package/Changelog.md
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
const { Formatter, Status, formatterHelpers } = require("@cucumber/cucumber");
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const { TestomatClient, TRConstants } = require('../../reporter');
|
|
5
|
+
const { parseTest } = require('../../util');
|
|
6
|
+
const { formatLocation, GherkinDocumentParser, PickleParser } = formatterHelpers
|
|
7
|
+
const {
|
|
8
|
+
getGherkinScenarioMap,
|
|
9
|
+
getGherkinScenarioLocationMap,
|
|
10
|
+
getGherkinStepMap,
|
|
11
|
+
} = GherkinDocumentParser
|
|
12
|
+
const { getPickleStepMap } = PickleParser
|
|
13
|
+
|
|
14
|
+
class CucumberReporter extends Formatter {
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super(options);
|
|
17
|
+
options.eventBroadcaster.on('envelope', this.parseEnvelope.bind(this))
|
|
18
|
+
this.failures = [];
|
|
19
|
+
this.cases = [];
|
|
20
|
+
|
|
21
|
+
const apiKey = process.env.TESTOMATIO;
|
|
22
|
+
if (!apiKey || apiKey === '') {
|
|
23
|
+
console.log(chalk.red('TESTOMATIO key is empty, ignoring reports'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.client = new TestomatClient({ apiKey });
|
|
28
|
+
this.status = TRConstants.PASSED;
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parseEnvelope(envelope) {
|
|
33
|
+
if (envelope.testCaseStarted && this.client) this.client.createRun()
|
|
34
|
+
if (envelope.testCaseFinished) this.onTestCaseFinished(envelope.testCaseFinished);
|
|
35
|
+
if (envelope.testRunFinished) this.onTestRunFinished(envelope);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onTestCaseFinished(testCaseFinished) {
|
|
39
|
+
const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseFinished.testCaseStartedId);
|
|
40
|
+
const statuses = [...new Set(Object.values(testCaseAttempt.stepResults || {}).map(r => r.status))]
|
|
41
|
+
|
|
42
|
+
let example;
|
|
43
|
+
let status = TRConstants.PASSED;
|
|
44
|
+
let color = 'green';
|
|
45
|
+
let message;
|
|
46
|
+
|
|
47
|
+
this.cases.push(testCaseAttempt);
|
|
48
|
+
|
|
49
|
+
if (testCaseAttempt.worstTestStepResult) {
|
|
50
|
+
if (testCaseAttempt.worstTestStepResult.status === 'SKIPPED') {
|
|
51
|
+
status = TRConstants.SKIPPED;
|
|
52
|
+
}
|
|
53
|
+
if (testCaseAttempt.worstTestStepResult.status === 'UNDEFINED') {
|
|
54
|
+
status = TRConstants.SKIPPED;
|
|
55
|
+
message = 'Undefined steps. Implement missing steps and rerun this scenario';
|
|
56
|
+
}
|
|
57
|
+
if (testCaseAttempt.worstTestStepResult.status === 'FAILED') {
|
|
58
|
+
message = testCaseAttempt?.worstTestStepResult?.message;
|
|
59
|
+
status = TRConstants.FAILED;
|
|
60
|
+
}
|
|
61
|
+
color = getStatusColor(testCaseAttempt.worstTestStepResult.status);
|
|
62
|
+
if (status !== TRConstants.PASSED) this.failures.push(testCaseAttempt);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (testCaseAttempt.pickle.astNodeIds.length > 1) {
|
|
66
|
+
example = getExample(testCaseAttempt);
|
|
67
|
+
testCaseAttempt.example = example;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const scenario = testCaseAttempt.pickle.name;
|
|
71
|
+
const testId = testCaseAttempt.pickle.id;
|
|
72
|
+
|
|
73
|
+
let exampleString = '';
|
|
74
|
+
if (example) exampleString = ` ${example.join(' | ')}`
|
|
75
|
+
let cliMessage = `${chalk.bold(scenario)}${exampleString}: ${chalk[color].bold(status.toUpperCase())} `;
|
|
76
|
+
|
|
77
|
+
if (message) cliMessage += chalk.gray(message.split('\n')[0]);
|
|
78
|
+
console.log(cliMessage);
|
|
79
|
+
|
|
80
|
+
if (status !== TRConstants.PASSED && status !== TRConstants.SKIPPED) {
|
|
81
|
+
this.status = TRConstants.FAILED;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const time = Object.values(testCaseAttempt.stepResults)
|
|
85
|
+
.map(t => t.duration)
|
|
86
|
+
.reduce((sum, duration) => {
|
|
87
|
+
return sum + (duration.seconds * 1000) + (duration.nanos / 1000000)
|
|
88
|
+
}, 0)
|
|
89
|
+
|
|
90
|
+
if (!this.client) return;
|
|
91
|
+
|
|
92
|
+
this.client.addTestRun(testId, status, {
|
|
93
|
+
// error: testCaseAttempt.worstTestStepResult.message,
|
|
94
|
+
message,
|
|
95
|
+
steps: getSteps(testCaseAttempt).map(s => s.toString()).join('\n').trim(),
|
|
96
|
+
example: Object.assign({}, example),
|
|
97
|
+
title: scenario,
|
|
98
|
+
time,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
onTestRunFinished(envelope) {
|
|
103
|
+
if (this.failures.length > 0) {
|
|
104
|
+
console.log(chalk.bold('\nSUMMARY:\n\n'));
|
|
105
|
+
|
|
106
|
+
this.failures.forEach((tc, i) => {
|
|
107
|
+
let message = ` ${i+1}) ${tc.pickle.name}\n`;
|
|
108
|
+
|
|
109
|
+
const steps = getSteps(tc);
|
|
110
|
+
|
|
111
|
+
steps.forEach(s => {
|
|
112
|
+
message += ` ${s.toString()}\n`
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
console.log(message);
|
|
116
|
+
if (tc?.worstTestStepResult?.message) {
|
|
117
|
+
console.log(chalk.red(tc?.worstTestStepResult?.message));
|
|
118
|
+
}
|
|
119
|
+
console.log();
|
|
120
|
+
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const { testRunFinished } = envelope;
|
|
125
|
+
|
|
126
|
+
const bgColor = testRunFinished.success ? 'bgGreen' : 'bgRed';
|
|
127
|
+
console.log();
|
|
128
|
+
console.log(chalk[bgColor](chalk.bold(testRunFinished.success ? ' SUCCESS ' : ' FALIURE ') + `| Total Scenarios: ${chalk.bold(this.cases.length)} `));
|
|
129
|
+
|
|
130
|
+
if (!this.client) return;
|
|
131
|
+
|
|
132
|
+
this.client.updateRunStatus(testRunFinished.success ? TRConstants.PASSED : TRConstants.FAILED)
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function getSteps(tc) {
|
|
137
|
+
const stepIds = Object.keys(tc.stepResults);
|
|
138
|
+
const pickleSteps = getPickleStepMap(tc.pickle)
|
|
139
|
+
return stepIds.map(stepId => {
|
|
140
|
+
const ts = tc.testCase.testSteps.find(ts => ts.id === stepId);
|
|
141
|
+
if (!ts) return;
|
|
142
|
+
if (!ts.pickleStepId) return;
|
|
143
|
+
const result = tc.stepResults[stepId];
|
|
144
|
+
const pickleStep = pickleSteps[ts.pickleStepId];
|
|
145
|
+
const sourceStepId = pickleStep.astNodeIds[0];
|
|
146
|
+
const step = {
|
|
147
|
+
text: pickleStep.text,
|
|
148
|
+
duration: result.duration,
|
|
149
|
+
status: result.status,
|
|
150
|
+
}
|
|
151
|
+
const color = getStatusColor(result.status);
|
|
152
|
+
if (sourceStepId && getGherkinStepMap(tc.gherkinDocument)[sourceStepId]) {
|
|
153
|
+
step.keyword = getGherkinStepMap(tc.gherkinDocument)[sourceStepId].keyword;
|
|
154
|
+
}
|
|
155
|
+
step.toString = function() {
|
|
156
|
+
const duration = step.duration.seconds * 1000 + (step.duration.nanos / 1000000)
|
|
157
|
+
const durationString = ' ' + chalk.gray(`(${Number(duration).toFixed(2)}ms)`);
|
|
158
|
+
const stepString = `${chalk.bold(this.keyword)}${this.text}`.trim();
|
|
159
|
+
if (color == 'red') return chalk.red(stepString) + durationString;
|
|
160
|
+
if (color == 'yellow') return chalk.yellow(stepString) + durationString;
|
|
161
|
+
return stepString + durationString;
|
|
162
|
+
}
|
|
163
|
+
return step;
|
|
164
|
+
}).filter(s => !!s)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getStatusColor(status) {
|
|
168
|
+
if (status === 'UNDEFINED') return 'yellow';
|
|
169
|
+
if (status === 'SKIPPED') return 'yellow';
|
|
170
|
+
if (status === 'FAILED') return 'red';
|
|
171
|
+
return 'green'
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getExample(testCaseAttempt) {
|
|
175
|
+
const nodesMap = getGherkinScenarioLocationMap(testCaseAttempt.gherkinDocument);
|
|
176
|
+
const exampleNodeId = testCaseAttempt.pickle.astNodeIds[1];
|
|
177
|
+
if (!nodesMap[exampleNodeId]) return;
|
|
178
|
+
const featureDoc = fs.readFileSync(testCaseAttempt.gherkinDocument.uri).toString();
|
|
179
|
+
const { line } = nodesMap[exampleNodeId];
|
|
180
|
+
const example = featureDoc.split('\n')[line-1];
|
|
181
|
+
if (example) {
|
|
182
|
+
return example.trim().split('|').filter(r => !!r).map(r => r.trim());
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = CucumberReporter;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-unresolved
|
|
2
|
+
const { Formatter } = require('cucumber');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { TestomatClient, TRConstants } = require('../../eporter');
|
|
5
|
+
const { parseTest } = require('../../util');
|
|
6
|
+
|
|
7
|
+
const createTestomatFormatter = apiKey => {
|
|
8
|
+
if (!apiKey || apiKey === '') {
|
|
9
|
+
console.log(chalk.red('TESTOMATIO key is empty, ignoring reports'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const documents = {};
|
|
13
|
+
const dataTableMap = {};
|
|
14
|
+
|
|
15
|
+
const addDocument = gherkinDocument => {
|
|
16
|
+
documents[gherkinDocument.uri] = gherkinDocument.document;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getTitle = scenario => {
|
|
20
|
+
let { name } = scenario;
|
|
21
|
+
if (scenario.tags.length) {
|
|
22
|
+
let tags = '';
|
|
23
|
+
for (const tag of scenario.tags) {
|
|
24
|
+
tags = `${tags} ${tag.name}`;
|
|
25
|
+
}
|
|
26
|
+
name = `${name}${tags}`;
|
|
27
|
+
}
|
|
28
|
+
return name;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getFeature = uri => documents[uri].feature;
|
|
32
|
+
|
|
33
|
+
const getScenario = location => {
|
|
34
|
+
const { children } = getFeature(location.uri);
|
|
35
|
+
for (const scenario of children) {
|
|
36
|
+
if (scenario.type === 'Scenario' && scenario.location.line === location.line) {
|
|
37
|
+
return scenario;
|
|
38
|
+
}
|
|
39
|
+
if (scenario.type === 'ScenarioOutline') {
|
|
40
|
+
for (const example of scenario.examples) {
|
|
41
|
+
for (const tableBody of example.tableBody) {
|
|
42
|
+
if (tableBody.location.line === location.line) {
|
|
43
|
+
return scenario;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const loadDataTable = (scenario, uri) => {
|
|
54
|
+
if (scenario.type === 'ScenarioOutline') {
|
|
55
|
+
for (const example of scenario.examples) {
|
|
56
|
+
for (const tableBody of example.tableBody) {
|
|
57
|
+
const dataMap = example.tableHeader.cells.reduce((acc, cell, index) => {
|
|
58
|
+
acc[cell.value] = tableBody.cells[index].value;
|
|
59
|
+
return acc;
|
|
60
|
+
}, {});
|
|
61
|
+
|
|
62
|
+
dataTableMap[`${uri}:${tableBody.location.line}`] = JSON.stringify(dataMap);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const getDataTableMap = (scenario, sourceLocation) => {
|
|
69
|
+
const key = `${sourceLocation.uri}:${sourceLocation.line}`;
|
|
70
|
+
if (!dataTableMap[key]) {
|
|
71
|
+
loadDataTable(scenario, sourceLocation.uri);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return dataTableMap[key] || '';
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getTestId = scenario => {
|
|
78
|
+
if (scenario) {
|
|
79
|
+
for (const tag of scenario.tags) {
|
|
80
|
+
const testId = parseTest(tag.name);
|
|
81
|
+
if (testId) return testId;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return class TestomatFormatter extends Formatter {
|
|
89
|
+
constructor(options) {
|
|
90
|
+
super(options);
|
|
91
|
+
|
|
92
|
+
if (!apiKey) return;
|
|
93
|
+
|
|
94
|
+
this.client = new TestomatClient({ apiKey });
|
|
95
|
+
this.status = TRConstants.PASSED;
|
|
96
|
+
|
|
97
|
+
options.eventBroadcaster.on('gherkin-document', addDocument);
|
|
98
|
+
options.eventBroadcaster.on('test-run-started', () => this.client.createRun());
|
|
99
|
+
options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
|
|
100
|
+
options.eventBroadcaster.on('test-run-finished', () => this.client.updateRunStatus(this.status));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
onTestCaseFinished(event) {
|
|
104
|
+
const scenario = getScenario(event.sourceLocation);
|
|
105
|
+
const testId = getTestId(scenario);
|
|
106
|
+
const status = event.result.status === 'undefined' ? TRConstants.SKIPPED : event.result.status;
|
|
107
|
+
|
|
108
|
+
let example = getDataTableMap(scenario, event.sourceLocation);
|
|
109
|
+
if (example) example = JSON.parse(example);
|
|
110
|
+
|
|
111
|
+
if (!scenario.name) return;
|
|
112
|
+
|
|
113
|
+
let message = '';
|
|
114
|
+
let cliMessage = `- ${scenario.name}: ${chalk.bold(status.toUpperCase())}`;
|
|
115
|
+
|
|
116
|
+
if (event.result.status === 'undefined') {
|
|
117
|
+
cliMessage += chalk.yellow(
|
|
118
|
+
' (undefined steps. Run Cucumber without this formatter and implement missing steps)',
|
|
119
|
+
);
|
|
120
|
+
message = 'Undefined steps. Implement missing steps and rerun this scenario';
|
|
121
|
+
}
|
|
122
|
+
console.log(cliMessage);
|
|
123
|
+
if (status !== TRConstants.PASSED && status !== TRConstants.SKIPPED) {
|
|
124
|
+
this.status = TRConstants.FAILED;
|
|
125
|
+
}
|
|
126
|
+
this.client.addTestRun(testId, status, {
|
|
127
|
+
error: event.result.exception,
|
|
128
|
+
message,
|
|
129
|
+
example,
|
|
130
|
+
title: getTitle(scenario),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
module.exports = createTestomatFormatter(process.env.TESTOMATIO);
|
package/lib/adapter/cucumber.js
CHANGED
|
@@ -1,136 +1,13 @@
|
|
|
1
1
|
// eslint-disable-next-line import/no-unresolved
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
try {
|
|
3
|
+
require.resolve('cucumber');
|
|
4
|
+
module.exports = require('./cucumber/legacy');
|
|
5
|
+
} catch (err) {
|
|
6
|
+
try {
|
|
7
|
+
require.resolve("@cucumber/cucumber");
|
|
8
|
+
module.exports = require('./cucumber/current');
|
|
9
|
+
} catch (err) {
|
|
10
|
+
console.error('Cucumber was not detected. Report won\'t be sent', err);
|
|
11
|
+
module.exports = {}
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
const documents = {};
|
|
13
|
-
const dataTableMap = {};
|
|
14
|
-
|
|
15
|
-
const addDocument = gherkinDocument => {
|
|
16
|
-
documents[gherkinDocument.uri] = gherkinDocument.document;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const getTitle = scenario => {
|
|
20
|
-
let { name } = scenario;
|
|
21
|
-
if (scenario.tags.length) {
|
|
22
|
-
let tags = '';
|
|
23
|
-
for (const tag of scenario.tags) {
|
|
24
|
-
tags = `${tags} ${tag.name}`;
|
|
25
|
-
}
|
|
26
|
-
name = `${name}${tags}`;
|
|
27
|
-
}
|
|
28
|
-
return name;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const getFeature = uri => documents[uri].feature;
|
|
32
|
-
|
|
33
|
-
const getScenario = location => {
|
|
34
|
-
const { children } = getFeature(location.uri);
|
|
35
|
-
for (const scenario of children) {
|
|
36
|
-
if (scenario.type === 'Scenario' && scenario.location.line === location.line) {
|
|
37
|
-
return scenario;
|
|
38
|
-
}
|
|
39
|
-
if (scenario.type === 'ScenarioOutline') {
|
|
40
|
-
for (const example of scenario.examples) {
|
|
41
|
-
for (const tableBody of example.tableBody) {
|
|
42
|
-
if (tableBody.location.line === location.line) {
|
|
43
|
-
return scenario;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return null;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const loadDataTable = (scenario, uri) => {
|
|
54
|
-
if (scenario.type === 'ScenarioOutline') {
|
|
55
|
-
for (const example of scenario.examples) {
|
|
56
|
-
for (const tableBody of example.tableBody) {
|
|
57
|
-
const dataMap = example.tableHeader.cells.reduce((acc, cell, index) => {
|
|
58
|
-
acc[cell.value] = tableBody.cells[index].value;
|
|
59
|
-
return acc;
|
|
60
|
-
}, {});
|
|
61
|
-
|
|
62
|
-
dataTableMap[`${uri}:${tableBody.location.line}`] = JSON.stringify(dataMap);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const getDataTableMap = (scenario, sourceLocation) => {
|
|
69
|
-
const key = `${sourceLocation.uri}:${sourceLocation.line}`;
|
|
70
|
-
if (!dataTableMap[key]) {
|
|
71
|
-
loadDataTable(scenario, sourceLocation.uri);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return dataTableMap[key] || '';
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const getTestId = scenario => {
|
|
78
|
-
if (scenario) {
|
|
79
|
-
for (const tag of scenario.tags) {
|
|
80
|
-
const testId = parseTest(tag.name);
|
|
81
|
-
if (testId) return testId;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return null;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
return class TestomatFormatter extends Formatter {
|
|
89
|
-
constructor(options) {
|
|
90
|
-
super(options);
|
|
91
|
-
|
|
92
|
-
if (!apiKey) return;
|
|
93
|
-
|
|
94
|
-
this.client = new TestomatClient({ apiKey });
|
|
95
|
-
this.status = TRConstants.PASSED;
|
|
96
|
-
|
|
97
|
-
options.eventBroadcaster.on('gherkin-document', addDocument);
|
|
98
|
-
options.eventBroadcaster.on('test-run-started', () => this.client.createRun());
|
|
99
|
-
options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
|
|
100
|
-
options.eventBroadcaster.on('test-run-finished', () => this.client.updateRunStatus(this.status));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
onTestCaseFinished(event) {
|
|
104
|
-
const scenario = getScenario(event.sourceLocation);
|
|
105
|
-
const testId = getTestId(scenario);
|
|
106
|
-
const status = event.result.status === 'undefined' ? TRConstants.SKIPPED : event.result.status;
|
|
107
|
-
|
|
108
|
-
let example = getDataTableMap(scenario, event.sourceLocation);
|
|
109
|
-
if (example) example = JSON.parse(example);
|
|
110
|
-
|
|
111
|
-
if (!scenario.name) return;
|
|
112
|
-
|
|
113
|
-
let message = '';
|
|
114
|
-
let cliMessage = `- ${scenario.name}: ${chalk.bold(status.toUpperCase())}`;
|
|
115
|
-
|
|
116
|
-
if (event.result.status === 'undefined') {
|
|
117
|
-
cliMessage += chalk.yellow(
|
|
118
|
-
' (undefined steps. Run Cucumber without this formatter and implement missing steps)',
|
|
119
|
-
);
|
|
120
|
-
message = 'Undefined steps. Implement missing steps and rerun this scenario';
|
|
121
|
-
}
|
|
122
|
-
console.log(cliMessage);
|
|
123
|
-
if (status !== TRConstants.PASSED && status !== TRConstants.SKIPPED) {
|
|
124
|
-
this.status = TRConstants.FAILED;
|
|
125
|
-
}
|
|
126
|
-
this.client.addTestRun(testId, status, {
|
|
127
|
-
error: event.result.exception,
|
|
128
|
-
message,
|
|
129
|
-
example,
|
|
130
|
-
title: getTitle(scenario),
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
module.exports = createTestomatFormatter(process.env.TESTOMATIO);
|
|
13
|
+
}
|
package/lib/fileUploader.js
CHANGED
|
@@ -2,6 +2,7 @@ const AWS = require('aws-sdk');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
+
const { randomUUID } = require('crypto');
|
|
5
6
|
const memoize = require('lodash.memoize');
|
|
6
7
|
const { APP_PREFIX } = require('./constants');
|
|
7
8
|
|
|
@@ -55,7 +56,7 @@ const uploadUsingS3 = async (filePath, runId) => {
|
|
|
55
56
|
const s3 = new AWS.S3(config);
|
|
56
57
|
const file = fs.readFileSync(filePath);
|
|
57
58
|
|
|
58
|
-
Key = `${runId}/${Key || path.basename(filePath)}`;
|
|
59
|
+
Key = `${runId}/${randomUUID()}-${Key || path.basename(filePath)}`;
|
|
59
60
|
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
60
61
|
|
|
61
62
|
try {
|
package/lib/xmlReader.js
CHANGED
|
@@ -8,7 +8,6 @@ const { XMLParser } = require("fast-xml-parser");
|
|
|
8
8
|
const { APP_PREFIX } = require('./constants');
|
|
9
9
|
const { isValidUrl, fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace } = require('./util');
|
|
10
10
|
const upload = require('./fileUploader');
|
|
11
|
-
|
|
12
11
|
const Adapter = require('./junit-adapter/adapter');
|
|
13
12
|
const JavaScriptAdapter = require('./junit-adapter/javascript');
|
|
14
13
|
const JavaAdapter = require('./junit-adapter/java');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-beta.1",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"repository": "git@github.com:testomatio/reporter.git",
|
|
@@ -30,15 +30,16 @@
|
|
|
30
30
|
"test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
|
|
31
31
|
"test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.js'",
|
|
32
32
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
33
|
-
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'"
|
|
33
|
+
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
34
|
+
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && cucumber-js"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"@wdio/reporter": "*",
|
|
37
38
|
"codeceptjs": "*",
|
|
38
|
-
"cucumber": "*",
|
|
39
39
|
"mocha": "*"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@cucumber/cucumber": "^8.6.0",
|
|
42
43
|
"@wdio/reporter": "^7.16.13",
|
|
43
44
|
"chai": "^4.3.6",
|
|
44
45
|
"codeceptjs": "^3.2.3",
|
|
@@ -5,6 +5,7 @@ const JestReporter = require('../../lib/adapter/jest');
|
|
|
5
5
|
const MochaReporter = require('../../lib/adapter/mocha');
|
|
6
6
|
const JasmineReporter = require('../../lib/adapter/jasmine');
|
|
7
7
|
const CodeceptReporter = require('../../lib/adapter/codecept');
|
|
8
|
+
const CucumberReporter = require('../../lib/adapter/cucumber');
|
|
8
9
|
const { registerHandlers } = require('./utils');
|
|
9
10
|
const { host, port, TESTOMATIO_URL, TESTOMATIO, RUN_ID } = require('./config');
|
|
10
11
|
|
|
@@ -29,6 +30,11 @@ const params = [
|
|
|
29
30
|
positiveCmd: `TESTOMATIO_URL=${TESTOMATIO_URL} TESTOMATIO=${TESTOMATIO} npm run test:adapter:jasmine:example`,
|
|
30
31
|
negativeCmd: `TESTOMATIO_URL=${TESTOMATIO_URL} npm run test:adapter:jasmine:example`,
|
|
31
32
|
},
|
|
33
|
+
{
|
|
34
|
+
adapterName: CucumberReporter.name,
|
|
35
|
+
positiveCmd: `TESTOMATIO_URL=${TESTOMATIO_URL} TESTOMATIO=${TESTOMATIO} npm run test:adapter:cucumber:example`,
|
|
36
|
+
negativeCmd: `TESTOMATIO_URL=${TESTOMATIO_URL} npm run test:adapter:cucumber:example`,
|
|
37
|
+
},
|
|
32
38
|
];
|
|
33
39
|
|
|
34
40
|
describe('Adapters', () => {
|