@testomatio/reporter 2.0.1-beta-2-ignore-xml → 2.0.1-beta.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.
Files changed (66) hide show
  1. package/lib/adapter/codecept.js +0 -2
  2. package/lib/adapter/cypress-plugin/index.js +0 -2
  3. package/lib/adapter/mocha.js +0 -1
  4. package/lib/adapter/nightwatch.d.ts +4 -0
  5. package/lib/adapter/nightwatch.js +80 -0
  6. package/lib/adapter/webdriver.d.ts +1 -1
  7. package/lib/adapter/webdriver.js +18 -8
  8. package/lib/bin/cli.js +73 -8
  9. package/lib/bin/reportXml.js +4 -2
  10. package/lib/bin/startTest.js +3 -2
  11. package/lib/bin/uploadArtifacts.js +5 -4
  12. package/lib/client.js +18 -9
  13. package/lib/config.js +2 -2
  14. package/lib/data-storage.d.ts +1 -1
  15. package/lib/data-storage.js +17 -7
  16. package/lib/junit-adapter/csharp.d.ts +1 -0
  17. package/lib/junit-adapter/csharp.js +11 -1
  18. package/lib/pipe/bitbucket.d.ts +2 -0
  19. package/lib/pipe/bitbucket.js +38 -26
  20. package/lib/pipe/debug.js +17 -3
  21. package/lib/pipe/github.d.ts +2 -2
  22. package/lib/pipe/github.js +35 -3
  23. package/lib/pipe/gitlab.d.ts +2 -0
  24. package/lib/pipe/gitlab.js +27 -9
  25. package/lib/pipe/html.d.ts +1 -0
  26. package/lib/pipe/html.js +1 -3
  27. package/lib/pipe/index.js +17 -7
  28. package/lib/pipe/testomatio.d.ts +3 -2
  29. package/lib/pipe/testomatio.js +85 -75
  30. package/lib/replay.d.ts +31 -0
  31. package/lib/replay.js +237 -0
  32. package/lib/reporter.d.ts +12 -12
  33. package/lib/services/artifacts.d.ts +1 -1
  34. package/lib/services/key-values.d.ts +1 -1
  35. package/lib/services/logger.d.ts +1 -1
  36. package/lib/services/logger.js +1 -2
  37. package/lib/template/testomatio.hbs +443 -68
  38. package/lib/uploader.js +10 -6
  39. package/lib/utils/utils.d.ts +3 -1
  40. package/lib/utils/utils.js +54 -21
  41. package/lib/xmlReader.js +54 -19
  42. package/package.json +8 -9
  43. package/src/adapter/codecept.js +0 -2
  44. package/src/adapter/cypress-plugin/index.js +0 -2
  45. package/src/adapter/mocha.js +0 -1
  46. package/src/adapter/nightwatch.js +88 -0
  47. package/src/adapter/webdriver.js +2 -2
  48. package/src/bin/cli.js +70 -2
  49. package/src/bin/reportXml.js +4 -1
  50. package/src/bin/startTest.js +2 -1
  51. package/src/bin/uploadArtifacts.js +2 -1
  52. package/src/client.js +1 -2
  53. package/src/config.js +2 -2
  54. package/src/junit-adapter/csharp.js +13 -1
  55. package/src/pipe/bitbucket.js +22 -24
  56. package/src/pipe/debug.js +18 -3
  57. package/src/pipe/github.js +1 -2
  58. package/src/pipe/gitlab.js +27 -9
  59. package/src/pipe/html.js +3 -4
  60. package/src/pipe/testomatio.js +106 -105
  61. package/src/replay.js +245 -0
  62. package/src/services/logger.js +1 -2
  63. package/src/template/testomatio.hbs +443 -68
  64. package/src/uploader.js +11 -6
  65. package/src/utils/utils.js +31 -12
  66. package/src/xmlReader.js +69 -17
@@ -5,12 +5,13 @@ import pc from 'picocolors';
5
5
  import createDebugMessages from 'debug';
6
6
  import TestomatClient from '../client.js';
7
7
  import { APP_PREFIX } from '../constants.js';
8
- import { version } from '../../package.json';
8
+ import { getPackageVersion } from '../utils/utils.js';
9
9
  import { config } from '../config.js';
10
10
  import { readLatestRunId } from '../utils/utils.js';
11
11
  import dotenv from 'dotenv';
12
12
 
13
13
  const debug = createDebugMessages('@testomatio/reporter:upload-cli');
14
+ const version = getPackageVersion();
14
15
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
15
16
  const program = new Command();
16
17
 
