@testomatio/reporter 2.0.1-beta.4 → 2.0.1-beta.5-timestamp

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 (148) hide show
  1. package/lib/adapter/codecept.js +335 -293
  2. package/lib/adapter/cucumber/current.js +203 -195
  3. package/lib/adapter/cucumber/legacy.js +155 -130
  4. package/lib/adapter/cucumber.js +16 -5
  5. package/lib/adapter/cypress-plugin/index.js +105 -91
  6. package/lib/adapter/jasmine.js +53 -54
  7. package/lib/adapter/jest.js +99 -97
  8. package/lib/adapter/mocha.js +141 -112
  9. package/lib/adapter/playwright.js +231 -199
  10. package/lib/adapter/vitest.js +149 -150
  11. package/lib/adapter/webdriver.js +121 -144
  12. package/lib/bin/cli.js +211 -229
  13. package/lib/bin/reportXml.js +52 -51
  14. package/lib/bin/startTest.js +95 -83
  15. package/lib/bin/uploadArtifacts.js +61 -56
  16. package/lib/client.js +465 -424
  17. package/lib/config.js +23 -18
  18. package/lib/constants.js +44 -50
  19. package/lib/data-storage.js +188 -216
  20. package/lib/junit-adapter/adapter.js +20 -17
  21. package/lib/junit-adapter/csharp.js +14 -28
  22. package/lib/junit-adapter/index.js +25 -27
  23. package/lib/junit-adapter/java.js +53 -41
  24. package/lib/junit-adapter/javascript.js +27 -30
  25. package/lib/junit-adapter/python.js +37 -38
  26. package/lib/junit-adapter/ruby.js +8 -11
  27. package/lib/output.js +52 -44
  28. package/lib/pipe/bitbucket.js +230 -223
  29. package/lib/pipe/csv.js +126 -113
  30. package/lib/pipe/debug.js +99 -118
  31. package/lib/pipe/github.js +213 -218
  32. package/lib/pipe/gitlab.js +206 -183
  33. package/lib/pipe/html.js +321 -258
  34. package/lib/pipe/index.js +66 -94
  35. package/lib/pipe/testomatio.js +474 -429
  36. package/lib/reporter-functions.js +26 -28
  37. package/lib/reporter.js +29 -34
  38. package/lib/services/artifacts.js +51 -55
  39. package/lib/services/index.js +12 -14
  40. package/lib/services/key-values.js +53 -56
  41. package/lib/services/logger.js +245 -226
  42. package/lib/template/testomatio.hbs +1366 -1026
  43. package/lib/uploader.js +364 -295
  44. package/lib/utils/pipe_utils.js +85 -89
  45. package/lib/utils/utils.js +307 -398
  46. package/lib/xmlReader.js +532 -525
  47. package/package.json +21 -64
  48. package/lib/adapter/codecept.d.ts +0 -2
  49. package/lib/adapter/cucumber/current.d.ts +0 -14
  50. package/lib/adapter/cucumber/legacy.d.ts +0 -0
  51. package/lib/adapter/cucumber.d.ts +0 -2
  52. package/lib/adapter/cypress-plugin/index.d.ts +0 -2
  53. package/lib/adapter/jasmine.d.ts +0 -11
  54. package/lib/adapter/jest.d.ts +0 -13
  55. package/lib/adapter/mocha.d.ts +0 -2
  56. package/lib/adapter/nightwatch.d.ts +0 -4
  57. package/lib/adapter/nightwatch.js +0 -80
  58. package/lib/adapter/playwright.d.ts +0 -14
  59. package/lib/adapter/vitest.d.ts +0 -35
  60. package/lib/adapter/webdriver.d.ts +0 -24
  61. package/lib/bin/cli.d.ts +0 -2
  62. package/lib/bin/reportXml.d.ts +0 -2
  63. package/lib/bin/startTest.d.ts +0 -2
  64. package/lib/bin/uploadArtifacts.d.ts +0 -2
  65. package/lib/client.d.ts +0 -76
  66. package/lib/config.d.ts +0 -1
  67. package/lib/constants.d.ts +0 -25
  68. package/lib/data-storage.d.ts +0 -34
  69. package/lib/junit-adapter/adapter.d.ts +0 -9
  70. package/lib/junit-adapter/csharp.d.ts +0 -5
  71. package/lib/junit-adapter/index.d.ts +0 -3
  72. package/lib/junit-adapter/java.d.ts +0 -5
  73. package/lib/junit-adapter/javascript.d.ts +0 -4
  74. package/lib/junit-adapter/python.d.ts +0 -5
  75. package/lib/junit-adapter/ruby.d.ts +0 -4
  76. package/lib/output.d.ts +0 -11
  77. package/lib/package.json +0 -3
  78. package/lib/pipe/bitbucket.d.ts +0 -25
  79. package/lib/pipe/csv.d.ts +0 -47
  80. package/lib/pipe/debug.d.ts +0 -29
  81. package/lib/pipe/github.d.ts +0 -30
  82. package/lib/pipe/gitlab.d.ts +0 -25
  83. package/lib/pipe/html.d.ts +0 -35
  84. package/lib/pipe/index.d.ts +0 -1
  85. package/lib/pipe/testomatio.d.ts +0 -71
  86. package/lib/replay.d.ts +0 -31
  87. package/lib/replay.js +0 -237
  88. package/lib/reporter-functions.d.ts +0 -34
  89. package/lib/reporter.d.ts +0 -232
  90. package/lib/services/artifacts.d.ts +0 -33
  91. package/lib/services/index.d.ts +0 -9
  92. package/lib/services/key-values.d.ts +0 -27
  93. package/lib/services/logger.d.ts +0 -64
  94. package/lib/uploader.d.ts +0 -60
  95. package/lib/utils/pipe_utils.d.ts +0 -41
  96. package/lib/utils/utils.d.ts +0 -54
  97. package/lib/xmlReader.d.ts +0 -92
  98. package/src/adapter/codecept.js +0 -373
  99. package/src/adapter/cucumber/current.js +0 -228
  100. package/src/adapter/cucumber/legacy.js +0 -158
  101. package/src/adapter/cucumber.js +0 -4
  102. package/src/adapter/cypress-plugin/index.js +0 -110
  103. package/src/adapter/jasmine.js +0 -60
  104. package/src/adapter/jest.js +0 -107
  105. package/src/adapter/mocha.cjs +0 -2
  106. package/src/adapter/mocha.js +0 -156
  107. package/src/adapter/nightwatch.js +0 -88
  108. package/src/adapter/playwright.js +0 -254
  109. package/src/adapter/vitest.js +0 -183
  110. package/src/adapter/webdriver.js +0 -142
  111. package/src/bin/cli.js +0 -348
  112. package/src/bin/reportXml.js +0 -77
  113. package/src/bin/startTest.js +0 -124
  114. package/src/bin/uploadArtifacts.js +0 -91
  115. package/src/client.js +0 -508
  116. package/src/config.js +0 -30
  117. package/src/constants.js +0 -53
  118. package/src/data-storage.js +0 -204
  119. package/src/junit-adapter/adapter.js +0 -23
  120. package/src/junit-adapter/csharp.js +0 -28
  121. package/src/junit-adapter/index.js +0 -28
  122. package/src/junit-adapter/java.js +0 -58
  123. package/src/junit-adapter/javascript.js +0 -31
  124. package/src/junit-adapter/python.js +0 -42
  125. package/src/junit-adapter/ruby.js +0 -10
  126. package/src/output.js +0 -57
  127. package/src/pipe/bitbucket.js +0 -252
  128. package/src/pipe/csv.js +0 -140
  129. package/src/pipe/debug.js +0 -119
  130. package/src/pipe/github.js +0 -232
  131. package/src/pipe/gitlab.js +0 -247
  132. package/src/pipe/html.js +0 -373
  133. package/src/pipe/index.js +0 -71
  134. package/src/pipe/testomatio.js +0 -504
  135. package/src/replay.js +0 -245
  136. package/src/reporter-functions.js +0 -55
  137. package/src/reporter.cjs_decprecated +0 -21
  138. package/src/reporter.js +0 -33
  139. package/src/services/artifacts.js +0 -59
  140. package/src/services/index.js +0 -13
  141. package/src/services/key-values.js +0 -59
  142. package/src/services/logger.js +0 -315
  143. package/src/template/emptyData.svg +0 -23
  144. package/src/template/testomatio.hbs +0 -1081
  145. package/src/uploader.js +0 -376
  146. package/src/utils/pipe_utils.js +0 -119
  147. package/src/utils/utils.js +0 -416
  148. package/src/xmlReader.js +0 -614
