@testomatio/reporter 0.8.0-beta.3 → 0.8.0-beta.30

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.
package/lib/pipe/index.js CHANGED
@@ -1,45 +1,61 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const chalk = require('chalk');
3
4
  const { APP_PREFIX } = require('../constants');
4
5
  const TestomatioPipe = require('./testomatio');
5
6
  const GitHubPipe = require('./github');
7
+ const GitLabPipe = require('./gitlab');
8
+ const CsvPipe = require('./csv');
6
9
 
7
- module.exports = function(params, opts) {
10
+ module.exports = function (params, opts) {
8
11
  const extraPipes = [];
9
12
 
10
13
  // Add extra pipes into package.json file:
11
14
  // "testomatio": {
12
- // "pipes": ["my-module-pipe", "./local/js/file/pipe"]
15
+ // "pipes": ["my-module-pipe", "./local/js/file/pipe"]
13
16
  // }
14
17
 
15
18
  const packageJsonFile = path.join(process.cwd(), 'package.json');
16
19
  if (fs.existsSync(packageJsonFile)) {
17
- const package = fs.readFileSync(packageJsonFile);
18
- const pipeDefs = package?.testomatio?.pipes || [];
19
-
20
+ const packageJson = fs.readFileSync(packageJsonFile);
21
+ const pipeDefs = packageJson?.testomatio?.pipes || [];
22
+
20
23
  for (const pipeDef of pipeDefs) {
21
- let pipeClass;
24
+ let PipeClass;
22
25
  try {
23
- pipeClass = require(pipeDef);
26
+ PipeClass = require(pipeDef);
24
27
  } catch (err) {
25
28
  console.log(APP_PREFIX, `Can't load module Testomatio pipe module from ${pipeDef}`);
26
29
  continue;
27
30
  }
28
31
 
29
32
  try {
30
- extraPipes.push(new pipeClass(params, opts))
33
+ extraPipes.push(new PipeClass(params, opts));
31
34
  } catch (err) {
32
35
  console.log(APP_PREFIX, `Can't instantiate Testomatio for ${pipeDef}`, err);
33
36
  continue;
34
37
  }
35
38
  }
36
39
  }
37
-
38
40
 
39
- const defaultPipes = [
41
+ const pipes = [
40
42
  new TestomatioPipe(params, opts),
41
- new GitHubPipe(params, opts),
43
+ new GitHubPipe(params, opts),
44
+ new GitLabPipe(params, opts),
45
+ new CsvPipe(params, opts),
46
+ ...extraPipes,
42
47
  ];
43
48
 
44
- return [...defaultPipes, ...extraPipes];
45
- }
49
+ console.log(
50
+ APP_PREFIX,
51
+ chalk.cyan('Pipes:'),
52
+ chalk.cyan(
53
+ pipes
54
+ .filter(p => p.isEnabled)
55
+ .map(p => p.toString())
56
+ .join(', ') || 'No pipes enabled',
57
+ ),
58
+ );
59
+
60
+ return pipes;
61
+ };
@@ -1,6 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
  const axios = require('axios');
3
3
  const JsonCycle = require('json-cycle');
4
+
4
5
  const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_RUN } = process.env;
5
6
  const { APP_PREFIX } = require('../constants');
6
7
  const { isValidUrl } = require('../util');
@@ -10,14 +11,20 @@ if (TESTOMATIO_RUN) {
10
11
  }
11
12
 
