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

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/client.js CHANGED
@@ -1,4 +1,4 @@
1
- const axios = require('axios');
1
+ const debug = require('debug')('@testomatio/reporter:client');
2
2
  const createCallsiteRecord = require('callsite-record');
3
3
  const { sep, join } = require('path');
4
4
  const fs = require('fs');
@@ -6,8 +6,8 @@ const chalk = require('chalk');
6
6
  const upload = require('./fileUploader');
7
7
  const { PASSED, FAILED, FINISHED, APP_PREFIX } = require('./constants');
8
8
  const pipesFactory = require('./pipe');
9
- const { TESTOMATIO_ENV } = process.env;
10
9
 
10
+ const { TESTOMATIO_ENV } = process.env;
11
11
 
12
12
  class TestomatClient {
13
13
  /**
@@ -15,17 +15,18 @@ class TestomatClient {
15
15
  *
16
16
  * @param {*} params
17
17
  */
18
- constructor(params) {
19
- this.apiKey = params.apiKey || process.env.TESTOMATIO;
18
+ constructor(params = {}) {
20
19
  this.title = params.title || process.env.TESTOMATIO_TITLE;
21
- this.env = TESTOMATIO_ENV;
20
+ this.createNewTests = params.createNewTests ?? !!process.env.TESTOMATIO_CREATE;
21
+ this.proceed = process.env.TESTOMATIO_PROCEED;
22
+ this.env = TESTOMATIO_ENV;
22
23
  this.parallel = params.parallel;
23
- const store = {}
24
+ const store = {};
24
25
  this.pipes = pipesFactory(params, store);
25
26
  this.queue = Promise.resolve();
26
- this.axios = axios.create();
27
27
  this.totalUploaded = 0;
28
28
  this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
29
+ console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
29
30
  }
30
31
 
31
32
  /**
@@ -34,7 +35,9 @@ class TestomatClient {
34
35
  * @returns {Promise} - resolves to Run id which should be used to update / add test
35
36
  */
36
37
  createRun() {
37
- const { runId } = process.env;
38
+ // all pipes disabled, skipping
39
+ if (!this.pipes.filter(p => p.isEnabled).length) return;
40
+
38
41
  const runParams = {
39
42
  title: this.title,
40
43
  parallel: this.parallel,
@@ -43,8 +46,10 @@ class TestomatClient {
43
46
 
44
47
  global.testomatioArtifacts = [];
45
48
 
46
- this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.createRun(runParams))));
47
-
49
+ this.queue = this.queue
50
+ .then(() => Promise.all(this.pipes.map(p => p.createRun(runParams))))
51
+ .catch(err => console.log(APP_PREFIX, err));
52
+ debug('Run', this.queue);
48
53
  return this.queue;
49
54
  }
50
55
 
@@ -53,7 +58,10 @@ class TestomatClient {
53
58
  *
54
59
  * @returns {Promise}
55
60
  */
56
- async addTestRun(testId, status, testData = {}) {
61
+ async addTestRun(testId, status, testData = {}, storeArtifacts = []) {
62
+ // all pipes disabled, skipping
63
+ if (!this.pipes.filter(p => p.isEnabled).length) return;
64
+
57
65
  const {
58
66
  error = '',
59
67
  time = '',
@@ -61,6 +69,7 @@ class TestomatClient {
61
69
  files = [],
62
70
  filesBuffers = [],
63
71
  steps,
72
+ code = null,
64
73
  title,
65
74
  suite_title,
66
75
  suite_id,
@@ -82,7 +91,13 @@ class TestomatClient {
82
91
  stack = this.formatSteps(stack, steps);
83
92
  }
84
93
 
94
+ if (Array.isArray(storeArtifacts) && storeArtifacts.length) {
95
+ debug("CLIENT storeArtefact", storeArtifacts);
96
+ files.push(...storeArtifacts);
97
+ }
98
+
85
99
  if (Array.isArray(global.testomatioArtifacts)) {
100
+ debug("CLIENT global[testomatioArtifacts]", global.testomatioArtifacts);
86
101
  files.push(...global.testomatioArtifacts);
87
102
  global.testomatioArtifacts = [];
88
103
  }
@@ -96,7 +111,7 @@ class TestomatClient {
96
111
  uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.runId));
97
112
  }
98
113
 
99
- const artifacts = await Promise.all(uploadedFiles)
114
+ const artifacts = await Promise.all(uploadedFiles);
100
115
 
101
116
  global.testomatioArtifacts = [];
102
117
 
@@ -108,7 +123,8 @@ class TestomatClient {
108
123
  status,
109
124
  stack,
110
125
  example,
111
- title,
126
+ code,
127
+ title,
112
128
  suite_title,
113
129
  suite_id,
114
130
  test_id,
@@ -117,7 +133,9 @@ class TestomatClient {
117
133
  artifacts,
118
134
  };
119
135
 
120
- this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.addTest(data))));
136
+ this.queue = this.queue
137
+ .then(() => Promise.all(this.pipes.map(p => p.addTest(data))))
138
+ .catch(err => console.log(APP_PREFIX, err));
121
139
  return this.queue;
122
140
  }
123
141
 
@@ -127,23 +145,32 @@ class TestomatClient {
127
145
  * @returns {Promise}
128
146
  */
129
147
  updateRunStatus(status, isParallel) {
148
+ // all pipes disabled, skipping
149
+ if (!this.pipes.filter(p => p.isEnabled).length) return;
150
+
130
151
  let statusEvent;
131
152
  if (status === FINISHED) statusEvent = 'finish';
132
153
  if (status === PASSED) statusEvent = 'pass';
133
154
  if (status === FAILED) statusEvent = 'fail';
134
155
  if (isParallel) statusEvent += '_parallel';
135
- const runParams = { status, statusEvent }
156
+ const runParams = { status, statusEvent };
157
+
158
+ this.queue = this.queue
159
+ .then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
160
+ .then(() => {
161
+ debug("TOTAL uploaded files", this.totalUploaded);
162
+
163
+ if (upload.isArtifactsEnabled() && this.totalUploaded > 0) {
164
+ console.log(
165
+ APP_PREFIX,
166
+ `🗄️ Total ${this.totalUploaded} artifacts ${
167
+ process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
168
+ } uploaded to S3 bucket `,
169
+ );
170
+ }
171
+ })
172
+ .catch(err => console.log(APP_PREFIX, err));
136
173
 
137
- this.queue = this.queue.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))));
138
-
139
- if (upload.isArtifactsEnabled && this.totalUploaded > 0) {
140
- console.log(
141
- APP_PREFIX,
142
- `🗄️ Total ${this.totalUploaded} artifacts ${
143
- process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
144
- } uploaded to S3 bucket `,
145
- );
146
- }
147
174
  return this.queue;
