@testomatio/reporter 2.8.0 → 2.8.2
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 +38 -2
- package/lib/bin/cli.js +3 -1
- package/lib/client.js +2 -1
- package/lib/pipe/bitbucket.d.ts +1 -0
- package/lib/pipe/bitbucket.js +4 -0
- package/lib/pipe/csv.d.ts +3 -3
- package/lib/pipe/csv.js +17 -3
- package/lib/pipe/github.d.ts +1 -0
- package/lib/pipe/github.js +4 -0
- package/lib/pipe/gitlab.d.ts +1 -0
- package/lib/pipe/gitlab.js +4 -0
- package/lib/pipe/html.d.ts +3 -2
- package/lib/pipe/html.js +9 -3
- package/lib/pipe/markdown.d.ts +3 -2
- package/lib/pipe/markdown.js +10 -3
- package/lib/pipe/testomatio.d.ts +1 -0
- package/lib/pipe/testomatio.js +13 -2
- package/package.json +1 -1
- package/src/adapter/codecept.js +45 -3
- package/src/bin/cli.js +4 -2
- package/src/client.js +2 -1
- package/src/pipe/bitbucket.js +6 -1
- package/src/pipe/csv.js +20 -3
- package/src/pipe/github.js +5 -1
- package/src/pipe/gitlab.js +6 -1
- package/src/pipe/html.js +10 -3
- package/src/pipe/markdown.js +11 -4
- package/src/pipe/testomatio.js +11 -2
- package/types/types.d.ts +3 -0
package/lib/adapter/codecept.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CodeceptReporter = CodeceptReporter;
|
|
7
7
|
const debug_1 = __importDefault(require("debug"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
8
9
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
10
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
10
11
|
const constants_js_1 = require("../constants.js");
|
|
@@ -47,8 +48,8 @@ function CodeceptReporter(config) {
|
|
|
47
48
|
const reportTestPromises = [];
|
|
48
49
|
let isRunFinalized = false;
|
|
49
50
|
const testTimeMap = {};
|
|
50
|
-
const
|
|
51
|
-
const client = new client_js_1.default(
|
|
51
|
+
const clientConfig = buildCodeceptClientConfig(config);
|
|
52
|
+
const client = new client_js_1.default(clientConfig);
|
|
52
53
|
// Store original output methods for fallback
|
|
53
54
|
const originalOutput = {
|
|
54
55
|
debug: output.debug,
|
|
@@ -492,5 +493,40 @@ function formatHookStep(step) {
|
|
|
492
493
|
return formattedStep;
|
|
493
494
|
}
|
|
494
495
|
module.exports = CodeceptReporter;
|
|
496
|
+
function buildCodeceptClientConfig(config = {}) {
|
|
497
|
+
const outputDir = resolveCodeceptOutputDir(config);
|
|
498
|
+
const reportDir = resolveCodeceptReportDir(config, outputDir);
|
|
499
|
+
return {
|
|
500
|
+
...config,
|
|
501
|
+
apiKey: config.apiKey,
|
|
502
|
+
framework: 'codeceptjs',
|
|
503
|
+
outputDir,
|
|
504
|
+
reportDir,
|
|
505
|
+
html: config.html,
|
|
506
|
+
markdown: config.markdown,
|
|
507
|
+
csv: config.csv,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function resolveCodeceptOutputDir(config = {}) {
|
|
511
|
+
const codeceptStore = /** @type {{ outputDir?: string }} */ (codeceptjs_1.default.store || {});
|
|
512
|
+
const candidates = [
|
|
513
|
+
config.outputDir,
|
|
514
|
+
config.output,
|
|
515
|
+
codeceptStore.outputDir,
|
|
516
|
+
codecept?.config?.get?.()?.output,
|
|
517
|
+
codecept?.config?.output,
|
|
518
|
+
];
|
|
519
|
+
const outputDir = candidates.find(value => typeof value === 'string' && value.trim());
|
|
520
|
+
return outputDir || 'output';
|
|
521
|
+
}
|
|
522
|
+
function resolveCodeceptReportDir(config = {}, outputDir = 'output') {
|
|
523
|
+
if (typeof config.reportDir === 'string' && config.reportDir.trim()) {
|
|
524
|
+
return config.reportDir;
|
|
525
|
+
}
|
|
526
|
+
if (path_1.default.isAbsolute(outputDir)) {
|
|
527
|
+
return path_1.default.join(outputDir, 'report');
|
|
528
|
+
}
|
|
529
|
+
return path_1.default.join(outputDir, 'report');
|
|
530
|
+
}
|
|
495
531
|
|
|
496
532
|
module.exports.CodeceptReporter = CodeceptReporter;
|
package/lib/bin/cli.js
CHANGED
|
@@ -103,9 +103,11 @@ program
|
|
|
103
103
|
const filteredCommand = (0, utils_js_1.applyFilter)(command, tests);
|
|
104
104
|
debug(`Execution pattern: "${pattern}"`);
|
|
105
105
|
if (opts.filterList) {
|
|
106
|
-
log_js_1.log.info(picocolors_1.default.blue(`Matched test/suite IDs: ${tests.join(', ')}`));
|
|
107
106
|
if (command)
|
|
108
107
|
log_js_1.log.info(picocolors_1.default.green(`Full Running Command: ${filteredCommand}`));
|
|
108
|
+
console.log();
|
|
109
|
+
console.log(`Grep string:`);
|
|
110
|
+
console.log(`${tests.join(', ')}`);
|
|
109
111
|
return;
|
|
110
112
|
}
|
|
111
113
|
if (command && command.split) {
|
package/lib/client.js
CHANGED
|
@@ -105,8 +105,9 @@ class Client {
|
|
|
105
105
|
* @returns {Promise<any>} - resolves to Run id which should be used to update / add test
|
|
106
106
|
*/
|
|
107
107
|
async createRun(params = {}) {
|
|
108
|
+
const pipeParams = { ...(this.paramsForPipesFactory || {}), ...(params || {}) };
|
|
108
109
|
if (!this.pipes || !this.pipes.length)
|
|
109
|
-
this.pipes = await (0, index_js_1.pipesFactory)(
|
|
110
|
+
this.pipes = await (0, index_js_1.pipesFactory)(pipeParams, this.pipeStore);
|
|
110
111
|
debug('Creating run...');
|
|
111
112
|
// all pipes disabled, skipping
|
|
112
113
|
if (!this.pipes?.filter(p => p.isEnabled).length)
|
package/lib/pipe/bitbucket.d.ts
CHANGED
package/lib/pipe/bitbucket.js
CHANGED
|
@@ -63,6 +63,7 @@ class BitbucketPipe {
|
|
|
63
63
|
this.tests = [];
|
|
64
64
|
// Bitbucket PAT looks like bbpat-*****
|
|
65
65
|
this.token = params.BITBUCKET_ACCESS_TOKEN || process.env.BITBUCKET_ACCESS_TOKEN || this.ENV.BITBUCKET_ACCESS_TOKEN;
|
|
66
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
66
67
|
this.hiddenCommentData = `Testomat.io report: ${process.env.BITBUCKET_BRANCH || ''}`;
|
|
67
68
|
debug(picocolors_1.default.yellow('Bitbucket Pipe:'), this.token ? 'TOKEN passed' : '*no token*', `Project key: ${this.ENV.BITBUCKET_PROJECT_KEY}, Pull request ID: ${this.ENV.BITBUCKET_PR_ID}`);
|
|
68
69
|
if (!this.token) {
|
|
@@ -168,6 +169,9 @@ class BitbucketPipe {
|
|
|
168
169
|
return text;
|
|
169
170
|
});
|
|
170
171
|
let body = summary;
|
|
172
|
+
if (this.description) {
|
|
173
|
+
body += `\n\n> ${(0, utils_js_1.truncate)(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
174
|
+
}
|
|
171
175
|
if (failures.length) {
|
|
172
176
|
body += `\n🟥 **Failures (${failures.length})**\n\n* ${failures.join('\n* ')}\n`;
|
|
173
177
|
if (failures.length > 10) {
|
package/lib/pipe/csv.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ declare class CsvPipe implements Pipe {
|
|
|
12
12
|
store: any;
|
|
13
13
|
title: any;
|
|
14
14
|
results: any[];
|
|
15
|
-
outputDir:
|
|
15
|
+
outputDir: any;
|
|
16
16
|
defaultReportName: string;
|
|
17
|
-
csvFilename:
|
|
17
|
+
csvFilename: any;
|
|
18
18
|
isEnabled: boolean;
|
|
19
19
|
outputFile: string;
|
|
20
20
|
prepareRun(): Promise<void>;
|
|
@@ -23,7 +23,7 @@ declare class CsvPipe implements Pipe {
|
|
|
23
23
|
/**
|
|
24
24
|
* Create a folder that will contain the exported files
|
|
25
25
|
*/
|
|
26
|
-
checkExportDir():
|
|
26
|
+
checkExportDir(): string;
|
|
27
27
|
/**
|
|
28
28
|
* Save data to the csv file.
|
|
29
29
|
* @param {Object} data - data that will be added to the CSV file.
|
package/lib/pipe/csv.js
CHANGED
|
@@ -23,9 +23,12 @@ class CsvPipe {
|
|
|
23
23
|
this.store = store || {};
|
|
24
24
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
25
25
|
this.results = [];
|
|
26
|
-
this.outputDir = 'export';
|
|
26
|
+
this.outputDir = params.reportDir || 'export';
|
|
27
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
28
|
+
this.outputDir = path_1.default.join(this.outputDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
29
|
+
}
|
|
27
30
|
this.defaultReportName = 'report.csv';
|
|
28
|
-
this.csvFilename =
|
|
31
|
+
this.csvFilename = resolveCsvFilename(params);
|
|
29
32
|
this.isEnabled = false;
|
|
30
33
|
if (this.csvFilename) {
|
|
31
34
|
const filenameParts = this.csvFilename.split('.');
|
|
@@ -51,7 +54,7 @@ class CsvPipe {
|
|
|
51
54
|
*/
|
|
52
55
|
checkExportDir() {
|
|
53
56
|
if (!fs_1.default.existsSync(this.outputDir)) {
|
|
54
|
-
return fs_1.default.mkdirSync(this.outputDir);
|
|
57
|
+
return fs_1.default.mkdirSync(this.outputDir, { recursive: true });
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
/**
|
|
@@ -127,3 +130,14 @@ class CsvPipe {
|
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
module.exports = CsvPipe;
|
|
133
|
+
function resolveCsvFilename(params = {}) {
|
|
134
|
+
if (typeof params.csvFilename === 'string' && params.csvFilename.trim()) {
|
|
135
|
+
return params.csvFilename;
|
|
136
|
+
}
|
|
137
|
+
if (typeof process.env.TESTOMATIO_CSV_FILENAME === 'string' && process.env.TESTOMATIO_CSV_FILENAME.trim()) {
|
|
138
|
+
return process.env.TESTOMATIO_CSV_FILENAME;
|
|
139
|
+
}
|
|
140
|
+
if (params.csv)
|
|
141
|
+
return 'report.csv';
|
|
142
|
+
return null;
|
|
143
|
+
}
|
package/lib/pipe/github.d.ts
CHANGED
package/lib/pipe/github.js
CHANGED
|
@@ -58,6 +58,7 @@ class GitHubPipe {
|
|
|
58
58
|
this.store = store;
|
|
59
59
|
this.tests = [];
|
|
60
60
|
this.token = params.GH_PAT || process.env.GH_PAT;
|
|
61
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
61
62
|
this.ref = process.env.GITHUB_REF;
|
|
62
63
|
this.repo = process.env.GITHUB_REPOSITORY;
|
|
63
64
|
this.jobKey = `${process.env.GITHUB_WORKFLOW || ''} / ${process.env.GITHUB_JOB || ''}`;
|
|
@@ -154,6 +155,9 @@ class GitHubPipe {
|
|
|
154
155
|
return text;
|
|
155
156
|
});
|
|
156
157
|
let body = summary;
|
|
158
|
+
if (this.description) {
|
|
159
|
+
body += `\n\n> ${(0, utils_js_1.truncate)(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
160
|
+
}
|
|
157
161
|
const coverageConfiguration = this.store?.coverageConfiguration;
|
|
158
162
|
const isManualRun = this.store?.runKind === 'manual';
|
|
159
163
|
if (isManualRun && coverageConfiguration) {
|
package/lib/pipe/gitlab.d.ts
CHANGED
package/lib/pipe/gitlab.js
CHANGED
|
@@ -29,6 +29,7 @@ class GitLabPipe {
|
|
|
29
29
|
this.tests = [];
|
|
30
30
|
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
31
31
|
this.token = params.GITLAB_PAT || process.env.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
32
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
32
33
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
|
|
33
34
|
debug(picocolors_1.default.yellow('GitLab Pipe:'), this.token ? 'TOKEN passed' : '*no token*', `Project id: ${this.ENV.CI_PROJECT_ID}, MR id: ${this.ENV.CI_MERGE_REQUEST_IID}`);
|
|
34
35
|
if (!this.ENV.CI_PROJECT_ID || !this.ENV.CI_MERGE_REQUEST_IID) {
|
|
@@ -116,6 +117,9 @@ class GitLabPipe {
|
|
|
116
117
|
return text;
|
|
117
118
|
});
|
|
118
119
|
let body = summary;
|
|
120
|
+
if (this.description) {
|
|
121
|
+
body += `\n\n> ${(0, utils_js_1.truncate)(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
122
|
+
}
|
|
119
123
|
if (failures.length) {
|
|
120
124
|
body += `\n<details>\n<summary><h3>🟥 Failures (${failures.length})</h4></summary>\n\n${failures.join('\n')}\n`;
|
|
121
125
|
if (failures.length > 20) {
|
package/lib/pipe/html.d.ts
CHANGED
|
@@ -3,14 +3,15 @@ declare class HtmlPipe {
|
|
|
3
3
|
constructor(params: any, store?: {});
|
|
4
4
|
store: {};
|
|
5
5
|
title: any;
|
|
6
|
+
description: any;
|
|
6
7
|
apiKey: any;
|
|
7
|
-
isHtml:
|
|
8
|
+
isHtml: any;
|
|
8
9
|
isEnabled: boolean;
|
|
9
10
|
htmlOutputPath: string;
|
|
10
11
|
filenameMsg: string;
|
|
11
12
|
tests: any[];
|
|
12
13
|
configuration: any;
|
|
13
|
-
htmlReportDir:
|
|
14
|
+
htmlReportDir: any;
|
|
14
15
|
htmlReportName: string;
|
|
15
16
|
templateFolderPath: string;
|
|
16
17
|
templateHtmlPath: string;
|
package/lib/pipe/html.js
CHANGED
|
@@ -20,8 +20,9 @@ class HtmlPipe {
|
|
|
20
20
|
constructor(params, store = {}) {
|
|
21
21
|
this.store = store || {};
|
|
22
22
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
23
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
23
24
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
24
|
-
this.isHtml = process.env.TESTOMATIO_HTML_REPORT_SAVE;
|
|
25
|
+
this.isHtml = params.html ?? process.env.TESTOMATIO_HTML_REPORT_SAVE;
|
|
25
26
|
debug('HTML Pipe: ', this.apiKey ? 'API KEY' : '*no api key provided*');
|
|
26
27
|
this.isEnabled = false;
|
|
27
28
|
this.htmlOutputPath = '';
|
|
@@ -30,7 +31,10 @@ class HtmlPipe {
|
|
|
30
31
|
this.configuration = null;
|
|
31
32
|
if (this.isHtml) {
|
|
32
33
|
this.isEnabled = true;
|
|
33
|
-
this.htmlReportDir = process.env.TESTOMATIO_HTML_REPORT_FOLDER || constants_js_1.HTML_REPORT.FOLDER;
|
|
34
|
+
this.htmlReportDir = params.reportDir || process.env.TESTOMATIO_HTML_REPORT_FOLDER || constants_js_1.HTML_REPORT.FOLDER;
|
|
35
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
36
|
+
this.htmlReportDir = path_1.default.join(this.htmlReportDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
37
|
+
}
|
|
34
38
|
if (process.env.TESTOMATIO_HTML_FILENAME && process.env.TESTOMATIO_HTML_FILENAME.endsWith('.html')) {
|
|
35
39
|
this.htmlReportName = process.env.TESTOMATIO_HTML_FILENAME;
|
|
36
40
|
}
|
|
@@ -204,7 +208,9 @@ class HtmlPipe {
|
|
|
204
208
|
runUrl: this.store.runUrl || '',
|
|
205
209
|
executionTime: testExecutionSumTime(aggregatedTests),
|
|
206
210
|
executionDate: getCurrentDateTimeFormatted(),
|
|
207
|
-
description: runParams.description || this.store.coverageDescription || this.store.description
|
|
211
|
+
description: [runParams.description || this.store.coverageDescription || this.store.description, this.description]
|
|
212
|
+
.filter(Boolean)
|
|
213
|
+
.join('\n\n') || '',
|
|
208
214
|
configuration: buildDisplayConfiguration(this.configuration || this.store.configuration || runParams.configuration || null),
|
|
209
215
|
tests: aggregatedTests,
|
|
210
216
|
envVars: collectEnvironmentVariables(),
|
package/lib/pipe/markdown.d.ts
CHANGED
|
@@ -3,14 +3,15 @@ declare class MarkdownPipe {
|
|
|
3
3
|
constructor(params: any, store?: {});
|
|
4
4
|
store: {};
|
|
5
5
|
title: any;
|
|
6
|
+
description: any;
|
|
6
7
|
apiKey: any;
|
|
7
|
-
isMarkdown:
|
|
8
|
+
isMarkdown: any;
|
|
8
9
|
isEnabled: boolean;
|
|
9
10
|
markdownOutputPath: string;
|
|
10
11
|
filenameMsg: string;
|
|
11
12
|
tests: any[];
|
|
12
13
|
configuration: any;
|
|
13
|
-
markdownReportDir:
|
|
14
|
+
markdownReportDir: any;
|
|
14
15
|
markdownReportName: string;
|
|
15
16
|
createRun(params?: {}): Promise<void>;
|
|
16
17
|
prepareRun(): Promise<void>;
|
package/lib/pipe/markdown.js
CHANGED
|
@@ -17,8 +17,9 @@ class MarkdownPipe {
|
|
|
17
17
|
constructor(params, store = {}) {
|
|
18
18
|
this.store = store || {};
|
|
19
19
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
20
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
20
21
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
21
|
-
this.isMarkdown = process.env.TESTOMATIO_MARKDOWN_REPORT_SAVE;
|
|
22
|
+
this.isMarkdown = params.markdown ?? process.env.TESTOMATIO_MARKDOWN_REPORT_SAVE;
|
|
22
23
|
debug('Markdown Pipe: ', this.apiKey ? 'API KEY' : '*no api key provided*');
|
|
23
24
|
this.isEnabled = false;
|
|
24
25
|
this.markdownOutputPath = '';
|
|
@@ -28,7 +29,11 @@ class MarkdownPipe {
|
|
|
28
29
|
if (!this.isMarkdown)
|
|
29
30
|
return;
|
|
30
31
|
this.isEnabled = true;
|
|
31
|
-
this.markdownReportDir =
|
|
32
|
+
this.markdownReportDir =
|
|
33
|
+
params.reportDir || process.env.TESTOMATIO_MARKDOWN_REPORT_FOLDER || constants_js_1.MARKDOWN_REPORT.FOLDER;
|
|
34
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
35
|
+
this.markdownReportDir = path_1.default.join(this.markdownReportDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
36
|
+
}
|
|
32
37
|
const envName = process.env.TESTOMATIO_MARKDOWN_FILENAME;
|
|
33
38
|
if (envName && envName.endsWith('.md')) {
|
|
34
39
|
this.markdownReportName = envName;
|
|
@@ -112,7 +117,9 @@ class MarkdownPipe {
|
|
|
112
117
|
isParallel: runParams?.isParallel,
|
|
113
118
|
executionTime: testExecutionSumTime(aggregated),
|
|
114
119
|
executionDate: getCurrentDateTimeFormatted(),
|
|
115
|
-
description: runParams?.description || this.store.coverageDescription || this.store.description
|
|
120
|
+
description: [runParams?.description || this.store.coverageDescription || this.store.description, this.description]
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.join('\n\n') || '',
|
|
116
123
|
configuration: this.configuration || this.store.configuration || runParams?.configuration || null,
|
|
117
124
|
tests: aggregated,
|
|
118
125
|
stats,
|
package/lib/pipe/testomatio.d.ts
CHANGED
package/lib/pipe/testomatio.js
CHANGED
|
@@ -67,6 +67,7 @@ class TestomatioPipe {
|
|
|
67
67
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
68
68
|
this.env = process.env.TESTOMATIO_ENV;
|
|
69
69
|
this.label = process.env.TESTOMATIO_LABEL;
|
|
70
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
70
71
|
// Create a new instance of gaxios with a custom config
|
|
71
72
|
this.client = new gaxios_1.Gaxios({
|
|
72
73
|
baseURL: `${this.url.trim()}`,
|
|
@@ -193,15 +194,17 @@ class TestomatioPipe {
|
|
|
193
194
|
buildUrl = undefined;
|
|
194
195
|
const accessEvent = process.env.TESTOMATIO_PUBLISH ? 'publish' : null;
|
|
195
196
|
const coverageConfiguration = this.store?.coverageConfiguration;
|
|
196
|
-
let
|
|
197
|
+
let coverageDescription = null;
|
|
197
198
|
let configuration = null;
|
|
198
199
|
if (coverageConfiguration && (coverageConfiguration.tests?.length || coverageConfiguration.suites?.length)) {
|
|
199
|
-
|
|
200
|
+
coverageDescription = this.store?.coverageDescription || null;
|
|
200
201
|
configuration = {
|
|
201
202
|
tests: coverageConfiguration.tests?.map(id => id.replace(/^T/, '')) || [],
|
|
202
203
|
suites: coverageConfiguration.suites?.map(id => id.replace(/^S/, '')) || [],
|
|
203
204
|
};
|
|
204
205
|
}
|
|
206
|
+
// Run description: coverage-derived block (if any) with the user-provided TESTOMATIO_DESCRIPTION appended after it.
|
|
207
|
+
const description = [coverageDescription, this.description].filter(Boolean).join('\n\n') || null;
|
|
205
208
|
// Merge caller-supplied configuration (e.g. { exploratory: true }) into runParams.configuration.
|
|
206
209
|
// Caller values win on key conflict; coverage-derived tests/suites lists are preserved when not overridden.
|
|
207
210
|
if (params.configuration && typeof params.configuration === 'object') {
|
|
@@ -509,11 +512,19 @@ class TestomatioPipe {
|
|
|
509
512
|
const statusCode = error.status || error.code || error.response?.status || '<unknown status code>';
|
|
510
513
|
const method = error.response?.config?.method || '<unknown method>';
|
|
511
514
|
const url = String(error.response?.config?.url || '<unknown url>');
|
|
515
|
+
const statusText = error.response?.statusText || '';
|
|
512
516
|
let message = picocolors_1.default.yellow('⚠️ Request to Testomat.io failed:\n');
|
|
513
517
|
message += picocolors_1.default.bold(`${picocolors_1.default.red(statusCode)} ${method} ${picocolors_1.default.gray(url)}\n`);
|
|
518
|
+
const apiMessage = error.response?.data?.message;
|
|
514
519
|
if (statusCode === 403) {
|
|
515
520
|
message += `\t${picocolors_1.default.red('Please check your API token. It might be invalid or expired.')}\n`;
|
|
516
521
|
}
|
|
522
|
+
else if (apiMessage) {
|
|
523
|
+
message += `\t${picocolors_1.default.red(apiMessage)}\n`;
|
|
524
|
+
}
|
|
525
|
+
else if (statusText) {
|
|
526
|
+
message += `\t${picocolors_1.default.red(statusText)}\n`;
|
|
527
|
+
}
|
|
517
528
|
message += `\t${picocolors_1.default.bold('response: ')}${picocolors_1.default.gray(responseBody)}\n`;
|
|
518
529
|
const requestBody = hideTestomatioToken(stringify(error.response?.config?.data));
|
|
519
530
|
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG || requestBody.length < 1000) {
|
package/package.json
CHANGED
package/src/adapter/codecept.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import createDebugMessages from 'debug';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import pc from 'picocolors';
|
|
3
4
|
import TestomatClient from '../client.js';
|
|
4
5
|
import { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR, SCREENSHOTS_ON_STEPS } from '../constants.js';
|
|
@@ -53,9 +54,8 @@ function CodeceptReporter(config) {
|
|
|
53
54
|
let isRunFinalized = false;
|
|
54
55
|
|
|
55
56
|
const testTimeMap = {};
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const client = new TestomatClient({ apiKey });
|
|
57
|
+
const clientConfig = buildCodeceptClientConfig(config);
|
|
58
|
+
const client = new TestomatClient(clientConfig);
|
|
59
59
|
|
|
60
60
|
// Store original output methods for fallback
|
|
61
61
|
const originalOutput = {
|
|
@@ -577,3 +577,45 @@ function formatHookStep(step) {
|
|
|
577
577
|
|
|
578
578
|
export { CodeceptReporter };
|
|
579
579
|
export default CodeceptReporter;
|
|
580
|
+
|
|
581
|
+
function buildCodeceptClientConfig(config = {}) {
|
|
582
|
+
const outputDir = resolveCodeceptOutputDir(config);
|
|
583
|
+
const reportDir = resolveCodeceptReportDir(config, outputDir);
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
...config,
|
|
587
|
+
apiKey: config.apiKey,
|
|
588
|
+
framework: 'codeceptjs',
|
|
589
|
+
outputDir,
|
|
590
|
+
reportDir,
|
|
591
|
+
html: config.html,
|
|
592
|
+
markdown: config.markdown,
|
|
593
|
+
csv: config.csv,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function resolveCodeceptOutputDir(config = {}) {
|
|
598
|
+
const codeceptStore = /** @type {{ outputDir?: string }} */ (codeceptjs.store || {});
|
|
599
|
+
const candidates = [
|
|
600
|
+
config.outputDir,
|
|
601
|
+
config.output,
|
|
602
|
+
codeceptStore.outputDir,
|
|
603
|
+
codecept?.config?.get?.()?.output,
|
|
604
|
+
codecept?.config?.output,
|
|
605
|
+
];
|
|
606
|
+
|
|
607
|
+
const outputDir = candidates.find(value => typeof value === 'string' && value.trim());
|
|
608
|
+
return outputDir || 'output';
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function resolveCodeceptReportDir(config = {}, outputDir = 'output') {
|
|
612
|
+
if (typeof config.reportDir === 'string' && config.reportDir.trim()) {
|
|
613
|
+
return config.reportDir;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (path.isAbsolute(outputDir)) {
|
|
617
|
+
return path.join(outputDir, 'report');
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
return path.join(outputDir, 'report');
|
|
621
|
+
}
|
package/src/bin/cli.js
CHANGED
|
@@ -116,8 +116,10 @@ program
|
|
|
116
116
|
debug(`Execution pattern: "${pattern}"`);
|
|
117
117
|
|
|
118
118
|
if(opts.filterList) {
|
|
119
|
-
log.info(
|
|
120
|
-
|
|
119
|
+
if (command) log.info(pc.green(`Full Running Command: ${filteredCommand}`));
|
|
120
|
+
console.log();
|
|
121
|
+
console.log(`Grep string:`);
|
|
122
|
+
console.log(`${tests.join(', ')}`);
|
|
121
123
|
return;
|
|
122
124
|
}
|
|
123
125
|
|
package/src/client.js
CHANGED
|
@@ -116,8 +116,9 @@ class Client {
|
|
|
116
116
|
* @returns {Promise<any>} - resolves to Run id which should be used to update / add test
|
|
117
117
|
*/
|
|
118
118
|
async createRun(params = {}) {
|
|
119
|
+
const pipeParams = { ...(this.paramsForPipesFactory || {}), ...(params || {}) };
|
|
119
120
|
if (!this.pipes || !this.pipes.length)
|
|
120
|
-
this.pipes = await pipesFactory(
|
|
121
|
+
this.pipes = await pipesFactory(pipeParams, this.pipeStore);
|
|
121
122
|
debug('Creating run...');
|
|
122
123
|
// all pipes disabled, skipping
|
|
123
124
|
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
package/src/pipe/bitbucket.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { APP_PREFIX, testomatLogoURL } from '../constants.js';
|
|
2
|
-
import { ansiRegExp, isSameTest } from '../utils/utils.js';
|
|
2
|
+
import { ansiRegExp, isSameTest, truncate } from '../utils/utils.js';
|
|
3
3
|
import { statusEmoji, fullName } from '../utils/pipe_utils.js';
|
|
4
4
|
import { Gaxios } from 'gaxios';
|
|
5
5
|
import pc from 'picocolors';
|
|
@@ -27,6 +27,7 @@ export class BitbucketPipe {
|
|
|
27
27
|
this.tests = [];
|
|
28
28
|
// Bitbucket PAT looks like bbpat-*****
|
|
29
29
|
this.token = params.BITBUCKET_ACCESS_TOKEN || process.env.BITBUCKET_ACCESS_TOKEN || this.ENV.BITBUCKET_ACCESS_TOKEN;
|
|
30
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
30
31
|
this.hiddenCommentData = `Testomat.io report: ${process.env.BITBUCKET_BRANCH || ''}`;
|
|
31
32
|
|
|
32
33
|
debug(
|
|
@@ -170,6 +171,10 @@ export class BitbucketPipe {
|
|
|
170
171
|
|
|
171
172
|
let body = summary;
|
|
172
173
|
|
|
174
|
+
if (this.description) {
|
|
175
|
+
body += `\n\n> ${truncate(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
173
178
|
if (failures.length) {
|
|
174
179
|
body += `\n🟥 **Failures (${failures.length})**\n\n* ${failures.join('\n* ')}\n`;
|
|
175
180
|
if (failures.length > 10) {
|
package/src/pipe/csv.js
CHANGED
|
@@ -20,9 +20,12 @@ class CsvPipe {
|
|
|
20
20
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
21
21
|
this.results = [];
|
|
22
22
|
|
|
23
|
-
this.outputDir = 'export';
|
|
23
|
+
this.outputDir = params.reportDir || 'export';
|
|
24
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
25
|
+
this.outputDir = path.join(this.outputDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
26
|
+
}
|
|
24
27
|
this.defaultReportName = 'report.csv';
|
|
25
|
-
this.csvFilename =
|
|
28
|
+
this.csvFilename = resolveCsvFilename(params);
|
|
26
29
|
this.isEnabled = false;
|
|
27
30
|
|
|
28
31
|
if (this.csvFilename) {
|
|
@@ -57,7 +60,7 @@ class CsvPipe {
|
|
|
57
60
|
*/
|
|
58
61
|
checkExportDir() {
|
|
59
62
|
if (!fs.existsSync(this.outputDir)) {
|
|
60
|
-
return fs.mkdirSync(this.outputDir);
|
|
63
|
+
return fs.mkdirSync(this.outputDir, { recursive: true });
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
@@ -142,3 +145,17 @@ class CsvPipe {
|
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
export default CsvPipe;
|
|
148
|
+
|
|
149
|
+
function resolveCsvFilename(params = {}) {
|
|
150
|
+
if (typeof params.csvFilename === 'string' && params.csvFilename.trim()) {
|
|
151
|
+
return params.csvFilename;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (typeof process.env.TESTOMATIO_CSV_FILENAME === 'string' && process.env.TESTOMATIO_CSV_FILENAME.trim()) {
|
|
155
|
+
return process.env.TESTOMATIO_CSV_FILENAME;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (params.csv) return 'report.csv';
|
|
159
|
+
|
|
160
|
+
return null;
|
|
161
|
+
}
|
package/src/pipe/github.js
CHANGED
|
@@ -4,7 +4,7 @@ import pc from 'picocolors';
|
|
|
4
4
|
import humanizeDuration from 'humanize-duration';
|
|
5
5
|
import merge from 'lodash.merge';
|
|
6
6
|
import { testomatLogoURL } from '../constants.js';
|
|
7
|
-
import { ansiRegExp, isSameTest } from '../utils/utils.js';
|
|
7
|
+
import { ansiRegExp, isSameTest, truncate } from '../utils/utils.js';
|
|
8
8
|
import { statusEmoji, fullName } from '../utils/pipe_utils.js';
|
|
9
9
|
import { log } from '../utils/log.js';
|
|
10
10
|
|
|
@@ -22,6 +22,7 @@ class GitHubPipe {
|
|
|
22
22
|
this.store = store;
|
|
23
23
|
this.tests = [];
|
|
24
24
|
this.token = params.GH_PAT || process.env.GH_PAT;
|
|
25
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
25
26
|
this.ref = process.env.GITHUB_REF;
|
|
26
27
|
this.repo = process.env.GITHUB_REPOSITORY;
|
|
27
28
|
this.jobKey = `${process.env.GITHUB_WORKFLOW || ''} / ${process.env.GITHUB_JOB || ''}`;
|
|
@@ -143,6 +144,9 @@ class GitHubPipe {
|
|
|
143
144
|
});
|
|
144
145
|
|
|
145
146
|
let body = summary;
|
|
147
|
+
if (this.description) {
|
|
148
|
+
body += `\n\n> ${truncate(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
149
|
+
}
|
|
146
150
|
const coverageConfiguration = this.store?.coverageConfiguration;
|
|
147
151
|
const isManualRun = this.store?.runKind === 'manual';
|
|
148
152
|
if (isManualRun && coverageConfiguration) {
|
package/src/pipe/gitlab.js
CHANGED
|
@@ -5,7 +5,7 @@ import humanizeDuration from 'humanize-duration';
|
|
|
5
5
|
import merge from 'lodash.merge';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { APP_PREFIX, testomatLogoURL } from '../constants.js';
|
|
8
|
-
import { ansiRegExp, isSameTest } from '../utils/utils.js';
|
|
8
|
+
import { ansiRegExp, isSameTest, truncate } from '../utils/utils.js';
|
|
9
9
|
import { statusEmoji, fullName } from '../utils/pipe_utils.js';
|
|
10
10
|
import { log } from '../utils/log.js';
|
|
11
11
|
|
|
@@ -27,6 +27,7 @@ class GitLabPipe {
|
|
|
27
27
|
this.tests = [];
|
|
28
28
|
// GitLab PAT looks like glpat-nKGdja3jsG4850sGksh7
|
|
29
29
|
this.token = params.GITLAB_PAT || process.env.GITLAB_PAT || this.ENV.GITLAB_PAT;
|
|
30
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
30
31
|
this.hiddenCommentData = `<!--- testomat.io report ${process.env.CI_JOB_NAME || ''} -->`;
|
|
31
32
|
|
|
32
33
|
debug(
|
|
@@ -145,6 +146,10 @@ class GitLabPipe {
|
|
|
145
146
|
|
|
146
147
|
let body = summary;
|
|
147
148
|
|
|
149
|
+
if (this.description) {
|
|
150
|
+
body += `\n\n> ${truncate(this.description, 1024).replace(/\r?\n/g, '\n> ')}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
148
153
|
if (failures.length) {
|
|
149
154
|
body += `\n<details>\n<summary><h3>🟥 Failures (${failures.length})</h4></summary>\n\n${failures.join('\n')}\n`;
|
|
150
155
|
if (failures.length > 20) {
|
package/src/pipe/html.js
CHANGED
|
@@ -19,8 +19,9 @@ class HtmlPipe {
|
|
|
19
19
|
constructor(params, store = {}) {
|
|
20
20
|
this.store = store || {};
|
|
21
21
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
22
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
22
23
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
23
|
-
this.isHtml = process.env.TESTOMATIO_HTML_REPORT_SAVE;
|
|
24
|
+
this.isHtml = params.html ?? process.env.TESTOMATIO_HTML_REPORT_SAVE;
|
|
24
25
|
|
|
25
26
|
debug('HTML Pipe: ', this.apiKey ? 'API KEY' : '*no api key provided*');
|
|
26
27
|
|
|
@@ -32,7 +33,10 @@ class HtmlPipe {
|
|
|
32
33
|
|
|
33
34
|
if (this.isHtml) {
|
|
34
35
|
this.isEnabled = true;
|
|
35
|
-
this.htmlReportDir = process.env.TESTOMATIO_HTML_REPORT_FOLDER || HTML_REPORT.FOLDER;
|
|
36
|
+
this.htmlReportDir = params.reportDir || process.env.TESTOMATIO_HTML_REPORT_FOLDER || HTML_REPORT.FOLDER;
|
|
37
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
38
|
+
this.htmlReportDir = path.join(this.htmlReportDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
39
|
+
}
|
|
36
40
|
|
|
37
41
|
if (process.env.TESTOMATIO_HTML_FILENAME && process.env.TESTOMATIO_HTML_FILENAME.endsWith('.html')) {
|
|
38
42
|
this.htmlReportName = process.env.TESTOMATIO_HTML_FILENAME;
|
|
@@ -254,7 +258,10 @@ class HtmlPipe {
|
|
|
254
258
|
runUrl: this.store.runUrl || '',
|
|
255
259
|
executionTime: testExecutionSumTime(aggregatedTests),
|
|
256
260
|
executionDate: getCurrentDateTimeFormatted(),
|
|
257
|
-
description:
|
|
261
|
+
description:
|
|
262
|
+
[runParams.description || this.store.coverageDescription || this.store.description, this.description]
|
|
263
|
+
.filter(Boolean)
|
|
264
|
+
.join('\n\n') || '',
|
|
258
265
|
configuration: buildDisplayConfiguration(
|
|
259
266
|
this.configuration || this.store.configuration || runParams.configuration || null,
|
|
260
267
|
),
|
package/src/pipe/markdown.js
CHANGED
|
@@ -15,8 +15,9 @@ class MarkdownPipe {
|
|
|
15
15
|
constructor(params, store = {}) {
|
|
16
16
|
this.store = store || {};
|
|
17
17
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
18
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
18
19
|
this.apiKey = params.apiKey || process.env.TESTOMATIO;
|
|
19
|
-
this.isMarkdown = process.env.TESTOMATIO_MARKDOWN_REPORT_SAVE;
|
|
20
|
+
this.isMarkdown = params.markdown ?? process.env.TESTOMATIO_MARKDOWN_REPORT_SAVE;
|
|
20
21
|
|
|
21
22
|
debug('Markdown Pipe: ', this.apiKey ? 'API KEY' : '*no api key provided*');
|
|
22
23
|
|
|
@@ -29,7 +30,11 @@ class MarkdownPipe {
|
|
|
29
30
|
if (!this.isMarkdown) return;
|
|
30
31
|
|
|
31
32
|
this.isEnabled = true;
|
|
32
|
-
this.markdownReportDir =
|
|
33
|
+
this.markdownReportDir =
|
|
34
|
+
params.reportDir || process.env.TESTOMATIO_MARKDOWN_REPORT_FOLDER || MARKDOWN_REPORT.FOLDER;
|
|
35
|
+
if (process.env.TESTOMATIO_RUNGROUP_TITLE) {
|
|
36
|
+
this.markdownReportDir = path.join(this.markdownReportDir, process.env.TESTOMATIO_RUNGROUP_TITLE);
|
|
37
|
+
}
|
|
33
38
|
|
|
34
39
|
const envName = process.env.TESTOMATIO_MARKDOWN_FILENAME;
|
|
35
40
|
if (envName && envName.endsWith('.md')) {
|
|
@@ -134,7 +139,10 @@ class MarkdownPipe {
|
|
|
134
139
|
isParallel: runParams?.isParallel,
|
|
135
140
|
executionTime: testExecutionSumTime(aggregated),
|
|
136
141
|
executionDate: getCurrentDateTimeFormatted(),
|
|
137
|
-
description:
|
|
142
|
+
description:
|
|
143
|
+
[runParams?.description || this.store.coverageDescription || this.store.description, this.description]
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.join('\n\n') || '',
|
|
138
146
|
configuration: this.configuration || this.store.configuration || runParams?.configuration || null,
|
|
139
147
|
tests: aggregated,
|
|
140
148
|
stats,
|
|
@@ -143,7 +151,6 @@ class MarkdownPipe {
|
|
|
143
151
|
const md = renderDocument(data);
|
|
144
152
|
|
|
145
153
|
fs.writeFileSync(outputPath, md, 'utf-8');
|
|
146
|
-
|
|
147
154
|
if (fs.existsSync(outputPath)) {
|
|
148
155
|
const absolutePath = path.resolve(outputPath);
|
|
149
156
|
const fileUrlPath = fileUrl(absolutePath, { resolve: true });
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -83,6 +83,7 @@ class TestomatioPipe {
|
|
|
83
83
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
84
84
|
this.env = process.env.TESTOMATIO_ENV;
|
|
85
85
|
this.label = process.env.TESTOMATIO_LABEL;
|
|
86
|
+
this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
|
|
86
87
|
|
|
87
88
|
// Create a new instance of gaxios with a custom config
|
|
88
89
|
this.client = new Gaxios({
|
|
@@ -227,15 +228,17 @@ class TestomatioPipe {
|
|
|
227
228
|
const accessEvent = process.env.TESTOMATIO_PUBLISH ? 'publish' : null;
|
|
228
229
|
|
|
229
230
|
const coverageConfiguration = this.store?.coverageConfiguration;
|
|
230
|
-
let
|
|
231
|
+
let coverageDescription = null;
|
|
231
232
|
let configuration = null;
|
|
232
233
|
if (coverageConfiguration && (coverageConfiguration.tests?.length || coverageConfiguration.suites?.length)) {
|
|
233
|
-
|
|
234
|
+
coverageDescription = this.store?.coverageDescription || null;
|
|
234
235
|
configuration = {
|
|
235
236
|
tests: coverageConfiguration.tests?.map(id => id.replace(/^T/, '')) || [],
|
|
236
237
|
suites: coverageConfiguration.suites?.map(id => id.replace(/^S/, '')) || [],
|
|
237
238
|
};
|
|
238
239
|
}
|
|
240
|
+
// Run description: coverage-derived block (if any) with the user-provided TESTOMATIO_DESCRIPTION appended after it.
|
|
241
|
+
const description = [coverageDescription, this.description].filter(Boolean).join('\n\n') || null;
|
|
239
242
|
|
|
240
243
|
// Merge caller-supplied configuration (e.g. { exploratory: true }) into runParams.configuration.
|
|
241
244
|
// Caller values win on key conflict; coverage-derived tests/suites lists are preserved when not overridden.
|
|
@@ -576,12 +579,18 @@ class TestomatioPipe {
|
|
|
576
579
|
const statusCode = error.status || error.code || error.response?.status || '<unknown status code>';
|
|
577
580
|
const method = error.response?.config?.method || '<unknown method>';
|
|
578
581
|
const url = String(error.response?.config?.url || '<unknown url>');
|
|
582
|
+
const statusText = error.response?.statusText || '';
|
|
579
583
|
|
|
580
584
|
let message = pc.yellow('⚠️ Request to Testomat.io failed:\n');
|
|
581
585
|
message += pc.bold(`${pc.red(statusCode)} ${method} ${pc.gray(url)}\n`);
|
|
582
586
|
|
|
587
|
+
const apiMessage = error.response?.data?.message;
|
|
583
588
|
if (statusCode === 403) {
|
|
584
589
|
message += `\t${pc.red('Please check your API token. It might be invalid or expired.')}\n`;
|
|
590
|
+
} else if (apiMessage) {
|
|
591
|
+
message += `\t${pc.red(apiMessage)}\n`;
|
|
592
|
+
} else if (statusText) {
|
|
593
|
+
message += `\t${pc.red(statusText)}\n`;
|
|
585
594
|
}
|
|
586
595
|
|
|
587
596
|
message += `\t${pc.bold('response: ')}${pc.gray(responseBody)}\n`;
|
package/types/types.d.ts
CHANGED
|
@@ -267,6 +267,9 @@ export interface RunData {
|
|
|
267
267
|
/** If duration is pre-set value as in XML tests set it */
|
|
268
268
|
duration?: number;
|
|
269
269
|
|
|
270
|
+
/** Free-form run description (from `TESTOMATIO_DESCRIPTION`); appended to any coverage-derived description. */
|
|
271
|
+
description?: string;
|
|
272
|
+
|
|
270
273
|
/**
|
|
271
274
|
* An array of `TestData` objects representing the individual test cases in the test run.
|
|
272
275
|
* Used for JUNit report when we don't send the tests in realtime but in a batch as a part of final result */
|