@testomatio/reporter 1.4.1-beta-1 → 1.4.1

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 (45) hide show
  1. package/README.md +60 -57
  2. package/lib/adapter/codecept.js +86 -27
  3. package/lib/adapter/cucumber/current.js +17 -10
  4. package/lib/adapter/cucumber/legacy.js +5 -4
  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 +48 -10
  9. package/lib/adapter/mocha.js +90 -10
  10. package/lib/adapter/playwright.js +68 -18
  11. package/lib/adapter/webdriver.js +47 -2
  12. package/lib/bin/reportXml.js +21 -16
  13. package/lib/bin/startTest.js +12 -12
  14. package/lib/client.js +112 -52
  15. package/lib/config.js +34 -0
  16. package/lib/constants.js +28 -1
  17. package/lib/data-storage.js +203 -0
  18. package/lib/fileUploader.js +66 -53
  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 +11 -11
  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 +35 -32
  27. package/lib/pipe/github.js +4 -3
  28. package/lib/pipe/gitlab.js +17 -10
  29. package/lib/pipe/html.js +361 -0
  30. package/lib/pipe/index.js +3 -1
  31. package/lib/pipe/testomatio.js +230 -51
  32. package/lib/reporter-functions.js +12 -9
  33. package/lib/reporter.js +8 -12
  34. package/lib/services/artifacts.js +57 -0
  35. package/lib/services/index.js +13 -0
  36. package/lib/{storages/key-value-storage.js → services/key-values.js} +19 -19
  37. package/lib/{storages → services}/logger.js +58 -37
  38. package/lib/template/emptyData.svg +23 -0
  39. package/lib/template/testomatio.hbs +1421 -0
  40. package/lib/utils/pipe_utils.js +73 -79
  41. package/lib/utils/utils.js +53 -40
  42. package/lib/xmlReader.js +113 -105
  43. package/package.json +13 -7
  44. package/lib/storages/artifact-storage.js +0 -70
  45. package/lib/storages/data-storage.js +0 -307
@@ -6,17 +6,18 @@ const { APP_PREFIX } = require('../constants');
6
6
  * @param {Object} artifacts - The artifacts object containing S3 credentials.
7
7
  */
8
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();
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.SESSION_TOKEN) process.env.S3_SESSION_TOKEN = artifacts.SESSION_TOKEN;
19
+ if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
20
+ resetConfig();
20
21
  }
21
22
  /**
22
23
  * Generates mode request parameters based on the input params.
@@ -27,25 +28,25 @@ function setS3Credentials(artifacts) {
27
28
  * @returns {Object|null} - An object containing the generated request parameters, or null if the type is invalid.
28
29
  */
29
30
  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
- }
31
+ const { type, id, apiKey } = params;
32
+
33
+ if (!type) {
34
+ return;
35
+ }
36
+
37
+ if (!id) {
38
+ console.error(APP_PREFIX, `Please make sure your settings "${type.toUpperCase()}"= "${id}" is correct!`);
39
+ return;
40
+ }
41
+
42
+ return {
43
+ params: {
44
+ type,
45
+ id: encodeURIComponent(id),
46
+ api_key: apiKey,
47
+ },
48
+ responseType: 'json',
49
+ };
49
50
  }
50
51
 
51
52
  /**
@@ -55,13 +56,13 @@ function generateFilterRequestParams(params) {
55
56
  * The object has properties "type" and "id".
56
57
  */
57
58
  function parseFilterParams(opts) {
58
- const [type, id] = opts.split("=");
59
- const validType = updateFilterType(type);
59
+ const [type, id] = opts.split('=');
60
+ const validType = updateFilterType(type);
60
61
 
61
- return {
62
- type: validType,
63
- id
64
- };
62
+ return {
63
+ type: validType,
64
+ id,
65
+ };
65
66
  }
66
67
 
67
68
  /**
@@ -71,33 +72,26 @@ function parseFilterParams(opts) {
71
72
  * Returns undefined if the type is not valid.
72
73
  */
73
74
  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
