@testomatio/reporter 1.3.1-beta → 1.3.1-beta.1-exclude-skipped-tests

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.
Files changed (47) hide show
  1. package/README.md +61 -54
  2. package/lib/adapter/codecept.js +96 -35
  3. package/lib/adapter/cucumber/current.js +19 -12
  4. package/lib/adapter/cucumber/legacy.js +7 -6
  5. package/lib/adapter/cucumber.js +2 -2
  6. package/lib/adapter/cypress-plugin/index.js +53 -26
  7. package/lib/adapter/jasmine.js +2 -2
  8. package/lib/adapter/jest.js +50 -17
  9. package/lib/adapter/mocha.js +110 -58
  10. package/lib/adapter/playwright.js +95 -33
  11. package/lib/adapter/webdriver.js +47 -2
  12. package/lib/bin/reportXml.js +22 -16
  13. package/lib/bin/startTest.js +27 -6
  14. package/lib/client.js +185 -62
  15. package/lib/config.js +34 -0
  16. package/lib/constants.js +32 -7
  17. package/lib/data-storage.js +203 -0
  18. package/lib/fileUploader.js +135 -54
  19. package/lib/junit-adapter/adapter.js +0 -2
  20. package/lib/junit-adapter/csharp.js +3 -4
  21. package/lib/junit-adapter/index.js +3 -3
  22. package/lib/junit-adapter/java.js +35 -17
  23. package/lib/junit-adapter/javascript.js +1 -2
  24. package/lib/junit-adapter/python.js +12 -14
  25. package/lib/junit-adapter/ruby.js +1 -2
  26. package/lib/pipe/csv.js +37 -31
  27. package/lib/pipe/github.js +27 -39
  28. package/lib/pipe/gitlab.js +22 -26
  29. package/lib/pipe/html.js +361 -0
  30. package/lib/pipe/index.js +3 -1
  31. package/lib/pipe/testomatio.js +337 -54
  32. package/lib/reporter-functions.js +46 -0
  33. package/lib/reporter.js +11 -9
  34. package/lib/services/artifacts.js +57 -0
  35. package/lib/services/index.js +13 -0
  36. package/lib/services/key-values.js +58 -0
  37. package/lib/services/logger.js +311 -0
  38. package/lib/template/emptyData.svg +23 -0
  39. package/lib/template/testomatio.hbs +1421 -0
  40. package/lib/utils/pipe_utils.js +129 -0
  41. package/lib/{util.js → utils/utils.js} +147 -17
  42. package/lib/xmlReader.js +218 -122
  43. package/package.json +17 -9
  44. package/lib/_ArtifactStorageOld.js +0 -142
  45. package/lib/artifactStorage.js +0 -25
  46. package/lib/dataStorage.js +0 -244
  47. package/lib/logger.js +0 -301
@@ -3,12 +3,11 @@ const path = require('path');
3
3
  const Adapter = require('./adapter');
4
4
 
