@testomatio/reporter 2.3.6-beta.2-fix-beforesuite → 2.3.7-beta.-stack-artifacts
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 +22 -2
- package/lib/bin/cli.js +0 -0
- package/lib/bin/reportXml.js +0 -0
- package/lib/bin/startTest.js +0 -0
- package/lib/bin/uploadArtifacts.js +0 -0
- package/lib/client.js +28 -19
- package/lib/pipe/testomatio.js +0 -1
- package/lib/reporter.d.ts +19 -9
- package/lib/reporter.js +40 -5
- package/lib/utils/utils.d.ts +1 -0
- package/lib/utils/utils.js +9 -0
- package/package.json +1 -1
- package/src/adapter/codecept.js +27 -3
- package/src/client.js +53 -25
- package/src/pipe/testomatio.js +0 -2
- package/src/reporter.js +7 -4
- package/src/utils/utils.js +8 -0
package/lib/adapter/codecept.js
CHANGED
|
@@ -110,6 +110,25 @@ function CodeceptReporter(config) {
|
|
|
110
110
|
output.stepShift = 2;
|
|
111
111
|
index_js_1.services.setContext(null);
|
|
112
112
|
});
|
|
113
|
+
// mark as failed all tests inside the failed hook
|
|
114
|
+
event.dispatcher.on(event.hook.failed, hook => {
|
|
115
|
+
if (hook.name !== 'BeforeSuiteHook')
|
|
116
|
+
return;
|
|
117
|
+
const suite = hook.runnable.parent;
|
|
118
|
+
if (!suite)
|
|
119
|
+
return;
|
|
120
|
+
const error = hook?.ctx?.currentTest?.err;
|
|
121
|
+
for (const test of suite.tests) {
|
|
122
|
+
client.addTestRun('failed', {
|
|
123
|
+
...stripExampleFromTitle(test.title),
|
|
124
|
+
rid: test.uid,
|
|
125
|
+
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(test.title),
|
|
126
|
+
suite_title: stripTagsFromTitle(suite.title),
|
|
127
|
+
error,
|
|
128
|
+
time: hook?.runnable?.duration,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
113
132
|
event.dispatcher.on(event.suite.before, suite => {
|
|
114
133
|
data_storage_js_1.dataStorage.setContext(suite.fullTitle());
|
|
115
134
|
});
|
|
@@ -379,7 +398,7 @@ function formatCodeceptStep(step) {
|
|
|
379
398
|
if (!step)
|
|
380
399
|
return null;
|
|
381
400
|
const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
|
|
382
|
-
const title =
|
|
401
|
+
const title = (0, utils_js_1.truncate)(step); // Use built-in toString
|
|
383
402
|
const duration = step.duration || 0; // Use built-in duration
|
|
384
403
|
const formattedStep = {
|
|
385
404
|
category,
|
|
@@ -403,10 +422,11 @@ function formatHookStep(step) {
|
|
|
403
422
|
if (step.actor && step.name) {
|
|
404
423
|
title = `${step.actor} ${step.name}`;
|
|
405
424
|
if (step.args && step.args.length > 0) {
|
|
406
|
-
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
425
|
+
const argsStr = step.args.map(arg => (0, utils_js_1.truncate)(JSON.stringify(arg))).join(', ');
|
|
407
426
|
title += ` ${argsStr}`;
|
|
408
427
|
}
|
|
409
428
|
}
|
|
429
|
+
title = (0, utils_js_1.truncate)(title);
|
|
410
430
|
return {
|
|
411
431
|
category: 'hook',
|
|
412
432
|
title,
|
package/lib/bin/cli.js
CHANGED
|
File without changes
|
package/lib/bin/reportXml.js
CHANGED
|
File without changes
|
package/lib/bin/startTest.js
CHANGED
|
File without changes
|
|
File without changes
|
package/lib/client.js
CHANGED
|
@@ -51,7 +51,9 @@ const node_url_1 = require("node:url");
|
|
|
51
51
|
const uploader_js_1 = require("./uploader.js");
|
|
52
52
|
const utils_js_1 = require("./utils/utils.js");
|
|
53
53
|
const filesize_1 = require("filesize");
|
|
54
|
+
const util_1 = require("util");
|
|
54
55
|
const debug = (0, debug_1.default)('@testomatio/reporter:client');
|
|
56
|
+
const stripColors = util_1.stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
|
|
55
57
|
// removed __dirname usage, because:
|
|
56
58
|
// 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
|
|
57
59
|
// 2. got error "__dirname already defined" in compiles js code (cjs dir)
|
|
@@ -158,17 +160,6 @@ class Client {
|
|
|
158
160
|
* @returns {Promise<PipeResult[]>}
|
|
159
161
|
*/
|
|
160
162
|
async addTestRun(status, testData) {
|
|
161
|
-
if (!this.pipes || !this.pipes.length)
|
|
162
|
-
this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
163
|
-
// all pipes disabled, skipping
|
|
164
|
-
if (!this.pipes?.filter(p => p.isEnabled).length)
|
|
165
|
-
return [];
|
|
166
|
-
if (isTestShouldBeExculedFromReport(testData))
|
|
167
|
-
return [];
|
|
168
|
-
if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
169
|
-
debug('Skipping test from report', testData?.title);
|
|
170
|
-
return []; // do not log skipped tests
|
|
171
|
-
}
|
|
172
163
|
if (!testData)
|
|
173
164
|
testData = {
|
|
174
165
|
title: 'Unknown test',
|
|
@@ -181,9 +172,12 @@ class Client {
|
|
|
181
172
|
/**
|
|
182
173
|
* @type {TestData}
|
|
183
174
|
*/
|
|
184
|
-
const { rid, error = null,
|
|
175
|
+
const { rid, error = null, steps: originalSteps, title, suite_title, } = testData;
|
|
176
|
+
const steps = originalSteps;
|
|
177
|
+
const uploadedFiles = [];
|
|
178
|
+
const stackArtifactsEnabled = (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_STACK_ARTIFACTS);
|
|
179
|
+
const { time = 0, example = null, files = [], filesBuffers = [], code = null, file, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
|
|
185
180
|
let { message = '', meta = {} } = testData;
|
|
186
|
-
// stringify meta values and limit keys and values length to 255
|
|
187
181
|
meta = Object.entries(meta)
|
|
188
182
|
.filter(([, value]) => value !== null && value !== undefined)
|
|
189
183
|
.reduce((acc, [key, value]) => {
|
|
@@ -191,19 +185,34 @@ class Client {
|
|
|
191
185
|
acc[key] = value;
|
|
192
186
|
return acc;
|
|
193
187
|
}, {});
|
|
194
|
-
// Get links from storage using the test context
|
|
195
188
|
const testContext = suite_title ? `${suite_title} ${title}` : title;
|
|
196
189
|
let errorFormatted = '';
|
|
197
190
|
if (error) {
|
|
198
191
|
errorFormatted += this.formatError(error) || '';
|
|
199
192
|
message = error?.message;
|
|
200
193
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
|
|
195
|
+
if (stackArtifactsEnabled) {
|
|
196
|
+
const timestamp = +new Date;
|
|
197
|
+
uploadedFiles.push(this.uploader.uploadFileAsBuffer(Buffer.from(stripColors(fullLogs), 'utf8'), [this.runId, rid, `logs_${timestamp}.log`]));
|
|
198
|
+
fullLogs = '';
|
|
199
|
+
}
|
|
200
|
+
if (!this.pipes || !this.pipes.length)
|
|
201
|
+
this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
202
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) {
|
|
203
|
+
if (uploadedFiles.length > 0) {
|
|
204
|
+
await Promise.all(uploadedFiles);
|
|
205
|
+
}
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
if (isTestShouldBeExculedFromReport(testData))
|
|
209
|
+
return [];
|
|
210
|
+
if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
211
|
+
debug('Skipping test from report', testData?.title);
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
204
214
|
if (manuallyAttachedArtifacts?.length)
|
|
205
215
|
files.push(...manuallyAttachedArtifacts);
|
|
206
|
-
const uploadedFiles = [];
|
|
207
216
|
for (let f of files) {
|
|
208
217
|
if (!f)
|
|
209
218
|
continue; // f === null
|
|
@@ -329,7 +338,7 @@ class Client {
|
|
|
329
338
|
*/
|
|
330
339
|
formatLogs({ error, steps, logs }) {
|
|
331
340
|
error = error?.trim();
|
|
332
|
-
logs = logs?.trim();
|
|
341
|
+
logs = logs?.trim().split('\n').map(l => (0, utils_js_1.truncate)(l)).join('\n');
|
|
333
342
|
if (Array.isArray(steps)) {
|
|
334
343
|
steps = steps
|
|
335
344
|
.map(step => (0, utils_js_1.formatStep)(step))
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -410,7 +410,6 @@ class TestomatioPipe {
|
|
|
410
410
|
tests: params.tests,
|
|
411
411
|
}
|
|
412
412
|
});
|
|
413
|
-
console.log(constants_js_1.APP_PREFIX, '✅ Testrun finished');
|
|
414
413
|
if (this.runUrl) {
|
|
415
414
|
console.log(constants_js_1.APP_PREFIX, '📊 Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
|
|
416
415
|
}
|
package/lib/reporter.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export { Client };
|
|
2
|
+
export const STATUS: {
|
|
3
|
+
PASSED: string;
|
|
4
|
+
FAILED: string;
|
|
5
|
+
SKIPPED: string;
|
|
6
|
+
FINISHED: string;
|
|
7
|
+
};
|
|
1
8
|
export const artifact: (data: string | {
|
|
2
9
|
path: string;
|
|
3
10
|
type: string;
|
|
@@ -80,7 +87,7 @@ export const label: (key: string, value?: string | null) => void;
|
|
|
80
87
|
export const linkTest: (...testIds: string[]) => void;
|
|
81
88
|
export const linkJira: (...jiraIds: string[]) => void;
|
|
82
89
|
declare namespace _default {
|
|
83
|
-
let testomatioLogger: {
|
|
90
|
+
export let testomatioLogger: {
|
|
84
91
|
"__#13@#originalUserLogger": {
|
|
85
92
|
assert(condition?: boolean, ...data: any[]): void;
|
|
86
93
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
@@ -148,13 +155,13 @@ declare namespace _default {
|
|
|
148
155
|
}): void;
|
|
149
156
|
prettyObjects: boolean;
|
|
150
157
|
};
|
|
151
|
-
let artifact: (data: string | {
|
|
158
|
+
export let artifact: (data: string | {
|
|
152
159
|
path: string;
|
|
153
160
|
type: string;
|
|
154
161
|
name: string;
|
|
155
162
|
}, context?: any) => void;
|
|
156
|
-
let log: (...args: any[]) => void;
|
|
157
|
-
let logger: {
|
|
163
|
+
export let log: (...args: any[]) => void;
|
|
164
|
+
export let logger: {
|
|
158
165
|
"__#13@#originalUserLogger": {
|
|
159
166
|
assert(condition?: boolean, ...data: any[]): void;
|
|
160
167
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
@@ -222,13 +229,15 @@ declare namespace _default {
|
|
|
222
229
|
}): void;
|
|
223
230
|
prettyObjects: boolean;
|
|
224
231
|
};
|
|
225
|
-
let meta: (keyValue: {
|
|
232
|
+
export let meta: (keyValue: {
|
|
226
233
|
[key: string]: string;
|
|
227
234
|
} | string, value?: string | null) => void;
|
|
228
|
-
let step: (message: string) => void;
|
|
229
|
-
let label: (key: string, value?: string | null) => void;
|
|
230
|
-
let linkTest: (...testIds: string[]) => void;
|
|
231
|
-
let linkJira: (...jiraIds: string[]) => void;
|
|
235
|
+
export let step: (message: string) => void;
|
|
236
|
+
export let label: (key: string, value?: string | null) => void;
|
|
237
|
+
export let linkTest: (...testIds: string[]) => void;
|
|
238
|
+
export let linkJira: (...jiraIds: string[]) => void;
|
|
239
|
+
export { Client as TestomatioClient };
|
|
240
|
+
export { STATUS };
|
|
232
241
|
}
|
|
233
242
|
export default _default;
|
|
234
243
|
export type ArtifactFunction = typeof import("./reporter-functions.js").default.artifact;
|
|
@@ -237,3 +246,4 @@ export type LoggerService = typeof import("./services/index.js").services.logger
|
|
|
237
246
|
export type MetaFunction = typeof import("./reporter-functions.js").default.keyValue;
|
|
238
247
|
export type StepFunction = typeof import("./reporter-functions.js").default.step;
|
|
239
248
|
export type LabelFunction = typeof import("./reporter-functions.js").default.label;
|
|
249
|
+
import Client from './client.js';
|
package/lib/reporter.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
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
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
39
|
+
exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = exports.STATUS = exports.Client = void 0;
|
|
40
|
+
const client_js_1 = __importDefault(require("./client.js"));
|
|
41
|
+
exports.Client = client_js_1.default;
|
|
42
|
+
const TestomatioConstants = __importStar(require("./constants.js"));
|
|
9
43
|
const index_js_1 = require("./services/index.js");
|
|
10
44
|
const reporter_functions_js_1 = __importDefault(require("./reporter-functions.js"));
|
|
45
|
+
exports.STATUS = TestomatioConstants.STATUS;
|
|
11
46
|
exports.artifact = reporter_functions_js_1.default.artifact;
|
|
12
47
|
exports.log = reporter_functions_js_1.default.log;
|
|
13
48
|
exports.logger = index_js_1.services.logger;
|
|
@@ -37,6 +72,6 @@ module.exports = {
|
|
|
37
72
|
label: reporter_functions_js_1.default.label,
|
|
38
73
|
linkTest: reporter_functions_js_1.default.linkTest,
|
|
39
74
|
linkJira: reporter_functions_js_1.default.linkJira,
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
TestomatioClient: client_js_1.default,
|
|
76
|
+
STATUS: exports.STATUS,
|
|
42
77
|
};
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export function getPackageVersion(): any;
|
|
|
2
2
|
export const TEST_ID_REGEX: RegExp;
|
|
3
3
|
export const SUITE_ID_REGEX: RegExp;
|
|
4
4
|
export function ansiRegExp(): RegExp;
|
|
5
|
+
export function truncate(s: any, size?: number): any;
|
|
5
6
|
export function cleanLatestRunId(): any;
|
|
6
7
|
export function isSameTest(test: any, t: any): boolean;
|
|
7
8
|
export function fetchSourceCode(contents: any, opts?: {}): string;
|
package/lib/utils/utils.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
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
40
|
exports.getPackageVersion = getPackageVersion;
|
|
41
|
+
exports.truncate = truncate;
|
|
41
42
|
exports.cleanLatestRunId = cleanLatestRunId;
|
|
42
43
|
exports.formatStep = formatStep;
|
|
43
44
|
exports.readLatestRunId = readLatestRunId;
|
|
@@ -469,9 +470,17 @@ function transformEnvVarToBoolean(value) {
|
|
|
469
470
|
// if not recognized, return truthy if any value is set
|
|
470
471
|
return Boolean(value);
|
|
471
472
|
}
|
|
473
|
+
function truncate(s, size = 255) {
|
|
474
|
+
if (s.toString().trim().length < size) {
|
|
475
|
+
return s.toString();
|
|
476
|
+
}
|
|
477
|
+
return `${s.toString().substring(0, size)}...`;
|
|
478
|
+
}
|
|
472
479
|
|
|
473
480
|
module.exports.getPackageVersion = getPackageVersion;
|
|
474
481
|
|
|
482
|
+
module.exports.truncate = truncate;
|
|
483
|
+
|
|
475
484
|
module.exports.cleanLatestRunId = cleanLatestRunId;
|
|
476
485
|
|
|
477
486
|
module.exports.formatStep = formatStep;
|
package/package.json
CHANGED
package/src/adapter/codecept.js
CHANGED
|
@@ -2,7 +2,7 @@ import createDebugMessages from 'debug';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import TestomatClient from '../client.js';
|
|
4
4
|
import { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
|
|
5
|
-
import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
|
|
5
|
+
import { getTestomatIdFromTestTitle, truncate, fileSystem } from '../utils/utils.js';
|
|
6
6
|
import { services } from '../services/index.js';
|
|
7
7
|
import { dataStorage } from '../data-storage.js';
|
|
8
8
|
import codeceptjs from 'codeceptjs';
|
|
@@ -127,6 +127,28 @@ function CodeceptReporter(config) {
|
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
|
|
130
|
+
// mark as failed all tests inside the failed hook
|
|
131
|
+
event.dispatcher.on(event.hook.failed, hook => {
|
|
132
|
+
if (hook.name !== 'BeforeSuiteHook') return;
|
|
133
|
+
const suite = hook.runnable.parent;
|
|
134
|
+
|
|
135
|
+
if (!suite) return;
|
|
136
|
+
|
|
137
|
+
const error = hook?.ctx?.currentTest?.err;
|
|
138
|
+
|
|
139
|
+
for (const test of suite.tests) {
|
|
140
|
+
client.addTestRun('failed', {
|
|
141
|
+
...stripExampleFromTitle(test.title),
|
|
142
|
+
rid: test.uid,
|
|
143
|
+
test_id: getTestomatIdFromTestTitle(test.title),
|
|
144
|
+
suite_title: stripTagsFromTitle(suite.title),
|
|
145
|
+
error,
|
|
146
|
+
time: hook?.runnable?.duration,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
|
|
130
152
|
event.dispatcher.on(event.suite.before, suite => {
|
|
131
153
|
dataStorage.setContext(suite.fullTitle());
|
|
132
154
|
});
|
|
@@ -441,7 +463,7 @@ function formatCodeceptStep(step) {
|
|
|
441
463
|
if (!step) return null;
|
|
442
464
|
|
|
443
465
|
const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
|
|
444
|
-
const title = step
|
|
466
|
+
const title = truncate(step); // Use built-in toString
|
|
445
467
|
const duration = step.duration || 0; // Use built-in duration
|
|
446
468
|
|
|
447
469
|
const formattedStep = {
|
|
@@ -469,10 +491,11 @@ function formatHookStep(step) {
|
|
|
469
491
|
if (step.actor && step.name) {
|
|
470
492
|
title = `${step.actor} ${step.name}`;
|
|
471
493
|
if (step.args && step.args.length > 0) {
|
|
472
|
-
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
494
|
+
const argsStr = step.args.map(arg => truncate(JSON.stringify(arg))).join(', ');
|
|
473
495
|
title += ` ${argsStr}`;
|
|
474
496
|
}
|
|
475
497
|
}
|
|
498
|
+
title = truncate(title);
|
|
476
499
|
|
|
477
500
|
return {
|
|
478
501
|
category: 'hook',
|
|
@@ -481,5 +504,6 @@ function formatHookStep(step) {
|
|
|
481
504
|
};
|
|
482
505
|
}
|
|
483
506
|
|
|
507
|
+
|
|
484
508
|
export { CodeceptReporter };
|
|
485
509
|
export default CodeceptReporter;
|
package/src/client.js
CHANGED
|
@@ -10,11 +10,21 @@ import { glob } from 'glob';
|
|
|
10
10
|
import path, { sep } from 'path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { S3Uploader } from './uploader.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
formatStep,
|
|
15
|
+
truncate,
|
|
16
|
+
readLatestRunId,
|
|
17
|
+
storeRunId,
|
|
18
|
+
validateSuiteId,
|
|
19
|
+
transformEnvVarToBoolean
|
|
20
|
+
} from './utils/utils.js';
|
|
14
21
|
import { filesize as prettyBytes } from 'filesize';
|
|
22
|
+
import { stripVTControlCharacters } from 'util';
|
|
15
23
|
|
|
16
24
|
const debug = createDebugMessages('@testomatio/reporter:client');
|
|
17
25
|
|
|
26
|
+
const stripColors = stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
|
|
27
|
+
|
|
18
28
|
// removed __dirname usage, because:
|
|
19
29
|
// 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
|
|
20
30
|
// 2. got error "__dirname already defined" in compiles js code (cjs dir)
|
|
@@ -139,19 +149,6 @@ class Client {
|
|
|
139
149
|
* @returns {Promise<PipeResult[]>}
|
|
140
150
|
*/
|
|
141
151
|
async addTestRun(status, testData) {
|
|
142
|
-
if (!this.pipes || !this.pipes.length)
|
|
143
|
-
this.pipes = await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
144
|
-
|
|
145
|
-
// all pipes disabled, skipping
|
|
146
|
-
if (!this.pipes?.filter(p => p.isEnabled).length) return [];
|
|
147
|
-
|
|
148
|
-
if (isTestShouldBeExculedFromReport(testData)) return [];
|
|
149
|
-
|
|
150
|
-
if (status === STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
151
|
-
debug('Skipping test from report', testData?.title);
|
|
152
|
-
return []; // do not log skipped tests
|
|
153
|
-
}
|
|
154
|
-
|
|
155
152
|
if (!testData)
|
|
156
153
|
testData = {
|
|
157
154
|
title: 'Unknown test',
|
|
@@ -169,15 +166,23 @@ class Client {
|
|
|
169
166
|
const {
|
|
170
167
|
rid,
|
|
171
168
|
error = null,
|
|
169
|
+
steps: originalSteps,
|
|
170
|
+
title,
|
|
171
|
+
suite_title,
|
|
172
|
+
} = testData;
|
|
173
|
+
const steps = originalSteps;
|
|
174
|
+
|
|
175
|
+
const uploadedFiles = [];
|
|
176
|
+
const stackArtifactsEnabled = transformEnvVarToBoolean(process.env.TESTOMATIO_STACK_ARTIFACTS);
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
const {
|
|
172
180
|
time = 0,
|
|
173
181
|
example = null,
|
|
174
182
|
files = [],
|
|
175
183
|
filesBuffers = [],
|
|
176
|
-
steps,
|
|
177
184
|
code = null,
|
|
178
|
-
title,
|
|
179
185
|
file,
|
|
180
|
-
suite_title,
|
|
181
186
|
suite_id,
|
|
182
187
|
test_id,
|
|
183
188
|
timestamp,
|
|
@@ -188,7 +193,6 @@ class Client {
|
|
|
188
193
|
} = testData;
|
|
189
194
|
let { message = '', meta = {} } = testData;
|
|
190
195
|
|
|
191
|
-
// stringify meta values and limit keys and values length to 255
|
|
192
196
|
meta = Object.entries(meta)
|
|
193
197
|
.filter(([, value]) => value !== null && value !== undefined)
|
|
194
198
|
.reduce((acc, [key, value]) => {
|
|
@@ -196,7 +200,6 @@ class Client {
|
|
|
196
200
|
return acc;
|
|
197
201
|
}, {});
|
|
198
202
|
|
|
199
|
-
// Get links from storage using the test context
|
|
200
203
|
const testContext = suite_title ? `${suite_title} ${title}` : title;
|
|
201
204
|
|
|
202
205
|
let errorFormatted = '';
|
|
@@ -205,13 +208,38 @@ class Client {
|
|
|
205
208
|
message = error?.message;
|
|
206
209
|
}
|
|
207
210
|
|
|
208
|
-
|
|
209
|
-
const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
|
|
211
|
+
let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
|
|
210
212
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
if (stackArtifactsEnabled) {
|
|
214
|
+
const timestamp = +new Date;
|
|
215
|
+
uploadedFiles.push(
|
|
216
|
+
this.uploader.uploadFileAsBuffer(
|
|
217
|
+
Buffer.from(stripColors(fullLogs), 'utf8'),
|
|
218
|
+
[this.runId, rid, `logs_${timestamp}.log`]
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
fullLogs = '';
|
|
222
|
+
}
|
|
213
223
|
|
|
214
|
-
|
|
224
|
+
|
|
225
|
+
if (!this.pipes || !this.pipes.length)
|
|
226
|
+
this.pipes = await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
227
|
+
|
|
228
|
+
if (!this.pipes?.filter(p => p.isEnabled).length) {
|
|
229
|
+
if (uploadedFiles.length > 0) {
|
|
230
|
+
await Promise.all(uploadedFiles);
|
|
231
|
+
}
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (isTestShouldBeExculedFromReport(testData)) return [];
|
|
236
|
+
|
|
237
|
+
if (status === STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
238
|
+
debug('Skipping test from report', testData?.title);
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (manuallyAttachedArtifacts?.length) files.push(...manuallyAttachedArtifacts);
|
|
215
243
|
|
|
216
244
|
for (let f of files) {
|
|
217
245
|
if (!f) continue; // f === null
|
|
@@ -387,7 +415,7 @@ class Client {
|
|
|
387
415
|
*/
|
|
388
416
|
formatLogs({ error, steps, logs }) {
|
|
389
417
|
error = error?.trim();
|
|
390
|
-
logs = logs?.trim();
|
|
418
|
+
logs = logs?.trim().split('\n').map(l => truncate(l)).join('\n');
|
|
391
419
|
|
|
392
420
|
if (Array.isArray(steps)) {
|
|
393
421
|
steps = steps
|
package/src/pipe/testomatio.js
CHANGED
package/src/reporter.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Client from './client.js';
|
|
2
|
+
import * as TestomatioConstants from './constants.js';
|
|
3
3
|
import { services } from './services/index.js';
|
|
4
4
|
import reporterFunctions from './reporter-functions.js';
|
|
5
5
|
|
|
6
|
+
export { Client };
|
|
7
|
+
export const STATUS = TestomatioConstants.STATUS;
|
|
6
8
|
export const artifact = reporterFunctions.artifact;
|
|
7
9
|
export const log = reporterFunctions.log;
|
|
8
10
|
export const logger = services.logger;
|
|
@@ -35,6 +37,7 @@ export default {
|
|
|
35
37
|
linkTest: reporterFunctions.linkTest,
|
|
36
38
|
linkJira: reporterFunctions.linkJira,
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
TestomatioClient: Client,
|
|
41
|
+
STATUS,
|
|
42
|
+
|
|
40
43
|
};
|
package/src/utils/utils.js
CHANGED
|
@@ -428,8 +428,16 @@ function transformEnvVarToBoolean(value) {
|
|
|
428
428
|
return Boolean(value);
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
function truncate(s, size = 255) {
|
|
432
|
+
if (s.toString().trim().length < size) {
|
|
433
|
+
return s.toString();
|
|
434
|
+
}
|
|
435
|
+
return `${s.toString().substring(0, size)}...`;
|
|
436
|
+
}
|
|
437
|
+
|
|
431
438
|
export {
|
|
432
439
|
ansiRegExp,
|
|
440
|
+
truncate,
|
|
433
441
|
cleanLatestRunId,
|
|
434
442
|
isSameTest,
|
|
435
443
|
fetchSourceCode,
|