@testomatio/reporter 1.0.0-beta.4 → 1.0.1-6.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.
@@ -4,27 +4,32 @@ const fs = require('fs');
4
4
  const os = require('os');
5
5
  const uuid = require('uuid');
6
6
  const { TESTOMAT_ARTIFACT_SUFFIX } = require('./constants');
7
- const { specificTestInfo } = require('./util');
7
+ const { specificTestInfo } = require('./utils/utils');
8
8
 
9
9
  class ArtifactStorage {
10
-
10
+
11
11
  constructor(params) {
12
- this._tmpPrefix = "tsmt_reporter";
13
12
  this.isFile = false;
14
13
  this.isMemory = true;
15
14
  this.storage = params?.toFile;
16
15
 
17
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)
18
21
  this.isFile = true;
19
22
  this.isMemory = false;
20
- this.tmpDirFullpath = this.createTestomatTmpDir(this._tmpPrefix);
23
+ this.tmpDirFullpath = this.createTestomatTmpDir(ArtifactStorage._tmpPrefix);
21
24
  debug('SAVE to tmp folder mode enabled!');
22
25
  }
23
26
  }
24
27
 
25
28
  static async storeToFile(tmpDirName, artifact, testSuffix = "test") {
29
+ // this assumes to use multiple files for each test. do we really want this?
26
30
  const suffix = TESTOMAT_ARTIFACT_SUFFIX + uuid.v4() + testSuffix;
27
31
  const dirpath = join(os.tmpdir(), tmpDirName);
32
+ // why json?
28
33
  const filepath = resolve(dirpath, `${suffix}.json`);
29
34
 
30
35
  return fs.promises.appendFile(filepath, JSON.stringify(artifact));
@@ -34,6 +39,7 @@ class ArtifactStorage {
34
39
  // TODO: mode for different pre-hooks. As variant - "all-tests" || "only_fail"
35
40
  // const mode = process.env.TESTOMAT_ARTIFACTS || "only_fail";
36
41
 
42
+ // you save the artifact without specifying its source - test id
37
43
  if (Array.isArray(global.testomatioArtifacts)) {
38
44
  debug("Saving artifacts to global storage");
39
45
 
@@ -71,6 +77,7 @@ class ArtifactStorage {
71
77
  return list;
72
78
  }
73
79
 
80
+ // returns all the content from all files; do we really need it?
74
81
  async tmpContents() {
75
82
  const contents = [];
76
83
 
@@ -103,8 +110,9 @@ class ArtifactStorage {
103
110
  }
104
111
  }
105
112
 
113
+ // no need to use multiple dirs; we can implement it later if required
106
114
  static tmpTestomatDirNames() {
107
- const subname = this._tmpPrefix || "tsmt_reporter";
115
+ const subname = ArtifactStorage._tmpPrefix || "tsmt_reporter";
108
116
 
109
117
  return fs.readdirSync(os.tmpdir(), { withFileTypes: true })
110
118
  .filter((item) => item.isDirectory())
@@ -113,6 +121,7 @@ class ArtifactStorage {
113
121
  }
114
122
 
115
123
  cleanup() {
124
+ // when you do a cleanup, I know nothing about isFile param
116
125
  if (this.isFile && this.tmpDirFullpath) {
117
126
  const tmpDirNames = ArtifactStorage.tmpTestomatDirNames();
118
127
 
@@ -128,4 +137,6 @@ class ArtifactStorage {
128
137
  }
129
138
  }
130
139
 
140
+ ArtifactStorage._tmpPrefix = "tsmt_reporter";
141
+
131
142
  module.exports = ArtifactStorage;
@@ -1,9 +1,9 @@
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 } = require('../constants');
4
+ const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE } = require('../constants');
5
5
  const upload = require('../fileUploader');
6
- const Output = require('../output');
6
+ const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
7
7
 
8
8
  if (!global.codeceptjs) {
9
9
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
@@ -16,9 +16,9 @@ let currentMetaStep = [];
16
16
  let error;
17
17
  let stepShift = 0;
18
18
 
19
- const output = new Output({
20
- filterFn: stack => !stack.includes('codeceptjs/lib/output'), // output from codeceptjs
21
- });
19
+ // const output = new Output({
20
+ // filterFn: stack => !stack.includes('codeceptjs/lib/output'), // output from codeceptjs
21
+ // });
22
22
 
23
23
  let stepStart = new Date();
24
24
 
@@ -33,6 +33,7 @@ if (MAJOR_VERSION < 3) {
33
33
  function CodeceptReporter(config) {
34
34
  let failedTests = [];
35
35
  let videos = [];
36
+ let traces = [];
36
37
  const reportTestPromises = [];
37
38
 
38
39
  const testTimeMap = {};
@@ -50,45 +51,46 @@ function CodeceptReporter(config) {
50
51
 
51
52
  recorder.startUnlessRunning();
52
53
 
54
+ global.testomatioRunningEnvironment = 'codeceptjs';
55
+
53
56
  // Listening to events
54
57
  event.dispatcher.on(event.all.before, () => {
58
+ // clear tmp dir
59
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
60
+
55
61
  recorder.add('Creating new run', () => client.createRun());
56
62
  videos = [];
63
+ traces = [];
64
+
65
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
57
66
  });
58
67
 
59
68
  event.dispatcher.on(event.test.before, () => {
60
69
  recorder.add(() => {
61
70
  currentMetaStep = [];
62
- output.reset();
63
- output.start();
71
+ // output.reset();
72
+ // output.start();
64
73
  stepShift = 0;
65
74
  });
75
+
76
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
77
+ // reset steps
78
+ global.testomatioDataStore.steps = [];
66
79
  });
67
80
 
68
81
  event.dispatcher.on(event.test.started, test => {
69
82
  testTimeMap[test.id] = Date.now();
83
+ if (global.testomatioDataStore) global.testomatioDataStore.currentlyRunningTestId = getIdFromTestTitle(test.title);
70
84
  });
71
85
 
72
86
  event.dispatcher.on(event.all.result, async () => {
87
+ debug('waiting for all tests to be reported');
73
88
  // all tests were reported and we can upload videos
74
89
  await Promise.all(reportTestPromises);
75
90
 
76
- if (videos.length && upload.isArtifactsEnabled()) {
77
- console.log(APP_PREFIX, `🎞️ Uploading ${videos.length} videos...`);
78
-
79
- const promises = [];
80
- for (const video of videos) {
81
- const { testId, title, path, type } = video;
82
- const file = { path, type, title };
83
- promises.push(
84
- client.addTestRun(undefined, {
85
- ...stripExampleFromTitle(title),
86
- test_id: testId,
87
- files: [file],
88
- }),
89
- );
90
- }
91
- await Promise.all(promises);
91
+ if (upload.isArtifactsEnabled()) {
92
+ uploadAttachments(client, videos, '🎞️ Uploading', 'video');
93
+ uploadAttachments(client, traces, '📁 Uploading', 'trace');
92
94
  }
93
95
 
94
96
  const status = failedTests.length === 0 ? STATUS.PASSED : STATUS.FAILED;
@@ -107,10 +109,10 @@ function CodeceptReporter(config) {
107
109
  suite_title: test.parent && test.parent.title,
108
110
  message: testObj.message,
109
111
  time: getDuration(test),
110
- steps: output.text(),
112
+ steps: global.testomatioDataStore.steps.join('\n') || null,
111
113
  test_id: testId,
112
114
  });
113
- output.stop();
115
+ // output.stop();
114
116
  });
115
117
 
116
118
  event.dispatcher.on(event.test.failed, (test, err) => {
@@ -135,7 +137,7 @@ function CodeceptReporter(config) {
135
137
  time: 0,
136
138
  });
137
139
  }
138
- output.stop();
140
+ // output.stop();
139
141
  });
140
142
 
141
143
  event.dispatcher.on(event.test.after, test => {
@@ -151,30 +153,33 @@ function CodeceptReporter(config) {
151
153
 
152
154
  const files = [];
153
155
  if (artifacts.screenshot) files.push({ path: artifacts.screenshot, type: 'image/png' });
154
- // todo: video must be uploaded later....
156
+ // todo: video must be uploaded later....
155
157
 
156
-
157
- const reportTestPromise = client.addTestRun(STATUS.FAILED, {
158
- ...stripExampleFromTitle(title),
159
- test_id: testId,
160
- suite_title: test.parent && test.parent.title,
161
- error,
162
- message: testObj.message,
163
- time: getDuration(test),
164
- files,
165
- steps: output.text(),
166
- }).then(pipes => {
167
- testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
168
-
169
- for (const aid in artifacts) {
170
- if (aid.startsWith('video')) videos.push({ testId, title, path: artifacts[aid], type: 'video/webm' });
171
- if (aid.startsWith('trace')) files.push({ testId, title, path: artifacts[aid], type: 'application/zip' });
172
- }
173
- });
158
+ const reportTestPromise = client
159
+ .addTestRun(STATUS.FAILED, {
160
+ ...stripExampleFromTitle(title),
161
+ test_id: testId,
162
+ suite_title: test.parent && test.parent.title,
163
+ error,
164
+ message: testObj.message,
165
+ time: getDuration(test),
166
+ files,
167
+ steps: global.testomatioDataStore?.steps?.join('\n') || null,
168
+ })
169
+ .then(pipes => {
170
+ testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
171
+
172
+ debug('artifacts', artifacts);
173
+
174
+ for (const aid in artifacts) {
175
+ if (aid.startsWith('video')) videos.push({ testId, title, path: artifacts[aid], type: 'video/webm' });
176
+ if (aid.startsWith('trace')) traces.push({ testId, title, path: artifacts[aid], type: 'application/zip' });
177
+ }
178
+ });
174
179
 
175
180
  reportTestPromises.push(reportTestPromise);
176
181
 
177
- output.stop();
182
+ // output.stop();
178
183
  });
179
184
 
180
185
  event.dispatcher.on(event.test.skipped, test => {
@@ -190,7 +195,7 @@ function CodeceptReporter(config) {
190
195
  message: testObj.message,
191
196
  time: getDuration(test),
192
197
  });
193
- output.stop();
198
+ // output.stop();
194
199
  });
195
200
 
196
201
  event.dispatcher.on(event.step.started, step => {
@@ -215,33 +220,58 @@ function CodeceptReporter(config) {
215
220
  // eslint-disable-next-line no-continue
216
221
  if (!metaSteps[i]) continue;
217
222
  if (metaSteps[i].isBDD()) {
218
- output.push(repeat(stepShift) + chalk.bold(metaSteps[i].toString()) + metaSteps[i].comment);
223
+ // output.push(repeat(stepShift) + chalk.bold(metaSteps[i].toString()) + metaSteps[i].comment);
224
+ global.testomatioDataStore?.steps?.push(
225
+ repeat(stepShift) + chalk.bold(metaSteps[i].toString()) + metaSteps[i].comment,
226
+ );
219
227
  } else {
220
- output.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
228
+ // output.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
229
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + chalk.green.bold(metaSteps[i].toString()));
221
230
  }
222
231
  }
223
232
  }
224
233
  currentMetaStep = metaSteps;
225
234
  stepShift = 2 * shift;
226
235
 
227
- const durationMs = (+new Date() - (+stepStart));
236
+ const durationMs = +new Date() - (+stepStart);
228
237
  let duration = '';
229
238
  if (durationMs) {
230
239
  duration = repeat(1) + chalk.grey(`(${durationMs}ms)`);
231
240
  }
232
241
 
233
242
  if (step.status === STATUS.FAILED) {
234
- output.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
243
+ // output.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
244
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + chalk.red(step.toString()) + duration);
235
245
  } else {
236
- output.push(repeat(stepShift) + step.toString() + duration);
246
+ // output.push(repeat(stepShift) + step.toString() + duration);
247
+ global.testomatioDataStore?.steps?.push(repeat(stepShift) + step.toString() + duration);
237
248
  }
238
249
  });
239
250
 
240
251
  event.dispatcher.on(event.step.comment, step => {
241
- output.push(chalk.cyan.bold(step.toString()));
252
+ // output.push(chalk.cyan.bold(step.toString()));
253
+ global.testomatioDataStore?.steps?.push(chalk.cyan.bold(step.toString()));
242
254
  });
243
255
  }
244
256
 
257
+ async function uploadAttachments(client, attachments, messagePrefix, attachmentType) {
258
+ if (attachments.length > 0) {
259
+ console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType}/-s ...`);
260
+
261
+ const promises = attachments.map(async (attachment) => {
262
+ const { testId, title, path, type } = attachment;
263
+ const file = { path, type, title };
264
+ return client.addTestRun(undefined, {
265
+ ...stripExampleFromTitle(title),
266
+ test_id: testId,
267
+ files: [file],
268
+ });
269
+ });
270
+
271
+ await Promise.all(promises);
272
+ }
273
+ }
274
+
245
275
  function parseTest(tags) {
246
276
  if (tags) {
247
277
  for (const tag of tags) {
@@ -1,35 +1,59 @@
1
- // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
- const { Formatter, formatterHelpers } = require("@cucumber/cucumber");
1
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved
2
+ const { Formatter, formatterHelpers } = require('@cucumber/cucumber');
3
3
  const chalk = require('chalk');
4
4
  const fs = require('fs');
5
- const { STATUS } = require('../../constants');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
+ const logger = require('../../logger');
8
+ const { parseTest, fileSystem } = require('../../utils/utils');
9
+
10
+ const { GherkinDocumentParser, PickleParser } = formatterHelpers;
11
+ const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
12
+ const { getPickleStepMap } = PickleParser;
13
+
14
+ function getTestId(scenario) {
15
+ if (scenario) {
16
+ for (const tag of scenario.tags) {
17
+ const testId = parseTest(tag.name);
18
+ if (testId) return testId;
19
+ }
20
+ }
7
21
 
8
- const { GherkinDocumentParser, PickleParser } = formatterHelpers
9
- const {
10
- getGherkinScenarioLocationMap,
11
- getGherkinStepMap,
12
- } = GherkinDocumentParser
13
- const { getPickleStepMap } = PickleParser
22
+ return null;
23
+ }
14
24
 
15
25
  class CucumberReporter extends Formatter {
16
26
  constructor(options) {
17
27
  super(options);
18
- options.eventBroadcaster.on('envelope', this.parseEnvelope.bind(this))
28
+ options.eventBroadcaster.on('envelope', this.parseEnvelope.bind(this));
19
29
  this.failures = [];
20
30
  this.cases = [];
21
31
 
22
- this.client = new TestomatClient();
32
+ this.client = new TestomatClient({ apiKey: options.apiKey || process.env.TESTOMATIO });
23
33
  this.status = STATUS.PASSED;
24
34
 
35
+ global.testomatioRunningEnvironment = 'cucumber:current';
25
36
  }
26
37
 
27
38
  parseEnvelope(envelope) {
28
- if (envelope.testCaseStarted && this.client) this.client.createRun()
39
+ if (envelope.testRunStarted) {
40
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
41
+ }
42
+ if (envelope.testCaseStarted && this.client) {
43
+ this.client.createRun();
44
+ this.onTestCaseStarted(envelope.testCaseStarted);
45
+ }
29
46
  if (envelope.testCaseFinished) this.onTestCaseFinished(envelope.testCaseFinished);
30
47
  if (envelope.testRunFinished) this.onTestRunFinished(envelope);
31
48
  }
32
49
 
50
+ onTestCaseStarted(testCaseStarted) {
51
+ const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseStarted.id);
52
+ const testId = getTestId(testCaseAttempt.pickle);
53
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
54
+ global.testomatioDataStore.currentlyRunningTestId = testId;
55
+ }
56
+
33
57
  onTestCaseFinished(testCaseFinished) {
34
58
  const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseFinished.testCaseStartedId);
35
59
 
@@ -45,10 +69,10 @@ class CucumberReporter extends Formatter {
45
69
  if (testCaseAttempt.worstTestStepResult.status === 'SKIPPED') {
46
70
  status = STATUS.SKIPPED;
47
71
  }
48
- if (testCaseAttempt.worstTestStepResult.status === 'UNDEFINED') {
49
- status = STATUS.SKIPPED;
50
- message = 'Undefined steps. Implement missing steps and rerun this scenario';
51
- }
72
+ // if (testCaseAttempt.worstTestStepResult.status === 'UNDEFINED') {
73
+ // status = STATUS.SKIPPED;
74
+ // message = 'Undefined steps. Implement missing steps and rerun this scenario';
75
+ // }
52
76
  if (testCaseAttempt.worstTestStepResult.status === 'FAILED') {
53
77
  message = testCaseAttempt?.worstTestStepResult?.message;
54
78
  status = STATUS.FAILED;
@@ -64,10 +88,12 @@ class CucumberReporter extends Formatter {
64
88
  }
65
89
 
66
90
  const scenario = testCaseAttempt.pickle.name;
67
- const testId = testCaseAttempt.pickle.id;
91
+ // this may broke something (it is supposed to work, but I am sure it did not)
92
+ // const testId = testCaseAttempt.pickle.id;
93
+ const testId = getTestId(testCaseAttempt.pickle);
68
94
 
69
95
  let exampleString = '';
70
- if (example) exampleString = ` ${example.join(' | ')}`
96
+ if (example) exampleString = ` ${example.join(' | ')}`;
71
97
  let cliMessage = `${chalk.bold(scenario)}${exampleString}: ${chalk[color].bold(status.toUpperCase())} `;
72
98
 
73
99
  if (message) cliMessage += chalk.gray(message.split('\n')[0]);
@@ -79,15 +105,21 @@ class CucumberReporter extends Formatter {
79
105
 
80
106
  const time = Object.values(testCaseAttempt.stepResults)
81
107
  .map(t => t.duration)
82
- .reduce((sum, duration) => sum + (duration.seconds * 1000) + (duration.nanos / 1000000), 0)
108
+ .reduce((sum, duration) => sum + duration.seconds * 1000 + duration.nanos / 1000000, 0);
83
109
 
84
110
  if (!this.client) return;
85
111
 
112
+ const logs = logger.getLogs(testId);
113
+
86
114
  this.client.addTestRun(status, {
87
115
  // error: testCaseAttempt.worstTestStepResult.message,
88
116
  message,
89
- steps: getSteps(testCaseAttempt).map(s => s.toString()).join('\n').trim(),
90
- example: { ...example},
117
+ steps: getSteps(testCaseAttempt)
118
+ .map(s => s.toString())
119
+ .join('\n')
120
+ .trim(),
121
+ example: { ...example },
122
+ stack: logs,
91
123
  title: scenario,
92
124
  test_id: testId,
93
125
  time,
@@ -99,21 +131,20 @@ class CucumberReporter extends Formatter {
99
131
  console.log(chalk.bold('\nSUMMARY:\n\n'));
100
132
 
101
133
  this.failures.forEach((tc, i) => {
102
- let message = ` ${i+1}) ${tc.pickle.name}\n`;
134
+ let message = ` ${i + 1}) ${tc.pickle.name}\n`;
103
135
 
104
136
  const steps = getSteps(tc);
105
137
 
106
138
  steps.forEach(s => {
107
- message += ` ${s.toString()}\n`
108
- })
139
+ message += ` ${s.toString()}\n`;
140
+ });
109
141
 
110
142
  console.log(message);
111
143
  if (tc?.worstTestStepResult?.message) {
112
144
  console.log(chalk.red(tc?.worstTestStepResult?.message));
113
145
  }
114
146
  console.log();
115
-
116
- })
147
+ });
117
148
  }
118
149
 
119
150
  const { testRunFinished } = envelope;
@@ -125,46 +156,48 @@ class CucumberReporter extends Formatter {
125
156
 
126
157
  if (!this.client) return;
127
158
 
128
- this.client.updateRunStatus(testRunFinished.success ? STATUS.PASSED : STATUS.FAILED)
159
+ this.client.updateRunStatus(testRunFinished.success ? STATUS.PASSED : STATUS.FAILED);
129
160
  }
130
161
  }
131
162
 
132
163
  function getSteps(tc) {
133
164
  const stepIds = Object.keys(tc.stepResults);
134
- const pickleSteps = getPickleStepMap(tc.pickle)
135
- return stepIds.map(stepId => {
136
- const ts = tc.testCase.testSteps.find(t => t.id === stepId);
137
- if (!ts) return;
138
- if (!ts.pickleStepId) return;
139
- const result = tc.stepResults[stepId];
140
- const pickleStep = pickleSteps[ts.pickleStepId];
141
- const sourceStepId = pickleStep.astNodeIds[0];
142
- const step = {
143
- text: pickleStep.text,
144
- duration: result.duration,
145
- status: result.status,
146
- }
147
- const color = getStatusColor(result.status);
148
- if (sourceStepId && getGherkinStepMap(tc.gherkinDocument)[sourceStepId]) {
149
- step.keyword = getGherkinStepMap(tc.gherkinDocument)[sourceStepId].keyword;
150
- }
151
- step.toString = function toString() {
152
- const duration = step.duration.seconds * 1000 + (step.duration.nanos / 1000000)
153
- const durationString = ` ${ chalk.gray(`(${Number(duration).toFixed(2)}ms)`)}`;
154
- const stepString = `${chalk.bold(this.keyword)}${this.text}`.trim();
155
- if (color === 'red') return chalk.red(stepString) + durationString;
156
- if (color === 'yellow') return chalk.yellow(stepString) + durationString;
157
- return stepString + durationString;
158
- }
159
- return step;
160
- }).filter(s => !!s)
165
+ const pickleSteps = getPickleStepMap(tc.pickle);
166
+ return stepIds
167
+ .map(stepId => {
168
+ const ts = tc.testCase.testSteps.find(t => t.id === stepId);
169
+ if (!ts) return;
170
+ if (!ts.pickleStepId) return;
171
+ const result = tc.stepResults[stepId];
172
+ const pickleStep = pickleSteps[ts.pickleStepId];
173
+ const sourceStepId = pickleStep.astNodeIds[0];
174
+ const step = {
175
+ text: pickleStep.text,
176
+ duration: result.duration,
177
+ status: result.status,
178
+ };
179
+ const color = getStatusColor(result.status);
180
+ if (sourceStepId && getGherkinStepMap(tc.gherkinDocument)[sourceStepId]) {
181
+ step.keyword = getGherkinStepMap(tc.gherkinDocument)[sourceStepId].keyword;
182
+ }
183
+ step.toString = function toString() {
184
+ const duration = step.duration.seconds * 1000 + step.duration.nanos / 1000000;
185
+ const durationString = ` ${chalk.gray(`(${Number(duration).toFixed(2)}ms)`)}`;
186
+ const stepString = `${chalk.bold(this.keyword)}${this.text}`.trim();
187
+ if (color === 'red') return chalk.red(stepString) + durationString;
188
+ if (color === 'yellow') return chalk.yellow(stepString) + durationString;
189
+ return stepString + durationString;
190
+ };
191
+ return step;
192
+ })
193
+ .filter(s => !!s);
161
194
  }
162
195
 
163
196
  function getStatusColor(status) {
164
197
  if (status === 'UNDEFINED') return 'yellow';
165
198
  if (status === 'SKIPPED') return 'yellow';
166
199
  if (status === 'FAILED') return 'red';
167
- return 'green'
200
+ return 'green';
168
201
  }
169
202
 
170
203
  function getExample(testCaseAttempt) {
@@ -173,11 +206,14 @@ function getExample(testCaseAttempt) {
173
206
  if (!nodesMap[exampleNodeId]) return;
174
207
  const featureDoc = fs.readFileSync(testCaseAttempt.gherkinDocument.uri).toString();
175
208
  const { line } = nodesMap[exampleNodeId];
176
- const example = featureDoc.split('\n')[line-1];
209
+ const example = featureDoc.split('\n')[line - 1];
177
210
  if (example) {
178
- return example.trim().split('|').filter(r => !!r).map(r => r.trim());
179
- };
180
-
211
+ return example
212
+ .trim()
213
+ .split('|')
214
+ .filter(r => !!r)
215
+ .map(r => r.trim());
216
+ }
181
217
  }
182
218
 
183
219
  module.exports = CucumberReporter;
@@ -1,8 +1,8 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies, import/no-unresolved
2
2
  const { Formatter } = require('cucumber');
3
3
  const chalk = require('chalk');
4
- const { parseTest } = require('../../util');
5
- const { STATUS } = require('../../constants');
4
+ const { parseTest, fileSystem } = require('../../utils/utils');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
7
 
8
8
  const createTestomatFormatter = apiKey => {
@@ -96,9 +96,23 @@ const createTestomatFormatter = apiKey => {
96
96
  this.status = STATUS.PASSED.toString();
97
97
 
98
98
  options.eventBroadcaster.on('gherkin-document', addDocument);
99
- options.eventBroadcaster.on('test-run-started', () => this?.client?.createRun());
99
+ options.eventBroadcaster.on('test-run-started', () => {
100
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
101
+ this?.client?.createRun();
102
+ });
100
103
  options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
104
+ options.eventBroadcaster.on('test-case-started', this.onTestCaseStarted.bind(this));
101
105
  options.eventBroadcaster.on('test-run-finished', () => this?.client?.updateRunStatus(this.status));
106
+
107
+ global.testomatioRunningEnvironment = 'cucumber:legacy';
108
+ }
109
+
110
+ onTestCaseStarted(event) {
111
+ const scenario = getScenario(event.sourceLocation);
112
+ const testId = getTestId(scenario);
113
+
114
+ if (!global.testomatioDataStore) global.testomatioDataStore = {};
115
+ global.testomatioDataStore.currentlyRunningTestId = testId;
102
116
  }
103
117
 
104
118
  onTestCaseFinished(event) {
@@ -111,15 +125,15 @@ const createTestomatFormatter = apiKey => {
111
125
 
112
126
  if (!scenario.name) return;
113
127
 
114
- let message = '';
115
- let cliMessage = `- ${scenario.name}: ${chalk.bold(status.toUpperCase())}`;
128
+ const message = '';
129
+ const cliMessage = `- ${scenario.name}: ${chalk.bold(status.toUpperCase())}`;
116
130
 
117
- if (event.result.status === 'undefined') {
118
- cliMessage += chalk.yellow(
119
- ' (undefined steps. Run Cucumber without this formatter and implement missing steps)',
120
- );
121
- message = 'Undefined steps. Implement missing steps and rerun this scenario';
122
- }
131
+ // if (event.result.status === 'undefined') {
132
+ // cliMessage += chalk.yellow(
133
+ // ' (undefined steps. Run Cucumber without this formatter and implement missing steps)',
134
+ // );
135
+ // message = 'Undefined steps. Implement missing steps and rerun this scenario';
136
+ // }
123
137
  console.log(cliMessage);
124
138
  if (status !== STATUS.PASSED && status !== STATUS.SKIPPED) {
125
139
  this.status = STATUS.FAILED;
@@ -1,5 +1,5 @@
1
1
  const { STATUS } = require('../../constants');
2
- const { parseTest, parseSuite } = require('../../util');
2
+ const { parseTest, parseSuite } = require('../../utils/utils');
3
3
  const TestomatClient = require('../../client');
4
4
 
5
5
  const testomatioReporter = on => {
@@ -1,5 +1,5 @@
1
1
  const TestomatClient = require('../client');
2
- const { parseTest, ansiRegExp } = require('../util');
2
+ const { parseTest, ansiRegExp } = require('../utils/utils');
3
3
  const { STATUS } = require('../constants');
4
4
 
5
5
  class JasmineReporter {