75
+ const typeLowerCase = type.toLowerCase();
76
+
77
+ const filterTypes = ['tag-name', 'plan-id', 'label', 'jira-ticket'];
78
+
79
+ const filterApi = [
80
+ 'tag',
81
+ 'plan',
82
+ 'label',
83
+ 'jira',
84
+ // "ims-issue", //TODO: WIP
85
+ ];
86
+
87
+ if (!filterTypes.includes(typeLowerCase)) {
88
+ console.log(APP_PREFIX, `❗❗❗ Invalid "filter=${type}" start settings! Available option list: ${filterTypes}`);
89
+ return;
90
+ }
91
+
92
+ const index = filterTypes.indexOf(typeLowerCase);
93
+
94
+ return index !== -1 ? filterApi[index] : undefined;
101
95
  }
102
96
 
103
97
  /**
@@ -106,10 +100,10 @@ function updateFilterType(type) {
106
100
  * @returns {string} - An emoji corresponding to the provided status.
107
101
  */
108
102
  function statusEmoji(status) {
109
- if (status === 'passed') return '🟢';
110
- if (status === 'failed') return '🔴';
111
- if (status === 'skipped') return '🟡';
112
- return '';
103
+ if (status === 'passed') return '🟢';
104
+ if (status === 'failed') return '🔴';
105
+ if (status === 'skipped') return '🟡';
106
+ return '';
113
107
  }
114
108
 
115
109
  /**
@@ -118,18 +112,18 @@ function statusEmoji(status) {
118
112
  * @returns {string} - A formatted full name string for the test object.
119
113
  */
120
114
  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;
115
+ let line = '';
116
+ if (t.suite_title) line = `${t.suite_title}: `;
117
+ line += `**${t.title}**`;
118
+ if (t.example) line += ` \`[${Object.values(t.example)}]\``;
119
+ return line;
126
120
  }
127
121
 
128
122
  module.exports = {
129
- updateFilterType,
130
- parseFilterParams,
131
- generateFilterRequestParams,
132
- setS3Credentials,
133
- statusEmoji,
134
- fullName
135
- };
123
+ updateFilterType,
124
+ parseFilterParams,
125
+ generateFilterRequestParams,
126
+ setS3Credentials,
127
+ statusEmoji,
128
+ fullName,
129
+ };
@@ -10,13 +10,13 @@ const debug = require('debug')('@testomatio/reporter:util');
10
10
  *
11
11
  * @returns {String|null} testId
12
12
  */
