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

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 (149) hide show
  1. package/README.md +1 -0
  2. package/lib/adapter/codecept.d.ts +2 -0
  3. package/lib/adapter/codecept.js +293 -335
  4. package/lib/adapter/cucumber/current.d.ts +14 -0
  5. package/lib/adapter/cucumber/current.js +195 -203
  6. package/lib/adapter/cucumber/legacy.d.ts +0 -0
  7. package/lib/adapter/cucumber/legacy.js +130 -155
  8. package/lib/adapter/cucumber.d.ts +2 -0
  9. package/lib/adapter/cucumber.js +5 -16
  10. package/lib/adapter/cypress-plugin/index.d.ts +2 -0
  11. package/lib/adapter/cypress-plugin/index.js +91 -105
  12. package/lib/adapter/jasmine.d.ts +11 -0
  13. package/lib/adapter/jasmine.js +54 -53
  14. package/lib/adapter/jest.d.ts +13 -0
  15. package/lib/adapter/jest.js +97 -99
  16. package/lib/adapter/mocha.d.ts +2 -0
  17. package/lib/adapter/mocha.js +112 -141
  18. package/lib/adapter/nightwatch.d.ts +4 -0
  19. package/lib/adapter/nightwatch.js +80 -0
  20. package/lib/adapter/playwright.d.ts +14 -0
  21. package/lib/adapter/playwright.js +199 -231
  22. package/lib/adapter/vitest.d.ts +35 -0
  23. package/lib/adapter/vitest.js +150 -149
  24. package/lib/adapter/webdriver.d.ts +24 -0
  25. package/lib/adapter/webdriver.js +144 -121
  26. package/lib/bin/cli.d.ts +2 -0
  27. package/lib/bin/cli.js +229 -211
  28. package/lib/bin/reportXml.d.ts +2 -0
  29. package/lib/bin/reportXml.js +51 -52
  30. package/lib/bin/startTest.d.ts +2 -0
  31. package/lib/bin/startTest.js +83 -95
  32. package/lib/bin/uploadArtifacts.d.ts +2 -0
  33. package/lib/bin/uploadArtifacts.js +56 -61
  34. package/lib/client.d.ts +76 -0
  35. package/lib/client.js +429 -465
  36. package/lib/config.d.ts +1 -0
  37. package/lib/config.js +18 -23
  38. package/lib/constants.d.ts +25 -0
  39. package/lib/constants.js +50 -44
  40. package/lib/data-storage.d.ts +34 -0
  41. package/lib/data-storage.js +216 -188
  42. package/lib/junit-adapter/adapter.d.ts +9 -0
  43. package/lib/junit-adapter/adapter.js +17 -20
  44. package/lib/junit-adapter/csharp.d.ts +5 -0
  45. package/lib/junit-adapter/csharp.js +28 -14
  46. package/lib/junit-adapter/index.d.ts +3 -0
  47. package/lib/junit-adapter/index.js +27 -25
  48. package/lib/junit-adapter/java.d.ts +5 -0
  49. package/lib/junit-adapter/java.js +41 -53
  50. package/lib/junit-adapter/javascript.d.ts +4 -0
  51. package/lib/junit-adapter/javascript.js +30 -27
  52. package/lib/junit-adapter/python.d.ts +5 -0
  53. package/lib/junit-adapter/python.js +38 -37
  54. package/lib/junit-adapter/ruby.d.ts +4 -0
  55. package/lib/junit-adapter/ruby.js +11 -8
  56. package/lib/output.d.ts +11 -0
  57. package/lib/output.js +44 -52
  58. package/lib/package.json +3 -0
  59. package/lib/pipe/bitbucket.d.ts +25 -0
  60. package/lib/pipe/bitbucket.js +223 -230
  61. package/lib/pipe/csv.d.ts +47 -0
  62. package/lib/pipe/csv.js +113 -126
  63. package/lib/pipe/debug.d.ts +29 -0
  64. package/lib/pipe/debug.js +125 -99
  65. package/lib/pipe/github.d.ts +30 -0
  66. package/lib/pipe/github.js +218 -213
  67. package/lib/pipe/gitlab.d.ts +25 -0
  68. package/lib/pipe/gitlab.js +183 -206
  69. package/lib/pipe/html.d.ts +35 -0
  70. package/lib/pipe/html.js +258 -321
  71. package/lib/pipe/index.d.ts +1 -0
  72. package/lib/pipe/index.js +94 -66
  73. package/lib/pipe/testomatio.d.ts +71 -0
  74. package/lib/pipe/testomatio.js +429 -474
  75. package/lib/replay.d.ts +31 -0
  76. package/lib/replay.js +255 -0
  77. package/lib/reporter-functions.d.ts +34 -0
  78. package/lib/reporter-functions.js +28 -26
  79. package/lib/reporter.d.ts +232 -0
  80. package/lib/reporter.js +34 -29
  81. package/lib/services/artifacts.d.ts +33 -0
  82. package/lib/services/artifacts.js +55 -51
  83. package/lib/services/index.d.ts +9 -0
  84. package/lib/services/index.js +14 -12
  85. package/lib/services/key-values.d.ts +27 -0
  86. package/lib/services/key-values.js +56 -53
  87. package/lib/services/logger.d.ts +64 -0
  88. package/lib/services/logger.js +226 -245
  89. package/lib/template/testomatio.hbs +1026 -1366
  90. package/lib/uploader.d.ts +60 -0
  91. package/lib/uploader.js +295 -364
  92. package/lib/utils/pipe_utils.d.ts +41 -0
  93. package/lib/utils/pipe_utils.js +89 -85
  94. package/lib/utils/utils.d.ts +54 -0
  95. package/lib/utils/utils.js +398 -307
  96. package/lib/xmlReader.d.ts +92 -0
  97. package/lib/xmlReader.js +525 -532
  98. package/package.json +64 -21
  99. package/src/adapter/codecept.js +373 -0
  100. package/src/adapter/cucumber/current.js +228 -0
  101. package/src/adapter/cucumber/legacy.js +158 -0
  102. package/src/adapter/cucumber.js +4 -0
  103. package/src/adapter/cypress-plugin/index.js +110 -0
  104. package/src/adapter/jasmine.js +60 -0
  105. package/src/adapter/jest.js +107 -0
  106. package/src/adapter/mocha.cjs +2 -0
  107. package/src/adapter/mocha.js +156 -0
  108. package/src/adapter/nightwatch.js +88 -0
  109. package/src/adapter/playwright.js +254 -0
  110. package/src/adapter/vitest.js +183 -0
  111. package/src/adapter/webdriver.js +142 -0
  112. package/src/bin/cli.js +348 -0
  113. package/src/bin/reportXml.js +77 -0
  114. package/src/bin/startTest.js +124 -0
  115. package/src/bin/uploadArtifacts.js +91 -0
  116. package/src/client.js +515 -0
  117. package/src/config.js +30 -0
  118. package/src/constants.js +53 -0
  119. package/src/data-storage.js +204 -0
  120. package/src/junit-adapter/adapter.js +23 -0
  121. package/src/junit-adapter/csharp.js +28 -0
  122. package/src/junit-adapter/index.js +28 -0
  123. package/src/junit-adapter/java.js +58 -0
  124. package/src/junit-adapter/javascript.js +31 -0
  125. package/src/junit-adapter/python.js +42 -0
  126. package/src/junit-adapter/ruby.js +10 -0
  127. package/src/output.js +57 -0
  128. package/src/pipe/bitbucket.js +252 -0
  129. package/src/pipe/csv.js +140 -0
  130. package/src/pipe/debug.js +125 -0
  131. package/src/pipe/github.js +232 -0
  132. package/src/pipe/gitlab.js +247 -0
  133. package/src/pipe/html.js +373 -0
  134. package/src/pipe/index.js +71 -0
  135. package/src/pipe/testomatio.js +504 -0
  136. package/src/replay.js +262 -0
  137. package/src/reporter-functions.js +55 -0
  138. package/src/reporter.cjs_decprecated +21 -0
  139. package/src/reporter.js +33 -0
  140. package/src/services/artifacts.js +59 -0
  141. package/src/services/index.js +13 -0
  142. package/src/services/key-values.js +59 -0
  143. package/src/services/logger.js +315 -0
  144. package/src/template/emptyData.svg +23 -0
  145. package/src/template/testomatio.hbs +1081 -0
  146. package/src/uploader.js +376 -0
  147. package/src/utils/pipe_utils.js +119 -0
  148. package/src/utils/utils.js +416 -0
  149. package/src/xmlReader.js +614 -0