12
13
  class TestomatioPipe {
13
- isEnabled = false;
14
-
14
+
15
15
  constructor(params, store = {}) {
16
+ this.isEnabled = false;
16
17
  this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
17
18
  this.apiKey = params.apiKey || process.env.TESTOMATIO;
19
+ if (process.env.DEBUG) {
20
+ console.log(APP_PREFIX, 'Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
21
+ }
18
22
  if (!this.apiKey) {
19
23
  return;
20
24
  }
25
+ if (process.env.DEBUG) {
26
+ console.log(APP_PREFIX, 'Testomatio Pipe: Enabled');
27
+ }
21
28
  this.store = store;
22
29
  this.title = params.title || process.env.TESTOMATIO_TITLE;
23
30
  this.axios = axios.create();
@@ -26,14 +33,13 @@ class TestomatioPipe {
26
33
  this.runId = params.runId || process.env.runId;
27
34
  this.createNewTests = !!process.env.TESTOMATIO_CREATE;
28
35
 
29
-
30
36
  if (!isValidUrl(this.url.trim())) {
31
37
  this.isEnabled = false;
32
38
  console.log(
33
39
  APP_PREFIX,
34
40
  chalk.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`),
35
41
  );
36
- return;
42
+
37
43
  }
38
44
  }
39
45
 
@@ -56,7 +62,7 @@ class TestomatioPipe {
56
62
  this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
57
63
  this.store.runUrl = this.runUrl;
58
64
  this.store.runId = this.runId;
59
- console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId, chalk.gray(`v${this.version}`));
65
+ console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
60
66
  process.env.runId = this.runId;
61
67
  } catch (err) {
62
68
  console.log(
@@ -85,11 +91,11 @@ class TestomatioPipe {
85
91
  } catch (err) {
86
92
  if (err.response) {
87
93
  if (err.response.status >= 400) {
88
- const data = err.response.data || { message: '' };
94
+ const responseData = err.response.data || { message: '' };
89
95
  console.log(
90
96
  APP_PREFIX,
91
97
  chalk.blue(this.title),
92
- `Report couldn't be processed: (${err.response.status}) ${data.message}`,
98
+ `Report couldn't be processed: (${err.response.status}) ${responseData.message}`,
93
99
  );
94
100
  return;
95
101
  }
@@ -109,6 +115,7 @@ class TestomatioPipe {
109
115
  api_key: this.apiKey,
110
116
  status_event: params.statusEvent,
111
117
  status: params.status,
118
+ tests: params.tests,
112
119
  });
113
120
  if (this.runUrl) {
114
121
  console.log(APP_PREFIX, '📊 Report Saved. Report URL:', chalk.magenta(this.runUrl));
@@ -122,7 +129,11 @@ class TestomatioPipe {
122
129
  } catch (err) {
123
130
  console.log(APP_PREFIX, 'Error updating status, skipping...', err);
124
131
  }
125
- }
132
+ }
133
+
134
+ toString() {
135
+ return 'Testomatio Reporter';
136
+ }
126
137
  }
127
138
 
128
139
  module.exports = TestomatioPipe;
package/lib/util.js CHANGED
@@ -79,6 +79,8 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
79
79
  const prepend = 3;
80
80
  const source = fetchSourceCode(fs.readFileSync(file).toString(), { line, prepend, limit: 7 })
81
81
 
82
+ if (!source) return '';
83
+
82
84
  return source.split('\n')
83
85
  .map((l, i) => {
84
86
  if (i === prepend) return `${line} > ${chalk.bold(l)}`;
@@ -137,8 +139,18 @@ const fetchSourceCode = (contents, opts = {}) => {
137
139
  }
138
140
  }
139
141
 
140
- const isSameTest = (test, t) => {
141
- return t.title == test.title && t.suite_title == test.suite_title && Object.values(t.example) == Object.values(test.example) && t.test_id == test.test_id;
142
+ const isSameTest = (test, t) => (typeof t === 'object')
143
+ && (typeof test === 'object')
144
+ && t.title === test.title
145
+ && t.suite_title === test.suite_title
146
+ && Object.values(t.example || {}) === Object.values(test.example || {})
147
+ && t.test_id === test.test_id;
148
+
149
+ const getCurrentDateTime = () => {
150
+ const today = new Date();
151
+
152
+ return `${today.getFullYear() }_${ today.getMonth() + 1 }_${ today.getDate() }_${
153
+ today.getHours() }_${ today.getMinutes() }_${ today.getSeconds()}`;
142
154
  }
143
155
 
144
156
  module.exports = {
@@ -150,4 +162,5 @@ module.exports = {
150
162
  fetchSourceCode,
151
163
  fetchSourceCodeFromStackTrace,
152
164
  fetchFilesFromStackTrace,
165
+ getCurrentDateTime
153
166
  };
package/lib/xmlReader.js CHANGED
@@ -1,10 +1,11 @@
1
+ const debug = require('debug')('@testomatio/reporter:xml');
1
2
  const path = require("path");
2
3
  const chalk = require('chalk');
3
4
  const fs = require("fs");
4
5
 
5
6
  // const util = require("util"); // you can see a result
6
7
  const { XMLParser } = require("fast-xml-parser");
7
- const { PASSED, FAILED, SKIPPED } = require('./constants');
8
+ const { APP_PREFIX, PASSED, FAILED, SKIPPED } = require('./constants');
8
9
  const { fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace } = require('./util');
9
10
  const upload = require('./fileUploader');
10
11
  const pipesFactory = require('./pipe');
@@ -45,6 +46,9 @@ class XmlReader {
45
46
  this.stats = {}
46
47
  this.stats.language = opts.lang?.toLowerCase();
47
48
  this.filesToUpload = {}
49
+
50
+ this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
51
+ console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
48
52
  }
49
53
 
50
54
  connectAdapter() {
@@ -116,7 +120,10 @@ class XmlReader {
116
120
  }
117
121
 
118
122
  processTRX(jsonSuite) {
119
- const tests = jsonSuite?.TestRun?.TestDefinitions?.UnitTest?.map(td => {
123
+ let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
124
+ if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
125
+
126
+ const tests = defs.map(td => {
120
127
  const title = td.name.replace(/\(.*?\)/, '').trim();
121
128
  let example = td.name.match(/\((.*?)\)/);
122
129
  if (example) example = { ...example[1].split(',')};
@@ -131,15 +138,20 @@ class XmlReader {
131
138
  }
132
139
  }) || [];
133
140
 
134
- const results = jsonSuite?.TestRun?.Results?.UnitTestResult?.map(td => ({
141
+ let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
142
+ if (!Array.isArray(result)) result = [result].filter(d => !!d);
143
+
144
+ const results = result.map(td => ({
135
145
  id: td.executionId,
136
146
  run_time: parseFloat(td.duration),
137
147
  status: td.outcome,
138
- stack: td.Output.StdOut
148
+ stack: td.Output.StdOut,
149
+ files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
139
150
  }));
140
151
 
152
+
141
153
  results.forEach(r => {
142
- const test = tests.find(t => t.id === r.id) || {};
154
+ const test = tests.find(t => t.id === r.id) || { };
143
155
  r.suite_title = test.suite_title;
144
156
  r.title = test.title?.trim();
145
157
  if (test.example) r.example = test.example;
@@ -150,6 +162,8 @@ class XmlReader {
150
162
  if (r.status === 'Skipped') r.status = SKIPPED;
151
163
  delete r.id;
152
164
  });
165
+
166
+ debug(results);
153
167
 
154
168
  const counters = jsonSuite?.TestRun?.ResultSummary?.Counters || {};
155
169
 
@@ -158,7 +172,7 @@ class XmlReader {
158
172
  let status = PASSED;
159
173
  if (failed_count > 0) status = FAILED;
160
174
 
161
- this.tests = results;
175
+ this.tests = results.filter(t => !!t.title);
162
176
 
163
177
  return {
164
178
  status,
@@ -209,9 +223,7 @@ class XmlReader {
209
223
  const contents = fs.readFileSync(file).toString();
210
224
  t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
211
225
  } catch (err) {
212
- if (process.env.DEBUG) {
213
- console.log(err)
214
- }
226
+ debug(err)
215
227
  }
216
228
  });
217
229
  }
@@ -261,7 +273,8 @@ class XmlReader {
261
273
 
262
274
  async uploadArtifacts() {
263
275
  for (const test of this.tests.filter(t => !!t.stack)) {
264
- const files = fetchFilesFromStackTrace(test.stack);
276
+ const files = [...test.files.map(f => path.join(process.cwd(), f)), ...fetchFilesFromStackTrace(test.stack)];
277
+ debug('Uploading files', files)
265
278
  test.artifacts = await Promise.all(files.map(f => upload.uploadFileByPath(f, this.runId)));
266
279
  }
267
280
  }
@@ -274,7 +287,7 @@ class XmlReader {
274
287
  group_title: this.requestParams.group_title,
275
288
  };
276
289
 
277
- if (process.env.DEBUG) console.log("Run", runParams);
290
+ debug("Run", runParams);
278
291
 
279
292
  return Promise.all(this.pipes.map(p => p.createRun(runParams)));
280
293
  }
@@ -287,12 +300,12 @@ class XmlReader {
287
300
  this.formatErrors();
288
301
  this.formatTests();
289
302
 
290
- if (process.env.DEBUG) {
291
- console.log({
292
- ...this.stats,
293
- tests: this.tests,
294
- })
295
- }
303
+ debug(
304
+ 'Uploading data',
305
+ {
306
+ ...this.stats,
307
+ tests: this.tests,
308
+ })
296
309
 
297
310
  const dataString = {
298
311
  ...this.stats,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "0.8.0-beta.3",
3
+ "version": "0.8.0-beta.30",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "repository": "git@github.com:testomatio/reporter.git",
@@ -9,6 +9,8 @@
9
9
  "dependencies": {
10
10
  "@octokit/rest": "^19.0.5",
11
11
  "aws-sdk": "^2.1072.0",
12
+ "@aws-sdk/client-s3": "^3.279.0",
13
+ "@aws-sdk/lib-storage": "^3.279.0",
12
14
  "axios": "^0.25.0",
13
15
  "callsite-record": "^4.1.4",
14
16
  "chalk": "^4.1.0",
@@ -17,10 +19,12 @@
17
19
  "fast-xml-parser": "^4.0.8",
18
20
  "glob": "^8.0.3",
19
21
  "has-flag": "^5.0.1",
22
+ "humanize-duration": "^3.27.3",
20
23
  "is-valid-path": "^0.1.1",
21
24
  "json-cycle": "^1.3.0",
22
25
  "lodash.memoize": "^4.1.2",
23
- "lodash.merge": "^4.6.2"
26
+ "lodash.merge": "^4.6.2",
27
+ "csv-writer": "^1.6.0"
24
28
  },
25
29
  "files": [
26
30
  "bin",
@@ -28,20 +32,21 @@
28
32
  "testcafe"
29
33
  ],
30
34
  "scripts": {
35
+ "clear-exportdir": "rm -rf export/",
31
36
  "pretty": "prettier --write .",
32
37
  "lint": "eslint lib",
33
38
  "lint:fix": "eslint lib --fix",
34
- "test": "mocha tests/** -R ./lib/adapter/mocha.js",
39
+ "test": "mocha tests/**",
35
40
  "init": "cd ./tests/adapter/examples/cucumber && npm i",
36
41
  "test:unit": "mocha tests",
37
42
  "test:adapter": "mocha './tests/adapter/index.test.js'",
43
+ "test:pipes": "mocha './tests/pipes/*_test.js'",
38
44
  "test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
39
45
  "test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.js'",
40
46
  "test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
41
47
  "test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
42
48
  "test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js"
43
49
  },
44
- "peerDependencies": {},
45
50
  "devDependencies": {
46
51
  "@cucumber/cucumber": "^8.6.0",
47
52
  "@wdio/reporter": "^7.16.13",
package/testcafe/index.js DELETED
@@ -1,61 +0,0 @@
1
- const TestomatClient = require('@testomatio/reporter/lib/client');
2
- const TRConstants = require('@testomatio/reporter/lib/constants');
3
- const util = require('@testomatio/reporter/lib/util');
4
-
5
- module.exports = () => {
6
- const apiKey = process.env.TESTOMATIO;
7
- let failed = false;
8
-
9
- if (apiKey === '' || apiKey === undefined) {
10
- throw new Error('Testomat.io API key cannot be empty');
11
- }
12
- const client = new TestomatClient({ apiKey });
13
-
14
- return {
15
- reportTaskStart(startTime, userAgents) {
16
- console.log('TestCafe started with: ', userAgents);
17
- client.createRun();
18
- },
19
-
20
- reportFixtureStart(name) {
21
- console.log(`Suite : ${name}`);
22
- },
23
-
24
- reportTestDone(name, testRunInfo) {
25
- let status = TRConstants.PASSED;
26
- let message = '';
27
-
28
- if (testRunInfo.skipped) {
29
- status = TRConstants.SKIPPED;
30
- }
31
- if (testRunInfo.errs.length) {
32
- status = TRConstants.FAILED;
33
- message = this.renderErrors(testRunInfo.errs);
34
- failed = true;
35
- }
36
- console.log(` - ${name} : ${status}`);
37
- client.addTestRun(util.parseTest(name), status, {
38
- error: testRunInfo.errs.length ? testRunInfo.errs[0] : null,
39
- message,
40
- title: name,
41
- time: testRunInfo.durationMs,
42
- });
43
- },
44
-
45
- renderErrors(errors) {
46
- let errorMessage = '';
47
- errors.forEach((error, id) => {
48
- errorMessage = `${errorMessage}${this.formatError(error, `${id + 1} `)}\n`;
49
- });
50
-
51
- console.log(errorMessage);
52
- return errorMessage.replace(util.ansiRegExp(), '');
53
- },
54
-
55
- reportTaskDone() {
56
- const status = failed ? TRConstants.FAILED : TRConstants.PASSED;
57
- console.log(`Status : ${status}`);
58
- client.updateRunStatus(status);
59
- },
60
- };
61
- };