148
175
  }
149
176
 
package/lib/constants.js CHANGED
@@ -1,6 +1,15 @@
1
1
  const chalk = require('chalk');
2
2
 
3
3
  const APP_PREFIX = chalk.gray('[TESTOMATIO]');
4
+ const TESTOMAT_ARTIFACT_SUFFIX = "testomatio_artifact_";
5
+
6
+ const CSV_HEADERS = [
7
+ { id: 'suite_title', title: 'Suite_title' },
8
+ { id: 'title', title: 'Title' },
9
+ { id: 'status', title: 'Status' },
10
+ { id: 'message', title: 'Message' },
11
+ { id: 'stack', title: 'Stack' },
12
+ ];
4
13
 
5
14
  module.exports = Object.freeze({
6
15
  PASSED: 'passed',
@@ -8,4 +17,6 @@ module.exports = Object.freeze({
8
17
  SKIPPED: 'skipped',
9
18
  FINISHED: 'finished',
10
19
  APP_PREFIX,
20
+ CSV_HEADERS,
21
+ TESTOMAT_ARTIFACT_SUFFIX
11
22
  });
@@ -1,4 +1,6 @@
1
- const AWS = require('aws-sdk');
1
+ const debug = require('debug')('@testomatio/reporter:file-uploader');
2
+ const { S3 } = require('@aws-sdk/client-s3');
3
+ const { Upload } = require("@aws-sdk/lib-storage");
2
4
  const fs = require('fs');
3
5
  const path = require('path');
4
6
  const chalk = require('chalk');
@@ -6,18 +8,38 @@ const { randomUUID } = require('crypto');
6
8
  const memoize = require('lodash.memoize');
7
9
  const { APP_PREFIX } = require('./constants');
8
10
 
9
- const {
10
- S3_ENDPOINT,
11
- S3_REGION,
12
- S3_BUCKET,
13
- S3_ACCESS_KEY_ID,
14
- S3_SECRET_ACCESS_KEY,
15
- TESTOMATIO_DISABLE_ARTIFACTS,
16
- TESTOMATIO_PRIVATE_ARTIFACTS,
17
- S3_FORCE_PATH_STYLE,
18
- } = process.env;
19
-
20
- const isArtifactsEnabled = S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS;
11
+ const keys = [
12
+ 'S3_ENDPOINT',
13
+ 'S3_REGION',
14
+ 'S3_BUCKET',
15
+ 'S3_ACCESS_KEY_ID',
16
+ 'S3_SECRET_ACCESS_KEY',
17
+ 'TESTOMATIO_DISABLE_ARTIFACTS',
18
+ 'TESTOMATIO_PRIVATE_ARTIFACTS',
19
+ 'S3_FORCE_PATH_STYLE',
20
+ ];
21
+
22
+ let config;
23
+
24
+ function getConfig() {
25
+ if (config) return config;
26
+ config = keys.reduce((acc, key) => {
27
+ acc[key] = process.env[key];
28
+ return acc;
29
+ }, {});
30
+ debug('Config', config);
31
+ return config;
32
+ }
33
+
34
+ let isEnabled;
35
+
36
+ const isArtifactsEnabled = () => {
37
+ if (isEnabled !== undefined) return isEnabled;
38
+ const { S3_BUCKET, TESTOMATIO_DISABLE_ARTIFACTS } = getConfig();
39
+ isEnabled = !!(S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS);
40
+ debug(`Upload is ${isEnabled ? 'enabled' : 'disabled'}`);
41
+ return isEnabled;
42
+ };
21
43
 
22
44
  const _getFileExtBase64 = str => {
23
45
  const type = str.charAt(0);
@@ -32,6 +54,25 @@ const _getFileExtBase64 = str => {
32
54
  );
33
55
  };
34
56
 
57
+ const _getS3Config = () => {
58
+ const { S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } = getConfig();
59
+
60
+ const cfg = {
61
+ region: S3_REGION,
62
+ credentials: {
63
+ accessKeyId: S3_ACCESS_KEY_ID,
64
+ secretAccessKey: S3_SECRET_ACCESS_KEY,
65
+ s3ForcePathStyle: S3_FORCE_PATH_STYLE,
66
+ }
67
+ };
68
+
69
+ if (S3_ENDPOINT) {
70
+ cfg.endpoint = S3_ENDPOINT;
71
+ }
72
+
73
+ return cfg;
74
+ }
75
+
35
76
  const uploadUsingS3 = async (filePath, runId) => {
36
77
  let ContentType;
37
78
  let Key;
@@ -42,40 +83,39 @@ const uploadUsingS3 = async (filePath, runId) => {
42
83
  Key = filePath.name;
43
84
  }
44
85
 
45
- const config = {
46
- region: S3_REGION,
47
- accessKeyId: S3_ACCESS_KEY_ID,
48
- secretAccessKey: S3_SECRET_ACCESS_KEY,
49
- s3ForcePathStyle: S3_FORCE_PATH_STYLE,
50
- };
51
-
52
- if (S3_ENDPOINT) {
53
- config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
54
- }
55
-
56
- const s3 = new AWS.S3(config);
57
86
  if (!fs.existsSync(filePath)) {
58
87
  console.error(chalk.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
59
88
  return;
60
89
  }
90
+
91
+ const {
92
+ S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
93
+ } = getConfig();
61
94
 
62
95
  const file = fs.readFileSync(filePath);
63
96
 
64
97
  Key = `${runId}/${randomUUID()}-${Key || path.basename(filePath)}`;
65
98
  const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
66
99
 
100
+ const s3 = new S3(_getS3Config());
101
+
67
102
  try {
68
- const out = await s3
69
- .upload({
103
+ const out = new Upload({
104
+ client: s3,
105
+
106
+ params: {
70
107
  Bucket: S3_BUCKET,
71
108
  Key,
72
109
  Body: file,
73
110
  ContentType,
74
111
  ACL,
75
- })
76
- .promise();
112
+ }
113
+ });
77
114
 
78
- return out.Location;
115
+ await out.done();
116
+ debug('Uploaded', out.singleUploadResult.Location)
117
+
118
+ return out.singleUploadResult.Location;
79
119
  } catch (e) {
80
120
  console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
81
121
  accessKeyId: S3_ACCESS_KEY_ID,
@@ -100,35 +140,32 @@ const uploadUsingS3 = async (filePath, runId) => {
100
140
  };
101
141
 
102
142
  const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
103
- const config = {
104
- region: S3_REGION,
105
- accessKeyId: S3_ACCESS_KEY_ID,
106
- secretAccessKey: S3_SECRET_ACCESS_KEY,
107
- s3ForcePathStyle: S3_FORCE_PATH_STYLE,
108
- };
109
-
110
- if (S3_ENDPOINT) {
111
- config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
112
- }
113
143
 
114
- const s3 = new AWS.S3(config);
144
+ const {
145
+ S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
146
+ } = getConfig();
115
147
 
116
148
  const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
117
149
 
118
150
  const fileExtension = _getFileExtBase64(buffer.toString('base64'));
119
151
  const Key = `${runId}/${fileName}${fileExtension}`;
120
152
 
153
+ const s3 = new S3(_getS3Config());
154
+
121
155
  try {
122
- const out = await s3
123
- .upload({
156
+ const out = new Upload({
157
+ client: s3,
158
+
159
+ params: {
124
160
  Bucket: S3_BUCKET,
125
161
  Key,
126
162
  Body: buffer,
127
163
  ACL,
128
- })
129
- .promise();
164
+ }
165
+ });
166
+ await out.done();
130
167
 
131
- return out.Location;
168
+ return out.singleUploadResult.Location;
132
169
  } catch (e) {
133
170
  console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
134
171
  accessKeyId: S3_ACCESS_KEY_ID,
@@ -154,7 +191,7 @@ const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
154
191
 
155
192
  const uploadFileByPath = async (filePath, runId) => {
156
193
  try {
157
- if (isArtifactsEnabled) {
194
+ if (isArtifactsEnabled()) {
158
195
  return uploadUsingS3(filePath, runId);
159
196
  }
160
197
  } catch (e) {
@@ -164,7 +201,7 @@ const uploadFileByPath = async (filePath, runId) => {
164
201
 
165
202
  const uploadFileAsBuffer = async (buffer, fileName, runId) => {
166
203
  try {
167
- if (isArtifactsEnabled) {
204
+ if (isArtifactsEnabled()) {
168
205
  return uploadUsingS3AsBuffer(buffer, fileName, runId);
169
206
  }
170
207
  } catch (e) {
@@ -175,5 +212,5 @@ const uploadFileAsBuffer = async (buffer, fileName, runId) => {
175
212
  module.exports = {
176
213
  uploadFileByPath: memoize(uploadFileByPath),
177
214
  uploadFileAsBuffer: memoize(uploadFileAsBuffer),
178
- isArtifactsEnabled,
215
+ isArtifactsEnabled: memoize(isArtifactsEnabled),
179
216
  };
@@ -0,0 +1,121 @@
1
+ const debug = require('debug')('@testomatio/reporter:pipe:csv');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const csvWriter = require('csv-writer');
5
+ const chalk = require('chalk');
6
+ const merge = require('lodash.merge');
7
+ const { isSameTest, getCurrentDateTime } = require('../util');
8
+ const { CSV_HEADERS } = require('../constants');
9
+
10
+ class CsvPipe {
11
+ constructor(params, store = {}) {
12
+ this.store = store;
13
+ this.title = params.title || process.env.TESTOMATIO_TITLE;
14
+ this.isEnabled = true;
15
+ this.results = [];
16
+
17
+ this.outputDir = 'export';
18
+ this.csvFilename = process.env.TESTOMATIO_CSV_FILENAME;
19
+ this.isCsvSave = false;
20
+
21
+ if (this.csvFilename !== undefined && this.csvFilename.split('.').length > 0) {
22
+ this.isCsvSave = true;
23
+
24
+ if (this.csvFilename.split('.')[0] === 'report') {
25
+ this.outputFile = path.resolve(process.cwd(), this.outputDir, 'report.csv');
26
+ } else {
27
+ this.outputFile = path.resolve(
28
+ process.cwd(),
29
+ this.outputDir,
30
+ `${getCurrentDateTime()}_${this.csvFilename.split('.')[0]}.csv`,
31
+ );
32
+ }
33
+ }
34
+ }
35
+
36
+ async createRun() {
37
+ // empty
38
+ }
39
+
40
+ updateRun() {}
41
+
42
+ /**
43
+ * Create a folder that will contain the exported files
44
+ */
45
+ checkExportDir() {
46
+ if (!fs.existsSync(this.outputDir)) {
47
+ return fs.mkdirSync(this.outputDir);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Save data to the csv file.
53
+ * @param {Object} data - data that will be added to the CSV file.
54
+ * Example: [{suite_title: "Suite #1", test: "Test-case-1", message: "Test msg"}]
55
+ * @param {Object} headers - csv file headers. Example: [{ id: 'suite_title', title: 'Suite_title' }]
56
+ */
57
+ async saveToCsv(data, headers) {
58
+ debug('Data', data);
59
+ // First, we check whether the export directory exists: if yes - OK, no - create it.
60
+ this.checkExportDir();
61
+
62
+ console.log(chalk.yellow(`The test results will be added to the csv. It will take some time...`));
63
+ // Create csv writer object
64
+ const writer = csvWriter.createObjectCsvWriter({
65
+ path: this.outputFile,
66
+ header: headers,
67
+ });
68
+ // Save csv file based on the current data
69
+ try {
70
+ await writer.writeRecords(data);
71
+ console.log(chalk.green(`Recording completed! You can check the result in file = ${this.outputFile}`));
72
+ } catch (e) {
73
+ console.log('Unknown error: ', e);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Add test data to the result array for saving. As a result of this function, we get a result object to save.
79
+ * @param {Object} test - object which includes each test entry.
80
+ */
81
+ addTest(test) {
82
+ if (!this.isEnabled) return;
83
+
84
+ const index = this.results.findIndex(t => isSameTest(t, test));
85
+ // update if they were already added
86
+ if (index >= 0) {
87
+ this.results[index] = merge(this.results[index], test);
88
+ return;
89
+ }
90
+
91
+ const { suite_title, title, status, message, stack } = test;
92
+
93
+ this.results.push({
94
+ suite_title,
95
+ title,
96
+ status,
97
+ message,
98
+ stack,
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Save short tests data to the csv file.
104
+ * @param {Object} runParams - run params.
105
+ */
106
+ async finishRun(runParams) {
107
+ if (!this.isEnabled) return;
108
+
109
+ if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
110
+ // Save results based on the default headers
111
+ if (this.isCsvSave) {
112
+ this.saveToCsv(this.results, CSV_HEADERS);
113
+ }
114
+ }
115
+
116
+ toString() {
117
+ return 'csv exporter';
118
+ }
119
+ }
120
+
121
+ module.exports = CsvPipe;