13
- const parseTest = testTitle => {
13
+ const getTestomatIdFromTestTitle = testTitle => {
14
14
  if (!testTitle) return null;
15
15
 
16
- const captures = testTitle.match(/@T([\w\d]+)/);
16
+ const captures = testTitle.match(/@T[\w\d]{8}/);
17
17
 
18
18
  if (captures) {
19
- return captures[1];
19
+ return captures[0];
20
20
  }
21
21
 
22
22
  return null;
@@ -28,7 +28,7 @@ const parseTest = testTitle => {
28
28
  * @returns {String|null} suiteId
29
29
  */
30
30
  const parseSuite = suiteTitle => {
31
- const captures = suiteTitle.match(/@S([\w\d]+)/);
31
+ const captures = suiteTitle.match(/@S[\w\d]{8}/);
32
32
  if (captures) {
33
33
  return captures[1];
34
34
  }
@@ -55,21 +55,20 @@ const isValidUrl = s => {
55
55
  }
56
56
  };
57
57
 
58
- const fileMatchRegex = /file:(\/\/?[^:\s]+?\.(png|avi|webm|jpg|html|txt))/ig;
58
+ const fileMatchRegex = /file:(\/\/?[^:\s]+?\.(png|avi|webm|jpg|html|txt))/gi;
59
59
 
60
60
  const fetchFilesFromStackTrace = (stack = '') => {
61
-
62
61
  const files = Array.from(stack.matchAll(fileMatchRegex))
63
- .map(f => f[1].trim())
64
- .map(f => f.startsWith('//') ? f.substring(1) : f )
65
-
62
+ .map(f => f[1].trim())
63
+ .map(f => (f.startsWith('//') ? f.substring(1) : f));
64
+
66
65
  debug('Found files in stack trace: ', files);
67
66
 
68
67
  return files.filter(f => {
69
- const isFile = fs.existsSync(f);
70
- if (!isFile) debug('File %s could not be found and uploaded as artifact', f);
71
- return isFile;
72
- });
68
+ const isFile = fs.existsSync(f);
69
+ if (!isFile) debug('File %s could not be found and uploaded as artifact', f);
70
+ return isFile;
71
+ });
73
72
  };
74
73
 
75
74
  const fetchSourceCodeFromStackTrace = (stack = '') => {
@@ -109,25 +108,30 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
109
108
  const TEST_ID_REGEX = /@T([\w\d]{8})/;
110
109
 
111
110
  const fetchIdFromCode = (code, opts = {}) => {
112
- const comments = code.split('\n').map(l => l.trim()).filter(l => {
113
- switch (opts.lang) {
114
- case 'ruby':
115
- case 'python':
116
- return l.startsWith('# ');
117
- default:
118
- return l.startsWith('// ');
119
- }
120
- });
111
+ const comments = code
112
+ .split('\n')
113
+ .map(l => l.trim())
114
+ .filter(l => {
115
+ switch (opts.lang) {
116
+ case 'ruby':
117
+ case 'python':
118
+ return l.startsWith('# ');
119
+ default:
120
+ return l.startsWith('// ');
121
+ }
122
+ });
121
123
 
122
124
  return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
123
- }
124
-
125
+ };
125
126
 
126
- const fetchIdFromOutput = (output) => {
127
- const lines = output.split('\n').map(l => l.trim()).filter(l => l.startsWith('tid://'));
127
+ const fetchIdFromOutput = output => {
128
+ const lines = output
129
+ .split('\n')
130
+ .map(l => l.trim())
131
+ .filter(l => l.startsWith('tid://'));
128
132
 
129
133
  return lines.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
130
- }
134
+ };
131
135
 
132
136
  const fetchSourceCode = (contents, opts = {}) => {
133
137
  if (!opts.title && !opts.line) return '';
@@ -141,7 +145,15 @@ const fetchSourceCode = (contents, opts = {}) => {
141
145
  // remove special chars from title
142
146
  if (!lineIndex && opts.title) {
143
147
  const title = opts.title.replace(/[([@].*/g, '');
144
- lineIndex = lines.findIndex(l => l.includes(title));
148
+
149
+ if (opts.lang === 'java') {
150
+ lineIndex = lines.findIndex(l => l.includes(`test${title}`));
151
+ if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
152
+ if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
153
+ if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
154
+ } else {
155
+ lineIndex = lines.findIndex(l => l.includes(title));
156
+ }
145
157
  }
146
158
 
147
159
  if (opts.prepend) {
@@ -227,14 +239,11 @@ const fileSystem = {
227
239
  },
228
240
  };
229
241
 
230
-
231
242
  const foundedTestLog = (app, tests) => {
232
243
  const n = tests.length;
233
244
 
234
- return (n === 1)
235
- ? console.log(app, `✅ We found one test!`)
236
- : console.log(app, `✅ We found ${n} tests!`);
237
- }
245
+ return n === 1 ? console.log(app, `✅ We found one test!`) : console.log(app, `✅ We found ${n} tests!`);
246
+ };
238
247
 
239
248
  const humanize = text => {
240
249
  text = decamelize(text);
@@ -290,13 +299,17 @@ function removeColorCodes(input) {
290
299
  return input.replace(/\x1b\[[0-9;]*m/g, '');
291
300
  }
292
301
 
293
- const jestHelpers = {
294
- getIdOfCurrentlyRunningTest: () => {
302
+ const testRunnerHelper = {
303
+ // for Jest
304
+ getNameOfCurrentlyRunningTest: () => {
305
+ if (global.testomatioTestTitle) return global.testomatioTestTitle;
306
+
295
307
  if (!process.env.JEST_WORKER_ID) return null;
296
308
  try {
309
+ // TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
297
310
  // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
298
311
  // eslint-disable-next-line no-undef
299
- if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
312
+ return expect?.getState()?.currentTestName;
300
313
  } catch (e) {
301
314
  return null;
302
315
  }
@@ -312,13 +325,13 @@ module.exports = {
312
325
  fetchFilesFromStackTrace,
313
326
  fileSystem,
314
327
  getCurrentDateTime,
315
- jestHelpers,
316
328
  specificTestInfo,
317
329
  isValidUrl,
318
330
  ansiRegExp,
319
- parseTest,
331
+ getTestomatIdFromTestTitle,
320
332
  parseSuite,
321
333
  humanize,
322
334
  removeColorCodes,
323
- foundedTestLog
324
- };
335
+ foundedTestLog,
336
+ testRunnerHelper,
337
+ };