@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.
- package/README.md +1 -0
- package/lib/adapter/codecept.d.ts +2 -0
- package/lib/adapter/codecept.js +293 -335
- package/lib/adapter/cucumber/current.d.ts +14 -0
- package/lib/adapter/cucumber/current.js +195 -203
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber/legacy.js +130 -155
- package/lib/adapter/cucumber.d.ts +2 -0
- package/lib/adapter/cucumber.js +5 -16
- package/lib/adapter/cypress-plugin/index.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.js +91 -105
- package/lib/adapter/jasmine.d.ts +11 -0
- package/lib/adapter/jasmine.js +54 -53
- package/lib/adapter/jest.d.ts +13 -0
- package/lib/adapter/jest.js +97 -99
- package/lib/adapter/mocha.d.ts +2 -0
- package/lib/adapter/mocha.js +112 -141
- package/lib/adapter/nightwatch.d.ts +4 -0
- package/lib/adapter/nightwatch.js +80 -0
- package/lib/adapter/playwright.d.ts +14 -0
- package/lib/adapter/playwright.js +199 -231
- package/lib/adapter/vitest.d.ts +35 -0
- package/lib/adapter/vitest.js +150 -149
- package/lib/adapter/webdriver.d.ts +24 -0
- package/lib/adapter/webdriver.js +144 -121
- package/lib/bin/cli.d.ts +2 -0
- package/lib/bin/cli.js +229 -211
- package/lib/bin/reportXml.d.ts +2 -0
- package/lib/bin/reportXml.js +51 -52
- package/lib/bin/startTest.d.ts +2 -0
- package/lib/bin/startTest.js +83 -95
- package/lib/bin/uploadArtifacts.d.ts +2 -0
- package/lib/bin/uploadArtifacts.js +56 -61
- package/lib/client.d.ts +76 -0
- package/lib/client.js +429 -465
- package/lib/config.d.ts +1 -0
- package/lib/config.js +18 -23
- package/lib/constants.d.ts +25 -0
- package/lib/constants.js +50 -44
- package/lib/data-storage.d.ts +34 -0
- package/lib/data-storage.js +216 -188
- package/lib/junit-adapter/adapter.d.ts +9 -0
- package/lib/junit-adapter/adapter.js +17 -20
- package/lib/junit-adapter/csharp.d.ts +5 -0
- package/lib/junit-adapter/csharp.js +28 -14
- package/lib/junit-adapter/index.d.ts +3 -0
- package/lib/junit-adapter/index.js +27 -25
- package/lib/junit-adapter/java.d.ts +5 -0
- package/lib/junit-adapter/java.js +41 -53
- package/lib/junit-adapter/javascript.d.ts +4 -0
- package/lib/junit-adapter/javascript.js +30 -27
- package/lib/junit-adapter/python.d.ts +5 -0
- package/lib/junit-adapter/python.js +38 -37
- package/lib/junit-adapter/ruby.d.ts +4 -0
- package/lib/junit-adapter/ruby.js +11 -8
- package/lib/output.d.ts +11 -0
- package/lib/output.js +44 -52
- package/lib/package.json +3 -0
- package/lib/pipe/bitbucket.d.ts +25 -0
- package/lib/pipe/bitbucket.js +223 -230
- package/lib/pipe/csv.d.ts +47 -0
- package/lib/pipe/csv.js +113 -126
- package/lib/pipe/debug.d.ts +29 -0
- package/lib/pipe/debug.js +125 -99
- package/lib/pipe/github.d.ts +30 -0
- package/lib/pipe/github.js +218 -213
- package/lib/pipe/gitlab.d.ts +25 -0
- package/lib/pipe/gitlab.js +183 -206
- package/lib/pipe/html.d.ts +35 -0
- package/lib/pipe/html.js +258 -321
- package/lib/pipe/index.d.ts +1 -0
- package/lib/pipe/index.js +94 -66
- package/lib/pipe/testomatio.d.ts +71 -0
- package/lib/pipe/testomatio.js +429 -474
- package/lib/replay.d.ts +31 -0
- package/lib/replay.js +255 -0
- package/lib/reporter-functions.d.ts +34 -0
- package/lib/reporter-functions.js +28 -26
- package/lib/reporter.d.ts +232 -0
- package/lib/reporter.js +34 -29
- package/lib/services/artifacts.d.ts +33 -0
- package/lib/services/artifacts.js +55 -51
- package/lib/services/index.d.ts +9 -0
- package/lib/services/index.js +14 -12
- package/lib/services/key-values.d.ts +27 -0
- package/lib/services/key-values.js +56 -53
- package/lib/services/logger.d.ts +64 -0
- package/lib/services/logger.js +226 -245
- package/lib/template/testomatio.hbs +1026 -1366
- package/lib/uploader.d.ts +60 -0
- package/lib/uploader.js +295 -364
- package/lib/utils/pipe_utils.d.ts +41 -0
- package/lib/utils/pipe_utils.js +89 -85
- package/lib/utils/utils.d.ts +54 -0
- package/lib/utils/utils.js +398 -307
- package/lib/xmlReader.d.ts +92 -0
- package/lib/xmlReader.js +525 -532
- package/package.json +64 -21
- package/src/adapter/codecept.js +373 -0
- package/src/adapter/cucumber/current.js +228 -0
- package/src/adapter/cucumber/legacy.js +158 -0
- package/src/adapter/cucumber.js +4 -0
- package/src/adapter/cypress-plugin/index.js +110 -0
- package/src/adapter/jasmine.js +60 -0
- package/src/adapter/jest.js +107 -0
- package/src/adapter/mocha.cjs +2 -0
- package/src/adapter/mocha.js +156 -0
- package/src/adapter/nightwatch.js +88 -0
- package/src/adapter/playwright.js +254 -0
- package/src/adapter/vitest.js +183 -0
- package/src/adapter/webdriver.js +142 -0
- package/src/bin/cli.js +348 -0
- package/src/bin/reportXml.js +77 -0
- package/src/bin/startTest.js +124 -0
- package/src/bin/uploadArtifacts.js +91 -0
- package/src/client.js +515 -0
- package/src/config.js +30 -0
- package/src/constants.js +53 -0
- package/src/data-storage.js +204 -0
- package/src/junit-adapter/adapter.js +23 -0
- package/src/junit-adapter/csharp.js +28 -0
- package/src/junit-adapter/index.js +28 -0
- package/src/junit-adapter/java.js +58 -0
- package/src/junit-adapter/javascript.js +31 -0
- package/src/junit-adapter/python.js +42 -0
- package/src/junit-adapter/ruby.js +10 -0
- package/src/output.js +57 -0
- package/src/pipe/bitbucket.js +252 -0
- package/src/pipe/csv.js +140 -0
- package/src/pipe/debug.js +125 -0
- package/src/pipe/github.js +232 -0
- package/src/pipe/gitlab.js +247 -0
- package/src/pipe/html.js +373 -0
- package/src/pipe/index.js +71 -0
- package/src/pipe/testomatio.js +504 -0
- package/src/replay.js +262 -0
- package/src/reporter-functions.js +55 -0
- package/src/reporter.cjs_decprecated +21 -0
- package/src/reporter.js +33 -0
- package/src/services/artifacts.js +59 -0
- package/src/services/index.js +13 -0
- package/src/services/key-values.js +59 -0
- package/src/services/logger.js +315 -0
- package/src/template/emptyData.svg +23 -0
- package/src/template/testomatio.hbs +1081 -0
- package/src/uploader.js +376 -0
- package/src/utils/pipe_utils.js +119 -0
- package/src/utils/utils.js +416 -0
- package/src/xmlReader.js +614 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { URL } from 'url';
|
|
2
|
+
import path, { sep, basename } from 'path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import isValid from 'is-valid-path';
|
|
6
|
+
import createDebugMessages from 'debug';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const debug = createDebugMessages('@testomatio/reporter:util');
|
|
11
|
+
|
|
12
|
+
// Use __dirname directly since we're compiling to CommonJS
|
|
13
|
+
// prettier-ignore
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
// eslint-disable-next-line max-len
|
|
16
|
+
const __dirname = typeof global.__dirname !== 'undefined' ? global.__dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {String} testTitle - Test title
|
|
20
|
+
*
|
|
21
|
+
* @returns {String|null} testId
|
|
22
|
+
*/
|
|
23
|
+
const getTestomatIdFromTestTitle = testTitle => {
|
|
24
|
+
if (!testTitle) return null;
|
|
25
|
+
|
|
26
|
+
const captures = testTitle.match(/@T[\w\d]{8}/);
|
|
27
|
+
|
|
28
|
+
if (captures) {
|
|
29
|
+
return captures[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {String} suiteTitle - suite title
|
|
37
|
+
*
|
|
38
|
+
* @returns {String|null} suiteId
|
|
39
|
+
*/
|
|
40
|
+
const parseSuite = suiteTitle => {
|
|
41
|
+
const captures = suiteTitle.match(/@S[\w\d]{8}/);
|
|
42
|
+
if (captures) {
|
|
43
|
+
return captures[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validates TESTOMATIO_SUITE environment variable format
|
|
51
|
+
* @param {String} suiteId - suite ID to validate
|
|
52
|
+
* @returns {String|null} validated suite ID or null if invalid
|
|
53
|
+
*/
|
|
54
|
+
const validateSuiteId = suiteId => {
|
|
55
|
+
if (!suiteId) return null;
|
|
56
|
+
|
|
57
|
+
const match = suiteId.match(SUITE_ID_REGEX);
|
|
58
|
+
return match ? match[0] : null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const ansiRegExp = () => {
|
|
62
|
+
const pattern = [
|
|
63
|
+
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
64
|
+
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
|
|
65
|
+
].join('|');
|
|
66
|
+
|
|
67
|
+
return new RegExp(pattern, 'g');
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isValidUrl = s => {
|
|
71
|
+
try {
|
|
72
|
+
new URL(s);
|
|
73
|
+
return true;
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const fileMatchRegex = /file:(\/+(?:[A-Za-z]:[\\/]|\/)?[^\s]*?\.(png|avi|webm|jpg|html|txt))/gi;
|
|
80
|
+
|
|
81
|
+
const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
82
|
+
const files = Array.from(stack.matchAll(fileMatchRegex))
|
|
83
|
+
.map(f => f[1].trim())
|
|
84
|
+
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
85
|
+
.map(f => {
|
|
86
|
+
// Convert Windows paths to Linux paths for testing purposes
|
|
87
|
+
if (f.match(/^[A-Za-z]:[\\\/]/)) {
|
|
88
|
+
// Convert Windows path to Linux equivalent for test scenarios
|
|
89
|
+
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
90
|
+
}
|
|
91
|
+
return f;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
debug('Found files in stack trace: ', files);
|
|
95
|
+
|
|
96
|
+
return files.filter(f => {
|
|
97
|
+
if (!checkExists) return true;
|
|
98
|
+
const isFile = fs.existsSync(f);
|
|
99
|
+
if (!isFile) debug('File %s could not be found and uploaded as artifact', f);
|
|
100
|
+
return isFile;
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
105
|
+
const stackLines = stack
|
|
106
|
+
.split('\n')
|
|
107
|
+
.filter(l => l.includes(':'))
|
|
108
|
+
// .map(l => l.match(/\[(.*?)\]/)?.[1] || l) // minitest format
|
|
109
|
+
// .map(l => l.split(':')[0])
|
|
110
|
+
.map(l => l.trim())
|
|
111
|
+
.map(l => l.split(' ').find(p => p.includes(':')) || '')
|
|
112
|
+
.filter(l => isValid(l?.split(':')[0]))
|
|
113
|
+
|
|
114
|
+
// // filter out 3rd party libs
|
|
115
|
+
.filter(l => !l?.includes(`vendor${sep}`))
|
|
116
|
+
.filter(l => !l?.includes(`node_modules${sep}`))
|
|
117
|
+
.filter(l => fs.existsSync(l.split(':')[0]))
|
|
118
|
+
.filter(l => fs.lstatSync(l.split(':')[0]).isFile());
|
|
119
|
+
|
|
120
|
+
if (!stackLines.length) return '';
|
|
121
|
+
|
|
122
|
+
const [file, line] = stackLines[0].split(':');
|
|
123
|
+
|
|
124
|
+
const prepend = 3;
|
|
125
|
+
const source = fetchSourceCode(fs.readFileSync(file).toString(), { line, prepend, limit: 7 });
|
|
126
|
+
|
|
127
|
+
if (!source) return '';
|
|
128
|
+
|
|
129
|
+
return source
|
|
130
|
+
.split('\n')
|
|
131
|
+
.map((l, i) => {
|
|
132
|
+
if (i === prepend) return `${line} > ${pc.bold(l)}`;
|
|
133
|
+
return `${+line - prepend + i} | ${l}`;
|
|
134
|
+
})
|
|
135
|
+
.join('\n');
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
139
|
+
export const SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
140
|
+
|
|
141
|
+
const fetchIdFromCode = (code, opts = {}) => {
|
|
142
|
+
const comments = code
|
|
143
|
+
.split('\n')
|
|
144
|
+
.map(l => l.trim())
|
|
145
|
+
.filter(l => {
|
|
146
|
+
switch (opts.lang) {
|
|
147
|
+
case 'ruby':
|
|
148
|
+
case 'python':
|
|
149
|
+
return l.startsWith('# ');
|
|
150
|
+
default:
|
|
151
|
+
return l.startsWith('// ');
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const fetchIdFromOutput = output => {
|
|
159
|
+
const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${TEST_ID_REGEX.source})`);
|
|
160
|
+
|
|
161
|
+
return output.match(TID_FULL_PATTERN)?.[2];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const fetchSourceCode = (contents, opts = {}) => {
|
|
165
|
+
if (!opts.title && !opts.line) return '';
|
|
166
|
+
|
|
167
|
+
// code fragment is 20 lines
|
|
168
|
+
const limit = opts.limit || 50;
|
|
169
|
+
let lineIndex;
|
|
170
|
+
if (opts.line) lineIndex = opts.line - 1;
|
|
171
|
+
const lines = contents.split('\n');
|
|
172
|
+
|
|
173
|
+
// remove special chars from title
|
|
174
|
+
if (!lineIndex && opts.title) {
|
|
175
|
+
const title = opts.title.replace(/[([@].*/g, '');
|
|
176
|
+
|
|
177
|
+
if (opts.lang === 'java') {
|
|
178
|
+
lineIndex = lines.findIndex(l => l.includes(`test${title}`));
|
|
179
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
|
|
180
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
181
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
182
|
+
} else if (opts.lang === 'csharp') {
|
|
183
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
184
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
185
|
+
} else {
|
|
186
|
+
lineIndex = lines.findIndex(l => l.includes(title));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (opts.prepend) {
|
|
191
|
+
lineIndex -= opts.prepend;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (lineIndex) {
|
|
195
|
+
const result = [];
|
|
196
|
+
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
197
|
+
if (lines[i] === undefined) continue;
|
|
198
|
+
|
|
199
|
+
if (i > lineIndex + 2 && !opts.prepend) {
|
|
200
|
+
// annotation
|
|
201
|
+
if (opts.lang === 'php' && lines[i].trim().startsWith('#[')) break;
|
|
202
|
+
if (opts.lang === 'php' && lines[i].includes(' private function ')) break;
|
|
203
|
+
if (opts.lang === 'php' && lines[i].includes(' protected function ')) break;
|
|
204
|
+
if (opts.lang === 'php' && lines[i].includes(' public function ')) break;
|
|
205
|
+
if (opts.lang === 'python' && lines[i].trim().match(/^@\w+/)) break;
|
|
206
|
+
if (opts.lang === 'python' && lines[i].includes(' def ')) break;
|
|
207
|
+
if (opts.lang === 'ruby' && lines[i].includes(' def ')) break;
|
|
208
|
+
if (opts.lang === 'ruby' && lines[i].includes(' test ')) break;
|
|
209
|
+
if (opts.lang === 'ruby' && lines[i].includes(' it ')) break;
|
|
210
|
+
if (opts.lang === 'ruby' && lines[i].includes(' specify ')) break;
|
|
211
|
+
if (opts.lang === 'ruby' && lines[i].includes(' context ')) break;
|
|
212
|
+
if (opts.lang === 'ts' && lines[i].includes(' it(')) break;
|
|
213
|
+
if (opts.lang === 'ts' && lines[i].includes(' test(')) break;
|
|
214
|
+
if (opts.lang === 'js' && lines[i].includes(' it(')) break;
|
|
215
|
+
if (opts.lang === 'js' && lines[i].includes(' test(')) break;
|
|
216
|
+
if (opts.lang === 'java' && lines[i].trim().match(/^@\w+/)) break;
|
|
217
|
+
if (opts.lang === 'java' && lines[i].includes(' public void ')) break;
|
|
218
|
+
if (opts.lang === 'java' && lines[i].includes(' class ')) break;
|
|
219
|
+
}
|
|
220
|
+
result.push(lines[i]);
|
|
221
|
+
}
|
|
222
|
+
return result.join('\n');
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const isSameTest = (test, t) =>
|
|
227
|
+
typeof t === 'object' &&
|
|
228
|
+
typeof test === 'object' &&
|
|
229
|
+
t.title === test.title &&
|
|
230
|
+
t.suite_title === test.suite_title &&
|
|
231
|
+
Object.values(t.example || {}) === Object.values(test.example || {}) &&
|
|
232
|
+
t.test_id === test.test_id;
|
|
233
|
+
|
|
234
|
+
const getCurrentDateTime = () => {
|
|
235
|
+
const today = new Date();
|
|
236
|
+
|
|
237
|
+
return `${today.getFullYear()}_${
|
|
238
|
+
today.getMonth() + 1
|
|
239
|
+
}_${today.getDate()}_${today.getHours()}_${today.getMinutes()}_${today.getSeconds()}`;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {Object} test - Test adapter object
|
|
244
|
+
*
|
|
245
|
+
* @returns {String|null} testInfo as one string
|
|
246
|
+
*/
|
|
247
|
+
const specificTestInfo = test => {
|
|
248
|
+
// TODO: afterEach has another context.... need to add specific handler, maybe...
|
|
249
|
+
if (test?.title && test?.file) {
|
|
250
|
+
return `${basename(test.file).split('.').join('#')}#${test.title.split(' ').join('#')}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return null;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const fileSystem = {
|
|
257
|
+
createDir(dirPath) {
|
|
258
|
+
if (!fs.existsSync(dirPath)) {
|
|
259
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
260
|
+
debug('Created dir: ', dirPath);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
clearDir(dirPath) {
|
|
264
|
+
if (fs.existsSync(dirPath)) {
|
|
265
|
+
fs.rmSync(dirPath, { recursive: true });
|
|
266
|
+
debug(`Dir ${dirPath} was deleted`);
|
|
267
|
+
} else {
|
|
268
|
+
debug(`Trying to delete ${dirPath} but it doesn't exist`);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const foundedTestLog = (app, tests) => {
|
|
274
|
+
const n = tests.length;
|
|
275
|
+
|
|
276
|
+
return n === 1 ? console.log(app, `✅ We found one test!`) : console.log(app, `✅ We found ${n} tests!`);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const humanize = text => {
|
|
280
|
+
// if there are no spaces, decamelize
|
|
281
|
+
if (!text.trim().includes(' ')) text = decamelize(text);
|
|
282
|
+
|
|
283
|
+
return text
|
|
284
|
+
.replace(/_./g, match => ` ${match.charAt(1).toUpperCase()}`)
|
|
285
|
+
.trim()
|
|
286
|
+
.replace(/^(.)|\s(.)/g, $1 => $1.toUpperCase())
|
|
287
|
+
.trim()
|
|
288
|
+
.replace(/\sA\s/g, ' a ') // replace a|the
|
|
289
|
+
.replace(/\sThe\s/g, ' the ') // replace a|the
|
|
290
|
+
.replace(/^Test\s/, '')
|
|
291
|
+
.replace(/^Should\s/, '');
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* From https://github.com/sindresorhus/decamelize/blob/main/index.js
|
|
296
|
+
* @param {*} text
|
|
297
|
+
* @returns
|
|
298
|
+
*/
|
|
299
|
+
const decamelize = text => {
|
|
300
|
+
const separator = '_';
|
|
301
|
+
const replacement = `$1${separator}$2`;
|
|
302
|
+
|
|
303
|
+
// Split lowercase sequences followed by uppercase character.
|
|
304
|
+
// `dataForUSACounties` → `data_For_USACounties`
|
|
305
|
+
// `myURLstring → `my_URLstring`
|
|
306
|
+
let decamelized = text.replace(/([\p{Lowercase_Letter}\d])(\p{Uppercase_Letter})/gu, replacement);
|
|
307
|
+
|
|
308
|
+
// Lowercase all single uppercase characters. As we
|
|
309
|
+
// want to preserve uppercase sequences, we cannot
|
|
310
|
+
// simply lowercase the separated string at the end.
|
|
311
|
+
// `data_For_USACounties` → `data_for_USACounties`
|
|
312
|
+
decamelized = decamelized.replace(
|
|
313
|
+
/((?<![\p{Uppercase_Letter}\d])[\p{Uppercase_Letter}\d](?![\p{Uppercase_Letter}\d]))/gu,
|
|
314
|
+
$0 => $0.toLowerCase(),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// Remaining uppercase sequences will be separated from lowercase sequences.
|
|
318
|
+
// `data_For_USACounties` → `data_for_USA_counties`
|
|
319
|
+
return decamelized.replace(
|
|
320
|
+
/(\p{Uppercase_Letter}+)(\p{Uppercase_Letter}\p{Lowercase_Letter}+)/gu,
|
|
321
|
+
(_, $1, $2) => $1 + separator + $2.toLowerCase(),
|
|
322
|
+
);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Used to remove color codes
|
|
327
|
+
* @param {*} input
|
|
328
|
+
* @returns
|
|
329
|
+
*/
|
|
330
|
+
function removeColorCodes(input) {
|
|
331
|
+
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const testRunnerHelper = {
|
|
335
|
+
// for Jest
|
|
336
|
+
getNameOfCurrentlyRunningTest: () => {
|
|
337
|
+
if (global.testomatioTestTitle) return global.testomatioTestTitle;
|
|
338
|
+
|
|
339
|
+
if (!process.env.JEST_WORKER_ID) return null;
|
|
340
|
+
try {
|
|
341
|
+
// TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
|
|
342
|
+
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
343
|
+
return expect?.getState()?.currentTestName;
|
|
344
|
+
} catch (e) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
function storeRunId(runId) {
|
|
351
|
+
if (!runId || runId === 'undefined') return;
|
|
352
|
+
const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
|
|
353
|
+
fs.writeFileSync(filePath, runId);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function readLatestRunId() {
|
|
357
|
+
try {
|
|
358
|
+
const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
|
|
359
|
+
const stats = fs.statSync(filePath);
|
|
360
|
+
const diff = +new Date() - +stats.mtime;
|
|
361
|
+
const diffHours = diff / 1000 / 60 / 60;
|
|
362
|
+
if (diffHours > 1) return;
|
|
363
|
+
|
|
364
|
+
return fs.readFileSync(filePath)?.toString()?.trim();
|
|
365
|
+
} catch (e) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function formatStep(step, shift = 0) {
|
|
371
|
+
const prefix = ' '.repeat(shift);
|
|
372
|
+
|
|
373
|
+
const lines = [];
|
|
374
|
+
|
|
375
|
+
if (step.error) {
|
|
376
|
+
lines.push(`${prefix}${pc.red(step.title)} ${pc.gray(`${step.duration}ms`)}`);
|
|
377
|
+
} else {
|
|
378
|
+
lines.push(`${prefix}${step.title} ${pc.gray(`${step.duration}ms`)}`);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for (const child of step.steps || []) {
|
|
382
|
+
lines.push(...formatStep(child, shift + 2));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return lines;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function getPackageVersion() {
|
|
389
|
+
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
390
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
391
|
+
return packageJson.version;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export {
|
|
395
|
+
ansiRegExp,
|
|
396
|
+
isSameTest,
|
|
397
|
+
fetchSourceCode,
|
|
398
|
+
fetchSourceCodeFromStackTrace,
|
|
399
|
+
fetchIdFromCode,
|
|
400
|
+
fetchIdFromOutput,
|
|
401
|
+
fetchFilesFromStackTrace,
|
|
402
|
+
fileSystem,
|
|
403
|
+
foundedTestLog,
|
|
404
|
+
formatStep,
|
|
405
|
+
getCurrentDateTime,
|
|
406
|
+
getTestomatIdFromTestTitle,
|
|
407
|
+
humanize,
|
|
408
|
+
isValidUrl,
|
|
409
|
+
parseSuite,
|
|
410
|
+
readLatestRunId,
|
|
411
|
+
removeColorCodes,
|
|
412
|
+
specificTestInfo,
|
|
413
|
+
storeRunId,
|
|
414
|
+
testRunnerHelper,
|
|
415
|
+
validateSuiteId,
|
|
416
|
+
};
|