@testomatio/reporter 1.2.0-beta-4 → 1.2.0

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,57 +1,78 @@
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, TESTOMAT_TMP_STORAGE } = require('../constants');
7
- const { parseTest, specificTestInfo, fileSystem } = require('../utils/utils');
8
- const ArtifactStorage = require('../_ArtifactStorageOld');
9
-
10
- const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
11
- Mocha.Runner.constants;
5
+ const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
6
+ const { parseTest, fileSystem } = require('../utils/utils');
7
+ const config = 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;
12
21
 
13
22
  function MochaReporter(runner, opts) {
14
23
  Mocha.reporters.Base.call(this, runner);
15
24
  let passes = 0;
16
25
  let failures = 0;
17
26
  let skipped = 0;
18
- let artifactStore;
27
+ // let artifactStore;
19
28
 
20
- const apiKey = opts?.reporterOptions?.apiKey || process.env.TESTOMATIO;
29
+ const apiKey = opts?.reporterOptions?.apiKey || config.TESTOMATIO;
21
30
 
22
31
  const client = new TestomatClient({ apiKey });
23
32
 
24
33
  runner.on(EVENT_RUN_BEGIN, () => {
25
34
  client.createRun();
26
35
 
27
- const params = {
28
- toFile: true,
29
- };
36
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
37
+ });
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
+ });
30
46
 
31
- artifactStore = runner._workerReporter !== undefined ? new ArtifactStorage(params) : new ArtifactStorage();
47
+ runner.on(EVENT_TEST_BEGIN, async test => {
48
+ services.setContext(test.fullTitle());
49
+ });
32
50
 
33
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
51
+ runner.on(EVENT_TEST_END, async () => {
52
+ services.setContext(null);
34
53
  });
35
54
 
36
55
  runner.on(EVENT_TEST_PASS, async test => {
37
56
  passes += 1;
57
+
38
58
  console.log(chalk.bold.green('✔'), test.fullTitle());
39
59
  const testId = parseTest(test.title);
40
60
 
41
- const specificTest = specificTestInfo(test);
42
- const content = await artifactStore.artifactByTestName(specificTest);
43
-
44
- debug(`test=${specificTest} content = `, content);
61
+ const logs = getTestLogs(test);
62
+ const artifacts = services.artifacts.get(test.fullTitle());
63
+ const keyValues = services.keyValues.get(test.fullTitle());
45
64
 
46
- client.addTestRun(
47
- STATUS.PASSED,
48
- {
49
- test_id: testId,
50
- title: test.title,
51
- time: test.duration,
52
- },
53
- content,
54
- );
65
+ client.addTestRun(STATUS.PASSED, {
66
+ test_id: testId,
67
+ suite_title: getSuiteTitle(test),
68
+ title: getTestName(test),
69
+ code: test.body.toString(),
70
+ file: getFile(test),
71
+ time: test.duration,
72
+ logs,
73
+ manuallyAttachedArtifacts: artifacts,
74
+ meta: keyValues,
75
+ });
55
76
  });
56
77
 