package/lib/uploader.js CHANGED
@@ -1,385 +1,316 @@
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
-
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');
12
18
  class S3Uploader {
13
- constructor() {
14
- this.isEnabled = undefined;
15
- this.storeEnabled = true;
16
- this.config = undefined;
17
-
18
- // counters
19
- /**
20
- * @type {{path: string, size: number}[]}
21
- */
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;
105
- }
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;
121
- }
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());
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
+ ];
137
44
  }
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 [];
45
+ resetConfig() {
46
+ this.config = undefined;
47
+ this.isEnabled = undefined;
153
48
  }
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 [];
49
+ /**
50
+ *
51
+ * @returns {Record<string, string>}
52
+ */
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;
165
61
  }
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, '');
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
+ ]));
178
67
  }
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;
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;
191
79
  }
192
-
193
- if (typeof filePath === 'string' && !path.isAbsolute(filePath)) {
194
- filePath = path.join(process.cwd(), filePath);
80
+ enableLogStorage() {
81
+ this.storeEnabled = true;
195
82
  }
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`);
83
+ disableLogStorage() {
84
+ this.storeEnabled = false;
226
85
  }
227
-
228
- if (!this.isEnabled) {
229
- this.storeUploadedFile(filePath, runId, rid, false);
230
- this.skippedUploads.push({ path: filePath, size: fileSize });
231
- return;
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
+ }
232
124
  }
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;
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));
243
152
  }
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;
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;
262
160
  }
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}`;
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);
292
176
  }
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) => {
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;
300
191
  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);
192
+ // file may not exist
193
+ fileSize = fs_1.default.statSync(filePath).size || 0;
194
+ fileSizeInMb = Number((fileSize / (1024 * 1024)).toFixed(2));
312
195
  }
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
- }
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;
334
227
  }
335
-
336
- // Normalize the URL
337
- if (!s3Location.startsWith('http')) {
338
- s3Location = `https://${s3Location}`;
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 });
339
245
  }
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());
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
+ });
371
267
  }
372
-
373
- if (S3_SESSION_TOKEN) {
374
- cfg.credentials.sessionToken = S3_SESSION_TOKEN;
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}`;
281
+ }
282
+ return s3Location;
375
283
  }
376
-
377
- if (S3_ENDPOINT) {
378
- cfg.endpoint = S3_ENDPOINT;
284
+ #getFileExtBase64(str) {
285
+ const type = str.charAt(0);
286
+ return ({
287
+ '/': 'jpg',
288
+ i: 'png',
289
+ R: 'gif',
290
+ U: 'webp',
291
+ }[type] || '');
292
+ }
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;
379
312
  }
380
-
381
- return cfg;
382
- }
383
313
  }
314
+ exports.S3Uploader = S3Uploader;
384
315
 
385
- module.exports = S3Uploader;
316
+ module.exports.S3Uploader = S3Uploader;