5
5
  class JavaScriptAdapter extends Adapter {
6
-
7
6
  formatStack(t) {
8
7
  let stack = super.formatStack(t);
9
8
 
10
9
  try {
11
- const error = new Error(stack.split('\n')[0])
10
+ const error = new Error(stack.split('\n')[0]);
12
11
  error.stack = stack;
13
12
  const record = createCallsiteRecord({
14
13
  forError: error,
@@ -3,7 +3,6 @@ const fs = require('fs');
3
3
  const Adapter = require('./adapter');
4
4
 
5
5
  class PythonAdapter extends Adapter {
6
-
7
6
  getFilePath(t) {
8
7
  let fileName = namespaceToFileName(t.suite_title, { checkFile: true });
9
8
  if (!fileName) fileName = namespaceToFileName(t.suite_title, { checkFile: false });
@@ -11,34 +10,33 @@ class PythonAdapter extends Adapter {
11
10
  }
12
11
 
13
12
  formatTest(t) {
14
- const fileParts = t.suite_title.split('.')
13
+ const fileParts = t.suite_title.split('.');
15
14
  const example = t.title.match(/\[(.*)\]/)?.[1];
16
- if (example) t.example = { "#": example }
15
+ if (example) t.example = { '#': example };
17
16
 
18
17
  t.file = namespaceToFileName(t.suite_title);
19
18
  t.title = t.title.split('[')[0];
20
- t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ')
19
+ t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ');
21
20
  return t;
22
21
  }
23
22
 
24
23
  formatMessage(t) {
25
24
  return t.message.split('
')[0];
26
25
  }
27
-
28
26
  }
29
27
 
30
28
  function namespaceToFileName(fileName, opts = {}) {
31
- const fileParts = fileName.split('.')
29
+ const fileParts = fileName.split('.');
32
30
 
33
- while (fileParts.length > 0) {
34
- const file = `${fileParts.join(path.sep) }.py`;
35
- if (!opts.checkFile) return file;
36
- if (fs.existsSync(`${fileParts.join(path.sep) }.py`)) {
37
- return file;
38
- }
39
- fileParts.pop();
31
+ while (fileParts.length > 0) {
32
+ const file = `${fileParts.join(path.sep)}.py`;
33
+ if (!opts.checkFile) return file;
34
+ if (fs.existsSync(`${fileParts.join(path.sep)}.py`)) {
35
+ return file;
40
36
  }
41
- return null;
37
+ fileParts.pop();
38
+ }
39
+ return null;
42
40
  }
43
41
 
44
42
  module.exports = PythonAdapter;
@@ -1,10 +1,9 @@
1
1
  const Adapter = require('./adapter');
2
2
 
3
3
  class RubyAdapter extends Adapter {
4
-
5
4
  formatStack(t) {
6
5
  const stack = super.formatStack(t);
7
- return stack.replace(/\[(.*?:.\d*)\]/g, '\n$1')
6
+ return stack.replace(/\[(.*?:.\d*)\]/g, '\n$1');
8
7
  }
9
8
  }
10
9
 
package/lib/pipe/csv.js CHANGED
@@ -4,7 +4,7 @@ const fs = require('fs');
4
4
  const csvWriter = require('csv-writer');
5
5
  const chalk = require('chalk');
6
6
  const merge = require('lodash.merge');
7
- const { isSameTest, getCurrentDateTime } = require('../util');
7
+ const { isSameTest, getCurrentDateTime, ansiRegExp } = require('../utils/utils');
8
8
  const { CSV_HEADERS } = require('../constants');
9
9
 
10
10
  /**
@@ -14,32 +14,37 @@ const { CSV_HEADERS } = require('../constants');
14
14
  * @implements {Pipe}
15
15
  */
16
16
  class CsvPipe {
17
-
18
17
  constructor(params, store) {
19
18
  this.store = store || {};
20
19
  this.title = params.title || process.env.TESTOMATIO_TITLE;
21
20
  this.results = [];
22
21
 
23
22
  this.outputDir = 'export';
23
+ this.defaultReportName = 'report.csv';
24
24
  this.csvFilename = process.env.TESTOMATIO_CSV_FILENAME;
25
- this.isEnabled = !!this.csvFilename
26
- this.isCsvSave = false;
27
-
28
- if (this.csvFilename !== undefined && this.csvFilename.split('.').length > 0) {
29
- this.isCsvSave = true;
30
-
31
- if (this.csvFilename.split('.')[0] === 'report') {
32
- this.outputFile = path.resolve(process.cwd(), this.outputDir, 'report.csv');
33
- } else {
34
- this.outputFile = path.resolve(
35
- process.cwd(),
36
- this.outputDir,
37
- `${getCurrentDateTime()}_${this.csvFilename.split('.')[0]}.csv`,
38
- );
25
+ this.isEnabled = false;
26
+
27
+ if (this.csvFilename) {
28
+ const filenameParts = this.csvFilename.split('.');
29
+
30
+ if (filenameParts.length > 0) {
31
+ this.isEnabled = true;
32
+ const baseFilename = filenameParts[0];
33
+ const defaultOutputFile = path.resolve(process.cwd(), this.outputDir, this.defaultReportName);
34
+
35
+ const outputFile =
36
+ baseFilename === this.defaultReportName.split('.')[0] // = 'report'
37
+ ? defaultOutputFile
38
+ : path.resolve(process.cwd(), this.outputDir, `${getCurrentDateTime()}_${baseFilename}.csv`);
39
+
40
+ this.outputFile = outputFile;
39
41
  }
40
42
  }
41
43
  }
42
44
 
45
+ // TODO: to using SET opts as argument => prepareRun(opts)
46
+ async prepareRun() {}
47
+
43
48
  async createRun() {
44
49
  // empty
45
50
  }
@@ -67,22 +72,22 @@ class CsvPipe {
67
72
  this.checkExportDir();
68
73
 
69
74
  if (!this.outputFile) {
70
- console.log(this, chalk.yellow(`CSV file is not set, ignoring`));
75
+ console.log(chalk.yellow(`⚠️ CSV file is not set, ignoring`));
71
76
  return;
72
77
  }
73
78
 
74
- console.log(this, chalk.yellow(`The test results will be added to the csv. It will take some time...`));
75
- // Create csv writer object
76
- const writer = csvWriter.createObjectCsvWriter({
77
- path: this.outputFile,
78
- header: headers,
79
- });
80
- // Save csv file based on the current data
79
+ console.log(chalk.yellow(`⏳ The test results will be added to the csv. It will take some time...`));
80
+
81
81
  try {
82
- await writer.writeRecords(data);
83
- console.log(this, chalk.green(`Recording completed! You can check the result in file = ${this.outputFile}`));
82
+ // Create csv writer object
83
+ const writer = csvWriter.createObjectCsvWriter({
84
+ path: this.outputFile,
85
+ header: headers,
86
+ });
87
+ // Save csv file based on the current data
88
+ return await writer.writeRecords(data);
84
89
  } catch (e) {
85
- console.log('Unknown error: ', e);
90
+ console.log('Unknown csv error: ', e);
86
91
  }
87
92
  }
88
93
 
@@ -106,8 +111,8 @@ class CsvPipe {
106
111
  suite_title,
107
112
  title,
108
113
  status,
109
- message,
110
- stack,
114
+ message: message.replace(ansiRegExp(), ''),
115
+ stack: stack.replace(ansiRegExp(), ''),
111
116
  });
112
117
  }
113
118
 
@@ -120,8 +125,9 @@ class CsvPipe {
120
125
 
121
126
  if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
122
127
  // Save results based on the default headers
123
- if (this.isCsvSave) {
124
- this.saveToCsv(this.results, CSV_HEADERS);
128
+ if (this.isEnabled) {
129
+ await this.saveToCsv(this.results, CSV_HEADERS);
130
+ console.log(chalk.green(`🗃️ Recording completed! You can check the result in file = ${this.outputFile}`));
125
131
  }
126
132
  }
127
133
 
@@ -4,8 +4,9 @@ const chalk = require('chalk');
4
4
  const humanizeDuration = require('humanize-duration');
5
5
  const merge = require('lodash.merge');
6
6
  const { Octokit } = require('@octokit/rest');
7
- const { APP_PREFIX } = require('../constants');
8
- const { ansiRegExp, isSameTest } = require('../util');
7
+ const { APP_PREFIX, testomatLogoURL } = require('../constants');
8
+ const { ansiRegExp, isSameTest } = require('../utils/utils');
9
+ const { statusEmoji, fullName } = require('../utils/pipe_utils');
9
10
 
10
11
  /**
11
12
  * @typedef {import('../../types').Pipe} Pipe
@@ -21,7 +22,8 @@ class GitHubPipe {
21
22
  this.token = params.GH_PAT || process.env.GH_PAT;
22
23
  this.ref = process.env.GITHUB_REF;
23
24
  this.repo = process.env.GITHUB_REPOSITORY;
24
- this.hiddenCommentData = `<!--- testomat.io report ${process.env.GITHUB_WORKFLOW || ''} -->`;
25
+ this.jobKey = `${process.env.GITHUB_WORKFLOW || ''} / ${process.env.GITHUB_JOB || ''}`;
26
+ this.hiddenCommentData = `<!--- testomat.io report ${this.jobKey} -->`;
25
27
 
26
28
  debug('GitHub Pipe: ', this.token ? 'TOKEN' : '*no token*', 'Ref:', this.ref, 'Repo:', this.repo);
27
29
 
@@ -36,6 +38,9 @@ class GitHubPipe {
36
38
  debug('GitHub Pipe: Enabled');
37
39
  }
38
40
 
41
+ // TODO: to using SET opts as argument => prepareRun(opts)
42
+ async prepareRun() {}
43
+
39
44
  async createRun() {}
40
45
 
41
46
  addTest(test) {
@@ -72,36 +77,34 @@ class GitHubPipe {
72
77
 
73
78
  let summary = `${this.hiddenCommentData}
74
79
 
75
- | [![Testomat.io Report](https://avatars.githubusercontent.com/u/59105116?s=36&v=4)](https://testomat.io) | ${
76
- statusEmoji(runParams.status,)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
80
+ | [![Testomat.io Report](${testomatLogoURL})](https://testomat.io) | ${statusEmoji(
81
+ runParams.status,
82
+ )} ${`${process.env.GITHUB_JOB} ${runParams.status}`.toUpperCase()} |
77
83
  | --- | --- |
78
84
  | Tests | ✔️ **${this.tests.length}** tests run |
79
- | Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
85
+ | Summary | ${failedCount ? `${statusEmoji('failed')} **${failedCount}** failed; ` : ''} ${statusEmoji(
80
86
  'passed',
81
87
  )} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
82
- | Duration | 🕐 **${humanizeDuration(parseInt(this.tests.reduce((a, t) => a + (t.run_time || 0), 0), 10), {
83
- maxDecimalPoints: 0,
84
- })}** |
85
- `;
88
+ | Duration | 🕐 **${humanizeDuration(
89
+ parseInt(
90
+ this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
91
+ 10,
92
+ ),
93
+ {
94
+ maxDecimalPoints: 0,
95
+ },
96
+ )}** |`;
97
+
86
98
  if (this.store.runUrl) {
87
- summary += `| Testomat.io Report | 📊 [Run #${this.store.runId}](${this.store.runUrl}) | `;
99
+ summary += `\n| Testomat.io Report | 📊 [Run #${this.store.runId}](${this.store.runUrl}) | `;
88
100
  }
89
101
  if (process.env.GITHUB_WORKFLOW) {
90
- summary += `| Workflow | 🗂️ ${process.env.GITHUB_WORKFLOW} | `;
102
+ summary += `\n| Job | 🗂️ [${this.jobKey}](${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${
103
+ this.repo
104
+ }/actions/runs/${process.env.GITHUB_RUN_ID}) | `;
91
105
  }
92
106
  if (process.env.RUNNER_OS) {
93
- summary += `| Operating System | 🖥️ \`${process.env.RUNNER_OS}\` ${process.env.RUNNER_ARCH || ''} | `;
94
- }
95
- if (process.env.GITHUB_HEAD_REF) {
96
- summary += `| Branch | 🌳 \`${process.env.GITHUB_HEAD_REF}\` | `;
97
- }
98
- if (process.env.GITHUB_RUN_ATTEMPT) {
99
- summary += `| Run Attempt | 🌒 \`${process.env.GITHUB_RUN_ATTEMPT}\` | `;
100
- }
101
- if (process.env.GITHUB_RUN_ID) {
102
- summary += `| Build Log | ✒️ ${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${
103
- this.repo
104
- }/actions/runs/${process.env.GITHUB_RUN_ID} | `;
107
+ summary += `\n| Operating System | 🖥️ \`${process.env.RUNNER_OS}\` ${process.env.RUNNER_ARCH || ''} | `;
105
108
  }
106
109
 
107
110
  const failures = this.tests
@@ -184,21 +187,6 @@ class GitHubPipe {
184
187
  }
185
188
  }
186
189
 
187
- function statusEmoji(status) {
188
- if (status === 'passed') return '🟢';
189
- if (status === 'failed') return '🔴';
190
- if (status === 'skipped') return '🟡';
191
- return '';
192
- }
193
-
194
- function fullName(t) {
195
- let line = '';
196
- if (t.suite_title) line = `${t.suite_title}: `;
197
- line += `**${t.title}**`;
198
- if (t.example) line += ` \`[${Object.values(t.example)}]\``;
199
- return line;
200
- }
201
-
202
190
  async function deletePreviousReport(octokit, owner, repo, issue, hiddenCommentData) {
203
191
  if (process.env.GH_KEEP_OUTDATED_REPORTS) return;
204
192
 
@@ -4,8 +4,9 @@ const chalk = require('chalk');
4
4
  const humanizeDuration = require('humanize-duration');
5
5
  const merge = require('lodash.merge');
6
6
  const path = require('path');
7
- const { APP_PREFIX } = require('../constants');
8
- const { ansiRegExp, isSameTest } = require('../util');
7
+ const { APP_PREFIX, testomatLogoURL } = require('../constants');
8
+ const { ansiRegExp, isSameTest } = require('../utils/utils');
9
+ const { statusEmoji, fullName } = require('../utils/pipe_utils');
9
10
 
10
11
  //! GITLAB_PAT environment variable is required for this functionality to work
11
12
  //! and your pipeline trigger should be merge_request
@@ -22,7 +23,7 @@ class GitLabPipe {
22
23
  this.store = store;
23
24
  this.tests = [];
24
25
  // GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
25
- this.token = params.GITLAB_PAT || this.ENV.GITLAB_PAT;
26
+ this.token = params.GITLAB_PAT || process.env.GITLAB_PAT || this.ENV.GITLAB_PAT;
26
27
  this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
27
28
 
28
29
  debug(
@@ -46,6 +47,9 @@ class GitLabPipe {
46
47
  debug('GitLab Pipe: Enabled');
47
48
  }
48
49
 
50
+ // TODO: to using SET opts as argument => prepareRun(opts)
51
+ async prepareRun() {}
52
+
49
53
  async createRun() {}
50
54
 
51
55
  addTest(test) {
@@ -73,17 +77,24 @@ class GitLabPipe {
73
77
 
74
78
  // constructing the table
75
79
  let summary = `${this.hiddenCommentData}
76
-
77
- | [![Testomat.io Report](https://avatars.githubusercontent.com/u/59105116?s=36&v=4)](https://testomat.io) | ${
78
- statusEmoji(runParams.status)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
80
+
81
+ | [![Testomat.io Report](${testomatLogoURL})](https://testomat.io) | ${statusEmoji(
82
+ runParams.status,
83
+ )} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
79
84
  | --- | --- |
80
85
  | Tests | ✔️ **${this.tests.length}** tests run |
81
86
  | Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
82
- 'passed',
83
- )} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
84
- | Duration | 🕐 **${humanizeDuration(parseInt(this.tests.reduce((a, t) => a + (t.run_time || 0), 0), 10), {
85
- maxDecimalPoints: 0,
86
- })}** |
87
+ 'passed',
88
+ )} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
89
+ | Duration | 🕐 **${humanizeDuration(
90
+ parseInt(
91
+ this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
92
+ 10,
93
+ ),
94
+ {
95
+ maxDecimalPoints: 0,
96
+ },
97
+ )}** |
87
98
  `;
88
99
 
89
100
  if (this.ENV.CI_JOB_NAME && this.ENV.CI_JOB_ID) {
@@ -179,21 +190,6 @@ class GitLabPipe {
179
190
  updateRun() {}
180
191
  }
181
192
 
182
- function statusEmoji(status) {
183
- if (status === 'passed') return '🟢';
184
- if (status === 'failed') return '🔴';
185
- if (status === 'skipped') return '🟡';
186
- return '';
187
- }
188
-
189
- function fullName(t) {
190
- let line = '';
191
- if (t.suite_title) line = `${t.suite_title}: `;
192
- line += `**${t.title}**`;
193
- if (t.example) line += ` \`[${Object.values(t.example)}]\``;
194
- return line;
195
- }
196
-
197
193
  async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
198
194
  if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
199
195