57
78
  runner.on(EVENT_TEST_PENDING, test => {
@@ -59,7 +80,10 @@ function MochaReporter(runner, opts) {
59
80
  console.log('skip: %s', test.fullTitle());
60
81
  const testId = parseTest(test.title);
61
82
  client.addTestRun(STATUS.SKIPPED, {
62
- title: test.title,
83
+ title: getTestName(test),
84
+ suite_title: getSuiteTitle(test),
85
+ code: test.body.toString(),
86
+ file: getFile(test),
63
87
  test_id: testId,
64
88
  time: test.duration,
65
89
  });
@@ -70,33 +94,63 @@ function MochaReporter(runner, opts) {
70
94
  console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
71
95
  const testId = parseTest(test.title);
72
96
 
73
- const specificTest = specificTestInfo(test);
74
- const content = await artifactStore.artifactByTestName(specificTest);
75
-
76
- debug(`fail test=${specificTest} content = `, content);
77
-
78
- client.addTestRun(
79
- STATUS.FAILED,
80
- {
81
- error: err,
82
- test_id: testId,
83
- title: test.title,
84
- time: test.duration,
85
- },
86
- content,
87
- );
97
+ const logs = getTestLogs(test);
98
+
99
+ client.addTestRun(STATUS.FAILED, {
100
+ error: err,
101
+ suite_title: getSuiteTitle(test),
102
+ file: getFile(test),
103
+ test_id: testId,
104
+ title: getTestName(test),
105
+ code: test.body.toString(),
106
+ time: test.duration,
107
+ logs,
108
+ });
88
109
  });
89
110
 
90
111
  runner.on(EVENT_RUN_END, () => {
91
112
  const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
92
113
  console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
93
114
  client.updateRunStatus(status);
94
-
95
- artifactStore.cleanup();
96
115
  });
97
116
  }
98
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
+
99
135
  // To have this reporter "extend" a built-in reporter uncomment the following line:
100
136
  Mocha.utils.inherits(MochaReporter, Mocha.reporters.Spec);
101
137
 
102
138
  module.exports = MochaReporter;
139
+
140
+ function getSuiteTitle(test, pathArr = []) {
141
+
142
+ if (test.parent.parent) getSuiteTitle(test.parent, pathArr);
143
+
144
+ pathArr.push(test.parent.title);
145
+
146
+ return pathArr.filter(t => !!t)[0];
147
+ }
148
+
149
+ function getFile(test) {
150
+ return test.parent.file?.replace(process.cwd(), '');
151
+ }
152
+
153
+ function getTestName(test) {
154
+ if (process.env.TESTOMATIO_CREATE === 'fulltitle') return test.fullTitle();
155
+ return test.title;
156
+ }
@@ -3,10 +3,13 @@ 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, TESTOMAT_TMP_STORAGE } = 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
9
  const { parseTest, fileSystem } = require('../utils/utils');
10
+ // const debug = require('debug')('@testomatio/reporter:adapter:playwright');
11
+ const { services } = require('../services');
12
+ const { dataStorage } = require('../data-storage');
10
13
 
11
14
  const reportTestPromises = [];
12
15
 
@@ -17,54 +20,90 @@ class PlaywrightReporter {
17
20
  this.uploads = [];
18
21
  }
19
22
 
