@testomatio/reporter 1.0.13 → 1.0.14

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,7 +4,7 @@ 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
 
@@ -3,7 +3,7 @@ const chalk = require('chalk');
3
3
  const TestomatClient = require('../client');
4
4
  const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE } = require('../constants');
5
5
  const upload = require('../fileUploader');
6
- const { parseTest: getIdFromTestTitle, fileSystem } = require('../util');
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
@@ -5,7 +5,7 @@ const fs = require('fs');
5
5
  const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
7
  const logger = require('../../logger');
8
- const { parseTest, fileSystem } = require('../../util');
8
+ const { parseTest, fileSystem } = require('../../utils/utils');
9
9
 
10
10
  const { GherkinDocumentParser, PickleParser } = formatterHelpers;
11
11
  const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
@@ -1,7 +1,7 @@
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, fileSystem } = require('../../util');
4
+ const { parseTest, fileSystem } = require('../../utils/utils');
5
5
  const { STATUS, TESTOMAT_TMP_STORAGE } = require('../../constants');
6
6
  const TestomatClient = require('../../client');
7
7
 
@@ -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
2
  const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
3
- const { parseTest, ansiRegExp, fileSystem } = require('../util');
3
+ const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
4
4
 
5
5
  class JestReporter {
6
6
  constructor(globalConfig, options) {
@@ -4,7 +4,7 @@ const debug = require('debug')('@testomatio/reporter:adapter:mocha');
4
4
  const chalk = require('chalk');
5
5
  const TestomatClient = require('../client');
6
6
  const { STATUS, TESTOMAT_TMP_STORAGE } = require('../constants');
7
- const { parseTest, specificTestInfo, fileSystem } = require('../util');
7
+ const { parseTest, specificTestInfo, fileSystem } = require('../utils/utils');
8
8
  const ArtifactStorage = require('../_ArtifactStorageOld');
9
9
 
10
10
  const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING } =
@@ -6,7 +6,7 @@ const fs = require('fs');
6
6
  const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
8
  const { isArtifactsEnabled } = require('../fileUploader');
9
- const { parseTest, fileSystem } = require('../util');
9
+ const { parseTest, fileSystem } = require('../utils/utils');
10
10
 
11
11
  const reportTestPromises = [];
12
12
 
@@ -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) {
@@ -13,9 +13,11 @@ program
13
13
  .option('--launch', 'Start a new run and return its ID')
14
14
  .option('--finish', 'Finish Run by its ID')
15
15
  .option("--env-file <envfile>", "Load environment variables from env file")
16
- .action(opts => {
16
+ .option("--filter <filter>", "Additional execution filter")
17
+ .action(async (opts) => {
18
+ const { launch, finish, filter } = opts;
19
+ let { command } = opts;
17
20
 
18
- const { command, launch, finish } = opts;
19
21
  if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
20
22
 
21
23
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || process.env.TESTOMATIO;
@@ -51,6 +53,27 @@ program
51
53
 
52
54
  let exitCode = 0;
53
55
 
56
+ const client = new TestomatClient({ apiKey, title, parallel: true });
57
+
58
+ if(filter) {
59
+ const [pipe, ...optsArray] = filter.split(":");
60
+ const pipeOptions = optsArray.join(":");
61
+
62
+ try {
63
+ const tests = await client.prepareRun({pipe, pipeOptions});
64
+
65
+ if(!tests || tests.length === 0) {
66
+ return;
67
+ }
68
+
69
+ const grep = ` --grep (${tests.join('|')})`;
70
+ command += grep;
71
+ }
72
+ catch(err) {
73
+ console.log(APP_PREFIX, err);
74
+ }
75
+ }
76
+
54
77
  if (!command.split) {
55
78
  process.exitCode = 255;
56
79
  console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
@@ -73,8 +96,6 @@ program
73
96
  return;
74
97
  }
75
98
 
76
- const client = new TestomatClient({ apiKey, title, parallel: true });
77
-
78
99
  client.createRun().then(() => {
79
100
  const cmd = spawn(testCmds[0], testCmds.slice(1), { stdio: 'inherit' });
80
101
 
package/lib/client.js CHANGED
@@ -27,15 +27,71 @@ class Client {
27
27
  this.queue = Promise.resolve();
28
28
  this.totalUploaded = 0;
29
29
  this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
30
+ this.executionList = Promise.resolve();
31
+
30
32
  console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
31
33
  }
32
34
 
35
+ /**
36
+ * Asynchronously prepares the execution list for running tests through various pipes.
37
+ * Each pipe in the client is checked for enablement,
38
+ * and if all pipes are disabled, the function returns a resolved Promise.
39
+ * Otherwise, it executes the `prepareRun` method for each enabled pipe and collects the results.
40
+ * The results are then filtered to remove any undefined values.
41
+ * If no valid results are found, the function returns undefined.
42
+ * Otherwise, it returns the first non-empty array from the filtered results.
43
+ *
44
+ * @param {Object} params - The options for preparing the test execution list.
45
+ * @param {string} params.pipe - Name of the executed pipe.
46
+ * @param {string} params.pipeOptions - Filter option.
47
+ * @returns {Promise<any>} - A Promise that resolves to an
48
+ * array containing the prepared execution list,
49
+ * or resolves to undefined if no valid results are found or if all pipes are disabled.
50
+ */
51
+ async prepareRun(params) {
52
+ const { pipe, pipeOptions } = params;
53
+ // all pipes disabled, skipping
54
+ if (!this.pipes.some(p => p.isEnabled)) {
55
+ return Promise.resolve();
56
+ }
57
+
58
+ try {
59
+ const filterPipe = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
60
+
61
+ if (!filterPipe.isEnabled) {
62
+ // TODO:for the future for the another pipes
63
+ console.warn(
64
+ APP_PREFIX,
65
+ `At the moment processing is available only for the "testomatio" key. Example: "testomatio:tag-name=xxx"`
66
+ );
67
+ return;
68
+ }
69
+
70
+ const results = await Promise.all(this.pipes.map(async p =>
71
+ ({ pipe: p.toString(), result: await p.prepareRun(pipeOptions) })
72
+ ));
73
+
74
+ const result = results.filter(p => p.pipe.includes('Testomatio'))[0]?.result;
75
+
76
+ if (!result || result.length === 0) {
77
+ return;
78
+ }
79
+
80
+ debug('Execution tests list', result);
81
+
82
+ return result;
83
+ } catch (err) {
84
+ console.error(APP_PREFIX, err);
85
+ }
86
+ }
87
+
33
88
  /**
34
89
  * Used to create a new Test run
35
90
  *
36
91
  * @returns {Promise<any>} - resolves to Run id which should be used to update / add test
37
92
  */
38
93
  createRun() {
94
+ debug('Creating run...');
39
95
  // all pipes disabled, skipping
40
96
  if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
41
97
 
@@ -59,6 +115,7 @@ class Client {
59
115
  * @returns {Promise<PipeResult[]>}
60
116
  */
61
117
  async addTestRun(status, testData, storeArtifacts = []) {
118
+ debug('Adding test run for test', testData?.test_id || 'unknown test');
62
119
  // all pipes disabled, skipping
63
120
  if (!this.pipes?.filter(p => p.isEnabled).length) return [];
64
121
 
@@ -170,6 +227,7 @@ class Client {
170
227
  * @returns {Promise<any>} - A Promise that resolves when finishes the run.
171
228
  */
172
229
  updateRunStatus(status, isParallel = false) {
230
+ debug('Updating run status...');
173
231
  // all pipes disabled, skipping
174
232
  if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
175
233
 
@@ -5,8 +5,8 @@ const os = require('os');
5
5
  const { join } = require('path');
6
6
  const JestReporter = require('./adapter/jest');
7
7
  const { TESTOMAT_TMP_STORAGE } = require('./constants');
8
- const { fileSystem } = require('./util');
9
- const getTestIdFromTestTitle = require('./util').parseTest;
8
+ const { fileSystem } = require('./utils/utils');
9
+ const getTestIdFromTestTitle = require('./utils/utils').parseTest;
10
10
 
11
11
  class DataStorage {
12
12
  /**
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 } = require('../utils/utils');
8
8
  const { CSV_HEADERS } = require('../constants');
9
9
 
10
10
  /**
@@ -40,6 +40,9 @@ class CsvPipe {
40
40
  }
41
41
  }
42
42
 
43
+ // TODO: to using SET opts as argument => prepareRun(opts)
44
+ async prepareRun() {}
45
+
43
46
  async createRun() {
44
47
  // empty
45
48
  }
@@ -5,7 +5,8 @@ const humanizeDuration = require('humanize-duration');
5
5
  const merge = require('lodash.merge');
6
6
  const { Octokit } = require('@octokit/rest');
7
7
  const { APP_PREFIX } = require('../constants');
8
- const { ansiRegExp, isSameTest } = require('../util');
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
@@ -37,6 +38,9 @@ class GitHubPipe {
37
38
  debug('GitHub Pipe: Enabled');
38
39
  }
39
40
 
41
+ // TODO: to using SET opts as argument => prepareRun(opts)
42
+ async prepareRun() {}
43
+
40
44
  async createRun() {}
41
45
 
42
46
  addTest(test) {
@@ -182,21 +186,6 @@ class GitHubPipe {
182
186
  }
183
187
  }
184
188
 
185
- function statusEmoji(status) {
186
- if (status === 'passed') return '🟢';
187
- if (status === 'failed') return '🔴';
188
- if (status === 'skipped') return '🟡';
189
- return '';
190
- }
191
-
192
- function fullName(t) {
193
- let line = '';
194
- if (t.suite_title) line = `${t.suite_title}: `;
195
- line += `**${t.title}**`;
196
- if (t.example) line += ` \`[${Object.values(t.example)}]\``;
197
- return line;
198
- }
199
-
200
189
  async function deletePreviousReport(octokit, owner, repo, issue, hiddenCommentData) {
201
190
  if (process.env.GH_KEEP_OUTDATED_REPORTS) return;
202
191
 
@@ -5,7 +5,8 @@ const humanizeDuration = require('humanize-duration');
5
5
  const merge = require('lodash.merge');
6
6
  const path = require('path');
7
7
  const { APP_PREFIX } = require('../constants');
8
- const { ansiRegExp, isSameTest } = require('../util');
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
@@ -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) {
@@ -179,21 +183,6 @@ class GitLabPipe {
179
183
  updateRun() {}
180
184
  }
181
185
 
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
186
  async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
198
187
  if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
199
188
 
@@ -3,8 +3,8 @@ const chalk = require('chalk');
3
3
  const axios = require('axios');
4
4
  const JsonCycle = require('json-cycle');
5
5
  const { APP_PREFIX, STATUS } = require('../constants');
6
- const { isValidUrl } = require('../util');
7
- const { resetConfig } = require('../fileUploader');
6
+ const { isValidUrl, foundedTestLog } = require('../utils/utils');
7
+ const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('../utils/pipe_utils');
8
8
 
9
9
  const { TESTOMATIO_RUN } = process.env;
10
10
  if (TESTOMATIO_RUN) {
@@ -33,7 +33,12 @@ class TestomatioPipe {
33
33
  this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
34
34
  this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
35
35
  this.env = process.env.TESTOMATIO_ENV;
36
- this.axios = axios.create();
36
+
37
+ this.axios = axios.create({
38
+ baseURL: `${this.url.trim()}`,
39
+ timeout: 30000
40
+ });
41
+
37
42
  this.isEnabled = true;
38
43
  // do not finish this run (for parallel testing)
39
44
  this.proceed = process.env.TESTOMATIO_PROCEED;
@@ -47,10 +52,52 @@ class TestomatioPipe {
47
52
  }
48
53
  }
49
54
 
55
+ /**
56
+ * Asynchronously prepares and retrieves the Testomat.io test grepList based on the provided options.
57
+ * @param {Object} opts - The options for preparing the test grepList.
58
+ * @returns {Promise<string[]>} - An array containing the retrieved
59
+ * test grepList, or an empty array if no tests are found or the request is disabled.
60
+ * @throws {Error} - Throws an error if there was a problem while making the request.
61
+ */
62
+ async prepareRun(opts) {
63
+ if (!this.isEnabled) return [];
64
+
65
+ const { type, id } = parseFilterParams(opts);
66
+
67
+ try {
68
+ const q = generateFilterRequestParams({
69
+ type,
70
+ id,
71
+ apiKey: this.apiKey.trim()
72
+ });
73
+
74
+ if (!q) {
75
+ return;
76
+ }
77
+
78
+ const resp = await this.axios.get('/api/test_grep', q);
79
+ const { data } = resp;
80
+
81
+ if (Array.isArray(data?.tests) && data?.tests?.length > 0) {
82
+ foundedTestLog(APP_PREFIX, data.tests);
83
+ return data.tests;
84
+ }
85
+
86
+ console.log(APP_PREFIX, `⛔ No tests found for your --filter --> ${type}=${id}`);
87
+ }
88
+ catch (err) {
89
+ console.error(
90
+ APP_PREFIX,
91
+ `🚩 Error getting Testomat.io test grepList: ${err}`,
92
+ );
93
+ }
94
+ }
95
+
50
96
  /**
51
97
  * @returns Promise<void>
52
98
  */
53
99
  async createRun() {
100
+ debug('Creating run...');
54
101
  if (!this.isEnabled) return;
55
102
 
56
103
  let buildUrl = process.env.BUILD_URL || process.env.CI_JOB_URL || process.env.CIRCLE_BUILD_URL;
@@ -58,7 +105,7 @@ class TestomatioPipe {
58
105
  // GitHub Actions Url
59
106
  if (!buildUrl && process.env.GITHUB_RUN_ID) {
60
107
  // eslint-disable-next-line max-len
61
- buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
108
+ buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
62
109
  }
63
110
 
64
111
  // Azure DevOps Url
@@ -83,17 +130,19 @@ class TestomatioPipe {
83
130
  env: this.env,
84
131
  title: this.title,
85
132
  shared_run: this.sharedRun,
86
- }).filter(([, value]) => !!value)
133
+ }).filter(([, value]) => !!value),
87
134
  );
135
+ debug('Run params', JSON.stringify(runParams, null, 2));
88
136
 
89
137
  if (this.runId) {
90
- const resp = await this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
138
+ debug(`Run with id ${this.runId} already created, updating...`);
139
+ const resp = await this.axios.put(`/api/reporter/${this.runId}`, runParams);
91
140
  if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
92
141
  return;
93
142
  }
94
143
 
95
144
  try {
96
- const resp = await this.axios.post(`${this.url.trim()}/api/reporter`, runParams, {
145
+ const resp = await this.axios.post(`/api/reporter`, runParams, {
97
146
  maxContentLength: Infinity,
98
147
  maxBodyLength: Infinity,
99
148
  });
@@ -106,27 +155,31 @@ class TestomatioPipe {
106
155
  this.store.runId = this.runId;
107
156
  console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
108
157
  process.env.runId = this.runId;
158
+ debug('Run created', this.runId);
109
159
  } catch (err) {
110
160
  console.error(
111
161
  APP_PREFIX,
112
162
  'Error creating Testomat.io report, please check if your API key is valid. Skipping report',
163
+ err,
113
164
  );
114
165
  }
166
+ debug('"createRun" function finished');
115
167
  }
116
168
 
117
169
  /**
118
- *
119
- * @param testData data
120
- * @returns
170
+ *
171
+ * @param testData data
172
+ * @returns
121
173
  */
122
174
  addTest(data) {
175
+ debug('Adding test...');
123
176
  if (!this.isEnabled) return;
124
177
  if (!this.runId) return;
125
178
  data.api_key = this.apiKey;
126
179
  data.create = this.createNewTests;
127
180
  const json = JsonCycle.stringify(data);
128
181
 
129
- return this.axios.post(`${this.url}/api/reporter/${this.runId}/testrun`, json, {
182
+ return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, {
130
183
  maxContentLength: Infinity,
131
184
  maxBodyLength: Infinity,
132
185
  headers: {
@@ -134,7 +187,7 @@ class TestomatioPipe {
134
187
  'Content-Type': 'application/json',
135
188
  },
136
189
  })
137
- .catch((err) => {
190
+ .catch(err => {
138
191
  if (err.response) {
139
192
  if (err.response.status >= 400) {
140
193
  const responseData = err.response.data || { message: '' };
@@ -145,11 +198,14 @@ class TestomatioPipe {
145
198
  );
146
199
  if (err.response.data.message.includes('could not be matched')) {
147
200
  this.hasUnmatchedTests = true;
148
- }
201
+ }
149
202
  return;
150
203
  }
151
- // eslint-disable-next-line max-len
152
- console.log(APP_PREFIX, chalk.blue(data?.title || ''), `Report couldn't be processed: ${err.response.data.message}`);
204
+ console.log(
205
+ APP_PREFIX,
206
+ chalk.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
207
+ `Report couldn't be processed: ${err?.response?.data?.message}`,
208
+ );
153
209
  } else {
154
210
  console.log(APP_PREFIX, chalk.blue(data?.title || ''), "Report couldn't be processed", err);
155
211
  }
@@ -157,10 +213,11 @@ class TestomatioPipe {
157
213
  }
158
214
 
159
215
  /**
160
- * @param {import('../../types').RunData} params
161
- * @returns
216
+ * @param {import('../../types').RunData} params
217
+ * @returns
162
218
  */
163
219
  async finishRun(params) {
220
+ debug('Finishing run...');
164
221
  if (!this.isEnabled) return;
165
222
 
166
223
  const { status, parallel } = params;
@@ -174,7 +231,7 @@ class TestomatioPipe {
174
231
 
175
232
  try {
176
233
  if (this.runId && !this.proceed) {
177
- await this.axios.put(`${this.url}/api/reporter/${this.runId}`, {
234
+ await this.axios.put(`/api/reporter/${this.runId}`, {
178
235
  api_key: this.apiKey,
179
236
  status_event,
180
237
  tests: params.tests,
@@ -185,7 +242,6 @@ class TestomatioPipe {
185
242
  if (this.runPublicUrl) {
186
243
  console.log(APP_PREFIX, '🌟 Public URL:', chalk.magenta(this.runPublicUrl));
187
244
  }
188
-
189
245
  }
190
246
  if (this.runUrl && this.proceed) {
191
247
  const notFinishedMessage = chalk.yellow.bold('Run was not finished because of $TESTOMATIO_PROCEED');
@@ -197,19 +253,30 @@ class TestomatioPipe {
197
253
  // eslint-disable-next-line max-len
198
254
  console.log(APP_PREFIX, chalk.yellow.bold('⚠️ Some reported tests were not found in Testomat.io project'));
199
255
  // eslint-disable-next-line max-len
200
- console.log(APP_PREFIX, `If you use Testomat.io as a reporter only, please re-run tests using ${chalk.bold('TESTOMATIO_CREATE=1')}`);
256
+ console.log(
257
+ APP_PREFIX,
258
+ `If you use Testomat.io as a reporter only, please re-run tests using ${chalk.bold('TESTOMATIO_CREATE=1')}`,
259
+ );
201
260
  // eslint-disable-next-line max-len
202
- console.log(APP_PREFIX, `But to keep your tests consistent it is recommended to ${chalk.bold('import tests first')}`);
261
+ console.log(
262
+ APP_PREFIX,
263
+ `But to keep your tests consistent it is recommended to ${chalk.bold('import tests first')}`,
264
+ );
203
265
  console.log(APP_PREFIX, 'If tests were imported but still not matched, assign test IDs to your tests.');
204
266
  console.log(APP_PREFIX, 'You can do that automatically via command line tools:');
205
267
  console.log(APP_PREFIX, chalk.bold('npx check-tests ... --update-ids'), 'See: https://bit.ly/js-update-ids');
206
268
  console.log(APP_PREFIX, 'or for Cucumber:');
207
269
  // eslint-disable-next-line max-len
208
- console.log(APP_PREFIX, chalk.bold('npx check-cucumber ... --update-ids'), 'See: https://bit.ly/bdd-update-ids');
270
+ console.log(
271
+ APP_PREFIX,
272
+ chalk.bold('npx check-cucumber ... --update-ids'),
273
+ 'See: https://bit.ly/bdd-update-ids',
274
+ );
209
275
  }
210
276
  } catch (err) {
211
277
  console.log(APP_PREFIX, 'Error updating status, skipping...', err);
212
278
  }
279
+ debug('Run finished');
213
280
  }
214
281
 
215
282
  toString() {
@@ -218,18 +285,3 @@ class TestomatioPipe {
218
285
  }
219
286
 
220
287
  module.exports = TestomatioPipe;
221
-
222
-
223
- function setS3Credentials(artifacts) {
224
- if (!Object.keys(artifacts).length) return;
225
-
226
- console.log(APP_PREFIX, 'S3 were credentials obtained from Testomat.io...');
227
-
228
- if (artifacts.ACCESS_KEY_ID) process.env.S3_ACCESS_KEY_ID = artifacts.ACCESS_KEY_ID;
229
- if (artifacts.SECRET_ACCESS_KEY) process.env.S3_SECRET_ACCESS_KEY = artifacts.SECRET_ACCESS_KEY;
230
- if (artifacts.REGION) process.env.S3_REGION = artifacts.REGION;
231
- if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
232
- if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
233
- if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
234
- resetConfig();
235
- }
@@ -0,0 +1,135 @@
1
+ const { resetConfig } = require('../fileUploader');
2
+ const { APP_PREFIX } = require('../constants');
3
+
4
+ /**
5
+ * Set S3 credentials from the provided artifacts object.
6
+ * @param {Object} artifacts - The artifacts object containing S3 credentials.
7
+ */
8
+ function setS3Credentials(artifacts) {
9
+ if (!Object.keys(artifacts).length) return;
10
+
11
+ console.log(APP_PREFIX, 'S3 were credentials obtained from Testomat.io...');
12
+
13
+ if (artifacts.ACCESS_KEY_ID) process.env.S3_ACCESS_KEY_ID = artifacts.ACCESS_KEY_ID;
14
+ if (artifacts.SECRET_ACCESS_KEY) process.env.S3_SECRET_ACCESS_KEY = artifacts.SECRET_ACCESS_KEY;
15
+ if (artifacts.REGION) process.env.S3_REGION = artifacts.REGION;
16
+ if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
17
+ if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
18
+ if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
19
+ resetConfig();
20
+ }
21
+ /**
22
+ * Generates mode request parameters based on the input params.
23
+ * @param {Object} params - The input parameters for the request.
24
+ * @param {string} params.type - The type of the request (e.g., "tag").
25
+ * @param {string} params.id - The ID associated with the request.
26
+ * @param {string} params.apiKey - The API key for authentication.
27
+ * @returns {Object|null} - An object containing the generated request parameters, or null if the type is invalid.
28
+ */
29
+ function generateFilterRequestParams(params) {
30
+ const { type, id, apiKey } = params;
31
+
32
+ if (!type) {
33
+ return;
34
+ }
35
+
36
+ if (!id) {
37
+ console.error(APP_PREFIX, `Please make sure your settings "${type.toUpperCase()}"= "${id}" is correct!`);
38
+ return;
39
+ }
40
+
41
+ return {
42
+ params: {
43
+ type,
44
+ id: encodeURIComponent(id),
45
+ api_key: apiKey
46
+ },
47
+ responseType: "json"
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Parse filter parameters from a string in the format "type=id".
53
+ * @param {string} opts - The input string containing the filter parameters.
54
+ * @returns {Object} An object containing the parsed filter parameters.
55
+ * The object has properties "type" and "id".
56
+ */
57
+ function parseFilterParams(opts) {
58
+ const [type, id] = opts.split("=");
59
+ const validType = updateFilterType(type);
60
+
61
+ return {
62
+ type: validType,
63
+ id
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Update and validate the filter type.
69
+ * @param {string} type - The original filter type.
70
+ * @returns {string|undefined} The updated and validated filter type.
71
+ * Returns undefined if the type is not valid.
72
+ */
73
+ function updateFilterType(type) {
74
+ const typeLowerCase = type.toLowerCase();
75
+
76
+ const filterTypes = [
77
+ "tag-name",
78
+ "plan-id",
79
+ "label",
80
+ "jira-ticket",
81
+ ];
82
+
83
+ const filterApi = [
84
+ "tag",
85
+ "plan",
86
+ "label",
87
+ "jira",
88
+ // "ims-issue", //TODO: WIP
89
+ ];
90
+
91
+ if (!filterTypes.includes(typeLowerCase)) {
92
+ console.log(APP_PREFIX, `❗❗❗ Invalid "filter=${type}" start settings! Available option list: ${filterTypes}`);
93
+ return;
94
+ }
95
+
96
+ const index = filterTypes.indexOf(typeLowerCase);
97
+
98
+ return (index !== -1)
99
+ ? filterApi[index]
100
+ : undefined
101
+ }
102
+
103
+ /**
104
+ * Return an emoji based on the provided status.
105
+ * @param {string} status - The status value ('passed', 'failed', or 'skipped').
106
+ * @returns {string} - An emoji corresponding to the provided status.
107
+ */
108
+ function statusEmoji(status) {
109
+ if (status === 'passed') return '🟢';
110
+ if (status === 'failed') return '🔴';
111
+ if (status === 'skipped') return '🟡';
112
+ return '';
113
+ }
114
+
115
+ /**
116
+ * Generate a full name string based on the provided test object.
117
+ * @param {object} t - The test object.
118
+ * @returns {string} - A formatted full name string for the test object.
119
+ */
120
+ function fullName(t) {
121
+ let line = '';
122
+ if (t.suite_title) line = `${t.suite_title}: `;
123
+ line += `**${t.title}**`;
124
+ if (t.example) line += ` \`[${Object.values(t.example)}]\``;
125
+ return line;
126
+ }
127
+
128
+ module.exports = {
129
+ updateFilterType,
130
+ parseFilterParams,
131
+ generateFilterRequestParams,
132
+ setS3Credentials,
133
+ statusEmoji,
134
+ fullName
135
+ };
@@ -227,6 +227,15 @@ const fileSystem = {
227
227
  },
228
228
  };
229
229
 
230
+
231
+ const foundedTestLog = (app, tests) => {
232
+ const n = tests.length;
233
+
234
+ return (n === 1)
235
+ ? console.log(app, `✅ We found one test!`)
236
+ : console.log(app, `✅ We found ${n} tests!`);
237
+ }
238
+
230
239
  const humanize = text => {
231
240
  text = decamelize(text);
232
241
  return text
@@ -297,4 +306,5 @@ module.exports = {
297
306
  parseSuite,
298
307
  humanize,
299
308
  removeColorCodes,
300
- };
309
+ foundedTestLog
310
+ };
package/lib/xmlReader.js CHANGED
@@ -8,12 +8,12 @@ const { fetchFilesFromStackTrace,
8
8
  fetchIdFromOutput,
9
9
  fetchSourceCode,
10
10
  fetchSourceCodeFromStackTrace,
11
- fetchIdFromCode
12
- } = require('./util');
11
+ fetchIdFromCode,
12
+ humanize
13
+ } = require('./utils/utils');
13
14
  const upload = require('./fileUploader');
14
15
  const pipesFactory = require('./pipe');
15
16
  const adapterFactory = require('./junit-adapter');
16
- const { humanize } = require('./util')
17
17
 
18
18
  const TESTOMATIO_URL = process.env.TESTOMATIO_URL || "https://app.testomat.io";
19
19
  const TESTOMATIO = process.env.TESTOMATIO; // key?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",
@@ -55,7 +55,7 @@
55
55
  "@redocly/cli": "^1.0.0-beta.125",
56
56
  "@wdio/reporter": "^7.16.13",
57
57
  "chai": "^4.3.6",
58
- "codeceptjs": "^3.2.3",
58
+ "codeceptjs": "latest",
59
59
  "cucumber": "^6.0.7",
60
60
  "eslint": "^8.7.0",
61
61
  "eslint-config-airbnb-base": "^15.0.0",
package/Changelog.md DELETED
@@ -1,322 +0,0 @@
1
- <!-- pending release updates -->
2
- # 1.0.13
3
-
4
- * JUnit improvements
5
- * Match test from source code by adding Test ID as a comment:
6
-
7
- ```java
8
- // @T8acca9eb
9
- ```
10
- * Match test from output by adding Test ID as output:
11
-
12
- ```java
13
- System.out.println("tid://@T8acca9eb");
14
- ```
15
- * Support for suite before and after output
16
- * Improved support for artifacts
17
-
18
- # 1.0.12
19
-
20
- & Logger refactoring by @olexandr13 in #208
21
- * fix undefined logs by @olexandr13 in #210
22
-
23
- # 1.0.11
24
-
25
- * fix steps duplication for codecept report by @olexandr13 in #209
26
-
27
- # 1.0.10
28
-
29
- * Added `TESTOMATIO_PUBLISH=1` variable to automatically publish run report
30
-
31
- # 1.0.9
32
-
33
- * Support XUnit format
34
- * Improved support for parametrized Java tests
35
-
36
- # 1.0.8
37
-
38
- * Fixed `Can't read push of undefined` when logging steps
39
-
40
- # 1.0.6
41
-
42
- * Testomat.io. Auto-detect current build url and report it to Testomat.io. Manually url can be set with `BUILD_URL` variable:
43
-
44
- ```
45
- BUILD_URL=https://.... TESTOMATIO=apiKey <actual test command>
46
- ```
47
-
48
- # 1.0.5
49
-
50
- * Fix "create tests" params processing for testomatio pipe
51
-
52
- # 1.0.4
53
-
54
- * Fixed parallel run
55
-
56
- # 1.0.3
57
-
58
- * Fixed reporting parallel runs
59
-
60
- # 1.0.0
61
-
62
-
63
- * Added [`TESTOMATIO_SHARED_RUN` option](https://github.com/testomatio/reporter/blob/master/docs/pipes.md#reporting-parallel-execution-to-to-same-run) to use a shared run for parallel executions
64
- * Reworked [documentation](https://github.com/testomatio/reporter/tree/master#readme).
65
- * Added an option to obtain [S3 configuration](https://github.com/testomatio/reporter/blob/master/docs/artifacts.md#configuration) from Testomat.io
66
- * Introduced [pipes](https://github.com/testomatio/reporter/blob/master/docs/pipes.md):
67
- * GitHub
68
- * GitLab
69
- * CSV Pipe
70
-
71
-
72
- # 0.7.6
73
-
74
- * Updated to use AWS S3 3.0 SDK for uploading
75
-
76
- # 0.7.5
77
-
78
- * Fixed reporting skipped tests in mocha
79
-
80
- # 0.7.4
81
-
82
- * Fixed parsing source code in JUnit files
83
-
84
- # 0.7.3
85
-
86
- * CodeceptJS: Upload all traces and videos from artifacts
87
- * Fixed reporting skipped test in XML
88
- * added `--timelimit` option to `report-xml` command line
89
-
90
- # 0.7.2
91
-
92
- * Fixed uploading non-existing file
93
-
94
- # 0.7.1
95
-
96
- * Support for NUnit XML v3 format
97
-
98
- # 0.7.0
99
-
100
- * Support for `@cucumber/cucumber` (>= 7.0) added
101
- * Initial support for C# and NUnit
102
-
103
- # 0.6.10
104
-
105
- * Fixed uploading multilpe artifacts in Playwright
106
-
107
- # 0.6.9
108
-
109
- * Fixed pending tests reports for Cypress
110
-
111
- # 0.6.8
112
- # 0.6.7
113
-
114
- * Pytest: fixed creating suites from reports
115
-
116
- # 0.6.6
117
-
118
- * JUnit reporter: prefer suite title over testcase classname in a report
119
-
120
- # 0.6.5
121
-
122
- * Fixed test statuses for runs in JUnit reporter
123
-
124
- # 0.6.4
125
-
126
- * Added `TESTOMATIO_PROCEED=1` param to not close current run
127
- * Fixed priority of commands from `npx @testomatio/reporter`
128
-
129
- # 0.6.3
130
-
131
- * Fixed `npx start-test-run` to launch commands
132
-
133
- # 0.6.2
134
-
135
- * Added `--env-file` option to load env variables from env file
136
-
137
- # 0.6.1
138
-
139
- * Fixed creating RunGroup with JUnit reporter
140
-
141
- # 0.6.0
142
-
143
- * JUnit reporter support
144
-
145
- # 0.5.10
146
-
147
- * Fixed reporting Scenario Outline in Cypress-Cucumber
148
- * Fixed error reports for Cypress when running in Chrome
149
-
150
- # 0.5.9
151
-
152
- * Added environment on Cypress report
153
-
154
- # 0.5.8
155
-
156
- * Fixed Cypress.io reporting
157
-
158
- # 0.5.7
159
-
160
- * Fixed webdriverio artifacts
161
-
162
- # 0.5.6
163
-
164
- * Unmark failed CodeceptJS tests as skipped
165
-
166
- # 0.5.5
167
-
168
- * Fixed `BeforeSuite` failures in CodeceptJS
169
-
170
- # 0.5.4
171
-
172
- Added `TESTOMATIO_CREATE=1` option to create unmatched tests on report
173
-
174
- ```
175
- TESTOMATIO_CREATE=1 TESTOMATIO=apiKey npx codeceptjs run
176
- ```
177
-
178
- # 0.5.3
179
-
180
- * Fixed parsing suites
181
-
182
- # 0.5.2
183
-
184
- * Fixed multiple upload of artifacts in Cypress.io
185
-
186
- # 0.5.1
187
-
188
- * Fixed Cypress.io to report tests inside nested suites
189
-
190
- # 0.5.0
191
-
192
- * Added Cypress.io plugin
193
- * Added artifacts upload to webdriverio
194
-
195
- # 0.4.6
196
-
197
- - Fixed CodeceptJS reporter to report tests failed in hooks
198
-
199
- # 0.4.5
200
-
201
- - Fixed "Total XX artifacts publicly uploaded to S3 bucket" when no S3 bucket is configured
202
- - Improved S3 connection error messages
203
-
204
- # 0.4.4
205
-
206
- - Fixed returning 0 exit code when a process fails when running tests in parallel via `start-test-run`. Previously was using the last exit code returned by a process. Currently prefers the highest exit code that was returned by a process.
207
-
208
- # 0.4.3
209
-
210
- - Added `TESTOMATIO_DISABLE_ARTIFACTS` env variable to disable publishing artifacts.
211
-
212
- # 0.4.2
213
-
214
- - print version of reporter
215
- - print number of uploaded artifacts
216
- - print access mode for uploaded artifacts
217
-
218
- # 0.4.1
219
-
220
- Added `global.testomatioArtifacts = []` array which can be used to add arbitrary artifacts to a report.
221
-
222
- ```js
223
- // inside a running test:
224
- global.testomatioArtifacts.push('file/to/upload.png');
225
- ```
226
-
227
- # 0.4.0
228
-
229
- - Playwright: Introduced playwright/test support with screenshots and video artifacts
230
-
231
- > Known issues: reporting using projects configured in Playwright does not work yet
232
-
233
- - CodeceptJS: added video uploads
234
-
235
- # 0.3.16
236
-
237
- - CodeceptJS: fixed reporting tests with empty steps (on retry)
238
-
239
- # 0.3.15
240
-
241
- - Finish Run via API:
242
-
243
- ```
244
- TESTOMATIO={apiKey} TESTOMATIO_RUN={runId} npx @testomatio/reporter@latest --finish
245
- ```
246
-
247
- # 0.3.14
248
-
249
- - Create an empty Run via API:
250
-
251
- ```
252
- TESTOMATIO={apiKey} npx @testomatio/reporter@latest --launch
253
- ```
254
-
255
- # 0.3.13
256
-
257
- - Checking for a valid report URL
258
- - Sending unlimited data on test report
259
-
260
- # 0.3.12
261
-
262
- - Fixed submitting arbitrary data on a test run
263
- - Jest: fixed sending errors with stack traces
264
- - Cypress: fixed sending reports
265
-
266
- # 0.3.11
267
-
268
- - Fixed circular JSON reference when submitting data to Testomatio
269
-
270
- # 0.3.10
271
-
272
- - Minor fixes
273
-
274
- # 0.3.9
275
-
276
- - Making all reporters to run without API key
277
-
278
- # 0.3.8
279
-
280
- - Fixed `npx start-test-run` to work with empty API keys
281
-
282
- # 0.3.7
283
-
284
- - Fixed release
285
-
286
- # 0.3.6
287
-
288
- - Update title and rungroup on start for scheduled runs.
289
-
290
- # 0.3.5
291
-
292
- - Added `TESTOMATIO_RUN` environment variable to pass id of a specific run to report
293
-
294
- # 0.3.4
295
-
296
- - Minor fixes
297
-
298
- # 0.3.3
299
-
300
- - [CodeceptJS] Fixed stack trace reporting
301
- - [CodeceptJS] Fixed displaying of nested steps
302
- - [CodeceptJS][mocha] Added assertion diff to report
303
-
304
- # 0.3.2
305
-
306
- - Fixed error message for S3 uploading
307
-
308
- # 0.3.1
309
-
310
- - [CodeceptJS] Better formatter for nested structures and BDD tests
311
-
312
- # 0.3.0
313
-
314
- - Added `TESTOMATIO_TITLE` env variable to set a name for Run
315
- - Added `TESTOMATIO_RUNGROUP_TITLE` env variable to attach Run to RunGroup
316
- - Added `TESTOMATIO_ENV` env variable to attach additional env values to report
317
- - [CodeceptJS] **CodeceptJS v3 support**
318
- - [CodeceptJS] Dropped support for CodeceptJS 2
319
- - [CodeceptJS] Added support for before hooks
320
- - [CodeceptJS] Log of steps
321
- - [CodeceptJS] Upload screenshots of failed tests to S3
322
- - [CodeceptJS] Updated to use with parallel execution