package/lib/uploader.js CHANGED
@@ -1,316 +1,385 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.S3Uploader = void 0;
7
- const debug_1 = __importDefault(require("debug"));
8
- const client_s3_1 = require("@aws-sdk/client-s3");
9
- const lib_storage_1 = require("@aws-sdk/lib-storage");
10
- const fs_1 = __importDefault(require("fs"));
11
- const os_1 = __importDefault(require("os"));
12
- const path_1 = __importDefault(require("path"));
13
- const promise_retry_1 = __importDefault(require("promise-retry"));
14
- const picocolors_1 = __importDefault(require("picocolors"));
15
- const constants_js_1 = require("./constants.js");
16
- const filesize_1 = require("filesize");
17
- const debug = (0, debug_1.default)('@testomatio/reporter:file-uploader');
1
+ const debug = require('debug')('@testomatio/reporter:uploader');
2
+ const { S3 } = require('@aws-sdk/client-s3');
3
+ const { Upload } = require('@aws-sdk/lib-storage');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const promiseRetry = require('promise-retry');
8
+ const chalk = require('chalk');
9
+ const { APP_PREFIX } = require('./constants');
10
+ const { filesize: prettyBytes } = require('filesize');
11
+
18
12
  class S3Uploader {
19
- constructor() {
20
- this.isEnabled = undefined;
21
- this.storeEnabled = true;
22
- this.config = undefined;
23
- /**
24
- * @type {{path: string, size: number}[]}
25
- */
26
- this.skippedUploads = [];
27
- this.failedUploads = [];
28
- /**
29
- * @type {{path: string, size: number, link: string}[]}
30
- */
31
- this.successfulUploads = [];
32
- this.configKeys = [
33
- 'S3_ENDPOINT',
34
- 'S3_REGION',
35
- 'S3_BUCKET',
36
- 'S3_ACCESS_KEY_ID',
37
- 'S3_SECRET_ACCESS_KEY',
38
- 'S3_SESSION_TOKEN',
39
- 'S3_FORCE_PATH_STYLE',
40
- 'TESTOMATIO_DISABLE_ARTIFACTS',
41
- 'TESTOMATIO_PRIVATE_ARTIFACTS',
42
- 'TESTOMATIO_ARTIFACT_MAX_SIZE_MB',
43
- ];
44
- }
45
- resetConfig() {
46
- this.config = undefined;
47
- this.isEnabled = undefined;
48
- }
13
+ constructor() {
14
+ this.isEnabled = undefined;
15
+ this.storeEnabled = true;
16
+ this.config = undefined;
17
+
18
+ // counters
49
19
  /**
50
- *
51
- * @returns {Record<string, string>}
20
+ * @type {{path: string, size: number}[]}
52
21
  */
53
- getConfig() {
54
- if (this.config)
55
- return this.config;
56
- this.config = this.configKeys.reduce((acc, key) => {
57
- acc[key] = process.env[key];
58
- return acc;
59
- }, {});
60
- return this.config;
22
+ this.skippedUploads = [];
23
+ this.failedUploads = [];
24
+ /**
25
+ * @type {{path: string, size: number, link: string}[]}
26
+ */
27
+ this.successfulUploads = [];
28
+
29
+ this.configKeys = [
30
+ 'S3_ENDPOINT',
31
+ 'S3_REGION',
32
+ 'S3_BUCKET',
33
+ 'S3_ACCESS_KEY_ID',
34
+ 'S3_SECRET_ACCESS_KEY',
35
+ 'S3_SESSION_TOKEN',
36
+ 'S3_FORCE_PATH_STYLE',
37
+ 'TESTOMATIO_DISABLE_ARTIFACTS',
38
+ 'TESTOMATIO_PRIVATE_ARTIFACTS',
39
+ 'TESTOMATIO_ARTIFACT_MAX_SIZE_MB',
40
+ ];
41
+ }
42
+
43
+ resetConfig() {
44
+ this.config = undefined;
45
+ this.isEnabled = undefined;
46
+ }
47
+
48
+ getConfig() {
49
+ if (this.config) return this.config;
50
+ this.config = this.configKeys.reduce((acc, key) => {
51
+ acc[key] = process.env[key];
52
+ return acc;
53
+ }, {});
54
+ return this.config;
55
+ }
56
+
57
+ getMaskedConfig() {
58
+ return Object.fromEntries(
59
+ Object.entries(this.getConfig()).map(([key, value]) => [
60
+ key,
61
+ key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value,
62
+ ]),
63
+ );
64
+ }
65
+
66
+ checkEnabled() {
67
+ if (this.isEnabled !== undefined) return this.isEnabled;
68
+
69
+ const { S3_BUCKET, TESTOMATIO_DISABLE_ARTIFACTS } = this.getConfig();
70
+ if (!S3_BUCKET) debug(`Artifacts uploading is disabled because S3_BUCKET is not set`);
71
+ this.isEnabled = !!(S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS);
72
+
73
+ if (this.isEnabled) debug('S3 uploader is enabled');
74
+ debug(this.getMaskedConfig());
75
+
76
+ return this.isEnabled;
77
+ }
78
+
79
+ enableLogStorage() {
80
+ this.storeEnabled = true;
81
+ }
82
+
83
+ disableLogStorage() {
84
+ this.storeEnabled = false;
85
+ }
86
+
87
+ /**
88
+ *
89
+ * @param {*} Body
90
+ * @param {*} Key
91
+ * @param {{path: string, size?: number}} file
92
+ * @returns
93
+ */
94
+ async #uploadToS3(Body, Key, file) {
95
+ const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS } = this.getConfig();
96
+ const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
97
+
98
+ if (!S3_BUCKET || !Body) {
99
+ console.log(
100
+ APP_PREFIX,
101
+ chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`),
102
+ this.getMaskedConfig(),
103
+ );
104
+ return;
61
105
  }
62
- getMaskedConfig() {
63
- return Object.fromEntries(Object.entries(this.getConfig()).map(([key, value]) => [
64
- key,
65
- key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value,
66
- ]));
106
+
107
+ debug('Uploading to S3:', Key);
108
+
109
+ const s3Config = this.#getS3Config();
110
+ const s3 = new S3(s3Config);
111
+
112
+ const params = {
113
+ Bucket: S3_BUCKET,
114
+ Key,
115
+ Body,
116
+ };
117
+
118
+ // disable ACL for I AM roles
119
+ if (!s3Config.credentials.sessionToken) {
120
+ params.ACL = ACL;
67
121
  }
68
- checkEnabled() {
69
- if (this.isEnabled !== undefined)
70
- return this.isEnabled;
71
- const { S3_BUCKET, TESTOMATIO_DISABLE_ARTIFACTS } = this.getConfig();
72
- if (!S3_BUCKET)
73
- debug(`Artifacts uploading is disabled because S3_BUCKET is not set`);
74
- this.isEnabled = !!(S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS);
75
- if (this.isEnabled)
76
- debug('S3 uploader is enabled');
77
- debug(this.getMaskedConfig());
78
- return this.isEnabled;
122
+
123
+ try {
124
+ const upload = new Upload({
125
+ client: s3,
126
+ params,
127
+ });
128
+
129
+ const link = await this.getS3LocationLink(upload);
130
+ this.successfulUploads.push({ path: file.path, size: file.size, link });
131
+ debug(`📤 Uploaded artifact. File: ${file.path}, size: ${prettyBytes(file.size || 0)}, link: ${link}`);
132
+ return link;
133
+ } catch (e) {
134
+ this.failedUploads.push({ path: file.path, size: file.size });
135
+ debug('S3 uploading error:', e);
136
+ console.log(APP_PREFIX, 'Upload failed:', e.message, '\nConfig:\n', this.getMaskedConfig());
79
137
  }
80
- enableLogStorage() {
81
- this.storeEnabled = true;
138
+ }
139
+
140
+ /**
141
+ * Returns an array of uploaded files
142
+ *
143
+ * @returns {{rid: string, file: string, uploaded: boolean}[]}
144
+ */
145
+ readUploadedFiles(runId) {
146
+ const tempFilePath = this.#getFilePathWithUploadsList(runId);
147
+
148
+ debug('Reading file', tempFilePath);
149
+
150
+ if (!fs.existsSync(tempFilePath)) {
151
+ debug('File not found:', tempFilePath);
152
+ return [];
82
153
  }
83
- disableLogStorage() {
84
- this.storeEnabled = false;
154
+
155
+ const stats = fs.statSync(tempFilePath);
156
+ debug('Artifacts file stats:', +stats.mtime);
157
+ debug('Current time:', +new Date());
158
+ const diff = +new Date() - +stats.mtime;
159
+ debug('Diff:', diff);
160
+ const diffHours = diff / 1000 / 60 / 60;
161
+ debug('Diff hours:', diffHours);
162
+ if (diffHours > 3) {
163
+ console.log(APP_PREFIX, "Artifacts file is too old, can't process artifacts. Please re-run the tests.");
164
+ return [];
85
165
  }
86
- /**
87
- *
88
- * @param {*} Body
89
- * @param {*} Key
90
- * @param {{path: string, size?: number}} file
91
- * @returns
92
- */
93
- async #uploadToS3(Body, Key, file) {
94
- const { S3_BUCKET, TESTOMATIO_PRIVATE_ARTIFACTS } = this.getConfig();
95
- const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
96
- if (!S3_BUCKET || !Body) {
97
- console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold(picocolors_1.default.red(`Failed uploading '${Key}'. Please check S3 credentials`)), this.getMaskedConfig());
98
- return;
99
- }
100
- debug('Uploading to S3:', Key);
101
- const s3Config = this.#getS3Config();
102
- const s3 = new client_s3_1.S3(s3Config);
103
- const params = {
104
- Bucket: S3_BUCKET,
105
- Key,
106
- Body,
107
- };
108
- // disable ACL for I AM roles
109
- if (!s3Config.credentials.sessionToken) {
110
- params.ACL = ACL;
111
- }
112
- try {
113
- const upload = new lib_storage_1.Upload({ client: s3, params });
114
- const link = await this.getS3LocationLink(upload);
115
- this.successfulUploads.push({ path: file.path, size: file.size, link });
116
- debug(`📤 Uploaded artifact. File: ${file.path}, size: ${(0, filesize_1.filesize)(file.size || 0)}, link: ${link}`);
117
- return link;
118
- }
119
- catch (e) {
120
- this.failedUploads.push({ path: file.path, size: file.size });
121
- debug('S3 uploading error:', e);
122
- console.log(constants_js_1.APP_PREFIX, 'Upload failed:', e.message, '\nConfig:\n', this.getMaskedConfig());
123
- }
166
+
167
+ const data = fs.readFileSync(tempFilePath, 'utf8');
168
+ debug('Artifacts file contents:', data);
169
+ const lines = data.split('\n').filter(Boolean);
170
+ return lines.map(line => JSON.parse(line));
171
+ }
172
+
173
+ #getFilePathWithUploadsList(runId) {
174
+ const tempFilePath = path.join(os.tmpdir(), `testomatio.run.${runId}.json`);
175
+ if (!fs.existsSync(tempFilePath)) {
176
+ debug('Creating artifacts file:', tempFilePath);
177
+ fs.writeFileSync(tempFilePath, '');
124
178
  }
125
- /**
126
- * Returns an array of uploaded files
127
- *
128
- * @returns {{rid: string, file: string, uploaded: boolean}[]}
129
- */
130
- readUploadedFiles(runId) {
131
- const tempFilePath = this.#getFilePathWithUploadsList(runId);
132
- debug('Reading file', tempFilePath);
133
- if (!fs_1.default.existsSync(tempFilePath)) {
134
- debug('File not found:', tempFilePath);
135
- return [];
136
- }
137
- const stats = fs_1.default.statSync(tempFilePath);
138
- debug('Artifacts file stats:', +stats.mtime);
139
- debug('Current time:', +new Date());
140
- const diff = +new Date() - +stats.mtime;
141
- debug('Diff:', diff);
142
- const diffHours = diff / 1000 / 60 / 60;
143
- debug('Diff hours:', diffHours);
144
- if (diffHours > 3) {
145
- console.log(constants_js_1.APP_PREFIX, "Artifacts file is too old, can't process artifacts. Please re-run the tests.");
146
- return [];
147
- }
148
- const data = fs_1.default.readFileSync(tempFilePath, 'utf8');
149
- debug('Artifacts file contents:', data);
150
- const lines = data.split('\n').filter(Boolean);
151
- return lines.map(line => JSON.parse(line));
179
+ return tempFilePath;
180
+ }
181
+
182
+ storeUploadedFile(filePath, runId, rid, uploaded = false) {
183
+ if (!this.storeEnabled) return;
184
+
185
+ if (!filePath || !runId || !rid) return;
186
+
187
+ const tempFilePath = this.#getFilePathWithUploadsList(runId);
188
+
189
+ if (typeof filePath === 'object') {
190
+ filePath = filePath.path;
152
191
  }
153
- #getFilePathWithUploadsList(runId) {
154
- const tempFilePath = path_1.default.join(os_1.default.tmpdir(), `testomatio.run.${runId}.json`);
155
- if (!fs_1.default.existsSync(tempFilePath)) {
156
- debug('Creating artifacts file:', tempFilePath);
157
- fs_1.default.writeFileSync(tempFilePath, '');
158
- }
159
- return tempFilePath;
192
+
193
+ if (typeof filePath === 'string' && !path.isAbsolute(filePath)) {
194
+ filePath = path.join(process.cwd(), filePath);
160
195
  }
161
- storeUploadedFile(filePath, runId, rid, uploaded = false) {
162
- if (!this.storeEnabled)
163
- return;
164
- if (!filePath || !runId || !rid)
165
- return;
166
- const tempFilePath = this.#getFilePathWithUploadsList(runId);
167
- if (typeof filePath === 'object') {
168
- filePath = filePath.path;
169
- }
170
- if (typeof filePath === 'string' && !path_1.default.isAbsolute(filePath)) {
171
- filePath = path_1.default.join(process.cwd(), filePath);
172
- }
173
- const data = { rid, file: filePath, uploaded };
174
- const jsonLine = `${JSON.stringify(data)}\n`;
175
- fs_1.default.appendFileSync(tempFilePath, jsonLine);
196
+
197
+ const data = { rid, file: filePath, uploaded };
198
+ const jsonLine = `${JSON.stringify(data)}\n`;
199
+ fs.appendFileSync(tempFilePath, jsonLine);
200
+ }
201
+
202
+ /**
203
+ *
204
+ * @param {*} filePath
205
+ * @param {*} pathInS3 contains runId, rid and filename
206
+ * @returns
207
+ */
208
+ async uploadFileByPath(filePath, pathInS3) {
209
+ /* WDIO: some artifacts uploading started before createRun function completion
210
+ probably, the reason is that run is NOT created in adapter (but via cli) */
211
+ this.isEnabled = this.isEnabled ?? this.checkEnabled();
212
+
213
+ const [runId, rid] = pathInS3;
214
+
215
+ if (!filePath) return;
216
+
217
+ let fileSize = null;
218
+ let fileSizeInMb = null;
219
+
220
+ try {
221
+ // file may not exist
222
+ fileSize = fs.statSync(filePath).size || 0;
223
+ fileSizeInMb = Number((fileSize / (1024 * 1024)).toFixed(2));
224
+ } catch (e) {
225
+ debug(`File ${filePath} does not exist`);
176
226
  }
177
- /**
178
- * @param {*} filePath
179
- * @param {*} pathInS3 contains runId, rid and filename
180
- * @returns
181
- */
182
- async uploadFileByPath(filePath, pathInS3) {
183
- /* WDIO: some artifacts uploading started before createRun function completion
184
- probably, the reason is that run is NOT created in adapter (but via cli) */
185
- this.isEnabled = this.isEnabled ?? this.checkEnabled();
186
- const [runId, rid] = pathInS3;
187
- if (!filePath)
188
- return;
189
- let fileSize = null;
190
- let fileSizeInMb = null;
191
- try {
192
- // file may not exist
193
- fileSize = fs_1.default.statSync(filePath).size || 0;
194
- fileSizeInMb = Number((fileSize / (1024 * 1024)).toFixed(2));
195
- }
196
- catch (e) {
197
- debug(`File ${filePath} does not exist`);
198
- }
199
- if (!this.isEnabled) {
200
- this.storeUploadedFile(filePath, runId, rid, false);
201
- this.skippedUploads.push({ path: filePath, size: fileSize });
202
- return;
203
- }
204
- const { S3_BUCKET, TESTOMATIO_ARTIFACT_MAX_SIZE_MB } = this.getConfig();
205
- debug('Started upload', filePath, 'to', S3_BUCKET);
206
- const isFileExist = await this.checkArtifactExistsInFileSystem(filePath, 20, 500);
207
- if (!isFileExist) {
208
- console.error(picocolors_1.default.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
209
- return;
210
- }
211
- // skipping artifact only if: 1. storing to file is enabled, 2. max size is set and 3. file size exceeds the limit
212
- if (this.storeEnabled &&
213
- TESTOMATIO_ARTIFACT_MAX_SIZE_MB &&
214
- fileSizeInMb > parseFloat(TESTOMATIO_ARTIFACT_MAX_SIZE_MB)) {
215
- const skippedArtifact = { path: filePath, size: fileSize };
216
- this.storeUploadedFile(filePath, runId, rid, false);
217
- this.skippedUploads.push(skippedArtifact);
218
- debug(picocolors_1.default.yellow(`Artifacts file ${JSON.stringify(skippedArtifact)} exceeds the maximum allowed size. Skipping.`));
219
- return;
220
- }
221
- debug('File:', filePath, 'exists, size:', (0, filesize_1.filesize)(fileSize));
222
- const fileStream = fs_1.default.createReadStream(filePath);
223
- const Key = pathInS3.filter(p => !!p).join('/');
224
- const link = await this.#uploadToS3(fileStream, Key, { path: filePath, size: fileSize });
225
- this.storeUploadedFile(filePath, runId, rid, !!link);
226
- return link;
227
+
228
+ if (!this.isEnabled) {
229
+ this.storeUploadedFile(filePath, runId, rid, false);
230
+ this.skippedUploads.push({ path: filePath, size: fileSize });
231
+ return;
227
232
  }
228
- /**
229
- * @param {Buffer} buffer
230
- * @param {string[]} pathInS3
231
- * @returns
232
- */
233
- async uploadFileAsBuffer(buffer, pathInS3) {
234
- /* WDIO: some artifacts uploading started before createRun function completion
235
- probably, the reason is that run is NOT created in adapter (but via cli) */
236
- this.isEnabled = this.isEnabled ?? this.checkEnabled();
237
- if (!this.isEnabled)
238
- return;
239
- let Key = pathInS3.filter(p => !!p).join('/');
240
- const ext = this.#getFileExtBase64(buffer.toString('base64'));
241
- if (ext) {
242
- Key = `${Key}.${ext}`;
243
- }
244
- return this.#uploadToS3(buffer, Key, { path: Key });
233
+
234
+ const { S3_BUCKET, TESTOMATIO_ARTIFACT_MAX_SIZE_MB } = this.getConfig();
235
+
236
+ debug('Started upload', filePath, 'to', S3_BUCKET);
237
+
238
+ const isFileExist = await this.checkArtifactExistsInFileSystem(filePath, 20, 500);
239
+
240
+ if (!isFileExist) {
241
+ console.error(chalk.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
242
+ return;
245
243
  }
246
- async checkArtifactExistsInFileSystem(filePath, attempts = 5, intervalMs = 500) {
247
- return (0, promise_retry_1.default)(async (retry, number) => {
248
- try {
249
- fs_1.default.accessSync(filePath);
250
- return true;
251
- }
252
- catch (err) {
253
- if (number === attempts) {
254
- return false;
255
- }
256
- debug(`File not found, retrying (attempt ${number}/${attempts})`);
257
- await new Promise(resolve => {
258
- setTimeout(resolve, intervalMs);
259
- });
260
- retry(err);
261
- }
262
- }, {
263
- retries: attempts,
264
- minTimeout: intervalMs,
265
- maxTimeout: intervalMs,
266
- });
244
+
245
+ // skipping artifact only if: 1. storing to file is enabled, 2. max size is set and 3. file size exceeds the limit
246
+ if (
247
+ this.storeEnabled &&
248
+ TESTOMATIO_ARTIFACT_MAX_SIZE_MB &&
249
+ fileSizeInMb > parseFloat(TESTOMATIO_ARTIFACT_MAX_SIZE_MB)
250
+ ) {
251
+ const skippedArtifact = { path: filePath, size: fileSize };
252
+ this.storeUploadedFile(filePath, runId, rid, false);
253
+ this.skippedUploads.push(skippedArtifact);
254
+ debug(
255
+ chalk.yellow(
256
+ `Artifacts file ${JSON.stringify(skippedArtifact)} exceeds the maximum allowed size ${
257
+ TESTOMATIO_ARTIFACT_MAX_SIZE_MB
258
+ }MB. Skipping.`,
259
+ ),
260
+ );
261
+ return;
267
262
  }
268
- async getS3LocationLink(out) {
269
- const response = await out.done();
270
- let s3Location = response?.Location?.trim();
271
- if (!s3Location) {
272
- s3Location = out?.singleUploadResult?.Location;
273
- debug('Uploaded singleUploadResult.Location', s3Location);
274
- if (!s3Location) {
275
- throw new Error("Problems getting the S3 artifact's link. Please check S3 permissions!");
276
- }
277
- }
278
- // Normalize the URL
279
- if (!s3Location.startsWith('http')) {
280
- s3Location = `https://${s3Location}`;
263
+ debug('File:', filePath, 'exists, size:', prettyBytes(fileSize));
264
+
265
+ const fileStream = fs.createReadStream(filePath);
266
+ const Key = pathInS3.filter(p => !!p).join('/');
267
+
268
+ const link = await this.#uploadToS3(fileStream, Key, { path: filePath, size: fileSize });
269
+
270
+ this.storeUploadedFile(filePath, runId, rid, !!link);
271
+
272
+ return link;
273
+ }
274
+
275
+ /**
276
+ * @param {Buffer} buffer
277
+ * @param {string[]} pathInS3
278
+ * @returns
279
+ */
280
+ async uploadFileAsBuffer(buffer, pathInS3) {
281
+ /* WDIO: some artifacts uploading started before createRun function completion
282
+ probably, the reason is that run is NOT created in adapter (but via cli) */
283
+
284
+ this.isEnabled = this.isEnabled ?? this.checkEnabled();
285
+ if (!this.isEnabled) return;
286
+
287
+ let Key = pathInS3.filter(p => !!p).join('/');
288
+ const ext = this.#getFileExtBase64(buffer.toString('base64'));
289
+
290
+ if (ext) {
291
+ Key = `${Key}.${ext}`;
292
+ }
293
+
294
+ return this.#uploadToS3(buffer, Key, { path: Key });
295
+ }
296
+
297
+ async checkArtifactExistsInFileSystem(filePath, attempts = 5, intervalMs = 500) {
298
+ return promiseRetry(
299
+ async (retry, number) => {
300
+ try {
301
+ fs.accessSync(filePath);
302
+ return true;
303
+ } catch (err) {
304
+ if (number === attempts) {
305
+ return false;
306
+ }
307
+ debug(`File not found, retrying (attempt ${number}/${attempts})`);
308
+ await new Promise(resolve => {
309
+ setTimeout(resolve, intervalMs);
310
+ });
311
+ retry(err);
281
312
  }
282
- return s3Location;
313
+ },
314
+ {
315
+ retries: attempts,
316
+ minTimeout: intervalMs,
317
+ maxTimeout: intervalMs,
318
+ },
319
+ );
320
+ }
321
+
322
+ async getS3LocationLink(out) {
323
+ const response = await out.done();
324
+
325
+ let s3Location = response?.Location?.trim();
326
+
327
+ if (!s3Location) {
328
+ s3Location = out?.singleUploadResult?.Location;
329
+ debug('Uploaded singleUploadResult.Location', s3Location);
330
+
331
+ if (!s3Location) {
332
+ throw new Error("Problems getting the S3 artifact's link. Please check S3 permissions!");
333
+ }
283
334
  }
284
- #getFileExtBase64(str) {
285
- const type = str.charAt(0);
286
- return ({
287
- '/': 'jpg',
288
- i: 'png',
289
- R: 'gif',
290
- U: 'webp',
291
- }[type] || '');
335
+
336
+ // Normalize the URL
337
+ if (!s3Location.startsWith('http')) {
338
+ s3Location = `https://${s3Location}`;
292
339
  }
293
- #getS3Config() {
294
- const { S3_REGION, S3_SESSION_TOKEN, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } = this.getConfig();
295
- const cfg = {
296
- region: S3_REGION,
297
- credentials: {
298
- accessKeyId: S3_ACCESS_KEY_ID,
299
- secretAccessKey: S3_SECRET_ACCESS_KEY,
300
- },
301
- };
302
- if (S3_FORCE_PATH_STYLE) {
303
- cfg.forcePathStyle = !['false', '0'].includes(String(S3_FORCE_PATH_STYLE || '').toLowerCase());
304
- }
305
- if (S3_SESSION_TOKEN) {
306
- cfg.credentials.sessionToken = S3_SESSION_TOKEN;
307
- }
308
- if (S3_ENDPOINT) {
309
- cfg.endpoint = S3_ENDPOINT;
310
- }
311
- return cfg;
340
+
341
+ return s3Location;
342
+ }
343
+
344
+ #getFileExtBase64(str) {
345
+ const type = str.charAt(0);
346
+
347
+ return (
348
+ {
349
+ '/': 'jpg',
350
+ i: 'png',
351
+ R: 'gif',
352
+ U: 'webp',
353
+ }[type] || ''
354
+ );
355
+ }
356
+
357
+ #getS3Config() {
358
+ const { S3_REGION, S3_SESSION_TOKEN, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_FORCE_PATH_STYLE, S3_ENDPOINT } =
359
+ this.getConfig();
360
+
361
+ const cfg = {
362
+ region: S3_REGION,
363
+ credentials: {
364
+ accessKeyId: S3_ACCESS_KEY_ID,
365
+ secretAccessKey: S3_SECRET_ACCESS_KEY,
366
+ },
367
+ };
368
+
369
+ if (S3_FORCE_PATH_STYLE) {
370
+ cfg.forcePathStyle = !['false', '0'].includes(String(S3_FORCE_PATH_STYLE || '').toLowerCase());
371
+ }
372
+
373
+ if (S3_SESSION_TOKEN) {
374
+ cfg.credentials.sessionToken = S3_SESSION_TOKEN;
312
375
  }
376
+
377
+ if (S3_ENDPOINT) {
378
+ cfg.endpoint = S3_ENDPOINT;
379
+ }
380
+
381
+ return cfg;
382
+ }
313
383
  }
314
- exports.S3Uploader = S3Uploader;
315
384
 
316
- module.exports.S3Uploader = S3Uploader;
385
+ module.exports = S3Uploader;