20
- onBegin(_config, suite) {
23
+ onBegin(config, suite) {
21
24
  // clean data storage
22
- fileSystem.clearDir(TESTOMAT_TMP_STORAGE.mainDir);
25
+ fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
23
26
  if (!this.client) return;
24
27
  this.suite = suite;
28
+ this.config = config;
25
29
  this.client.createRun();
26
30
  }
27
31
 
28
- // onTestBegin(test) {
29
- // const testId = parseTest(test.title);
30
- // }
32
+ onTestBegin(testInfo) {
33
+ const fullTestTitle = getTestContextName(testInfo);
34
+ dataStorage.setContext(fullTestTitle);
35
+ }
31
36
 
32
37
  onTestEnd(test, result) {
33
38
  if (!this.client) return;
34
39
 
35
- let testId = parseTest(test.title);
36
-
37
40
  const { title } = test;
41
+
42
+ let testId = parseTest(title);
43
+
38
44
  const { error, duration } = result;
39
45
 
40
- const suite_title = test.parent ? test.parent.title : null;
46
+ const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
41
47
 
42
48
  const steps = [];
43
49
  for (const step of result.steps) {
44
50
  appendStep(step, steps);
45
51
  }
46
52
 
47
- const logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
48
-
49
- const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
50
- error,
51
- test_id: testId,
52
- suite_title,
53
- title,
54
- steps: steps.join('\n'),
55
- time: duration,
56
- stack: logs,
57
- }).then(pipes => {
58
- testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
59
-
60
- this.uploads.push({
61
- testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
53
+ const fullTestTitle = getTestContextName(test);
54
+ let logs = '';
55
+ if (result.stderr.length || result.stdout.length) {
56
+ logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
57
+ }
58
+ const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
59
+ const keyValues = services.keyValues.get(fullTestTitle);
60
+
61
+ const reportTestPromise = this.client
62
+ .addTestRun(checkStatus(result.status), {
63
+ error,
64
+ test_id: testId,
65
+ suite_title,
66
+ title,
67
+ steps: steps.join('\n'),
68
+ time: duration,
69
+ logs,
70
+ manuallyAttachedArtifacts,
71
+ meta: keyValues,
72
+ file: test.location?.file,
73
+ })
74
+ .then(pipes => {
75
+ testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
76
+
77
+ this.uploads.push({
78
+ testId,
79
+ title,
80
+ suite_title,
81
+ files: result.attachments.filter(a => a.body || a.path),
82
+ file: test.location?.file,
83
+ });
84
+ // remove empty uploads
85
+ this.uploads = this.uploads.filter(upload => upload.files.length);
62
86
  });
63
- });
64
87
 
65
88
  reportTestPromises.push(reportTestPromise);
66
89
  }
67
90
 
91
+ #getArtifactPath(artifact) {
92
+ if (artifact.path) {
93
+ if (path.isAbsolute(artifact.path)) return artifact.path;
94
+
95
+ return path.join(this.config.outputDir || this.config.projects[0].outputDir, artifact.path);
96
+ }
97
+
98
+ if (artifact.body) {
99
+ const fileName = tmpFile();
100
+ fs.writeFileSync(fileName, artifact.body);
101
+ return fileName;
102
+ }
103
+
104
+ return null;
105
+ }
106
+
68
107
  async onEnd(result) {
69
108
  if (!this.client) return;
70
109
 
@@ -78,13 +117,11 @@ class PlaywrightReporter {
78
117
  for (const upload of this.uploads) {
79
118
  const { title, testId, suite_title } = upload;
80
119
 
81
- const files = upload.files.map(attachment => {
82
- if (attachment.body) {
83
- const fileName = tmpFile();
84
- fs.writeFileSync(fileName, attachment.body);
85
- }
86
- return { path: attachment.path, title, type: attachment.contentType };
87
- });
120
+ const files = upload.files.map(attachment => ({
121
+ path: this.#getArtifactPath(attachment),
122
+ title,
123
+ type: attachment.contentType
124
+ }));
88
125
 
89
126
  promises.push(
90
127
  this.client.addTestRun(undefined, {
@@ -92,6 +129,7 @@ class PlaywrightReporter {
92
129
  title,
93
130
  suite_title,
94
131
  files,
132
+ file: upload.file,
95
133
  }),
96
134
  );
97
135
  }
@@ -131,4 +169,28 @@ function tmpFile(prefix = 'tmp.') {
131
169
  return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex'));
132
170
  }
133
171
 
172
+ /**
173
+ * Returns filename + test title
174
+ * @param {*} test - testInfo object from Playwright
175
+ * @returns
176
+ */
177
+ function getTestContextName(test) {
178
+ return `${test._requireFile || ''}_${test.title}`;
179
+ }
180
+
181
+ function initPlaywrightForStorage() {
182
+ try {
183
+ // @ts-ignore-next-line
184
+ // eslint-disable-next-line import/no-unresolved
185
+ const { test } = require('@playwright/test');
186
+ // eslint-disable-next-line no-empty-pattern
187
+ test.beforeEach(async ({}, testInfo) => {
188
+ global.testomatioTestTitle = `${testInfo.file || ''}_${testInfo.title}`;
189
+ });
190
+ } catch (e) {
191
+ // ignore
192
+ }
193
+ }
194
+
134
195
  module.exports = PlaywrightReporter;
196
+ 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 config = 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'] || config.TESTOMATIO;
24
25
  const title = process.env.TESTOMATIO_TITLE;
25
26
 
26
27
  if (launch) {
@@ -53,6 +54,12 @@ program
53
54
 
54
55
  let exitCode = 0;
55
56
 
57
+ if (!command.split) {
58
+ process.exitCode = 255;
59
+ console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
60
+ return;
61
+ }
62
+
56
63
  const client = new TestomatClient({ apiKey, title, parallel: true });
57
64
 
58
65
  if(filter) {
@@ -74,12 +81,6 @@ program
74
81
  }
75
82
  }
76
83
 
77
- if (!command.split) {
78
- process.exitCode = 255;
79
- console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
80
- return;
81
- }
82
-
83
84
  const testCmds = command.split(' ');
84
85
  console.log(APP_PREFIX, `🚀 Running`, chalk.green(command));
85
86