package/src/client.js CHANGED
@@ -31,7 +31,6 @@ class Client {
31
31
  * Create a Testomat client instance
32
32
  * @returns
33
33
  */
34
- // eslint-disable-next-line
35
34
  constructor(params = {}) {
36
35
  this.paramsForPipesFactory = params;
37
36
  this.pipeStore = {};
@@ -79,7 +78,7 @@ class Client {
79
78
  try {
80
79
  const filterPipe = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
81
80
 
82
- if (!filterPipe.isEnabled) {
81
+ if (!filterPipe?.isEnabled) {
83
82
  // TODO:for the future for the another pipes
84
83
  console.warn(
85
84
  APP_PREFIX,
package/src/config.js CHANGED
@@ -6,10 +6,10 @@ const debug = createDebugMessages('@testomatio/reporter:config');
6
6
  /* for possibility to use multiple env files (reading different paths)
7
7
  const envFileVars = dotenv.config({ path: '.env' }).parsed; */
8
8
 
9
- if (process.env.TESTOMATIO_API_KEY) {
9
+ if (process.env.TESTOMATIO_API_KEY && !process.env.TESTOMATIO) {
10
10
  process.env.TESTOMATIO = process.env.TESTOMATIO_API_KEY;
11
11
  }
12
- if (process.env.TESTOMATIO_TOKEN) {
12
+ if (process.env.TESTOMATIO_TOKEN && !process.env.TESTOMATIO) {
13
13
  process.env.TESTOMATIO = process.env.TESTOMATIO_TOKEN;
14
14
  }
15
15
 
@@ -1,3 +1,4 @@
1
+ import path from 'path';
1
2
  import Adapter from './adapter.js';
2
3
 
3
4
  class CSharpAdapter extends Adapter {
@@ -7,10 +8,21 @@ class CSharpAdapter extends Adapter {
7
8
  if (example) t.example = { ...example[1].split(',') };
8
9
  const suite = t.suite_title.split('.');
9
10
  t.suite_title = suite.pop();
10
- t.file = suite.join('/');
11
+ t.file = namespaceToFileName(t.file);
11
12
  t.title = title.trim();
12
13
  return t;
13
14
  }
15
+
16
+ getFilePath(t) {
17
+ const fileName = namespaceToFileName(t.file);
18
+ return fileName;
19
+ }
14
20
  }
15
21
 
16
22
  export default CSharpAdapter;
23
+
24
+ function namespaceToFileName(fileName) {
25
+ const fileParts = fileName.split('.');
26
+ fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
27
+ return `${fileParts.join(path.sep)}.cs`;
28
+ }
@@ -1,7 +1,7 @@
1
1
  import { APP_PREFIX, testomatLogoURL } from '../constants.js';
2
2
  import { ansiRegExp, isSameTest } from '../utils/utils.js';
3
3
  import { statusEmoji, fullName } from '../utils/pipe_utils.js';
4
- import axios from 'axios';
4
+ import { Gaxios } from 'gaxios';
5
5
  import pc from 'picocolors';
6
6
  import humanizeDuration from 'humanize-duration';
7
7
  import merge from 'lodash.merge';
@@ -40,6 +40,13 @@ export class BitbucketPipe {
40
40
  }
41
41
 
42
42
  this.isEnabled = true;
43
+ this.client = new Gaxios({
44
+ baseURL: 'https://api.bitbucket.org/2.0',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'Authorization': `Bearer ${this.token}`
48
+ }
49
+ });
43
50
 
44
51
  debug('Bitbucket Pipe: Enabled');
45
52
  }
@@ -166,26 +173,21 @@ export class BitbucketPipe {
166
173
 
167
174
  // Construct Bitbucket API URL for comments
168
175
  // eslint-disable-next-line max-len
169
- const commentsRequestURL = `https://api.bitbucket.org/2.0/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
176
+ const commentsRequestURL = `/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
170
177
 
171
178
  // Delete previous report
172
- await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
179
+ await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData);
173
180
 
174
181
  // Add current report
175
182
  debug(`Adding comment via URL: ${commentsRequestURL}`);
176
183
  debug(`Final Bitbucket API call body: ${body}`);
177
184
 
178
185
  try {
179
- const addCommentResponse = await axios.post(
180
- commentsRequestURL,
181
- { content: { raw: body } },
182
- {
183
- headers: {
184
- Authorization: `Bearer ${this.token}`,
185
- 'Content-Type': 'application/json',
186
- },
187
- },
188
- );
186
+ const addCommentResponse = await this.client.request({
187
+ method: 'POST',
188
+ url: commentsRequestURL,
189
+ data: { content: { raw: body } }
190
+ });
189
191
 
190
192
  const commentID = addCommentResponse.data.id;
191
193
  // eslint-disable-next-line max-len
@@ -210,18 +212,16 @@ export class BitbucketPipe {
210
212
  updateRun() {}
211
213
  }
212
214
 
213
- async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
215
+ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData) {
214
216
  if (process.env.BITBUCKET_KEEP_OUTDATED_REPORTS) return;
215
217
 
216
218
  // Get comments
217
219
  let comments = [];
218
220
 
219
221
  try {
220
- const response = await axiosInstance.get(commentsRequestURL, {
221
- headers: {
222
- Authorization: `Bearer ${token}`,
223
- 'Content-Type': 'application/json',
224
- },
222
+ const response = await client.request({
223
+ method: 'GET',
224
+ url: commentsRequestURL
225
225
  });
226
226
  comments = response.data.values;
227
227
  } catch (e) {
@@ -236,11 +236,9 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
236
236
  try {
237
237
  // Delete previous comment
238
238
  const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
239
- await axiosInstance.delete(deleteCommentURL, {
240
- headers: {
241
- Authorization: `Bearer ${token}`,
242
- 'Content-Type': 'application/json',
243
- },
239
+ await client.request({
240
+ method: 'DELETE',
241
+ url: deleteCommentURL
244
242
  });
245
243
  } catch (e) {
246
244
  console.warn(`Can't delete previously added comment with testomat.io report. Ignored.`);
package/src/pipe/debug.js CHANGED
@@ -25,7 +25,22 @@ export class DebugPipe {
25
25
 
26
26
  debug('Creating debug file:', this.logFilePath);
27
27
  fs.writeFileSync(this.logFilePath, '');
28
- console.log(APP_PREFIX, '🪲. Debug created:');
28
+
29
+ // Create symlink to ensure consistent path to latest debug file
30
+ const symlinkPath = path.join(os.tmpdir(), 'testomatio.debug.latest.json');
31
+ try {
32
+ // Remove existing symlink if it exists
33
+ if (fs.existsSync(symlinkPath)) {
34
+ fs.unlinkSync(symlinkPath);
35
+ }
36
+ // Create new symlink pointing to the timestamped debug file
37
+ fs.symlinkSync(this.logFilePath, symlinkPath);
38
+ debug('Created symlink:', symlinkPath, '->', this.logFilePath);
39
+ } catch (err) {
40
+ debug('Failed to create symlink:', err.message);
41
+ }
42
+
43
+ console.log(APP_PREFIX, '🪲 Debug file created');
29
44
  this.testomatioEnvVars = Object.keys(process.env)
30
45
  .filter(key => key.startsWith('TESTOMATIO_'))
31
46
  .reduce((acc, key) => {
@@ -92,10 +107,10 @@ export class DebugPipe {
92
107
 
93
108
  async finishRun(params) {
94
109
  if (!this.isEnabled) return;
95
- this.logToFile({ actions: 'finishRun', params });
96
110
  await this.batchUpload();
97
111
  if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
98
- console.log(APP_PREFIX, '🪲. Debug Saved to', this.logFilePath);
112
+ this.logToFile({ action: 'finishRun', params });
113
+ console.log(APP_PREFIX, '🪲 Debug Saved to', this.logFilePath);
99
114
  }
100
115
 
101
116
  toString() {
@@ -3,7 +3,6 @@ import path from 'path';
3
3
  import pc from 'picocolors';
4
4
  import humanizeDuration from 'humanize-duration';
5
5
  import merge from 'lodash.merge';
6
- import { Octokit } from '@octokit/rest';
7
6
  import { APP_PREFIX, testomatLogoURL } from '../constants.js';
8
7
  import { ansiRegExp, isSameTest } from '../utils/utils.js';
9
8
  import { statusEmoji, fullName } from '../utils/pipe_utils.js';
@@ -64,6 +63,7 @@ class GitHubPipe {
64
63
  if (!this.issue) return;
65
64
 
66
65
  if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
66
+ const { Octokit } = await import('@octokit/rest');
67
67
 
68
68
  this.octokit = new Octokit({
69
69
  auth: this.token,
@@ -154,7 +154,6 @@ class GitHubPipe {
154
154
  if (this.tests.length > 0) {
155
155
  body += '\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n';
156
156
  body += this.tests
157
- // eslint-disable-next-line no-unsafe-optional-chaining
158
157
  .sort((a, b) => b?.run_time - a?.run_time)
159
158
  .slice(0, 5)
160
159
  .map(t => `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`)
@@ -1,5 +1,5 @@
1
1
  import createDebugMessages from 'debug';
2
- import axios from 'axios';
2
+ import { Gaxios } from 'gaxios';
3
3
  import pc from 'picocolors';
4
4
  import humanizeDuration from 'humanize-duration';
5
5
  import merge from 'lodash.merge';
@@ -45,6 +45,12 @@ class GitLabPipe {
45
45
  }
46
46
 
47
47
  this.isEnabled = true;
48
+ this.client = new Gaxios({
49
+ baseURL: 'https://gitlab.com/api/v4',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ }
53
+ });
48
54
 
49
55
  debug('GitLab Pipe: Enabled');
50
56
  }
@@ -149,7 +155,6 @@ class GitLabPipe {
149
155
  if (this.tests.length > 0) {
150
156
  body += '\n<details>\n<summary><h3>🐢 Slowest Tests</h3></summary>\n\n';
151
157
  body += this.tests
152
- // eslint-disable-next-line no-unsafe-optional-chaining
153
158
  .sort((a, b) => b?.run_time - a?.run_time)
154
159
  .slice(0, 5)
155
160
  .map(t => `* ${fullName(t)} (${humanizeDuration(parseFloat(t.run_time))})`)
@@ -158,16 +163,21 @@ class GitLabPipe {
158
163
  }
159
164
 
160
165
  // eslint-disable-next-line max-len
161
- const commentsRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
166
+ const commentsRequestURL = `/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
162
167
 
163
168
  // delete previous report
164
- await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
169
+ await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData, this.token);
165
170
 
166
171
  // add current report
167
172
  debug(`Adding comment via url: ${commentsRequestURL}`);
168
173
 
169
174
  try {
170
- const addCommentResponse = await axios.post(`${commentsRequestURL}?access_token=${this.token}`, { body });
175
+ const addCommentResponse = await this.client.request({
176
+ method: 'POST',
177
+ url: commentsRequestURL,
178
+ params: { access_token: this.token },
179
+ data: { body }
180
+ });
171
181
 
172
182
  const commentID = addCommentResponse.data.id;
173
183
  // eslint-disable-next-line max-len
@@ -192,14 +202,18 @@ class GitLabPipe {
192
202
  updateRun() {}
193
203
  }
194
204
 
195
- async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
205
+ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData, token) {
196
206
  if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
197
207
 
198
208
  // get comments
199
209
  let comments = [];
200
210
 
201
211
  try {
202
- const response = await axiosInstance.get(`${commentsRequestURL}?access_token=${token}`);
212
+ const response = await client.request({
213
+ method: 'GET',
214
+ url: commentsRequestURL,
215
+ params: { access_token: token }
216
+ });
203
217
  comments = response.data;
204
218
  } catch (e) {
205
219
  console.error('Error while attempt to retrieve comments on GitLab Merge Request:\n', e);
@@ -212,8 +226,12 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
212
226
  if (comment.body.includes(hiddenCommentData)) {
213
227
  try {
214
228
  // delete previous comment
215
- const deleteCommentURL = `${commentsRequestURL}/${comment.id}?access_token=${token}`;
216
- await axiosInstance.delete(deleteCommentURL);
229
+ const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
230
+ await client.request({
231
+ method: 'DELETE',
232
+ url: deleteCommentURL,
233
+ params: { access_token: token }
234
+ });
217
235
  } catch (e) {
218
236
  console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
219
237
  }
package/src/pipe/html.js CHANGED
@@ -66,6 +66,8 @@ class HtmlPipe {
66
66
  // empty
67
67
  }
68
68
 
69
+ async prepareRun() {}
70
+
69
71
  updateRun() {
70
72
  // empty
71
73
  }
@@ -235,7 +237,6 @@ class HtmlPipe {
235
237
  </select>`,
236
238
  ),
237
239
  );
238
- /* eslint-disable */
239
240
  handlebars.registerHelper('emptyDataComponent', () => {
240
241
  const svgFilePath = path.join(__dirname, '..', 'template', 'emptyData.svg');
241
242
  const svgContent = fs.readFileSync(svgFilePath, 'utf8');
@@ -252,13 +253,12 @@ class HtmlPipe {
252
253
  <div>`,
253
254
  );
254
255
  });
255
- /* eslint-enable */
256
256
  handlebars.registerHelper('pageDispleyElements', tests => {
257
257
  // We wrapp the lines to the HTML format we need
258
258
  const totalTests = JSON.parse(
259
259
  JSON.stringify(tests)
260
260
  .replace(/<script>/g, '&lt;script&gt;')
261
- .replace(/<\/script>/g, '&lt;/script&gt;'), // eslint-disable-line
261
+ .replace(/<\/script>/g, '&lt;/script&gt;'),
262
262
  );
263
263
 
264
264
  const paginationOptions = {
@@ -285,7 +285,6 @@ class HtmlPipe {
285
285
 
286
286
  statuses.forEach(status => {
287
287
  for (const option in paginationOptions) {
288
- // eslint-disable-next-line no-prototype-builtins
289
288
  if (paginationOptions.hasOwnProperty(option)) {
290
289
  const pageSize = paginationOptions[option];
291
290
  let filteredItems = totalTests;