@testomatio/reporter 1.1.0-beta.mocha-create.9 → 1.1.0-rc.2

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 CHANGED
@@ -65,23 +65,16 @@ yarn add @testomatio/reporter --dev
65
65
 
66
66
  ### 1️⃣ Attach Reporter to the Test Runner
67
67
 
68
- * #### [Playwright](./docs/frameworks.md#playwright)
69
- * #### [CodeceptJS](./docs/frameworks.md#CodeceptJS)
70
- * #### [Cypress](./docs/frameworks.md#Cypress)
71
- * #### [Jest](./docs/frameworks.md#Jest)
72
- * #### [Mocha](./docs/frameworks.md#Mocha)
73
- * #### [WebDriverIO](./docs/frameworks.md#WebDriverIO)
74
- * #### [TestCafe](./docs/frameworks.md#TestCafe)
75
- * #### [Detox](./docs/frameworks.md#Detox)
76
- * #### [Codeception](https://github.com/testomatio/php-reporter)
77
- * #### [Newman (Postman)](./docs/frameworks.md#Newman)
78
- * #### [JUnit](./docs/junit.md#junit)
79
- * #### [NUnit](./docs/junit.md#nunit)
80
- * #### [PyTest](./docs/junit.md#pytest)
81
- * #### [PHPUnit](./docs/junit.md#phpunit)
82
- * #### [Protractor](./docs/frameworks.md#protractor)
83
-
84
- or any [other via JUnit](./docs/junit.md) report....
68
+ | | | |
69
+ |--|--|--|
70
+ | [Playwright](./docs/frameworks.md#playwright) | [CodeceptJS](./docs/frameworks.md#CodeceptJS) | [Cypress](./docs/frameworks.md#Cypress) |
71
+ | [Jest](./docs/frameworks.md#Jest) | [Mocha](./docs/frameworks.md#Mocha) | [WebDriverIO](./docs/frameworks.md#WebDriverIO) |
72
+ | [TestCafe](./docs/frameworks.md#TestCafe) | [Detox](./docs/frameworks.md#Detox) | [Codeception](https://github.com/testomatio/php-reporter) |
73
+ | [Newman (Postman)](./docs/frameworks.md#Newman) | [JUnit](./docs/junit.md#junit) | [NUnit](./docs/junit.md#nunit) |
74
+ | [PyTest](./docs/junit.md#pytest) | [PHPUnit](./docs/junit.md#phpunit) | [Protractor](./docs/frameworks.md#protractor) |
75
+
76
+
77
+ or **any [other via JUnit](./docs/junit.md)** report....
85
78
 
86
79
  ### 2️⃣ Configure Reports
87
80
 
@@ -153,3 +146,4 @@ To print all reporter logs of a specific pipe:
153
146
  ```
154
147
  DEBUG=@testomatio/reporter:pipe:github
155
148
  ```
149
+
@@ -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', () => client.createRun());
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
- event.dispatcher.on(event.test.before, () => {
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() - (+stepStart);
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 (attachment) => {
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 || process.env.TESTOMATIO });
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
- global.testomatioDataStore.currentlyRunningTestId = testId;
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 logs = logger.getLogs(testId);
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
- stack: logs,
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(process.env.TESTOMATIO);
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 (!process.env.TESTOMATIO) {
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: process.env.TESTOMATIO });
11
+ const client = new TestomatClient({ apiKey: TESTOMATIO });
11
12
 
12
- on('before:run', async (run) => {
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
- const time = latestAttempt.duration;
30
- const error = latestAttempt.error;
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.inspect = function() { // eslint-disable-line func-names
45
- if (this && this.codeFrame) {
46
- return this.codeFrame.frame;
47
- }
48
- return '';
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': state = STATUS.PASSED; break;
68
- case 'failed': state = STATUS.FAILED; break;
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(client.addTestRun(state, {
76
- title, time, example, error, files, suite_title: suiteTitle, test_id: testId, suite_id: suiteId
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);
@@ -1,6 +1,10 @@
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');
7
+ const path = require('path');
4
8
 
5
9
  class JestReporter {
6
10
  constructor(globalConfig, options) {
@@ -16,6 +20,19 @@ class JestReporter {
16
20
  fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
17
21
  }
18
22
 
23
+ // start of test file (including beforeAll)
24
+ onTestStart(testFile) {
25
+ debug('Start running test file:', testFile.path);
26
+ services.setContext(testFile.path);
27
+ }
28
+
29
+ // start of the test (including beforeEach)
30
+ onTestCaseStart(test, testCase) {
31
+ debug('Start running test:', testCase.fullName);
32
+ services.setContext(testCase.fullName);
33
+ }
34
+
35
+ // end of test file! (there is also onTestCaseResult listener)
19
36
  onTestResult(test, testResult) {
20
37
  if (!this.client) return;
21
38
 
@@ -31,13 +48,15 @@ class JestReporter {
31
48
  steps = failureMessages[0];
32
49
  }
33
50
  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
- }
51
+
52
+ // suite titles from most outer to most inner, separated by space
53
+ let fullSuiteTitle = testResult.ancestorTitles?.join(' ');
54
+ // if no suite titles, use file name
55
+ if (!fullSuiteTitle && testResult.testFilePath) fullSuiteTitle = path.basename(testResult.testFilePath);
56
+
57
+ const logs = getTestLogs(result);
58
+ const artifacts = services.artifacts.get(testResult.fullName);
59
+ const keyValues = services.keyValues.get(testResult.fullName);
41
60
 
42
61
  const deducedStatus = status === 'pending' ? 'skipped' : status;
43
62
  // In jest if test is not matched with test name pattern it is considered as skipped.
@@ -45,11 +64,14 @@ class JestReporter {
45
64
  if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
46
65
  this.client.addTestRun(deducedStatus, {
47
66
  test_id: testId,
48
- suite_title,
67
+ suite_title: fullSuiteTitle,
49
68
  error,
50
69
  steps,
51
70
  title,
52
71
  time: duration,
72
+ logs,
73
+ manuallyAttachedArtifacts: artifacts,
74
+ meta: keyValues,
53
75
  });
54
76
  }
55
77
  }
@@ -64,4 +86,20 @@ class JestReporter {
64
86
  }
65
87
  }
66
88
 
89
+ function getTestLogs(testResult) {
90
+ const suiteLogsArr = services.logger.getLogs(testResult.testFilePath);
91
+ const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
92
+ const testLogsArr = services.logger.getLogs(testResult.fullName);
93
+ const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
94
+
95
+ let logs = '';
96
+ if (suiteLogs) {
97
+ logs += `${chalk.bold('\t--- Suite ---')}\n${suiteLogs}`;
98
+ }
99
+ if (testLogs) {
100
+ logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
101
+ }
102
+ return logs;
103
+ }
104
+
67
105
  module.exports = JestReporter;
@@ -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 { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
9
- Mocha.Runner.constants;
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 || process.env.TESTOMATIO;
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
- stack: logs,
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;
@@ -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'] || process.env.TESTOMATIO;
24
+ const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || TESTOMATIO;
24
25
  const title = process.env.TESTOMATIO_TITLE;
25
26
 
26
27
  if (launch) {