@testomatio/reporter 2.8.5-beta.2-yarn → 2.8.6-beta-fix-xml-batch
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/playwright.d.ts +2 -0
- package/lib/adapter/playwright.js +15 -5
- package/lib/bin/cli.js +1 -1
- package/lib/bin/uploadArtifacts.js +2 -1
- package/lib/constants.d.ts +6 -0
- package/lib/constants.js +11 -1
- package/lib/pipe/debug.d.ts +12 -9
- package/lib/pipe/debug.js +31 -40
- package/lib/pipe/testomatio.d.ts +13 -7
- package/lib/pipe/testomatio.js +21 -12
- package/lib/replay.js +1 -1
- package/lib/xmlReader.d.ts +1 -1
- package/lib/xmlReader.js +3 -4
- package/package.json +1 -1
- package/src/adapter/playwright.js +14 -6
- package/src/bin/cli.js +2 -2
- package/src/bin/uploadArtifacts.js +2 -1
- package/src/constants.js +9 -0
- package/src/pipe/debug.js +35 -36
- package/src/pipe/testomatio.js +22 -13
- package/src/replay.js +2 -2
- package/src/xmlReader.js +4 -5
- package/types/types.d.ts +10 -3
|
@@ -21,11 +21,12 @@ const playwright_js_1 = require("./utils/playwright.js");
|
|
|
21
21
|
Object.defineProperty(exports, "fetchLinksFromLogs", { enumerable: true, get: function () { return playwright_js_1.fetchLinksFromLogs; } });
|
|
22
22
|
const step_formatter_js_1 = require("./utils/step-formatter.js");
|
|
23
23
|
const log_js_1 = require("../utils/log.js");
|
|
24
|
-
const reportTestPromises = [];
|
|
25
24
|
class PlaywrightReporter {
|
|
26
25
|
constructor(config = {}) {
|
|
27
26
|
this.client = new client_js_1.default({ apiKey: config?.apiKey });
|
|
28
27
|
this.uploads = [];
|
|
28
|
+
this.reportTestPromises = [];
|
|
29
|
+
this.runPromise = Promise.resolve();
|
|
29
30
|
}
|
|
30
31
|
onBegin(config, suite) {
|
|
31
32
|
// clean data storage
|
|
@@ -34,7 +35,9 @@ class PlaywrightReporter {
|
|
|
34
35
|
return;
|
|
35
36
|
this.suite = suite;
|
|
36
37
|
this.config = config;
|
|
37
|
-
this.
|
|
38
|
+
this.uploads = [];
|
|
39
|
+
this.reportTestPromises = [];
|
|
40
|
+
this.runPromise = this.client.createRun();
|
|
38
41
|
}
|
|
39
42
|
onTestBegin(testInfo) {
|
|
40
43
|
const fullTestTitle = getTestContextName(testInfo);
|
|
@@ -44,6 +47,7 @@ class PlaywrightReporter {
|
|
|
44
47
|
// test.parent.project().__projectId
|
|
45
48
|
if (!this.client)
|
|
46
49
|
return;
|
|
50
|
+
await this.runPromise;
|
|
47
51
|
const { title } = test;
|
|
48
52
|
const { error, duration } = result;
|
|
49
53
|
const pwAttachments = (result.attachments || []).filter(a => a.body || a.path);
|
|
@@ -126,7 +130,12 @@ class PlaywrightReporter {
|
|
|
126
130
|
...meta,
|
|
127
131
|
...project.metadata, // metadata has any type (in playwright), but we will stringify it in client.js
|
|
128
132
|
...test.annotations?.reduce((acc, annotation) => {
|
|
129
|
-
acc[annotation.type]
|
|
133
|
+
if (acc[annotation.type]) {
|
|
134
|
+
acc[annotation.type] = `${acc[annotation.type]}, ${annotation.description}`;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
acc[annotation.type] = annotation.description;
|
|
138
|
+
}
|
|
130
139
|
return acc;
|
|
131
140
|
}, {}),
|
|
132
141
|
},
|
|
@@ -140,7 +149,7 @@ class PlaywrightReporter {
|
|
|
140
149
|
});
|
|
141
150
|
// remove empty uploads
|
|
142
151
|
this.uploads = this.uploads.filter(anUpload => anUpload.files.length);
|
|
143
|
-
reportTestPromises.push(reportTestPromise);
|
|
152
|
+
this.reportTestPromises.push(reportTestPromise);
|
|
144
153
|
}
|
|
145
154
|
#getArtifactPath(artifact) {
|
|
146
155
|
if (artifact.path) {
|
|
@@ -164,7 +173,8 @@ class PlaywrightReporter {
|
|
|
164
173
|
async onEnd(result) {
|
|
165
174
|
if (!this.client)
|
|
166
175
|
return;
|
|
167
|
-
await
|
|
176
|
+
await this.runPromise;
|
|
177
|
+
await Promise.all(this.reportTestPromises);
|
|
168
178
|
if (this.uploads.length) {
|
|
169
179
|
if (this.client.uploader.isEnabled)
|
|
170
180
|
log_js_1.log.info(`🎞️ Uploading ${this.uploads.length} files...`);
|
package/lib/bin/cli.js
CHANGED
|
@@ -333,7 +333,7 @@ program
|
|
|
333
333
|
const client = new client_js_1.default({
|
|
334
334
|
apiKey,
|
|
335
335
|
runId,
|
|
336
|
-
|
|
336
|
+
batchMode: constants_js_1.BATCH_MODE.DISABLED,
|
|
337
337
|
});
|
|
338
338
|
let testruns = client.uploader.readUploadedFiles(runId);
|
|
339
339
|
const numTotalArtifacts = testruns.length;
|
|
@@ -13,6 +13,7 @@ const config_js_1 = require("../config.js");
|
|
|
13
13
|
const utils_js_2 = require("../utils/utils.js");
|
|
14
14
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
15
15
|
const log_js_1 = require("../utils/log.js");
|
|
16
|
+
const constants_js_1 = require("../constants.js");
|
|
16
17
|
const debug = (0, debug_1.default)('@testomatio/reporter:upload-cli');
|
|
17
18
|
const version = (0, utils_js_1.getPackageVersion)();
|
|
18
19
|
console.log(picocolors_1.default.cyan(picocolors_1.default.bold(` 🤩 Testomat.io Reporter v${version}`)));
|
|
@@ -37,7 +38,7 @@ program
|
|
|
37
38
|
const client = new client_js_1.default({
|
|
38
39
|
apiKey,
|
|
39
40
|
runId,
|
|
40
|
-
|
|
41
|
+
batchMode: constants_js_1.BATCH_MODE.DISABLED,
|
|
41
42
|
});
|
|
42
43
|
let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
|
|
43
44
|
const numTotalArtifacts = testruns.length;
|
package/lib/constants.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export namespace STATUS {
|
|
|
10
10
|
let SKIPPED: string;
|
|
11
11
|
let FINISHED: string;
|
|
12
12
|
}
|
|
13
|
+
/** @type {{ AUTO: 'auto', MANUAL: 'manual', DISABLED: 'disabled' }} */
|
|
14
|
+
export const BATCH_MODE: {
|
|
15
|
+
AUTO: "auto";
|
|
16
|
+
MANUAL: "manual";
|
|
17
|
+
DISABLED: "disabled";
|
|
18
|
+
};
|
|
13
19
|
export namespace HTML_REPORT {
|
|
14
20
|
let FOLDER: string;
|
|
15
21
|
let REPORT_DEFAULT_NAME: string;
|
package/lib/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEBUG_FILE = exports.SCREENSHOTS_ON_STEPS = exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.REQUEST_TIMEOUT = exports.MARKDOWN_REPORT = exports.HTML_REPORT = exports.STATUS = exports.CSV_HEADERS = exports.TESTOMAT_TMP_STORAGE_DIR = exports.APP_PREFIX = void 0;
|
|
6
|
+
exports.DEBUG_FILE = exports.SCREENSHOTS_ON_STEPS = exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.REQUEST_TIMEOUT = exports.MARKDOWN_REPORT = exports.HTML_REPORT = exports.BATCH_MODE = exports.STATUS = exports.CSV_HEADERS = exports.TESTOMAT_TMP_STORAGE_DIR = exports.APP_PREFIX = void 0;
|
|
7
7
|
exports.getCreateRunRequestTimeout = getCreateRunRequestTimeout;
|
|
8
8
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
@@ -37,6 +37,14 @@ const STATUS = {
|
|
|
37
37
|
FINISHED: 'finished',
|
|
38
38
|
};
|
|
39
39
|
exports.STATUS = STATUS;
|
|
40
|
+
// batch upload mode
|
|
41
|
+
/** @type {{ AUTO: 'auto', MANUAL: 'manual', DISABLED: 'disabled' }} */
|
|
42
|
+
const BATCH_MODE = {
|
|
43
|
+
AUTO: 'auto',
|
|
44
|
+
MANUAL: 'manual',
|
|
45
|
+
DISABLED: 'disabled',
|
|
46
|
+
};
|
|
47
|
+
exports.BATCH_MODE = BATCH_MODE;
|
|
40
48
|
// html pipe var
|
|
41
49
|
const HTML_REPORT = {
|
|
42
50
|
FOLDER: 'html-report',
|
|
@@ -79,6 +87,8 @@ module.exports.CSV_HEADERS = CSV_HEADERS;
|
|
|
79
87
|
|
|
80
88
|
module.exports.STATUS = STATUS;
|
|
81
89
|
|
|
90
|
+
module.exports.BATCH_MODE = BATCH_MODE;
|
|
91
|
+
|
|
82
92
|
module.exports.HTML_REPORT = HTML_REPORT;
|
|
83
93
|
|
|
84
94
|
module.exports.MARKDOWN_REPORT = MARKDOWN_REPORT;
|
package/lib/pipe/debug.d.ts
CHANGED
|
@@ -3,18 +3,13 @@ export class DebugPipe {
|
|
|
3
3
|
params: any;
|
|
4
4
|
store: any;
|
|
5
5
|
isEnabled: boolean;
|
|
6
|
-
|
|
7
|
-
isEnabled: any;
|
|
8
|
-
intervalFunction: any;
|
|
9
|
-
intervalTime: number;
|
|
10
|
-
tests: any[];
|
|
11
|
-
batchIndex: number;
|
|
12
|
-
};
|
|
6
|
+
tests: any[];
|
|
13
7
|
logFilePath: string;
|
|
14
8
|
rootPath: string;
|
|
15
9
|
historyDir: string;
|
|
16
10
|
testomatioEnvVars: {};
|
|
17
|
-
|
|
11
|
+
flushOnExit: () => void;
|
|
12
|
+
exitListenerAttached: boolean;
|
|
18
13
|
/**
|
|
19
14
|
* Logs data to a file if logging is enabled.
|
|
20
15
|
*
|
|
@@ -24,9 +19,17 @@ export class DebugPipe {
|
|
|
24
19
|
logToFile(logData: any): Promise<void>;
|
|
25
20
|
lastActionTimestamp: number;
|
|
26
21
|
prepareRun(opts: any): Promise<any[]>;
|
|
27
|
-
createRun(params?: {}): Promise<
|
|
22
|
+
createRun(params?: {}): Promise<void>;
|
|
28
23
|
addTest(data: any): Promise<void>;
|
|
29
24
|
finishRun(params: any): Promise<void>;
|
|
30
25
|
sync(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Writes any buffered tests to the debug file as a single batch.
|
|
28
|
+
* Runs synchronously so it can also be invoked from a process `exit` handler,
|
|
29
|
+
* which is the only chance to persist tests when a hook failure (e.g. a failing
|
|
30
|
+
* AfterSuite) prevents `finishRun` from being reached. Idempotent: the buffer is
|
|
31
|
+
* drained on flush, so a later `finishRun`/exit flush is a no-op.
|
|
32
|
+
*/
|
|
33
|
+
flushBufferedTests(): void;
|
|
31
34
|
toString(): string;
|
|
32
35
|
}
|
package/lib/pipe/debug.js
CHANGED
|
@@ -17,13 +17,7 @@ class DebugPipe {
|
|
|
17
17
|
this.store = store || {};
|
|
18
18
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
19
19
|
if (this.isEnabled) {
|
|
20
|
-
this.
|
|
21
|
-
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
22
|
-
intervalFunction: null,
|
|
23
|
-
intervalTime: 5000,
|
|
24
|
-
tests: [],
|
|
25
|
-
batchIndex: 0,
|
|
26
|
-
};
|
|
20
|
+
this.tests = [];
|
|
27
21
|
const suffix = process.env.TESTOMATIO_REPLAY ? 'replay' : '';
|
|
28
22
|
const paths = (0, debug_js_1.getDebugFilePath)(suffix);
|
|
29
23
|
this.logFilePath = paths.tmp;
|
|
@@ -63,8 +57,12 @@ class DebugPipe {
|
|
|
63
57
|
this.logToFile({ datetime: new Date().toISOString(), timestamp: Date.now() });
|
|
64
58
|
this.logToFile({ data: 'variables', testomatioEnvVars: this.testomatioEnvVars });
|
|
65
59
|
this.logToFile({ data: 'store', store: this.store || {} });
|
|
66
|
-
//
|
|
67
|
-
|
|
60
|
+
// Safety net for hook failures (e.g. a failing AfterSuite) that abort the run
|
|
61
|
+
// before finishRun: buffered tests would otherwise be lost. The handler is
|
|
62
|
+
// attached lazily when the first test is buffered and detached once flushed,
|
|
63
|
+
// so processes that create many pipes don't pile up `exit` listeners.
|
|
64
|
+
this.flushOnExit = () => this.flushBufferedTests();
|
|
65
|
+
this.exitListenerAttached = false;
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
68
|
/**
|
|
@@ -89,46 +87,21 @@ class DebugPipe {
|
|
|
89
87
|
async createRun(params = {}) {
|
|
90
88
|
if (!this.isEnabled)
|
|
91
89
|
return;
|
|
92
|
-
if (params.isBatchEnabled === true || params.isBatchEnabled === false)
|
|
93
|
-
this.batch.isEnabled = params.isBatchEnabled;
|
|
94
|
-
if (!this.isEnabled)
|
|
95
|
-
return {};
|
|
96
|
-
if (this.batch.isEnabled)
|
|
97
|
-
this.batch.intervalFunction = setInterval(this.batchUpload, this.batch.intervalTime);
|
|
98
90
|
this.logToFile({ action: 'createRun', params });
|
|
99
91
|
}
|
|
100
92
|
async addTest(data) {
|
|
101
93
|
if (!this.isEnabled)
|
|
102
94
|
return;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
this.logToFile(logData);
|
|
95
|
+
this.tests.push(data);
|
|
96
|
+
if (!this.exitListenerAttached) {
|
|
97
|
+
process.once('exit', this.flushOnExit);
|
|
98
|
+
this.exitListenerAttached = true;
|
|
108
99
|
}
|
|
109
|
-
else
|
|
110
|
-
this.batch.tests.push(data);
|
|
111
|
-
if (!this.batch.intervalFunction)
|
|
112
|
-
await this.batchUpload();
|
|
113
|
-
}
|
|
114
|
-
async batchUpload() {
|
|
115
|
-
this.batch.batchIndex++;
|
|
116
|
-
if (!this.batch.isEnabled)
|
|
117
|
-
return;
|
|
118
|
-
if (!this.batch.tests.length)
|
|
119
|
-
return;
|
|
120
|
-
const testsToSend = this.batch.tests.splice(0);
|
|
121
|
-
const logData = { action: 'addTestsBatch', tests: testsToSend };
|
|
122
|
-
if (this.store.runId)
|
|
123
|
-
logData.runId = this.store.runId;
|
|
124
|
-
this.logToFile(logData);
|
|
125
100
|
}
|
|
126
101
|
async finishRun(params) {
|
|
127
102
|
if (!this.isEnabled)
|
|
128
103
|
return;
|
|
129
104
|
await this.sync();
|
|
130
|
-
if (this.batch.intervalFunction)
|
|
131
|
-
clearInterval(this.batch.intervalFunction);
|
|
132
105
|
const logData = { action: 'finishRun', params };
|
|
133
106
|
if (this.store.runId)
|
|
134
107
|
logData.runId = this.store.runId;
|
|
@@ -137,9 +110,27 @@ class DebugPipe {
|
|
|
137
110
|
log_js_1.log.info(`History: ${this.historyDir}`);
|
|
138
111
|
}
|
|
139
112
|
async sync() {
|
|
140
|
-
|
|
113
|
+
this.flushBufferedTests();
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Writes any buffered tests to the debug file as a single batch.
|
|
117
|
+
* Runs synchronously so it can also be invoked from a process `exit` handler,
|
|
118
|
+
* which is the only chance to persist tests when a hook failure (e.g. a failing
|
|
119
|
+
* AfterSuite) prevents `finishRun` from being reached. Idempotent: the buffer is
|
|
120
|
+
* drained on flush, so a later `finishRun`/exit flush is a no-op.
|
|
121
|
+
*/
|
|
122
|
+
flushBufferedTests() {
|
|
123
|
+
if (!this.isEnabled || !this.tests.length)
|
|
141
124
|
return;
|
|
142
|
-
|
|
125
|
+
const tests = this.tests.splice(0);
|
|
126
|
+
const logData = { action: 'addTestsBatch', tests };
|
|
127
|
+
if (this.store.runId)
|
|
128
|
+
logData.runId = this.store.runId;
|
|
129
|
+
this.logToFile(logData);
|
|
130
|
+
if (this.exitListenerAttached) {
|
|
131
|
+
process.removeListener('exit', this.flushOnExit);
|
|
132
|
+
this.exitListenerAttached = false;
|
|
133
|
+
}
|
|
143
134
|
}
|
|
144
135
|
toString() {
|
|
145
136
|
return 'Debug Reporter';
|
package/lib/pipe/testomatio.d.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
export default TestomatioPipe;
|
|
2
2
|
export type Pipe = import("../../types/types.js").Pipe;
|
|
3
3
|
export type TestData = import("../../types/types.js").TestData;
|
|
4
|
+
export type BatchMode = import("../../types/types.js").BatchMode;
|
|
5
|
+
export type CreateRunParams = import("../../types/types.js").CreateRunParams;
|
|
4
6
|
/**
|
|
5
7
|
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
6
8
|
* @typedef {import('../../types/types.js').TestData} TestData
|
|
9
|
+
* @typedef {import('../../types/types.js').BatchMode} BatchMode
|
|
10
|
+
* @typedef {import('../../types/types.js').CreateRunParams} CreateRunParams
|
|
7
11
|
* @class TestomatioPipe
|
|
8
12
|
* @implements {Pipe}
|
|
9
13
|
*/
|
|
10
14
|
declare class TestomatioPipe implements Pipe {
|
|
11
15
|
constructor(params: any, store: any);
|
|
12
16
|
batch: {
|
|
13
|
-
|
|
17
|
+
/** @type {BatchMode}
|
|
18
|
+
* Batch upload mode:
|
|
19
|
+
* - `auto`: upload tests automatically by time interval (e.g. every 5 seconds).
|
|
20
|
+
* - `manual`: buffer tests and upload only when `sync()` is invoked manually.
|
|
21
|
+
* - `disabled`: send one test per request, no batching.
|
|
22
|
+
*/
|
|
23
|
+
mode: BatchMode;
|
|
14
24
|
intervalFunction: any;
|
|
15
25
|
intervalTime: number;
|
|
16
26
|
tests: any[];
|
|
@@ -50,14 +60,10 @@ declare class TestomatioPipe implements Pipe {
|
|
|
50
60
|
prepareRun(opts: any): Promise<string[]>;
|
|
51
61
|
/**
|
|
52
62
|
* Creates a new run on Testomat.io
|
|
53
|
-
* @param {
|
|
63
|
+
* @param {CreateRunParams} params
|
|
54
64
|
* @returns Promise<void>
|
|
55
65
|
*/
|
|
56
|
-
createRun(params?:
|
|
57
|
-
isBatchEnabled?: boolean;
|
|
58
|
-
kind?: string;
|
|
59
|
-
configuration?: Record<string, any>;
|
|
60
|
-
}): Promise<void>;
|
|
66
|
+
createRun(params?: CreateRunParams): Promise<void>;
|
|
61
67
|
runUrl: string;
|
|
62
68
|
runPublicUrl: any;
|
|
63
69
|
/**
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -38,15 +38,23 @@ function parseCiParams(raw) {
|
|
|
38
38
|
/**
|
|
39
39
|
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
40
40
|
* @typedef {import('../../types/types.js').TestData} TestData
|
|
41
|
+
* @typedef {import('../../types/types.js').BatchMode} BatchMode
|
|
42
|
+
* @typedef {import('../../types/types.js').CreateRunParams} CreateRunParams
|
|
41
43
|
* @class TestomatioPipe
|
|
42
44
|
* @implements {Pipe}
|
|
43
45
|
*/
|
|
44
46
|
class TestomatioPipe {
|
|
45
47
|
constructor(params, store) {
|
|
46
48
|
this.batch = {
|
|
47
|
-
|
|
49
|
+
/** @type {BatchMode}
|
|
50
|
+
* Batch upload mode:
|
|
51
|
+
* - `auto`: upload tests automatically by time interval (e.g. every 5 seconds).
|
|
52
|
+
* - `manual`: buffer tests and upload only when `sync()` is invoked manually.
|
|
53
|
+
* - `disabled`: send one test per request, no batching.
|
|
54
|
+
*/
|
|
55
|
+
mode: params.batchMode || (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? constants_js_1.BATCH_MODE.DISABLED : constants_js_1.BATCH_MODE.AUTO),
|
|
48
56
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
49
|
-
intervalTime:
|
|
57
|
+
intervalTime: 6000, // how often tests are sent
|
|
50
58
|
tests: [], // array of tests in batch
|
|
51
59
|
batchIndex: 0, // represents the current batch index (starts from 1 and increments by 1 for each batch)
|
|
52
60
|
numberOfTimesCalledWithoutTests: 0, // how many times batch was called without tests
|
|
@@ -193,14 +201,15 @@ class TestomatioPipe {
|
|
|
193
201
|
}
|
|
194
202
|
/**
|
|
195
203
|
* Creates a new run on Testomat.io
|
|
196
|
-
* @param {
|
|
204
|
+
* @param {CreateRunParams} params
|
|
197
205
|
* @returns Promise<void>
|
|
198
206
|
*/
|
|
199
207
|
async createRun(params = {}) {
|
|
200
|
-
|
|
208
|
+
if (params.batchMode)
|
|
209
|
+
this.batch.mode = params.batchMode;
|
|
201
210
|
if (!this.isEnabled)
|
|
202
211
|
return;
|
|
203
|
-
if (this.batch.
|
|
212
|
+
if (this.batch.mode === constants_js_1.BATCH_MODE.AUTO && this.isEnabled)
|
|
204
213
|
this.batch.intervalFunction = setInterval(this.#batchUpload, this.batch.intervalTime);
|
|
205
214
|
if (this.store) {
|
|
206
215
|
this.store.runKind = params.kind;
|
|
@@ -391,7 +400,7 @@ class TestomatioPipe {
|
|
|
391
400
|
* Uploads tests as a batch (multiple tests at once). Intended to be used with a setInterval
|
|
392
401
|
*/
|
|
393
402
|
#batchUpload = async () => {
|
|
394
|
-
if (
|
|
403
|
+
if (this.batch.mode === constants_js_1.BATCH_MODE.DISABLED)
|
|
395
404
|
return;
|
|
396
405
|
if (!this.batch.tests.length)
|
|
397
406
|
return;
|
|
@@ -401,7 +410,7 @@ class TestomatioPipe {
|
|
|
401
410
|
if (this.batch.numberOfTimesCalledWithoutTests > 10) {
|
|
402
411
|
debug('📨 Batch upload: no tests to send for 10 times, stopping batch');
|
|
403
412
|
clearInterval(this.batch.intervalFunction);
|
|
404
|
-
this.batch.
|
|
413
|
+
this.batch.mode = constants_js_1.BATCH_MODE.DISABLED;
|
|
405
414
|
}
|
|
406
415
|
if (!this.batch.tests.length) {
|
|
407
416
|
debug('📨 Batch upload: no tests to send');
|
|
@@ -454,12 +463,12 @@ class TestomatioPipe {
|
|
|
454
463
|
}
|
|
455
464
|
this.#formatData(data);
|
|
456
465
|
let uploading = null;
|
|
457
|
-
if (
|
|
466
|
+
if (this.batch.mode === constants_js_1.BATCH_MODE.DISABLED)
|
|
458
467
|
uploading = this.#uploadSingleTest(data);
|
|
459
468
|
else
|
|
460
469
|
this.batch.tests.push(data);
|
|
461
|
-
//
|
|
462
|
-
if (!this.batch.intervalFunction)
|
|
470
|
+
// auto mode but no interval running yet (e.g. createRun hasn't started it): flush immediately
|
|
471
|
+
if (this.batch.mode === constants_js_1.BATCH_MODE.AUTO && !this.batch.intervalFunction)
|
|
463
472
|
uploading = this.#batchUpload();
|
|
464
473
|
// return promise to be able to wait for it
|
|
465
474
|
return uploading;
|
|
@@ -487,7 +496,7 @@ class TestomatioPipe {
|
|
|
487
496
|
// (e.g. if test has artifacts, add test function will be invoked only after artifacts are uploaded)
|
|
488
497
|
// batch stops working after run is finished; thus, disable it to use single test uploading
|
|
489
498
|
this.batch.intervalFunction = null;
|
|
490
|
-
this.batch.
|
|
499
|
+
this.batch.mode = constants_js_1.BATCH_MODE.DISABLED;
|
|
491
500
|
}
|
|
492
501
|
debug('Finishing run...');
|
|
493
502
|
if (this.reportingCanceledDueToReqFailures) {
|
|
@@ -549,7 +558,7 @@ class TestomatioPipe {
|
|
|
549
558
|
if (this.batch.intervalFunction) {
|
|
550
559
|
clearInterval(this.batch.intervalFunction);
|
|
551
560
|
this.batch.intervalFunction = null;
|
|
552
|
-
this.batch.
|
|
561
|
+
this.batch.mode = constants_js_1.BATCH_MODE.DISABLED;
|
|
553
562
|
}
|
|
554
563
|
this.batch.tests = [];
|
|
555
564
|
}
|
package/lib/replay.js
CHANGED
|
@@ -210,7 +210,7 @@ class Replay {
|
|
|
210
210
|
process.env.TESTOMATIO_REPLAY = '1';
|
|
211
211
|
const client = new client_js_1.default({
|
|
212
212
|
apiKey: this.apiKey,
|
|
213
|
-
|
|
213
|
+
batchMode: constants_js_1.BATCH_MODE.AUTO,
|
|
214
214
|
...runParams,
|
|
215
215
|
...(runId && { runId }),
|
|
216
216
|
});
|
package/lib/xmlReader.d.ts
CHANGED
package/lib/xmlReader.js
CHANGED
|
@@ -51,8 +51,7 @@ class XmlReader {
|
|
|
51
51
|
env: TESTOMATIO_ENV,
|
|
52
52
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
53
53
|
detach: TESTOMATIO_MARK_DETACHED,
|
|
54
|
-
|
|
55
|
-
isBatchEnabled: false,
|
|
54
|
+
batchMode: constants_js_1.BATCH_MODE.MANUAL,
|
|
56
55
|
};
|
|
57
56
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
58
57
|
this.adapter = (0, index_js_2.default)(opts.lang?.toLowerCase(), opts);
|
|
@@ -467,7 +466,7 @@ class XmlReader {
|
|
|
467
466
|
title: this.requestParams.title,
|
|
468
467
|
env: this.requestParams.env,
|
|
469
468
|
group_title: this.requestParams.group_title,
|
|
470
|
-
|
|
469
|
+
batchMode: this.requestParams.batchMode,
|
|
471
470
|
};
|
|
472
471
|
debug('Run', runParams);
|
|
473
472
|
this.pipes = this.pipes || (await this.pipesPromise);
|
|
@@ -521,7 +520,7 @@ class XmlReader {
|
|
|
521
520
|
this.formatTests();
|
|
522
521
|
this.pipes = this.pipes || (await this.pipesPromise);
|
|
523
522
|
// Create run before uploading tests to ensure runId is set
|
|
524
|
-
await this.createRun();
|
|
523
|
+
// await this.createRun(); // makes reporting stuck after finish, thus commenting out
|
|
525
524
|
if (!this.tests || !Array.isArray(this.tests) || this.tests.length === 0) {
|
|
526
525
|
debug('No tests to upload, finishing run');
|
|
527
526
|
const finishData = {
|
package/package.json
CHANGED
|
@@ -14,13 +14,13 @@ import { fetchLinksFromLogs } from './utils/playwright.js';
|
|
|
14
14
|
import { formatStep, addStatusToStep, addArtifactsToStep } from './utils/step-formatter.js';
|
|
15
15
|
import { log } from '../utils/log.js';
|
|
16
16
|
|
|
17
|
-
const reportTestPromises = [];
|
|
18
|
-
|
|
19
17
|
class PlaywrightReporter {
|
|
20
18
|
constructor(config = {}) {
|
|
21
19
|
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
22
20
|
|
|
23
21
|
this.uploads = [];
|
|
22
|
+
this.reportTestPromises = [];
|
|
23
|
+
this.runPromise = Promise.resolve();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
onBegin(config, suite) {
|
|
@@ -29,7 +29,9 @@ class PlaywrightReporter {
|
|
|
29
29
|
if (!this.client) return;
|
|
30
30
|
this.suite = suite;
|
|
31
31
|
this.config = config;
|
|
32
|
-
this.
|
|
32
|
+
this.uploads = [];
|
|
33
|
+
this.reportTestPromises = [];
|
|
34
|
+
this.runPromise = this.client.createRun();
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
onTestBegin(testInfo) {
|
|
@@ -41,6 +43,7 @@ class PlaywrightReporter {
|
|
|
41
43
|
// test.parent.project().__projectId
|
|
42
44
|
|
|
43
45
|
if (!this.client) return;
|
|
46
|
+
await this.runPromise;
|
|
44
47
|
|
|
45
48
|
const { title } = test;
|
|
46
49
|
const { error, duration } = result;
|
|
@@ -133,7 +136,11 @@ class PlaywrightReporter {
|
|
|
133
136
|
...meta,
|
|
134
137
|
...project.metadata, // metadata has any type (in playwright), but we will stringify it in client.js
|
|
135
138
|
...test.annotations?.reduce((acc, annotation) => {
|
|
136
|
-
acc[annotation.type]
|
|
139
|
+
if (acc[annotation.type]) {
|
|
140
|
+
acc[annotation.type] = `${acc[annotation.type]}, ${annotation.description}`;
|
|
141
|
+
} else {
|
|
142
|
+
acc[annotation.type] = annotation.description;
|
|
143
|
+
}
|
|
137
144
|
return acc;
|
|
138
145
|
}, {}),
|
|
139
146
|
},
|
|
@@ -149,7 +156,7 @@ class PlaywrightReporter {
|
|
|
149
156
|
// remove empty uploads
|
|
150
157
|
this.uploads = this.uploads.filter(anUpload => anUpload.files.length);
|
|
151
158
|
|
|
152
|
-
reportTestPromises.push(reportTestPromise);
|
|
159
|
+
this.reportTestPromises.push(reportTestPromise);
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
#getArtifactPath(artifact) {
|
|
@@ -173,7 +180,8 @@ class PlaywrightReporter {
|
|
|
173
180
|
async onEnd(result) {
|
|
174
181
|
if (!this.client) return;
|
|
175
182
|
|
|
176
|
-
await
|
|
183
|
+
await this.runPromise;
|
|
184
|
+
await Promise.all(this.reportTestPromises);
|
|
177
185
|
|
|
178
186
|
if (this.uploads.length) {
|
|
179
187
|
if (this.client.uploader.isEnabled) log.info(`🎞️ Uploading ${this.uploads.length} files...`);
|
package/src/bin/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { glob } from 'glob';
|
|
|
6
6
|
import createDebugMessages from 'debug';
|
|
7
7
|
import TestomatClient from '../client.js';
|
|
8
8
|
import XmlReader from '../xmlReader.js';
|
|
9
|
-
import { APP_PREFIX, STATUS, DEBUG_FILE } from '../constants.js';
|
|
9
|
+
import { APP_PREFIX, STATUS, DEBUG_FILE, BATCH_MODE } from '../constants.js';
|
|
10
10
|
import { cleanLatestRunId, getPackageVersion, applyFilter } from '../utils/utils.js';
|
|
11
11
|
import { config } from '../config.js';
|
|
12
12
|
import { readLatestRunId } from '../utils/utils.js';
|
|
@@ -369,7 +369,7 @@ program
|
|
|
369
369
|
const client = new TestomatClient({
|
|
370
370
|
apiKey,
|
|
371
371
|
runId,
|
|
372
|
-
|
|
372
|
+
batchMode: BATCH_MODE.DISABLED,
|
|
373
373
|
});
|
|
374
374
|
|
|
375
375
|
let testruns = client.uploader.readUploadedFiles(runId);
|
|
@@ -9,6 +9,7 @@ import { config } from '../config.js';
|
|
|
9
9
|
import { readLatestRunId } from '../utils/utils.js';
|
|
10
10
|
import dotenv from 'dotenv';
|
|
11
11
|
import { log } from '../utils/log.js';
|
|
12
|
+
import { BATCH_MODE } from '../constants.js';
|
|
12
13
|
|
|
13
14
|
const debug = createDebugMessages('@testomatio/reporter:upload-cli');
|
|
14
15
|
const version = getPackageVersion();
|
|
@@ -37,7 +38,7 @@ program
|
|
|
37
38
|
const client = new TestomatClient({
|
|
38
39
|
apiKey,
|
|
39
40
|
runId,
|
|
40
|
-
|
|
41
|
+
batchMode: BATCH_MODE.DISABLED,
|
|
41
42
|
});
|
|
42
43
|
let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
|
|
43
44
|
|
package/src/constants.js
CHANGED
|
@@ -28,6 +28,14 @@ const STATUS = {
|
|
|
28
28
|
SKIPPED: 'skipped',
|
|
29
29
|
FINISHED: 'finished',
|
|
30
30
|
};
|
|
31
|
+
|
|
32
|
+
// batch upload mode
|
|
33
|
+
/** @type {{ AUTO: 'auto', MANUAL: 'manual', DISABLED: 'disabled' }} */
|
|
34
|
+
const BATCH_MODE = {
|
|
35
|
+
AUTO: 'auto',
|
|
36
|
+
MANUAL: 'manual',
|
|
37
|
+
DISABLED: 'disabled',
|
|
38
|
+
};
|
|
31
39
|
// html pipe var
|
|
32
40
|
const HTML_REPORT = {
|
|
33
41
|
FOLDER: 'html-report',
|
|
@@ -61,6 +69,7 @@ export {
|
|
|
61
69
|
TESTOMAT_TMP_STORAGE_DIR,
|
|
62
70
|
CSV_HEADERS,
|
|
63
71
|
STATUS,
|
|
72
|
+
BATCH_MODE,
|
|
64
73
|
HTML_REPORT,
|
|
65
74
|
MARKDOWN_REPORT,
|
|
66
75
|
REQUEST_TIMEOUT,
|
package/src/pipe/debug.js
CHANGED
|
@@ -14,13 +14,7 @@ export class DebugPipe {
|
|
|
14
14
|
|
|
15
15
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
16
16
|
if (this.isEnabled) {
|
|
17
|
-
this.
|
|
18
|
-
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
19
|
-
intervalFunction: null,
|
|
20
|
-
intervalTime: 5000,
|
|
21
|
-
tests: [],
|
|
22
|
-
batchIndex: 0,
|
|
23
|
-
};
|
|
17
|
+
this.tests = [];
|
|
24
18
|
const suffix = process.env.TESTOMATIO_REPLAY ? 'replay' : '';
|
|
25
19
|
const paths = getDebugFilePath(suffix);
|
|
26
20
|
this.logFilePath = paths.tmp;
|
|
@@ -60,8 +54,13 @@ export class DebugPipe {
|
|
|
60
54
|
this.logToFile({ datetime: new Date().toISOString(), timestamp: Date.now() });
|
|
61
55
|
this.logToFile({ data: 'variables', testomatioEnvVars: this.testomatioEnvVars });
|
|
62
56
|
this.logToFile({ data: 'store', store: this.store || {} });
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
|
|
58
|
+
// Safety net for hook failures (e.g. a failing AfterSuite) that abort the run
|
|
59
|
+
// before finishRun: buffered tests would otherwise be lost. The handler is
|
|
60
|
+
// attached lazily when the first test is buffered and detached once flushed,
|
|
61
|
+
// so processes that create many pipes don't pile up `exit` listeners.
|
|
62
|
+
this.flushOnExit = () => this.flushBufferedTests();
|
|
63
|
+
this.exitListenerAttached = false;
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
|
|
@@ -88,42 +87,22 @@ export class DebugPipe {
|
|
|
88
87
|
|
|
89
88
|
async createRun(params = {}) {
|
|
90
89
|
if (!this.isEnabled) return;
|
|
91
|
-
if (params.isBatchEnabled === true || params.isBatchEnabled === false) this.batch.isEnabled = params.isBatchEnabled;
|
|
92
|
-
|
|
93
|
-
if (!this.isEnabled) return {};
|
|
94
|
-
if (this.batch.isEnabled) this.batch.intervalFunction = setInterval(this.batchUpload, this.batch.intervalTime);
|
|
95
90
|
|
|
96
91
|
this.logToFile({ action: 'createRun', params });
|
|
97
92
|
}
|
|
98
93
|
|
|
99
94
|
async addTest(data) {
|
|
100
95
|
if (!this.isEnabled) return;
|
|
101
|
-
|
|
102
|
-
if (!this.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
} else this.batch.tests.push(data);
|
|
107
|
-
|
|
108
|
-
if (!this.batch.intervalFunction) await this.batchUpload();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async batchUpload() {
|
|
112
|
-
this.batch.batchIndex++;
|
|
113
|
-
if (!this.batch.isEnabled) return;
|
|
114
|
-
if (!this.batch.tests.length) return;
|
|
115
|
-
|
|
116
|
-
const testsToSend = this.batch.tests.splice(0);
|
|
117
|
-
|
|
118
|
-
const logData = { action: 'addTestsBatch', tests: testsToSend };
|
|
119
|
-
if (this.store.runId) logData.runId = this.store.runId;
|
|
120
|
-
this.logToFile(logData);
|
|
96
|
+
this.tests.push(data);
|
|
97
|
+
if (!this.exitListenerAttached) {
|
|
98
|
+
process.once('exit', this.flushOnExit);
|
|
99
|
+
this.exitListenerAttached = true;
|
|
100
|
+
}
|
|
121
101
|
}
|
|
122
102
|
|
|
123
103
|
async finishRun(params) {
|
|
124
104
|
if (!this.isEnabled) return;
|
|
125
105
|
await this.sync();
|
|
126
|
-
if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
|
|
127
106
|
const logData = { action: 'finishRun', params };
|
|
128
107
|
if (this.store.runId) logData.runId = this.store.runId;
|
|
129
108
|
this.logToFile(logData);
|
|
@@ -133,8 +112,28 @@ export class DebugPipe {
|
|
|
133
112
|
}
|
|
134
113
|
|
|
135
114
|
async sync() {
|
|
136
|
-
|
|
137
|
-
|
|
115
|
+
this.flushBufferedTests();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Writes any buffered tests to the debug file as a single batch.
|
|
120
|
+
* Runs synchronously so it can also be invoked from a process `exit` handler,
|
|
121
|
+
* which is the only chance to persist tests when a hook failure (e.g. a failing
|
|
122
|
+
* AfterSuite) prevents `finishRun` from being reached. Idempotent: the buffer is
|
|
123
|
+
* drained on flush, so a later `finishRun`/exit flush is a no-op.
|
|
124
|
+
*/
|
|
125
|
+
flushBufferedTests() {
|
|
126
|
+
if (!this.isEnabled || !this.tests.length) return;
|
|
127
|
+
|
|
128
|
+
const tests = this.tests.splice(0);
|
|
129
|
+
const logData = { action: 'addTestsBatch', tests };
|
|
130
|
+
if (this.store.runId) logData.runId = this.store.runId;
|
|
131
|
+
this.logToFile(logData);
|
|
132
|
+
|
|
133
|
+
if (this.exitListenerAttached) {
|
|
134
|
+
process.removeListener('exit', this.flushOnExit);
|
|
135
|
+
this.exitListenerAttached = false;
|
|
136
|
+
}
|
|
138
137
|
}
|
|
139
138
|
|
|
140
139
|
toString() {
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -5,6 +5,7 @@ import JsonCycle from 'json-cycle';
|
|
|
5
5
|
import {
|
|
6
6
|
APP_PREFIX,
|
|
7
7
|
STATUS,
|
|
8
|
+
BATCH_MODE,
|
|
8
9
|
REQUEST_TIMEOUT,
|
|
9
10
|
getCreateRunRequestTimeout,
|
|
10
11
|
REPORTER_REQUEST_RETRIES,
|
|
@@ -46,17 +47,25 @@ function parseCiParams(raw) {
|
|
|
46
47
|
/**
|
|
47
48
|
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
48
49
|
* @typedef {import('../../types/types.js').TestData} TestData
|
|
50
|
+
* @typedef {import('../../types/types.js').BatchMode} BatchMode
|
|
51
|
+
* @typedef {import('../../types/types.js').CreateRunParams} CreateRunParams
|
|
49
52
|
* @class TestomatioPipe
|
|
50
53
|
* @implements {Pipe}
|
|
51
54
|
*/
|
|
52
55
|
class TestomatioPipe {
|
|
53
56
|
constructor(params, store) {
|
|
54
57
|
this.batch = {
|
|
55
|
-
|
|
58
|
+
/** @type {BatchMode}
|
|
59
|
+
* Batch upload mode:
|
|
60
|
+
* - `auto`: upload tests automatically by time interval (e.g. every 5 seconds).
|
|
61
|
+
* - `manual`: buffer tests and upload only when `sync()` is invoked manually.
|
|
62
|
+
* - `disabled`: send one test per request, no batching.
|
|
63
|
+
*/
|
|
64
|
+
mode: params.batchMode || (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? BATCH_MODE.DISABLED : BATCH_MODE.AUTO),
|
|
56
65
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
57
|
-
intervalTime:
|
|
66
|
+
intervalTime: 6000, // how often tests are sent
|
|
58
67
|
tests: [], // array of tests in batch
|
|
59
|
-
batchIndex: 0,
|
|
68
|
+
batchIndex: 0, // represents the current batch index (starts from 1 and increments by 1 for each batch)
|
|
60
69
|
numberOfTimesCalledWithoutTests: 0, // how many times batch was called without tests
|
|
61
70
|
};
|
|
62
71
|
this.retriesTimestamps = [];
|
|
@@ -222,13 +231,13 @@ class TestomatioPipe {
|
|
|
222
231
|
|
|
223
232
|
/**
|
|
224
233
|
* Creates a new run on Testomat.io
|
|
225
|
-
* @param {
|
|
234
|
+
* @param {CreateRunParams} params
|
|
226
235
|
* @returns Promise<void>
|
|
227
236
|
*/
|
|
228
237
|
async createRun(params = {}) {
|
|
229
|
-
this.batch.
|
|
238
|
+
if (params.batchMode) this.batch.mode = params.batchMode;
|
|
230
239
|
if (!this.isEnabled) return;
|
|
231
|
-
if (this.batch.
|
|
240
|
+
if (this.batch.mode === BATCH_MODE.AUTO && this.isEnabled)
|
|
232
241
|
this.batch.intervalFunction = setInterval(this.#batchUpload, this.batch.intervalTime);
|
|
233
242
|
if (this.store) {
|
|
234
243
|
this.store.runKind = params.kind;
|
|
@@ -433,14 +442,14 @@ class TestomatioPipe {
|
|
|
433
442
|
* Uploads tests as a batch (multiple tests at once). Intended to be used with a setInterval
|
|
434
443
|
*/
|
|
435
444
|
#batchUpload = async () => {
|
|
436
|
-
if (
|
|
445
|
+
if (this.batch.mode === BATCH_MODE.DISABLED) return;
|
|
437
446
|
if (!this.batch.tests.length) return;
|
|
438
447
|
if (this.#cancelTestReportingInCaseOfTooManyReqFailures()) return;
|
|
439
448
|
// prevent infinite loop
|
|
440
449
|
if (this.batch.numberOfTimesCalledWithoutTests > 10) {
|
|
441
450
|
debug('📨 Batch upload: no tests to send for 10 times, stopping batch');
|
|
442
451
|
clearInterval(this.batch.intervalFunction);
|
|
443
|
-
this.batch.
|
|
452
|
+
this.batch.mode = BATCH_MODE.DISABLED;
|
|
444
453
|
}
|
|
445
454
|
if (!this.batch.tests.length) {
|
|
446
455
|
debug('📨 Batch upload: no tests to send');
|
|
@@ -496,11 +505,11 @@ class TestomatioPipe {
|
|
|
496
505
|
this.#formatData(data);
|
|
497
506
|
|
|
498
507
|
let uploading = null;
|
|
499
|
-
if (
|
|
508
|
+
if (this.batch.mode === BATCH_MODE.DISABLED) uploading = this.#uploadSingleTest(data);
|
|
500
509
|
else this.batch.tests.push(data);
|
|
501
510
|
|
|
502
|
-
//
|
|
503
|
-
if (!this.batch.intervalFunction) uploading = this.#batchUpload();
|
|
511
|
+
// auto mode but no interval running yet (e.g. createRun hasn't started it): flush immediately
|
|
512
|
+
if (this.batch.mode === BATCH_MODE.AUTO && !this.batch.intervalFunction) uploading = this.#batchUpload();
|
|
504
513
|
|
|
505
514
|
// return promise to be able to wait for it
|
|
506
515
|
return uploading;
|
|
@@ -529,7 +538,7 @@ class TestomatioPipe {
|
|
|
529
538
|
// (e.g. if test has artifacts, add test function will be invoked only after artifacts are uploaded)
|
|
530
539
|
// batch stops working after run is finished; thus, disable it to use single test uploading
|
|
531
540
|
this.batch.intervalFunction = null;
|
|
532
|
-
this.batch.
|
|
541
|
+
this.batch.mode = BATCH_MODE.DISABLED;
|
|
533
542
|
}
|
|
534
543
|
|
|
535
544
|
debug('Finishing run...');
|
|
@@ -613,7 +622,7 @@ class TestomatioPipe {
|
|
|
613
622
|
if (this.batch.intervalFunction) {
|
|
614
623
|
clearInterval(this.batch.intervalFunction);
|
|
615
624
|
this.batch.intervalFunction = null;
|
|
616
|
-
this.batch.
|
|
625
|
+
this.batch.mode = BATCH_MODE.DISABLED;
|
|
617
626
|
}
|
|
618
627
|
this.batch.tests = [];
|
|
619
628
|
}
|
package/src/replay.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import TestomatClient from './client.js';
|
|
4
|
-
import { STATUS, DEBUG_FILE } from './constants.js';
|
|
4
|
+
import { STATUS, DEBUG_FILE, BATCH_MODE } from './constants.js';
|
|
5
5
|
import { config } from './config.js';
|
|
6
6
|
|
|
7
7
|
export class Replay {
|
|
@@ -216,7 +216,7 @@ export class Replay {
|
|
|
216
216
|
|
|
217
217
|
const client = new TestomatClient({
|
|
218
218
|
apiKey: this.apiKey,
|
|
219
|
-
|
|
219
|
+
batchMode: BATCH_MODE.AUTO,
|
|
220
220
|
...runParams,
|
|
221
221
|
...(runId && { runId }),
|
|
222
222
|
});
|
package/src/xmlReader.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { XMLParser } from 'fast-xml-parser';
|
|
6
|
-
import { APP_PREFIX, STATUS } from './constants.js';
|
|
6
|
+
import { APP_PREFIX, STATUS, BATCH_MODE } from './constants.js';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { NUnitXmlParser } from './junit-adapter/nunit-parser.js';
|
|
@@ -73,8 +73,7 @@ class XmlReader {
|
|
|
73
73
|
env: TESTOMATIO_ENV,
|
|
74
74
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
75
75
|
detach: TESTOMATIO_MARK_DETACHED,
|
|
76
|
-
|
|
77
|
-
isBatchEnabled: false,
|
|
76
|
+
batchMode: BATCH_MODE.MANUAL,
|
|
78
77
|
};
|
|
79
78
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
80
79
|
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
@@ -543,7 +542,7 @@ class XmlReader {
|
|
|
543
542
|
title: this.requestParams.title,
|
|
544
543
|
env: this.requestParams.env,
|
|
545
544
|
group_title: this.requestParams.group_title,
|
|
546
|
-
|
|
545
|
+
batchMode: this.requestParams.batchMode,
|
|
547
546
|
};
|
|
548
547
|
|
|
549
548
|
debug('Run', runParams);
|
|
@@ -611,7 +610,7 @@ class XmlReader {
|
|
|
611
610
|
this.pipes = this.pipes || (await this.pipesPromise);
|
|
612
611
|
|
|
613
612
|
// Create run before uploading tests to ensure runId is set
|
|
614
|
-
await this.createRun();
|
|
613
|
+
// await this.createRun(); // makes reporting stuck after finish, thus commenting out
|
|
615
614
|
|
|
616
615
|
if (!this.tests || !Array.isArray(this.tests) || this.tests.length === 0) {
|
|
617
616
|
debug('No tests to upload, finishing run');
|
package/types/types.d.ts
CHANGED
|
@@ -234,7 +234,7 @@ export interface HtmlTestData extends TestData {
|
|
|
234
234
|
/**
|
|
235
235
|
* Extended test data for Markdown reporter.
|
|
236
236
|
*/
|
|
237
|
-
export interface MarkdownTestData extends HtmlTestData {}
|
|
237
|
+
export interface MarkdownTestData extends HtmlTestData { }
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
240
|
* Object representing a result of a Run.
|
|
@@ -288,6 +288,13 @@ export enum RunStatus {
|
|
|
288
288
|
Finished = 'finished',
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/** Batch upload strategy:
|
|
292
|
+
* `auto` (by time interval, e.g. every 5 seconds),
|
|
293
|
+
* `manual` (send tests via manually invoking sync() ),
|
|
294
|
+
* `disabled` (one test per request, no batching).
|
|
295
|
+
*/
|
|
296
|
+
export type BatchMode = 'auto' | 'manual' | 'disabled';
|
|
297
|
+
|
|
291
298
|
export interface Pipe {
|
|
292
299
|
isEnabled: boolean;
|
|
293
300
|
store: {};
|
|
@@ -334,8 +341,8 @@ export interface CreateRunParams {
|
|
|
334
341
|
/** Run configuration merged into the server-side run configuration. */
|
|
335
342
|
configuration?: Record<string, any>;
|
|
336
343
|
|
|
337
|
-
/** Override batch upload
|
|
338
|
-
|
|
344
|
+
/** Override batch upload mode. */
|
|
345
|
+
batchMode?: BatchMode;
|
|
339
346
|
}
|
|
340
347
|
|
|
341
348
|
/**
|