@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
package/lib/utils/utils.js
CHANGED
|
@@ -1,379 +1,470 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
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
|
|
10
58
|
/**
|
|
11
59
|
* @param {String} testTitle - Test title
|
|
12
60
|
*
|
|
13
61
|
* @returns {String|null} testId
|
|
14
62
|
*/
|
|
15
63
|
const getTestomatIdFromTestTitle = testTitle => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return null;
|
|
64
|
+
if (!testTitle)
|
|
65
|
+
return null;
|
|
66
|
+
const captures = testTitle.match(/@T[\w\d]{8}/);
|
|
67
|
+
if (captures) {
|
|
68
|
+
return captures[0];
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
25
71
|
};
|
|
26
|
-
|
|
72
|
+
exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
|
|
27
73
|
/**
|
|
28
74
|
* @param {String} suiteTitle - suite title
|
|
29
75
|
*
|
|
30
76
|
* @returns {String|null} suiteId
|
|
31
77
|
*/
|
|
32
78
|
const parseSuite = suiteTitle => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return null;
|
|
79
|
+
const captures = suiteTitle.match(/@S[\w\d]{8}/);
|
|
80
|
+
if (captures) {
|
|
81
|
+
return captures[0];
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
39
84
|
};
|
|
40
|
-
|
|
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;
|
|
96
|
+
};
|
|
97
|
+
exports.validateSuiteId = validateSuiteId;
|
|
41
98
|
const ansiRegExp = () => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return new RegExp(pattern, 'g');
|
|
99
|
+
const pattern = [
|
|
100
|
+
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
101
|
+
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
|
|
102
|
+
].join('|');
|
|
103
|
+
return new RegExp(pattern, 'g');
|
|
48
104
|
};
|
|
49
|
-
|
|
105
|
+
exports.ansiRegExp = ansiRegExp;
|
|
50
106
|
const isValidUrl = s => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
107
|
+
try {
|
|
108
|
+
new url_1.URL(s);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
58
114
|
};
|
|
59
|
-
|
|
60
|
-
const fileMatchRegex = /file:(
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
115
|
+
exports.isValidUrl = isValidUrl;
|
|
116
|
+
const fileMatchRegex = /file:(\/+(?:[A-Za-z]:[\\/]|\/)?[^\s]*?\.(png|avi|webm|jpg|html|txt))/gi;
|
|
117
|
+
const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
118
|
+
const files = Array.from(stack.matchAll(fileMatchRegex))
|
|
119
|
+
.map(f => f[1].trim())
|
|
120
|
+
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
121
|
+
.map(f => {
|
|
122
|
+
// Convert Windows paths to Linux paths for testing purposes
|
|
123
|
+
if (f.match(/^[A-Za-z]:[\\\/]/)) {
|
|
124
|
+
// Convert Windows path to Linux equivalent for test scenarios
|
|
125
|
+
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
126
|
+
}
|
|
127
|
+
return f;
|
|
128
|
+
});
|
|
129
|
+
debug('Found files in stack trace: ', files);
|
|
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
|
+
});
|
|
74
138
|
};
|
|
75
|
-
|
|
139
|
+
exports.fetchFilesFromStackTrace = fetchFilesFromStackTrace;
|
|
76
140
|
const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
.map((l, i) => {
|
|
104
|
-
if (i === prepend) return `${line} > ${chalk.bold(l)}`;
|
|
105
|
-
return `${line - prepend + i} | ${l}`;
|
|
141
|
+
const stackLines = stack
|
|
142
|
+
.split('\n')
|
|
143
|
+
.filter(l => l.includes(':'))
|
|
144
|
+
// .map(l => l.match(/\[(.*?)\]/)?.[1] || l) // minitest format
|
|
145
|
+
// .map(l => l.split(':')[0])
|
|
146
|
+
.map(l => l.trim())
|
|
147
|
+
.map(l => l.split(' ').find(p => p.includes(':')) || '')
|
|
148
|
+
.filter(l => (0, is_valid_path_1.default)(l?.split(':')[0]))
|
|
149
|
+
// // filter out 3rd party libs
|
|
150
|
+
.filter(l => !l?.includes(`vendor${path_1.sep}`))
|
|
151
|
+
.filter(l => !l?.includes(`node_modules${path_1.sep}`))
|
|
152
|
+
.filter(l => fs_1.default.existsSync(l.split(':')[0]))
|
|
153
|
+
.filter(l => fs_1.default.lstatSync(l.split(':')[0]).isFile());
|
|
154
|
+
if (!stackLines.length)
|
|
155
|
+
return '';
|
|
156
|
+
const [file, line] = stackLines[0].split(':');
|
|
157
|
+
const prepend = 3;
|
|
158
|
+
const source = fetchSourceCode(fs_1.default.readFileSync(file).toString(), { line, prepend, limit: 7 });
|
|
159
|
+
if (!source)
|
|
160
|
+
return '';
|
|
161
|
+
return source
|
|
162
|
+
.split('\n')
|
|
163
|
+
.map((l, i) => {
|
|
164
|
+
if (i === prepend)
|
|
165
|
+
return `${line} > ${picocolors_1.default.bold(l)}`;
|
|
166
|
+
return `${+line - prepend + i} | ${l}`;
|
|
106
167
|
})
|
|
107
|
-
|
|
168
|
+
.join('\n');
|
|
108
169
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
170
|
+
exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
171
|
+
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
172
|
+
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
112
173
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
174
|
+
const comments = code
|
|
175
|
+
.split('\n')
|
|
176
|
+
.map(l => l.trim())
|
|
177
|
+
.filter(l => {
|
|
178
|
+
switch (opts.lang) {
|
|
179
|
+
case 'ruby':
|
|
180
|
+
case 'python':
|
|
181
|
+
return l.startsWith('# ');
|
|
182
|
+
default:
|
|
183
|
+
return l.startsWith('// ');
|
|
184
|
+
}
|
|
124
185
|
});
|
|
125
|
-
|
|
126
|
-
return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
|
|
186
|
+
return comments.find(c => c.match(exports.TEST_ID_REGEX))?.match(exports.TEST_ID_REGEX)?.[1];
|
|
127
187
|
};
|
|
128
|
-
|
|
188
|
+
exports.fetchIdFromCode = fetchIdFromCode;
|
|
129
189
|
const fetchIdFromOutput = output => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return output.match(TID_FULL_PATTERN)?.[2];
|
|
190
|
+
const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${exports.TEST_ID_REGEX.source})`);
|
|
191
|
+
return output.match(TID_FULL_PATTERN)?.[2];
|
|
133
192
|
};
|
|
134
|
-
|
|
193
|
+
exports.fetchIdFromOutput = fetchIdFromOutput;
|
|
135
194
|
const fetchSourceCode = (contents, opts = {}) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
195
|
+
if (!opts.title && !opts.line)
|
|
196
|
+
return '';
|
|
197
|
+
// code fragment is 20 lines
|
|
198
|
+
const limit = opts.limit || 50;
|
|
199
|
+
let lineIndex;
|
|
200
|
+
if (opts.line)
|
|
201
|
+
lineIndex = opts.line - 1;
|
|
202
|
+
const lines = contents.split('\n');
|
|
203
|
+
// remove special chars from title
|
|
204
|
+
if (!lineIndex && opts.title) {
|
|
205
|
+
const title = opts.title.replace(/[([@].*/g, '');
|
|
206
|
+
if (opts.lang === 'java') {
|
|
207
|
+
lineIndex = lines.findIndex(l => l.includes(`test${title}`));
|
|
208
|
+
if (lineIndex === -1)
|
|
209
|
+
lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
|
|
210
|
+
if (lineIndex === -1)
|
|
211
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
212
|
+
if (lineIndex === -1)
|
|
213
|
+
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
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
|
+
}
|
|
155
224
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
lineIndex
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
225
|
+
if (opts.prepend) {
|
|
226
|
+
lineIndex -= opts.prepend;
|
|
227
|
+
}
|
|
228
|
+
if (lineIndex) {
|
|
229
|
+
const result = [];
|
|
230
|
+
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
231
|
+
if (lines[i] === undefined)
|
|
232
|
+
continue;
|
|
233
|
+
if (i > lineIndex + 2 && !opts.prepend) {
|
|
234
|
+
// annotation
|
|
235
|
+
if (opts.lang === 'php' && lines[i].trim().startsWith('#['))
|
|
236
|
+
break;
|
|
237
|
+
if (opts.lang === 'php' && lines[i].includes(' private function '))
|
|
238
|
+
break;
|
|
239
|
+
if (opts.lang === 'php' && lines[i].includes(' protected function '))
|
|
240
|
+
break;
|
|
241
|
+
if (opts.lang === 'php' && lines[i].includes(' public function '))
|
|
242
|
+
break;
|
|
243
|
+
if (opts.lang === 'python' && lines[i].trim().match(/^@\w+/))
|
|
244
|
+
break;
|
|
245
|
+
if (opts.lang === 'python' && lines[i].includes(' def '))
|
|
246
|
+
break;
|
|
247
|
+
if (opts.lang === 'ruby' && lines[i].includes(' def '))
|
|
248
|
+
break;
|
|
249
|
+
if (opts.lang === 'ruby' && lines[i].includes(' test '))
|
|
250
|
+
break;
|
|
251
|
+
if (opts.lang === 'ruby' && lines[i].includes(' it '))
|
|
252
|
+
break;
|
|
253
|
+
if (opts.lang === 'ruby' && lines[i].includes(' specify '))
|
|
254
|
+
break;
|
|
255
|
+
if (opts.lang === 'ruby' && lines[i].includes(' context '))
|
|
256
|
+
break;
|
|
257
|
+
if (opts.lang === 'ts' && lines[i].includes(' it('))
|
|
258
|
+
break;
|
|
259
|
+
if (opts.lang === 'ts' && lines[i].includes(' test('))
|
|
260
|
+
break;
|
|
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');
|
|
189
275
|
}
|
|
190
|
-
return result.join('\n');
|
|
191
|
-
}
|
|
192
276
|
};
|
|
193
|
-
|
|
194
|
-
const isSameTest = (test, t) =>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
277
|
+
exports.fetchSourceCode = fetchSourceCode;
|
|
278
|
+
const isSameTest = (test, t) => typeof t === 'object' &&
|
|
279
|
+
typeof test === 'object' &&
|
|
280
|
+
t.title === test.title &&
|
|
281
|
+
t.suite_title === test.suite_title &&
|
|
282
|
+
Object.values(t.example || {}) === Object.values(test.example || {}) &&
|
|
283
|
+
t.test_id === test.test_id;
|
|
284
|
+
exports.isSameTest = isSameTest;
|
|
202
285
|
const getCurrentDateTime = () => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return `${today.getFullYear()}_${
|
|
206
|
-
today.getMonth() + 1
|
|
207
|
-
}_${today.getDate()}_${today.getHours()}_${today.getMinutes()}_${today.getSeconds()}`;
|
|
286
|
+
const today = new Date();
|
|
287
|
+
return `${today.getFullYear()}_${today.getMonth() + 1}_${today.getDate()}_${today.getHours()}_${today.getMinutes()}_${today.getSeconds()}`;
|
|
208
288
|
};
|
|
209
|
-
|
|
289
|
+
exports.getCurrentDateTime = getCurrentDateTime;
|
|
210
290
|
/**
|
|
211
291
|
* @param {Object} test - Test adapter object
|
|
212
292
|
*
|
|
213
293
|
* @returns {String|null} testInfo as one string
|
|
214
294
|
*/
|
|
215
295
|
const specificTestInfo = test => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return null;
|
|
296
|
+
// TODO: afterEach has another context.... need to add specific handler, maybe...
|
|
297
|
+
if (test?.title && test?.file) {
|
|
298
|
+
return `${(0, path_1.basename)(test.file).split('.').join('#')}#${test.title.split(' ').join('#')}`;
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
222
301
|
};
|
|
223
|
-
|
|
302
|
+
exports.specificTestInfo = specificTestInfo;
|
|
224
303
|
const fileSystem = {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
304
|
+
createDir(dirPath) {
|
|
305
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
306
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
307
|
+
debug('Created dir: ', dirPath);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
clearDir(dirPath) {
|
|
311
|
+
if (fs_1.default.existsSync(dirPath)) {
|
|
312
|
+
fs_1.default.rmSync(dirPath, { recursive: true });
|
|
313
|
+
debug(`Dir ${dirPath} was deleted`);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
debug(`Trying to delete ${dirPath} but it doesn't exist`);
|
|
317
|
+
}
|
|
318
|
+
},
|
|
239
319
|
};
|
|
240
|
-
|
|
320
|
+
exports.fileSystem = fileSystem;
|
|
241
321
|
const foundedTestLog = (app, tests) => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
return n === 1 ? console.log(app, `✅ We found one test!`) : console.log(app, `✅ We found ${n} tests!`);
|
|
322
|
+
const n = tests.length;
|
|
323
|
+
return n === 1 ? console.log(app, `✅ We found one test!`) : console.log(app, `✅ We found ${n} tests!`);
|
|
245
324
|
};
|
|
246
|
-
|
|
325
|
+
exports.foundedTestLog = foundedTestLog;
|
|
247
326
|
const humanize = text => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
327
|
+
// if there are no spaces, decamelize
|
|
328
|
+
if (!text.trim().includes(' '))
|
|
329
|
+
text = decamelize(text);
|
|
330
|
+
return text
|
|
331
|
+
.replace(/_./g, match => ` ${match.charAt(1).toUpperCase()}`)
|
|
332
|
+
.trim()
|
|
333
|
+
.replace(/^(.)|\s(.)/g, $1 => $1.toUpperCase())
|
|
334
|
+
.trim()
|
|
335
|
+
.replace(/\sA\s/g, ' a ') // replace a|the
|
|
336
|
+
.replace(/\sThe\s/g, ' the ') // replace a|the
|
|
337
|
+
.replace(/^Test\s/, '')
|
|
338
|
+
.replace(/^Should\s/, '');
|
|
260
339
|
};
|
|
261
|
-
|
|
340
|
+
exports.humanize = humanize;
|
|
262
341
|
/**
|
|
263
342
|
* From https://github.com/sindresorhus/decamelize/blob/main/index.js
|
|
264
343
|
* @param {*} text
|
|
265
344
|
* @returns
|
|
266
345
|
*/
|
|
267
346
|
const decamelize = text => {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
/(
|
|
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
|
-
);
|
|
347
|
+
const separator = '_';
|
|
348
|
+
const replacement = `$1${separator}$2`;
|
|
349
|
+
// Split lowercase sequences followed by uppercase character.
|
|
350
|
+
// `dataForUSACounties` → `data_For_USACounties`
|
|
351
|
+
// `myURLstring → `my_URLstring`
|
|
352
|
+
let decamelized = text.replace(/([\p{Lowercase_Letter}\d])(\p{Uppercase_Letter})/gu, replacement);
|
|
353
|
+
// Lowercase all single uppercase characters. As we
|
|
354
|
+
// want to preserve uppercase sequences, we cannot
|
|
355
|
+
// simply lowercase the separated string at the end.
|
|
356
|
+
// `data_For_USACounties` → `data_for_USACounties`
|
|
357
|
+
decamelized = decamelized.replace(/((?<![\p{Uppercase_Letter}\d])[\p{Uppercase_Letter}\d](?![\p{Uppercase_Letter}\d]))/gu, $0 => $0.toLowerCase());
|
|
358
|
+
// Remaining uppercase sequences will be separated from lowercase sequences.
|
|
359
|
+
// `data_For_USACounties` → `data_for_USA_counties`
|
|
360
|
+
return decamelized.replace(/(\p{Uppercase_Letter}+)(\p{Uppercase_Letter}\p{Lowercase_Letter}+)/gu, (_, $1, $2) => $1 + separator + $2.toLowerCase());
|
|
291
361
|
};
|
|
292
|
-
|
|
293
362
|
/**
|
|
294
363
|
* Used to remove color codes
|
|
295
364
|
* @param {*} input
|
|
296
365
|
* @returns
|
|
297
366
|
*/
|
|
298
367
|
function removeColorCodes(input) {
|
|
299
|
-
|
|
300
|
-
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
368
|
+
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
301
369
|
}
|
|
302
|
-
|
|
303
370
|
const testRunnerHelper = {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
371
|
+
// for Jest
|
|
372
|
+
getNameOfCurrentlyRunningTest: () => {
|
|
373
|
+
if (global.testomatioTestTitle)
|
|
374
|
+
return global.testomatioTestTitle;
|
|
375
|
+
if (!process.env.JEST_WORKER_ID)
|
|
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
|
+
},
|
|
318
386
|
};
|
|
319
|
-
|
|
387
|
+
exports.testRunnerHelper = testRunnerHelper;
|
|
320
388
|
function storeRunId(runId) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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);
|
|
324
393
|
}
|
|
325
|
-
|
|
326
394
|
function readLatestRunId() {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
395
|
+
try {
|
|
396
|
+
const filePath = path_1.default.join(os_1.default.tmpdir(), `testomatio.latest.run`);
|
|
397
|
+
const stats = fs_1.default.statSync(filePath);
|
|
398
|
+
const diff = +new Date() - +stats.mtime;
|
|
399
|
+
const diffHours = diff / 1000 / 60 / 60;
|
|
400
|
+
if (diffHours > 1)
|
|
401
|
+
return;
|
|
402
|
+
return fs_1.default.readFileSync(filePath)?.toString()?.trim();
|
|
403
|
+
}
|
|
404
|
+
catch (e) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
338
407
|
}
|
|
339
|
-
|
|
340
408
|
function formatStep(step, shift = 0) {
|
|
341
|
-
|
|
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`)}`);
|
|
416
|
+
}
|
|
417
|
+
for (const child of step.steps || []) {
|
|
418
|
+
lines.push(...formatStep(child, shift + 2));
|
|
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
|
+
}
|
|
342
427
|
|
|
343
|
-
|
|
428
|
+
module.exports.getPackageVersion = getPackageVersion;
|
|
344
429
|
|
|
345
|
-
|
|
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
|
-
}
|
|
430
|
+
module.exports.formatStep = formatStep;
|
|
350
431
|
|
|
351
|
-
|
|
352
|
-
lines.push(...formatStep(child, shift + 2));
|
|
353
|
-
}
|
|
432
|
+
module.exports.readLatestRunId = readLatestRunId;
|
|
354
433
|
|
|
355
|
-
|
|
356
|
-
}
|
|
434
|
+
module.exports.removeColorCodes = removeColorCodes;
|
|
357
435
|
|
|
358
|
-
module.exports =
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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;
|
|
453
|
+
|
|
454
|
+
module.exports.fetchIdFromOutput = fetchIdFromOutput;
|
|
455
|
+
|
|
456
|
+
module.exports.fetchSourceCode = fetchSourceCode;
|
|
457
|
+
|
|
458
|
+
module.exports.isSameTest = isSameTest;
|
|
459
|
+
|
|
460
|
+
module.exports.getCurrentDateTime = getCurrentDateTime;
|
|
461
|
+
|
|
462
|
+
module.exports.specificTestInfo = specificTestInfo;
|
|
463
|
+
|
|
464
|
+
module.exports.fileSystem = fileSystem;
|
|
465
|
+
|
|
466
|
+
module.exports.foundedTestLog = foundedTestLog;
|
|
467
|
+
|
|
468
|
+
module.exports.humanize = humanize;
|
|
469
|
+
|
|
470
|
+
module.exports.testRunnerHelper = testRunnerHelper;
|