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