@testomatio/reporter 2.3.9 → 2.4.0
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 -1
- package/lib/bin/cli.js +26 -7
- package/lib/client.js +18 -10
- package/lib/data-storage.d.ts +1 -1
- package/lib/helpers.d.ts +1 -0
- package/lib/helpers.js +4 -0
- package/lib/pipe/coverage.d.ts +82 -0
- package/lib/pipe/coverage.js +373 -0
- package/lib/pipe/index.js +2 -0
- package/lib/pipe/testomatio.d.ts +1 -1
- package/lib/pipe/testomatio.js +25 -4
- package/lib/reporter-functions.js +13 -9
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/links.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/lib/utils/pipe_utils.d.ts +15 -0
- package/lib/utils/pipe_utils.js +44 -2
- package/lib/utils/utils.d.ts +6 -0
- package/lib/utils/utils.js +71 -1
- package/package.json +5 -4
- package/src/bin/cli.js +35 -9
- package/src/client.js +22 -14
- package/src/helpers.js +1 -0
- package/src/pipe/coverage.js +440 -0
- package/src/pipe/index.js +2 -0
- package/src/pipe/testomatio.js +34 -5
- package/src/reporter-functions.js +13 -9
- package/src/utils/pipe_utils.js +52 -3
- package/src/utils/utils.js +75 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const helpers_js_1 = require("./helpers.js");
|
|
3
4
|
const index_js_1 = require("./services/index.js");
|
|
4
5
|
/**
|
|
5
6
|
* Stores path to file as artifact and uploads it to the S3 storage
|
|
@@ -8,9 +9,7 @@ const index_js_1 = require("./services/index.js");
|
|
|
8
9
|
* @returns {void}
|
|
9
10
|
*/
|
|
10
11
|
function saveArtifact(data, context = null) {
|
|
11
|
-
|
|
12
|
-
throw new Error(`This function is not available in Playwright framework.
|
|
13
|
-
/Playwright supports artifacts out of the box`);
|
|
12
|
+
showPlaywrightWarning('artifact', 'Playwright supports artifacts out of the box.');
|
|
14
13
|
if (!data)
|
|
15
14
|
return;
|
|
16
15
|
index_js_1.services.artifacts.put(data, context);
|
|
@@ -21,8 +20,6 @@ function saveArtifact(data, context = null) {
|
|
|
21
20
|
* @returns {void}
|
|
22
21
|
*/
|
|
23
22
|
function logMessage(...args) {
|
|
24
|
-
if (process.env.IS_PLAYWRIGHT)
|
|
25
|
-
throw new Error('This function is not available in Playwright framework');
|
|
26
23
|
index_js_1.services.logger._templateLiteralLog(...args);
|
|
27
24
|
}
|
|
28
25
|
/**
|
|
@@ -31,9 +28,11 @@ function logMessage(...args) {
|
|
|
31
28
|
* @returns {void}
|
|
32
29
|
*/
|
|
33
30
|
function addStep(message) {
|
|
34
|
-
if (process.env.IS_PLAYWRIGHT)
|
|
35
|
-
throw new Error('This function is not available in Playwright framework. Use playwright steps');
|
|
36
31
|
index_js_1.services.logger.step(message);
|
|
32
|
+
// this is done because Playwright reporter intercepts console logs and then we gather them and show on Testomat
|
|
33
|
+
// if not console.log, the step message will be lost from reporter
|
|
34
|
+
if (helpers_js_1.isPlaywright)
|
|
35
|
+
console.log(`Step: ${message}`);
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
39
38
|
* Add key-value pair(s) to the test report
|
|
@@ -42,8 +41,7 @@ function addStep(message) {
|
|
|
42
41
|
* @returns {void}
|
|
43
42
|
*/
|
|
44
43
|
function setKeyValue(keyValue, value = null) {
|
|
45
|
-
|
|
46
|
-
throw new Error('This function is not available in Playwright framework. Use test tag instead.');
|
|
44
|
+
showPlaywrightWarning('meta', 'Use test annotations instead.');
|
|
47
45
|
if (typeof keyValue === 'string') {
|
|
48
46
|
keyValue = { [keyValue]: value };
|
|
49
47
|
}
|
|
@@ -56,6 +54,7 @@ function setKeyValue(keyValue, value = null) {
|
|
|
56
54
|
* @returns {void}
|
|
57
55
|
*/
|
|
58
56
|
function setLabel(key, value = null) {
|
|
57
|
+
showPlaywrightWarning('label', 'Use test tag instead.');
|
|
59
58
|
if (Array.isArray(value)) {
|
|
60
59
|
return value.forEach(label => setLabel(key, label));
|
|
61
60
|
}
|
|
@@ -80,6 +79,11 @@ function linkJira(...jiraIds) {
|
|
|
80
79
|
const links = jiraIds.map(jiraId => ({ jira: jiraId }));
|
|
81
80
|
index_js_1.services.links.put(links);
|
|
82
81
|
}
|
|
82
|
+
function showPlaywrightWarning(functionName, recommendation) {
|
|
83
|
+
if (helpers_js_1.isPlaywright) {
|
|
84
|
+
console.warn(`[TESTOMATIO] '${functionName}' function is not supported for Playwright. ${recommendation}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
83
87
|
module.exports = {
|
|
84
88
|
artifact: saveArtifact,
|
|
85
89
|
log: logMessage,
|
package/lib/reporter.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const artifact: (data: string | {
|
|
|
12
12
|
}, context?: any) => void;
|
|
13
13
|
export const log: (...args: any[]) => void;
|
|
14
14
|
export const logger: {
|
|
15
|
-
"__#
|
|
15
|
+
"__#14@#originalUserLogger": {
|
|
16
16
|
assert(condition?: boolean, ...data: any[]): void;
|
|
17
17
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
18
18
|
clear(): void;
|
|
@@ -57,13 +57,13 @@ export const logger: {
|
|
|
57
57
|
profile(label?: string): void;
|
|
58
58
|
profileEnd(label?: string): void;
|
|
59
59
|
};
|
|
60
|
-
"__#
|
|
60
|
+
"__#14@#userLoggerWithOverridenMethods": any;
|
|
61
61
|
logLevel: string;
|
|
62
62
|
step(strings: any, ...values: any[]): void;
|
|
63
63
|
getLogs(context: string): string[];
|
|
64
|
-
"__#
|
|
64
|
+
"__#14@#stringifyLogs"(...args: any[]): string;
|
|
65
65
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
66
|
-
"__#
|
|
66
|
+
"__#14@#logWrapper"(argsArray: any, level: any): void;
|
|
67
67
|
assert(...args: any[]): void;
|
|
68
68
|
debug(...args: any[]): void;
|
|
69
69
|
error(...args: any[]): void;
|
|
@@ -88,7 +88,7 @@ export const linkTest: (...testIds: string[]) => void;
|
|
|
88
88
|
export const linkJira: (...jiraIds: string[]) => void;
|
|
89
89
|
declare namespace _default {
|
|
90
90
|
export let testomatioLogger: {
|
|
91
|
-
"__#
|
|
91
|
+
"__#14@#originalUserLogger": {
|
|
92
92
|
assert(condition?: boolean, ...data: any[]): void;
|
|
93
93
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
94
94
|
clear(): void;
|
|
@@ -133,13 +133,13 @@ declare namespace _default {
|
|
|
133
133
|
profile(label?: string): void;
|
|
134
134
|
profileEnd(label?: string): void;
|
|
135
135
|
};
|
|
136
|
-
"__#
|
|
136
|
+
"__#14@#userLoggerWithOverridenMethods": any;
|
|
137
137
|
logLevel: string;
|
|
138
138
|
step(strings: any, ...values: any[]): void;
|
|
139
139
|
getLogs(context: string): string[];
|
|
140
|
-
"__#
|
|
140
|
+
"__#14@#stringifyLogs"(...args: any[]): string;
|
|
141
141
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
142
|
-
"__#
|
|
142
|
+
"__#14@#logWrapper"(argsArray: any, level: any): void;
|
|
143
143
|
assert(...args: any[]): void;
|
|
144
144
|
debug(...args: any[]): void;
|
|
145
145
|
error(...args: any[]): void;
|
|
@@ -162,7 +162,7 @@ declare namespace _default {
|
|
|
162
162
|
}, context?: any) => void;
|
|
163
163
|
export let log: (...args: any[]) => void;
|
|
164
164
|
export let logger: {
|
|
165
|
-
"__#
|
|
165
|
+
"__#14@#originalUserLogger": {
|
|
166
166
|
assert(condition?: boolean, ...data: any[]): void;
|
|
167
167
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
168
168
|
clear(): void;
|
|
@@ -207,13 +207,13 @@ declare namespace _default {
|
|
|
207
207
|
profile(label?: string): void;
|
|
208
208
|
profileEnd(label?: string): void;
|
|
209
209
|
};
|
|
210
|
-
"__#
|
|
210
|
+
"__#14@#userLoggerWithOverridenMethods": any;
|
|
211
211
|
logLevel: string;
|
|
212
212
|
step(strings: any, ...values: any[]): void;
|
|
213
213
|
getLogs(context: string): string[];
|
|
214
|
-
"__#
|
|
214
|
+
"__#14@#stringifyLogs"(...args: any[]): string;
|
|
215
215
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
216
|
-
"__#
|
|
216
|
+
"__#14@#logWrapper"(argsArray: any, level: any): void;
|
|
217
217
|
assert(...args: any[]): void;
|
|
218
218
|
debug(...args: any[]): void;
|
|
219
219
|
error(...args: any[]): void;
|
package/lib/services/links.d.ts
CHANGED
package/lib/services/logger.d.ts
CHANGED
|
@@ -39,3 +39,18 @@ export function statusEmoji(status: string): string;
|
|
|
39
39
|
* @returns {string} - A formatted full name string for the test object.
|
|
40
40
|
*/
|
|
41
41
|
export function fullName(t: object): string;
|
|
42
|
+
/**
|
|
43
|
+
* Parses a comma-separated list of key-value pairs into an options object.
|
|
44
|
+
*
|
|
45
|
+
* The input string should be formatted as `"key1=value1,key2=value2,..."`.
|
|
46
|
+
* Whitespace around keys and values is trimmed. If the input is empty or undefined,
|
|
47
|
+
* an empty object is returned.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} [optionsStr] - A comma-separated string of key=value pairs.
|
|
50
|
+
* @returns {Object} An object mapping option keys to their string values.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* parsePipeOptions('foo=bar,baz=qux');
|
|
54
|
+
* => Returns: { foo: 'bar', baz: 'qux' }
|
|
55
|
+
*/
|
|
56
|
+
export function parsePipeOptions(optionsStr?: string): any;
|
package/lib/utils/pipe_utils.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.generateFilterRequestParams = generateFilterRequestParams;
|
|
|
6
6
|
exports.setS3Credentials = setS3Credentials;
|
|
7
7
|
exports.statusEmoji = statusEmoji;
|
|
8
8
|
exports.fullName = fullName;
|
|
9
|
+
exports.parsePipeOptions = parsePipeOptions;
|
|
9
10
|
const constants_js_1 = require("../constants.js");
|
|
10
11
|
/**
|
|
11
12
|
* Set S3 credentials from the provided artifacts object.
|
|
@@ -27,6 +28,8 @@ function setS3Credentials(artifacts) {
|
|
|
27
28
|
process.env.S3_SESSION_TOKEN = artifacts.SESSION_TOKEN;
|
|
28
29
|
if (artifacts.presign)
|
|
29
30
|
process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
|
|
31
|
+
if (artifacts.stack_artifacts)
|
|
32
|
+
process.env.TESTOMATIO_STACK_ARTIFACTS = '1';
|
|
30
33
|
// endpoint is not received from the server; and shuld be empty if IAM used (credentails obtained from the testomat)
|
|
31
34
|
process.env.S3_ENDPOINT = artifacts.ENDPOINT || '';
|
|
32
35
|
}
|
|
@@ -36,6 +39,11 @@ function setS3Credentials(artifacts) {
|
|
|
36
39
|
* @returns {Object|null} - An object containing the generated request parameters, or null if the type is invalid.
|
|
37
40
|
*/
|
|
38
41
|
function generateFilterRequestParams(params) {
|
|
42
|
+
// Defensive check: ensure params is an object
|
|
43
|
+
if (!params || typeof params !== 'object') {
|
|
44
|
+
console.error(constants_js_1.APP_PREFIX, `Invalid parameters provided. Expected an object, got: ${typeof params}`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
39
47
|
const { type, id, apiKey } = params;
|
|
40
48
|
if (!type) {
|
|
41
49
|
return;
|
|
@@ -60,8 +68,11 @@ function generateFilterRequestParams(params) {
|
|
|
60
68
|
* The object has properties "type" and "id".
|
|
61
69
|
*/
|
|
62
70
|
function parseFilterParams(opts) {
|
|
63
|
-
const [type,
|
|
71
|
+
const [type, ...idParts] = opts.split('=');
|
|
72
|
+
const id = idParts.join('=');
|
|
64
73
|
const validType = updateFilterType(type);
|
|
74
|
+
if (!validType)
|
|
75
|
+
return undefined;
|
|
65
76
|
return {
|
|
66
77
|
type: validType,
|
|
67
78
|
id,
|
|
@@ -74,6 +85,8 @@ function parseFilterParams(opts) {
|
|
|
74
85
|
* Returns undefined if the type is not valid.
|
|
75
86
|
*/
|
|
76
87
|
function updateFilterType(type) {
|
|
88
|
+
if (!type || typeof type !== 'string')
|
|
89
|
+
return;
|
|
77
90
|
let typeLowerCase = type.toLowerCase();
|
|
78
91
|
const filterTypes = ['tag-name', 'plan', 'label', 'jira-ticket'];
|
|
79
92
|
if (typeLowerCase === 'plan-id') {
|
|
@@ -87,7 +100,7 @@ function updateFilterType(type) {
|
|
|
87
100
|
// "ims-issue", //TODO: WIP
|
|
88
101
|
];
|
|
89
102
|
if (!filterTypes.includes(typeLowerCase)) {
|
|
90
|
-
console.log(constants_js_1.APP_PREFIX, `❗❗❗ Invalid "
|
|
103
|
+
console.log(constants_js_1.APP_PREFIX, `❗❗❗ Invalid filter: "${type}" start settings! Available option list: ${filterTypes}`);
|
|
91
104
|
return;
|
|
92
105
|
}
|
|
93
106
|
const index = filterTypes.indexOf(typeLowerCase);
|
|
@@ -121,6 +134,33 @@ function fullName(t) {
|
|
|
121
134
|
line += ` \`[${Object.values(t.example)}]\``;
|
|
122
135
|
return line;
|
|
123
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Parses a comma-separated list of key-value pairs into an options object.
|
|
139
|
+
*
|
|
140
|
+
* The input string should be formatted as `"key1=value1,key2=value2,..."`.
|
|
141
|
+
* Whitespace around keys and values is trimmed. If the input is empty or undefined,
|
|
142
|
+
* an empty object is returned.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} [optionsStr] - A comma-separated string of key=value pairs.
|
|
145
|
+
* @returns {Object} An object mapping option keys to their string values.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* parsePipeOptions('foo=bar,baz=qux');
|
|
149
|
+
* => Returns: { foo: 'bar', baz: 'qux' }
|
|
150
|
+
*/
|
|
151
|
+
function parsePipeOptions(optionsStr) {
|
|
152
|
+
const options = {};
|
|
153
|
+
if (!optionsStr)
|
|
154
|
+
return options;
|
|
155
|
+
const pairs = optionsStr.split(',');
|
|
156
|
+
for (const pair of pairs) {
|
|
157
|
+
const [key, value] = pair.split('=');
|
|
158
|
+
if (key && value) {
|
|
159
|
+
options[key.trim()] = value.trim();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return options;
|
|
163
|
+
}
|
|
124
164
|
|
|
125
165
|
module.exports.updateFilterType = updateFilterType;
|
|
126
166
|
|
|
@@ -133,3 +173,5 @@ module.exports.setS3Credentials = setS3Credentials;
|
|
|
133
173
|
module.exports.statusEmoji = statusEmoji;
|
|
134
174
|
|
|
135
175
|
module.exports.fullName = fullName;
|
|
176
|
+
|
|
177
|
+
module.exports.parsePipeOptions = parsePipeOptions;
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export namespace fileSystem {
|
|
|
17
17
|
export function foundedTestLog(app: any, tests: any): void;
|
|
18
18
|
export function formatStep(step: any, shift?: number): any;
|
|
19
19
|
export function getCurrentDateTime(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Gets current git commit SHA
|
|
22
|
+
* @returns {String|null} git commit SHA or null if not available
|
|
23
|
+
*/
|
|
24
|
+
export function getGitCommitSha(): string | null;
|
|
20
25
|
/**
|
|
21
26
|
* @param {String} testTitle - Test title
|
|
22
27
|
*
|
|
@@ -59,3 +64,4 @@ export function transformEnvVarToBoolean(value: any): boolean;
|
|
|
59
64
|
* @returns {String|null} validated suite ID or null if invalid
|
|
60
65
|
*/
|
|
61
66
|
export function validateSuiteId(suiteId: string): string | null;
|
|
67
|
+
export function applyFilter(command: any, tests: any): any;
|
package/lib/utils/utils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
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;
|
|
39
|
+
exports.validateSuiteId = exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getGitCommitSha = 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
40
|
exports.getPackageVersion = getPackageVersion;
|
|
41
41
|
exports.truncate = truncate;
|
|
42
42
|
exports.cleanLatestRunId = cleanLatestRunId;
|
|
@@ -45,6 +45,7 @@ exports.readLatestRunId = readLatestRunId;
|
|
|
45
45
|
exports.removeColorCodes = removeColorCodes;
|
|
46
46
|
exports.storeRunId = storeRunId;
|
|
47
47
|
exports.transformEnvVarToBoolean = transformEnvVarToBoolean;
|
|
48
|
+
exports.applyFilter = applyFilter;
|
|
48
49
|
const url_1 = require("url");
|
|
49
50
|
const path_1 = __importStar(require("path"));
|
|
50
51
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
@@ -53,6 +54,7 @@ const is_valid_path_1 = __importDefault(require("is-valid-path"));
|
|
|
53
54
|
const debug_1 = __importDefault(require("debug"));
|
|
54
55
|
const os_1 = __importDefault(require("os"));
|
|
55
56
|
const url_2 = require("url");
|
|
57
|
+
const child_process_1 = require("child_process");
|
|
56
58
|
const debug = (0, debug_1.default)('@testomatio/reporter:util');
|
|
57
59
|
// Use __dirname directly since we're compiling to CommonJS
|
|
58
60
|
// prettier-ignore
|
|
@@ -98,6 +100,22 @@ const validateSuiteId = suiteId => {
|
|
|
98
100
|
return match ? match[0] : null;
|
|
99
101
|
};
|
|
100
102
|
exports.validateSuiteId = validateSuiteId;
|
|
103
|
+
/**
|
|
104
|
+
* Gets current git commit SHA
|
|
105
|
+
* @returns {String|null} git commit SHA or null if not available
|
|
106
|
+
*/
|
|
107
|
+
const getGitCommitSha = () => {
|
|
108
|
+
try {
|
|
109
|
+
const sha = (0, child_process_1.execSync)('git rev-parse --short HEAD', {
|
|
110
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
111
|
+
}).toString().trim();
|
|
112
|
+
return sha || null;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
exports.getGitCommitSha = getGitCommitSha;
|
|
101
119
|
const ansiRegExp = () => {
|
|
102
120
|
const pattern = [
|
|
103
121
|
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
@@ -641,6 +659,54 @@ function truncate(s, size = 255) {
|
|
|
641
659
|
}
|
|
642
660
|
return `${str.substring(0, size)}...`;
|
|
643
661
|
}
|
|
662
|
+
function applyFilter(command, tests) {
|
|
663
|
+
if (!tests || !tests.length)
|
|
664
|
+
return command;
|
|
665
|
+
const lower = (command || '').toLowerCase();
|
|
666
|
+
const regexPattern = `(${tests.join('|')})`;
|
|
667
|
+
if (lower.includes('jest')) {
|
|
668
|
+
return `${command} --testNamePattern ${regexPattern}`;
|
|
669
|
+
}
|
|
670
|
+
if (lower.includes('cypress')) {
|
|
671
|
+
const grepValue = tests.join(',');
|
|
672
|
+
const baseEnv = {
|
|
673
|
+
grep: grepValue,
|
|
674
|
+
grepFilterSpecs: true,
|
|
675
|
+
grepOmitFiltered: true,
|
|
676
|
+
};
|
|
677
|
+
if (command.includes('--env')) {
|
|
678
|
+
return command.replace(/--env\s+(['"]?)([^\s'"]+)\1/, (match, quote, envVal) => {
|
|
679
|
+
const existingEnv = {};
|
|
680
|
+
if (envVal.startsWith('{') && envVal.endsWith('}')) {
|
|
681
|
+
try {
|
|
682
|
+
Object.assign(existingEnv, JSON.parse(envVal));
|
|
683
|
+
}
|
|
684
|
+
catch (e) {
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (!Object.keys(existingEnv).length) {
|
|
688
|
+
envVal.split(',').forEach((pair) => {
|
|
689
|
+
const [k, v] = pair.split('=');
|
|
690
|
+
if (!k)
|
|
691
|
+
return;
|
|
692
|
+
if (v === 'true')
|
|
693
|
+
existingEnv[k] = true;
|
|
694
|
+
else if (v === 'false')
|
|
695
|
+
existingEnv[k] = false;
|
|
696
|
+
else
|
|
697
|
+
existingEnv[k] = v;
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const merged = { ...existingEnv, ...baseEnv };
|
|
701
|
+
const json = JSON.stringify(merged);
|
|
702
|
+
return `--env ${json}`;
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
const json = JSON.stringify(baseEnv);
|
|
706
|
+
return `${command} --env ${json}`;
|
|
707
|
+
}
|
|
708
|
+
return `${command} --grep ${regexPattern}`;
|
|
709
|
+
}
|
|
644
710
|
|
|
645
711
|
module.exports.getPackageVersion = getPackageVersion;
|
|
646
712
|
|
|
@@ -658,12 +724,16 @@ module.exports.storeRunId = storeRunId;
|
|
|
658
724
|
|
|
659
725
|
module.exports.transformEnvVarToBoolean = transformEnvVarToBoolean;
|
|
660
726
|
|
|
727
|
+
module.exports.applyFilter = applyFilter;
|
|
728
|
+
|
|
661
729
|
module.exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
|
|
662
730
|
|
|
663
731
|
module.exports.parseSuite = parseSuite;
|
|
664
732
|
|
|
665
733
|
module.exports.validateSuiteId = validateSuiteId;
|
|
666
734
|
|
|
735
|
+
module.exports.getGitCommitSha = getGitCommitSha;
|
|
736
|
+
|
|
667
737
|
module.exports.ansiRegExp = ansiRegExp;
|
|
668
738
|
|
|
669
739
|
module.exports.isValidUrl = isValidUrl;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18"
|
|
@@ -18,21 +18,22 @@
|
|
|
18
18
|
"@cucumber/cucumber": "^10.9.0",
|
|
19
19
|
"@octokit/rest": "^21.1.1",
|
|
20
20
|
"aws-sdk": "^2.1072.0",
|
|
21
|
-
"gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
|
|
22
21
|
"callsite-record": "^4.1.4",
|
|
23
22
|
"commander": "^12",
|
|
24
23
|
"cross-spawn": "^7.0.3",
|
|
25
24
|
"csv-writer": "^1.6.0",
|
|
26
|
-
"debug": "
|
|
25
|
+
"debug": "4.3.4",
|
|
27
26
|
"dotenv": "^16.0.1",
|
|
28
27
|
"fast-xml-parser": "^4.4.1",
|
|
29
28
|
"file-url": "3.0.0",
|
|
30
29
|
"filesize": "^10.1.6",
|
|
30
|
+
"gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
|
|
31
31
|
"glob": "^10.3",
|
|
32
32
|
"handlebars": "^4.7.8",
|
|
33
33
|
"has-flag": "^5.0.1",
|
|
34
34
|
"humanize-duration": "^3.27.3",
|
|
35
35
|
"is-valid-path": "^0.1.1",
|
|
36
|
+
"js-yaml": "^4.1.1",
|
|
36
37
|
"json-cycle": "^1.3.0",
|
|
37
38
|
"lodash.memoize": "^4.1.2",
|
|
38
39
|
"lodash.merge": "^4.6.2",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"picocolors": "^1.0.1",
|
|
41
42
|
"pretty-ms": "^7.0.1",
|
|
42
43
|
"promise-retry": "^2.0.1",
|
|
43
|
-
"strip-ansi": "
|
|
44
|
+
"strip-ansi": "7.1.0",
|
|
44
45
|
"uuid": "^9.0.0"
|
|
45
46
|
},
|
|
46
47
|
"files": [
|
package/src/bin/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import createDebugMessages from 'debug';
|
|
|
7
7
|
import TestomatClient from '../client.js';
|
|
8
8
|
import XmlReader from '../xmlReader.js';
|
|
9
9
|
import { APP_PREFIX, STATUS } from '../constants.js';
|
|
10
|
-
import { cleanLatestRunId, getPackageVersion } from '../utils/utils.js';
|
|
10
|
+
import { cleanLatestRunId, getPackageVersion, applyFilter } from '../utils/utils.js';
|
|
11
11
|
import { config } from '../config.js';
|
|
12
12
|
import { readLatestRunId } from '../utils/utils.js';
|
|
13
13
|
import pc from 'picocolors';
|
|
@@ -81,6 +81,7 @@ program
|
|
|
81
81
|
.description('Run tests with the specified command')
|
|
82
82
|
.argument('<command>', 'Test runner command')
|
|
83
83
|
.option('--filter <filter>', 'Additional execution filter')
|
|
84
|
+
.option('--filter-list <filter>', 'Get a list of all tests by filter before running')
|
|
84
85
|
.option('--kind <type>', 'Specify run type: automated, manual, or mixed')
|
|
85
86
|
.action(async (command, opts) => {
|
|
86
87
|
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
|
|
@@ -93,17 +94,39 @@ program
|
|
|
93
94
|
|
|
94
95
|
const client = new TestomatClient({ apiKey, title });
|
|
95
96
|
|
|
96
|
-
if (opts.filter) {
|
|
97
|
-
|
|
97
|
+
if (opts.filter || opts.filterList) {
|
|
98
|
+
// Example of use: npx @testomatio/reporter run "npx jest" --filter "testomatio:tag-name=frontend"
|
|
99
|
+
// Example of use: npx @testomatio/reporter run "npx jest" --filter "coverage:file=coverage.yml"
|
|
100
|
+
// Example of use: npx @testomatio/reporter run "npx jest" --filter-list "coverage:file=coverage.yml"
|
|
101
|
+
const [pipe, ...optsArray] = opts?.filter ? opts?.filter.split(':') : opts?.filterList.split(':');
|
|
98
102
|
const pipeOptions = optsArray.join(':');
|
|
99
103
|
|
|
104
|
+
const prepareRunParams = { pipe, pipeOptions };
|
|
105
|
+
|
|
100
106
|
try {
|
|
101
|
-
const tests = await client.prepareRun(
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
const tests = await client.prepareRun(prepareRunParams);
|
|
108
|
+
|
|
109
|
+
if (!tests || tests.length === 0) {
|
|
110
|
+
console.log(APP_PREFIX, pc.yellow('No tests found.'));
|
|
111
|
+
return;
|
|
104
112
|
}
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
|
|
114
|
+
const pattern = `(${tests.join('|')})`;
|
|
115
|
+
const filteredCommand = applyFilter(command, tests);
|
|
116
|
+
|
|
117
|
+
debug(`Execution pattern: "${pattern}"`);
|
|
118
|
+
|
|
119
|
+
if(opts.filterList) {
|
|
120
|
+
console.log(APP_PREFIX, pc.blue(`Matched test/suite IDs: ${tests.join(', ')}`));
|
|
121
|
+
console.log(APP_PREFIX, pc.green(`Full Running Command: ${filteredCommand}`));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
command = filteredCommand;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.log(APP_PREFIX, err.message || err);
|
|
129
|
+
return;
|
|
107
130
|
}
|
|
108
131
|
}
|
|
109
132
|
|
|
@@ -113,7 +136,7 @@ program
|
|
|
113
136
|
const testCmds = command.split(' ');
|
|
114
137
|
const cmd = spawn(testCmds[0], testCmds.slice(1), {
|
|
115
138
|
stdio: 'inherit',
|
|
116
|
-
env: { ...process.env, TESTOMATIO_PROCEED: 'true', runId: client.runId },
|
|
139
|
+
env: { ...process.env, TESTOMATIO_PROCEED: 'true', runId: client.runId, TESTOMATIO_RUN: client.runId },
|
|
117
140
|
});
|
|
118
141
|
|
|
119
142
|
cmd.on('close', async code => {
|
|
@@ -128,6 +151,9 @@ program
|
|
|
128
151
|
};
|
|
129
152
|
|
|
130
153
|
const createRunParams = {};
|
|
154
|
+
if (title) {
|
|
155
|
+
createRunParams.title = title;
|
|
156
|
+
}
|
|
131
157
|
if (opts.kind) {
|
|
132
158
|
createRunParams.kind = opts.kind;
|
|
133
159
|
}
|
package/src/client.js
CHANGED
|
@@ -65,35 +65,43 @@ class Client {
|
|
|
65
65
|
* array containing the prepared execution list,
|
|
66
66
|
* or resolves to undefined if no valid results are found or if all pipes are disabled.
|
|
67
67
|
*/
|
|
68
|
+
|
|
68
69
|
async prepareRun(params) {
|
|
69
|
-
this.pipes = await pipesFactory(params || this.paramsForPipesFactory || {}, this.pipeStore);
|
|
70
70
|
const { pipe, pipeOptions } = params;
|
|
71
|
+
|
|
72
|
+
// ❗ Validation: pipe is required
|
|
73
|
+
if (!pipe || !pipeOptions) {
|
|
74
|
+
console.warn(`❗ No valid pipe found in filter cmd. Expected format: <pipe>:<options>
|
|
75
|
+
Examples:
|
|
76
|
+
--filter "testomatio:tag-name=frontend"
|
|
77
|
+
--filter "coverage:file=coverage.yml"
|
|
78
|
+
--filter-list "coverage:file=coverage.yml"
|
|
79
|
+
Received: "${params}"`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.pipes = await pipesFactory(params || this.paramsForPipesFactory || {}, this.pipeStore);
|
|
84
|
+
|
|
71
85
|
// all pipes disabled, skipping
|
|
72
86
|
if (!this.pipes.some(p => p.isEnabled)) {
|
|
73
87
|
return Promise.resolve();
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
try {
|
|
77
|
-
const
|
|
91
|
+
const p = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
|
|
92
|
+
// const p = this.pipes.find(p => p.id === `${pipe.toLowerCase()}`); TODO: as future updates
|
|
78
93
|
|
|
79
|
-
if (!
|
|
80
|
-
// TODO:for the future for the another pipes
|
|
94
|
+
if (!p?.isEnabled) {
|
|
81
95
|
console.warn(
|
|
82
96
|
APP_PREFIX,
|
|
83
|
-
|
|
97
|
+
"🚫 No active pipes were found in the system. Execution aborted!"
|
|
84
98
|
);
|
|
85
99
|
return;
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const result = results.filter(p => p.pipe.includes('Testomatio'))[0]?.result;
|
|
93
|
-
|
|
94
|
-
if (!result || result.length === 0) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
102
|
+
// Run only the selected pipe
|
|
103
|
+
const rawResult = await p.prepareRun(pipeOptions);
|
|
104
|
+
const result = Array.isArray(rawResult) ? rawResult : [];
|
|
97
105
|
|
|
98
106
|
debug('Execution tests list', result);
|
|
99
107
|
|
package/src/helpers.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const isPlaywright = Boolean(process.env.PLAYWRIGHT_TEST || process.env.PLAYWRIGHT);
|