@testomatio/reporter 1.1.0-beta → 1.1.0-beta-3

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.
@@ -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_DIR } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
+ const logger = require('../../storages/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_DIR);
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_DIR } = 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_DIR);
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 {
@@ -1,6 +1,6 @@
1
1
  const TestomatClient = require('../client');
2
- const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
3
- const { parseTest, ansiRegExp, fileSystem } = require('../util');
2
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
3
+ const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
4
4
 
5
5
  class JestReporter {
6
6
  constructor(globalConfig, options) {
@@ -11,14 +11,9 @@ class JestReporter {
11
11
  this.client.createRun();
12
12
  }
13
13
 
14
- static getIdOfCurrentlyRunningTest() {
15
- // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
16
- return parseTest(expect?.getState()?.currentTestName) || null; // eslint-disable-line
17
- }
18
-
19
14
  onRunStart() {
20
15
  // clear tmp dir
21
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
16
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
22
17
  }
23
18
 
24
19
  onTestResult(test, testResult) {
@@ -36,12 +31,21 @@ class JestReporter {
36
31
  steps = failureMessages[0];
37
32
  }
38
33
  const testId = parseTest(title);
34
+ let suite_title;
35
+ // this is test without a suite
36
+ if (result.fullName === result.title) {
37
+ suite_title = testResult.testFilePath.split('/').pop();
38
+ } else {
39
+ suite_title = result.fullName.replace(result.title, '');
40
+ }
41
+
39
42
  const deducedStatus = status === 'pending' ? 'skipped' : status;
40
43
  // In jest if test is not matched with test name pattern it is considered as skipped.
41
44
  // So adding a check if it is skipped for real or because of test pattern
42
45
  if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
43
46
  this.client.addTestRun(deducedStatus, {
44
47
  test_id: testId,
48
+ suite_title,
45
49
  error,
46
50
  steps,
47
51
  title,
@@ -57,9 +61,6 @@ class JestReporter {
57
61
  const { numFailedTests } = results;
58
62
  const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
59
63
  this.client.updateRunStatus(status);
60
-
61
- // clear tmp dir
62
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
63
64
  }
64
65
  }
65
66
 
@@ -1,58 +1,40 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
2
  const Mocha = require('mocha');
3
- const debug = require('debug')('@testomatio/reporter:adapter:mocha');
4
3
  const chalk = require('chalk');
5
4
  const TestomatClient = require('../client');
6
- const { STATUS } = require('../constants');
7
- const { parseTest, specificTestInfo } = require('../util');
8
- const ArtifactStorage = require('../_ArtifactStorageOld');
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
6
+ const { parseTest, fileSystem } = require('../utils/utils');
9
7
 
10
- const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } = Mocha.Runner.constants;
8
+ const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
9
+ Mocha.Runner.constants;
11
10
 
12
11
  function MochaReporter(runner, opts) {
13
12
  Mocha.reporters.Base.call(this, runner);
14
- let passes = 0; let failures = 0; let skipped = 0;
15
- let artifactStore;
13
+ let passes = 0;
14
+ let failures = 0;
15
+ let skipped = 0;
16
+ // let artifactStore;
16
17
 
17
18
  const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
18
19
 
19
- if (!apiKey) {
20
- debug('TESTOMATIO key is empty, ignoring reports');
21
- return;
22
- }
23
20
  const client = new TestomatClient({ apiKey });
24
21
 
25
22
  runner.on(EVENT_RUN_BEGIN, () => {
26
23
  client.createRun();
27
24
 
28
- const params = {
29
- toFile: true
30
- };
31
-
32
- artifactStore = runner._workerReporter !== undefined
33
- ? new ArtifactStorage(params)
34
- : new ArtifactStorage();
25
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
35
26
  });
36
27
 
37
- runner.on(EVENT_TEST_PASS, async(test) => {
28
+ runner.on(EVENT_TEST_PASS, async test => {
38
29
  passes += 1;
39
30
  console.log(chalk.bold.green('✔'), test.fullTitle());
40
31
  const testId = parseTest(test.title);
41
32
 
42
- const specificTest = specificTestInfo(test);
43
- const content = await artifactStore.artifactByTestName(specificTest);
44
-
45
- debug(`test=${specificTest} content = `, content);
46
-
47
- client.addTestRun(
48
- STATUS.PASSED,
49
- {
50
- test_id: testId,
51
- title: test.title,
52
- time: test.duration,
53
- },
54
- content
55
- );
33
+ client.addTestRun(STATUS.PASSED, {
34
+ test_id: testId,
35
+ title: test.title,
36
+ time: test.duration,
37
+ });
56
38
  });
57
39
 
58
40
  runner.on(EVENT_TEST_PENDING, test => {
@@ -66,33 +48,23 @@ function MochaReporter(runner, opts) {
66
48
  });
67
49
  });
68
50
 
69
- runner.on(EVENT_TEST_FAIL, async(test, err) => {
51
+ runner.on(EVENT_TEST_FAIL, async (test, err) => {
70
52
  failures += 1;
71
53
  console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
72
54
  const testId = parseTest(test.title);
73
55
 
74
- const specificTest = specificTestInfo(test);
75
- const content = await artifactStore.artifactByTestName(specificTest);
76
-
77
- debug(`fail test=${specificTest} content = `, content);
78
-
79
- client.addTestRun(
80
- STATUS.FAILED, {
81
- error: err,
82
- test_id: testId,
83
- title: test.title,
84
- time: test.duration,
85
- },
86
- content
87
- );
56
+ client.addTestRun(STATUS.FAILED, {
57
+ error: err,
58
+ test_id: testId,
59
+ title: test.title,
60
+ time: test.duration,
61
+ });
88
62
  });
89
63
 
90
64
  runner.on(EVENT_RUN_END, () => {
91
65
  const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
92
66
  console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
93
67
  client.updateRunStatus(status);
94
-
95
- artifactStore.cleanup();
96
68
  });
97
69
  }
98
70
 
@@ -3,14 +3,14 @@ const crypto = require('crypto');
3
3
  const os = require('os');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const { APP_PREFIX, STATUS: Status } = require('../constants');
6
+ const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
8
  const { isArtifactsEnabled } = require('../fileUploader');
9
- const { parseTest } = require('../util');
9
+ const { parseTest, fileSystem } = require('../utils/utils');
10
10
 
11
11
  const reportTestPromises = [];
12
12
 
13
- class TestomatioReporter {
13
+ class PlaywrightReporter {
14
14
  constructor(config = {}) {
15
15
  this.client = new TestomatioClient({ apiKey: config?.apiKey });
16
16
 
@@ -18,11 +18,19 @@ class TestomatioReporter {
18
18
  }
19
19
 
20
20
  onBegin(_config, suite) {
21
+ // clean data storage
22
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
21
23
  if (!this.client) return;
22
24
  this.suite = suite;
23
25
  this.client.createRun();
24
26
  }
25
27
 
28
+ /*
29
+ onTestBegin(test) {
30
+ // does not work; value is not to the storage context
31
+ global.testTitle = test.title;
32
+ } */
33
+
26
34
  onTestEnd(test, result) {
27
35
  if (!this.client) return;
28
36
 
@@ -37,21 +45,34 @@ class TestomatioReporter {
37
45
  for (const step of result.steps) {
38
46
  appendStep(step, steps);
39
47
  }
40
-
41
- const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
42
- error,
43
- test_id: testId,
44
- suite_title,
45
- title,
46
- steps: steps.join('\n'),
47
- time: duration,
48
- }).then(pipes => {
49
- testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
50
-
51
- this.uploads.push({
52
- testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
48
+
49
+ let logs = '';
50
+ if (result.stderr.length || result.stdout.length) {
51
+ logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
52
+ }
53
+
54
+ const reportTestPromise = this.client
55
+ .addTestRun(checkStatus(result.status), {
56
+ error,
57
+ test_id: testId,
58
+ suite_title,
59
+ title,
60
+ steps: steps.join('\n'),
61
+ time: duration,
62
+ stack: logs,
53
63
  })
54
- });;
64
+ .then(pipes => {
65
+ testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
66
+
67
+ this.uploads.push({
68
+ testId,
69
+ title,
70
+ suite_title,
71
+ files: result.attachments.filter(a => a.body || a.path),
72
+ });
73
+ // remove empty uploads
74
+ this.uploads = this.uploads.filter(upload => upload.files.length);
75
+ });
55
76
 
56
77
  reportTestPromises.push(reportTestPromise);
57
78
  }
@@ -67,10 +88,9 @@ class TestomatioReporter {
67
88
  const promises = [];
68
89
 
69
90
  for (const upload of this.uploads) {
70
-
71
91
  const { title, testId, suite_title } = upload;
72
92
 
73
- const files = upload.files.map((attachment) => {
93
+ const files = upload.files.map(attachment => {
74
94
  if (attachment.body) {
75
95
  const fileName = tmpFile();
76
96
  fs.writeFileSync(fileName, attachment.body);
@@ -78,7 +98,6 @@ class TestomatioReporter {
78
98
  return { path: attachment.path, title, type: attachment.contentType };
79
99
  });
80
100
 
81
-
82
101
  promises.push(
83
102
  this.client.addTestRun(undefined, {
84
103
  test_id: testId,
@@ -124,4 +143,4 @@ function tmpFile(prefix = 'tmp.') {
124
143
  return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex'));
125
144
  }
126
145
 
127
- module.exports = TestomatioReporter;
146
+ module.exports = PlaywrightReporter;
@@ -1,7 +1,7 @@
1
1
  // eslint-disable-next-line global-require, import/no-extraneous-dependencies
2
2
  const WDIOReporter = require('@wdio/reporter').default;
3
3
  const TestomatClient = require('../client');
4
- const { parseTest } = require('../util');
4
+ const { parseTest } = require('../utils/utils');
5
5
 
6
6
  class WebdriverReporter extends WDIOReporter {
7
7
  constructor(options) {
@@ -23,6 +23,7 @@ program
23
23
  }
24
24
  let { javaTests, lang } = opts;
25
25
  if (opts.envFile) {
26
+ console.log(APP_PREFIX, 'Loading env file:', opts.envFile);
26
27
  debug('Loading env file: %s', opts.envFile)
27
28
  require('dotenv').config({ path: opts.envFile }); // eslint-disable-line
28
29
  }