@testomatio/reporter 0.5.0-beta.3 → 0.5.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.
package/Changelog.md CHANGED
@@ -1,3 +1,16 @@
1
+ # 0.5.2
2
+
3
+ * Fixed multiple upload of artifacts in Cypress.io
4
+
5
+ # 0.5.1
6
+
7
+ * Fixed Cypress.io to report tests inside nested suites
8
+
9
+ # 0.5.0
10
+
11
+ * Added Cypress.io plugin
12
+ * Added artifacts upload to webdriverio
13
+
1
14
  # 0.4.6
2
15
 
3
16
  - Fixed CodeceptJS reporter to report tests failed in hooks
@@ -72,7 +72,7 @@ function CodeceptReporter(config) {
72
72
  });
73
73
 
74
74
  event.dispatcher.on(event.all.result, async () => {
75
- if (videos.length && upload.isEnabled()) {
75
+ if (videos.length && upload.isArtifactsEnabled) {
76
76
  console.log(TRConstants.APP_PREFIX, `🎞️ Uploading ${videos.length} videos...`);
77
77
 
78
78
  const promises = [];
@@ -1,5 +1,5 @@
1
1
  const TRConstants = require('../../constants');
2
- const { parseTest } = require('../../util');
2
+ const { parseTest, parseSuite } = require('../../util');
3
3
  const TestomatClient = require('../../client');
4
4
 
5
5
  const testomatioReporter = on => {
@@ -15,21 +15,34 @@ const testomatioReporter = on => {
15
15
  const videos = [results.video];
16
16
 
17
17
  for (const test of results.tests) {
18
- const latestAttempt = test.attempts[test.attempts.length - 1];
18
+ const lastAttemptIndex = test.attempts.length - 1;
19
+ const latestAttempt = test.attempts[lastAttemptIndex];
20
+
19
21
  const time = latestAttempt.duration;
20
22
  const error = latestAttempt.error;
21
23
 
22
- const title = test.title[1]; // [0] - spec title, [1] - test title
24
+ const title = test.title.pop();
25
+ const suiteTitle = test.title.pop();
26
+
23
27
  const testId = parseTest(title);
28
+ const suiteId = parseSuite(suiteTitle);
29
+
30
+ if (error) {
31
+ error.inspect = function() {
32
+ return this.codeFrame.frame;
33
+ }
34
+ }
24
35
 
25
36
  const screenshots = results.screenshots
26
37
  .filter(screenshot => screenshot.path.includes(title))
38
+ .filter(screenshot => screenshot.testAttemptIndex === lastAttemptIndex)
27
39
  .map(screenshot => screenshot.path);
40
+
28
41
  const files = [...videos, ...screenshots];
29
42
 
30
43
  const state = test.state === 'passed' ? TRConstants.PASSED : TRConstants.FAILED;
31
44
 
32
- addSpecTestsPromises.push(client.addTestRun(testId, state, { title, time, error, files }));
45
+ addSpecTestsPromises.push(client.addTestRun(testId, state, { title, time, error, files, suite_title: suiteTitle, suite_id: suiteId }));
33
46
  }
34
47
 
35
48
  await Promise.all(addSpecTestsPromises);
@@ -75,7 +75,7 @@ class TestomatioReporter {
75
75
  async onEnd(result) {
76
76
  if (!this.client) return;
77
77
 
78
- if (this.videos.length && upload.isEnabled()) {
78
+ if (this.videos.length && upload.isArtifactsEnabled) {
79
79
  console.log(Status.APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
80
80
 
81
81
  const promises = [];
@@ -11,23 +11,39 @@ class WebdriverReporter extends WDIOReporter {
11
11
 
12
12
  this.client = new TestomatClient({ apiKey });
13
13
  options = Object.assign(options, { stdout: true });
14
+
15
+ this._isSynchronising = false;
16
+ }
17
+
18
+ get isSynchronised() {
19
+ return this._isSynchronising === false;
14
20
  }
15
21
 
16
- onTestEnd(test) {
17
- this.addTest(test);
22
+ async onTestEnd(test) {
23
+ this._isSynchronising = true;
24
+
25
+ await this.addTest(test);
26
+
27
+ this._isSynchronising = false;
18
28
  }
19
29
 
20
- addTest(test) {
30
+ async addTest(test) {
21
31
  if (!this.client) return;
22
32
 
23
- const { title, _duration: duration, state, error } = test;
33
+ const { title, _duration: duration, state, error, output } = test;
24
34
 
25
35
  const testId = parseTest(title);
26
36
 
27
- this.client.addTestRun(testId, state, {
37
+ const screenshotEndpoint = '/session/:sessionId/screenshot';
38
+ const screenshotsBuffers = output
39
+ .filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
40
+ .map(el => Buffer.from(el.result.value, 'base64'));
41
+
42
+ await this.client.addTestRun(testId, state, {
28
43
  error,
29
44
  title,
30
45
  time: duration,
46
+ filesBuffers: screenshotsBuffers,
31
47
  });
32
48
  }
33
49
  }
package/lib/client.js CHANGED
@@ -65,12 +65,14 @@ class TestomatClient {
65
65
  }
66
66
 
67
67
  this.queue = this.queue
68
- .then(() => this.axios.post(`${TESTOMAT_URL.trim()}/api/reporter`, runParams).then(resp => {
69
- this.runId = resp.data.uid;
70
- this.runUrl = `${TESTOMAT_URL}/${resp.data.url.split('/').splice(3).join('/')}`;
71
- console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId, chalk.gray(`v${this.version}`));
72
- process.env.runId = this.runId;
73
- }))
68
+ .then(() =>
69
+ this.axios.post(`${TESTOMAT_URL.trim()}/api/reporter`, runParams).then(resp => {
70
+ this.runId = resp.data.uid;
71
+ this.runUrl = `${TESTOMAT_URL}/${resp.data.url.split('/').splice(3).join('/')}`;
72
+ console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId, chalk.gray(`v${this.version}`));
73
+ process.env.runId = this.runId;
74
+ }),
75
+ )
74
76
  .catch(() => {
75
77
  console.log(
76
78
  APP_PREFIX,
@@ -92,6 +94,7 @@ class TestomatClient {
92
94
  time = '',
93
95
  example = null,
94
96
  files = [],
97
+ filesBuffers = [],
95
98
  steps,
96
99
  title,
97
100
  suite_title,
@@ -121,7 +124,11 @@ class TestomatClient {
121
124
  }
122
125
 
123
126
  for (const file of files) {
124
- uploadedFiles.push(upload.uploadFile(file, this.runId));
127
+ uploadedFiles.push(upload.uploadFileByPath(file, this.runId));
128
+ }
129
+
130
+ for (const [idx, buffer] of filesBuffers.entries()) {
131
+ uploadedFiles.push(upload.uploadFileAsBuffer(buffer, idx + 1, this.runId));
125
132
  }
126
133
  }
127
134
 
@@ -200,11 +207,11 @@ class TestomatClient {
200
207
  console.log(APP_PREFIX, '📊 Report Saved. Report URL:', chalk.magenta(this.runUrl));
201
208
  }
202
209
 
203
- if (upload.isEnabled() && this.totalUploaded > 0) {
210
+ if (upload.isArtifactsEnabled && this.totalUploaded > 0) {
204
211
  console.log(
205
212
  APP_PREFIX,
206
213
  `🗄️ Total ${this.totalUploaded} artifacts ${
207
- upload.isPrivate ? 'privately' : chalk.bold('publicly')
214
+ process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
208
215
  } uploaded to S3 bucket `,
209
216
  );
210
217
  }
@@ -240,7 +247,8 @@ class TestomatClient {
240
247
  });
241
248
  if (record && !record.filename.startsWith('http')) {
242
249
  stack += record.renderSync({
243
- stackFilter: frame => frame.getFileName().indexOf(sep) > -1 &&
250
+ stackFilter: frame =>
251
+ frame.getFileName().indexOf(sep) > -1 &&
244
252
  frame.getFileName().indexOf('node_modules') < 0 &&
245
253
  frame.getFileName().indexOf('internal') < 0,
246
254
  });
@@ -5,63 +5,83 @@ const chalk = require('chalk');
5
5
  const memoize = require('lodash.memoize');
6
6
  const { APP_PREFIX } = require('./constants');
7
7
 
8
- const isPrivate = process.env.TESTOMATIO_PRIVATE_ARTIFACTS;
9
- const isEnabled = () => process.env.S3_BUCKET && !process.env.TESTOMATIO_DISABLE_ARTIFACTS;
8
+ const {
9
+ S3_ENDPOINT,
10
+ S3_REGION,
11
+ S3_BUCKET,
12
+ S3_ACCESS_KEY_ID,
13
+ S3_SECRET_ACCESS_KEY,
14
+ TESTOMATIO_DISABLE_ARTIFACTS,
15
+ TESTOMATIO_PRIVATE_ARTIFACTS,
16
+ S3_FORCE_PATH_STYLE,
17
+ } = process.env;
10
18
 
11
- const uploadUsingS3 = async (filePath, runId) => {
12
- const region = process.env.S3_REGION;
13
- const bucket = process.env.S3_BUCKET;
14
- const accessKeyId = process.env.S3_ACCESS_KEY_ID;
15
- const secretAccessKey = process.env.S3_SECRET_ACCESS_KEY;
16
- const isDisabled = process.env.TESTOMATIO_DISABLE_ARTIFACTS;
17
- const s3ForcePathStyle = process.env.S3_FORCE_PATH_STYLE;
19
+ const isArtifactsEnabled = S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS;
20
+
21
+ const _getFileExtBase64 = str => {
22
+ const type = str.charAt(0);
18
23
 
19
- if (isDisabled) return;
24
+ return (
25
+ {
26
+ '/': '.jpg',
27
+ i: '.png',
28
+ R: '.gif',
29
+ U: '.webp',
30
+ }[type] || ''
31
+ );
32
+ };
20
33
 
21
- let contentType;
22
- let fileName;
34
+ const uploadUsingS3 = async (filePath, runId) => {
35
+ let ContentType;
36
+ let Key;
23
37
 
24
38
  if (typeof filePath === 'object') {
25
- contentType = filePath.type;
39
+ ContentType = filePath.type;
26
40
  filePath = filePath.path;
27
- fileName = filePath.name;
41
+ Key = filePath.name;
28
42
  }
29
43
 
30
- const config = { region, accessKeyId, secretAccessKey, s3ForcePathStyle };
31
- if (process.env.S3_ENDPOINT) {
32
- config.endpoint = new AWS.Endpoint(process.env.S3_ENDPOINT);
44
+ const config = {
45
+ region: S3_REGION,
46
+ accessKeyId: S3_ACCESS_KEY_ID,
47
+ secretAccessKey: S3_SECRET_ACCESS_KEY,
48
+ s3ForcePathStyle: S3_FORCE_PATH_STYLE,
49
+ };
50
+
51
+ if (S3_ENDPOINT) {
52
+ config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
33
53
  }
34
54
 
35
55
  const s3 = new AWS.S3(config);
36
56
  const file = fs.readFileSync(filePath);
37
57
 
38
- fileName = `${runId}/${fileName || path.basename(filePath)}`;
39
- const acl = isPrivate ? 'private' : 'public-read';
58
+ Key = `${runId}/${Key || path.basename(filePath)}`;
59
+ const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
40
60
 
41
61
  try {
42
62
  const out = await s3
43
63
  .upload({
44
- Bucket: bucket,
45
- Key: fileName,
64
+ Bucket: S3_BUCKET,
65
+ Key,
46
66
  Body: file,
47
- ContentType: contentType,
48
- ACL: acl,
67
+ ContentType,
68
+ ACL,
49
69
  })
50
70
  .promise();
51
71
 
52
72
  return out.Location;
53
73
  } catch (e) {
54
- console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${fileName}'. Please check S3 credentials`), {
55
- accessKeyId,
56
- secretAccessKey: secretAccessKey ? '**** (hidden) ***' : '(empty)',
57
- region,
58
- bucket,
59
- acl,
60
- endpoint: process.env.S3_ENDPOINT,
74
+ console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
75
+ accessKeyId: S3_ACCESS_KEY_ID,
76
+ secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
77
+ region: S3_REGION,
78
+ bucket: S3_BUCKET,
79
+ acl: ACL,
80
+ endpoint: S3_ENDPOINT,
61
81
  });
62
82
 
63
83
  console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
64
- if (!isPrivate) {
84
+ if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
65
85
  console.log(APP_PREFIX, `To enable ${chalk.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
66
86
  } else {
67
87
  console.log(
@@ -70,23 +90,84 @@ const uploadUsingS3 = async (filePath, runId) => {
70
90
  );
71
91
  }
72
92
  console.log(APP_PREFIX, '---------------');
73
- return null;
74
93
  }
75
94
  };
76
95
 
77
- const uploadFile = async (filePath, runId) => {
96
+ const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
97
+ const config = {
98
+ region: S3_REGION,
99
+ accessKeyId: S3_ACCESS_KEY_ID,
100
+ secretAccessKey: S3_SECRET_ACCESS_KEY,
101
+ s3ForcePathStyle: S3_FORCE_PATH_STYLE,
102
+ };
103
+
104
+ if (S3_ENDPOINT) {
105
+ config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
106
+ }
107
+
108
+ const s3 = new AWS.S3(config);
109
+
110
+ const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
111
+
112
+ const fileExtension = _getFileExtBase64(buffer.toString('base64'));
113
+ const Key = `${runId}/${fileName}${fileExtension}`;
114
+
78
115
  try {
79
- if (isEnabled()) {
116
+ const out = await s3
117
+ .upload({
118
+ Bucket: S3_BUCKET,
119
+ Key,
120
+ Body: buffer,
121
+ ACL,
122
+ })
123
+ .promise();
124
+
125
+ return out.Location;
126
+ } catch (e) {
127
+ console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
128
+ accessKeyId: S3_ACCESS_KEY_ID,
129
+ secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
130
+ region: S3_REGION,
131
+ bucket: S3_BUCKET,
132
+ acl: ACL,
133
+ endpoint: S3_ENDPOINT,
134
+ });
135
+
136
+ console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
137
+ if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
138
+ console.log(APP_PREFIX, `To enable ${chalk.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
139
+ } else {
140
+ console.log(
141
+ APP_PREFIX,
142
+ `To enable ${chalk.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`,
143
+ );
144
+ }
145
+ console.log(APP_PREFIX, '---------------');
146
+ }
147
+ };
148
+
149
+ const uploadFileByPath = async (filePath, runId) => {
150
+ try {
151
+ if (isArtifactsEnabled) {
80
152
  return uploadUsingS3(filePath, runId);
81
153
  }
82
154
  } catch (e) {
83
155
  console.error(chalk.red('Error occurred while uploading artifacts'), e);
84
156
  }
85
- return null;
157
+ };
158
+
159
+ const uploadFileAsBuffer = async (buffer, fileName, runId) => {
160
+ try {
161
+ if (isArtifactsEnabled) {
162
+ return uploadUsingS3AsBuffer(buffer, fileName, runId);
163
+ }
164
+ } catch (e) {
165
+ console.error(chalk.red('Error occurred while uploading artifacts'), e);
166
+ }
86
167
  };
87
168
 
88
169
  module.exports = {
89
- uploadFile: memoize(uploadFile),
90
- isPrivate,
91
- isEnabled,
170
+ uploadFileByPath: memoize(uploadFileByPath),
171
+ uploadFileAsBuffer: memoize(uploadFileAsBuffer),
172
+ isArtifactsEnabled,
92
173
  };
package/lib/util.js CHANGED
@@ -14,6 +14,21 @@ const parseTest = testTitle => {
14
14
  return null;
15
15
  };
16
16
 
17
+ /**
18
+ * @param {String} suiteTitle - suite title
19
+ *
20
+ * @returns {String} suiteId
21
+ */
22
+ const parseSuite = suiteTitle => {
23
+ const captures = suiteTitle.match(/@S([\w\d]+)/);
24
+ if (captures) {
25
+ return captures[1];
26
+ }
27
+
28
+ return null;
29
+ };
30
+
31
+
17
32
  const ansiRegExp = () => {
18
33
  const pattern = [
19
34
  '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "0.5.0-beta.3",
3
+ "version": "0.5.2",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "repository": "git@github.com:testomatio/reporter.git",