@testomatio/reporter 1.1.1-beta-codecept-logger → 1.1.1-beta.1.fix-logger
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -15
- package/lib/adapter/codecept.js +26 -14
- package/lib/adapter/cucumber/current.js +15 -8
- package/lib/adapter/cucumber/legacy.js +2 -1
- package/lib/adapter/cypress-plugin/index.js +46 -26
- package/lib/adapter/jest.js +46 -8
- package/lib/adapter/mocha.js +45 -11
- package/lib/adapter/playwright.js +21 -9
- package/lib/bin/startTest.js +2 -1
- package/lib/client.js +12 -13
- package/lib/config.js +5 -0
- package/lib/{storages/data-storage.js → data-storage.js} +76 -43
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/testomatio.js +4 -2
- package/lib/reporter-functions.js +10 -18
- package/lib/reporter.js +8 -13
- package/lib/{storages/artifact-storage.js → services/artifacts.js} +9 -31
- package/lib/services/index.js +13 -0
- package/lib/{storages/key-value-storage.js → services/key-values.js} +10 -25
- package/lib/{storages → services}/logger.js +15 -33
- package/lib/utils/utils.js +25 -2
- package/lib/xmlReader.js +106 -104
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -130,9 +130,12 @@ class Client {
|
|
|
130
130
|
steps,
|
|
131
131
|
code = null,
|
|
132
132
|
title,
|
|
133
|
+
file,
|
|
133
134
|
suite_title,
|
|
134
135
|
suite_id,
|
|
135
136
|
test_id,
|
|
137
|
+
manuallyAttachedArtifacts,
|
|
138
|
+
meta,
|
|
136
139
|
} = testData;
|
|
137
140
|
let { message = '' } = testData;
|
|
138
141
|
|
|
@@ -146,18 +149,13 @@ class Client {
|
|
|
146
149
|
// Attach logs
|
|
147
150
|
const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
|
|
148
151
|
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
// if (artifactFiles) files.push(...artifactFiles);
|
|
152
|
+
// add artifacts
|
|
153
|
+
if (manuallyAttachedArtifacts?.length) files.push(...manuallyAttachedArtifacts);
|
|
152
154
|
|
|
153
|
-
// // GET KEY-VALUEs from storage
|
|
154
|
-
// const keyValueStorage = require('./storages/key-value-storage');
|
|
155
|
-
// const keyValues = keyValueStorage.get(test_id);
|
|
156
|
-
|
|
157
155
|
const uploadedFiles = [];
|
|
158
156
|
|
|
159
|
-
for (const
|
|
160
|
-
uploadedFiles.push(upload.uploadFileByPath(
|
|
157
|
+
for (const f of files) {
|
|
158
|
+
uploadedFiles.push(upload.uploadFileByPath(f, this.uuid));
|
|
161
159
|
}
|
|
162
160
|
|
|
163
161
|
for (const [idx, buffer] of filesBuffers.entries()) {
|
|
@@ -177,6 +175,7 @@ class Client {
|
|
|
177
175
|
status,
|
|
178
176
|
stack: fullLogs,
|
|
179
177
|
example,
|
|
178
|
+
file,
|
|
180
179
|
code,
|
|
181
180
|
title,
|
|
182
181
|
suite_title,
|
|
@@ -185,7 +184,7 @@ class Client {
|
|
|
185
184
|
message,
|
|
186
185
|
run_time: parseFloat(time),
|
|
187
186
|
artifacts,
|
|
188
|
-
|
|
187
|
+
meta,
|
|
189
188
|
};
|
|
190
189
|
|
|
191
190
|
debug('Adding test run...', data);
|
|
@@ -251,9 +250,9 @@ class Client {
|
|
|
251
250
|
logs = logs?.trim();
|
|
252
251
|
|
|
253
252
|
let testLogs = '';
|
|
254
|
-
if (steps) testLogs += `${chalk.bold.blue('################[ Steps ]################')}\n${steps}`;
|
|
255
|
-
if (logs) testLogs +=
|
|
256
|
-
if (error) testLogs +=
|
|
253
|
+
if (steps) testLogs += `${chalk.bold.blue('################[ Steps ]################')}\n${steps}\n\n`;
|
|
254
|
+
if (logs) testLogs += `${chalk.bold.gray('################[ Logs ]################')}\n${logs}\n\n`;
|
|
255
|
+
if (error) testLogs += `${chalk.bold.red('################[ Failure ]################')}\n${error}`;
|
|
257
256
|
return testLogs;
|
|
258
257
|
}
|
|
259
258
|
|
package/lib/config.js
CHANGED
|
@@ -8,6 +8,10 @@ const debug = require('debug')('@testomatio/reporter:config');
|
|
|
8
8
|
const dotenv = require('dotenv');
|
|
9
9
|
const envFileVars = dotenv.config({ path: '.env' }).parsed; */
|
|
10
10
|
|
|
11
|
+
const TESTOMATIO = process.env.TESTOMATIO || process.env.TESTOMATIO_API_KEY || process.env.TESTOMATIO_TOKEN || '';
|
|
12
|
+
if (TESTOMATIO === 'undefined') console.error('TESTOMATIO is "undefined". Something went wrong. Contact dev team.');
|
|
13
|
+
process.env.TESTOMATIO = TESTOMATIO;
|
|
14
|
+
|
|
11
15
|
// select only TESTOMATIO related variables (only to print them in debug)
|
|
12
16
|
const testomatioEnvVars =
|
|
13
17
|
Object.keys(process.env)
|
|
@@ -22,3 +26,4 @@ debug('TESTOMATIO variables:', testomatioEnvVars);
|
|
|
22
26
|
const config = process.env;
|
|
23
27
|
|
|
24
28
|
module.exports = config;
|
|
29
|
+
module.exports.TESTOMATIO = TESTOMATIO;
|
|
@@ -3,25 +3,38 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const { join } = require('path');
|
|
6
|
-
const { TESTOMAT_TMP_STORAGE_DIR } = require('
|
|
7
|
-
const { fileSystem } = require('
|
|
6
|
+
const { TESTOMAT_TMP_STORAGE_DIR } = require('./constants');
|
|
7
|
+
const { fileSystem, jestHelpers } = require('./utils/utils');
|
|
8
8
|
|
|
9
9
|
class DataStorage {
|
|
10
|
+
static #instance;
|
|
11
|
+
|
|
12
|
+
context;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @returns {DataStorage}
|
|
17
|
+
*/
|
|
18
|
+
static getInstance() {
|
|
19
|
+
if (!this.#instance) {
|
|
20
|
+
this.#instance = new DataStorage();
|
|
21
|
+
}
|
|
22
|
+
return this.#instance;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setContext(context) {
|
|
26
|
+
this.context = context;
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
/**
|
|
11
|
-
* Creates data storage instance
|
|
12
|
-
* Stores data to global variable or to file depending on what is applicable for current test runner
|
|
30
|
+
* Creates data storage instance as singleton
|
|
31
|
+
* Stores data to global variable or to file depending on what is applicable for current test runner (adapter)
|
|
13
32
|
* Recommend to use composition while using this class (instead of inheritance).
|
|
14
33
|
* ! Also the class which will use data storage should be singleton (to avoid data loss).
|
|
15
|
-
* But the data storage itself is not singleton. It will have an instance per data type.
|
|
16
|
-
* @param {'log' | 'artifact' | 'keyvalue'} dataType – type of data to store
|
|
17
34
|
*/
|
|
18
|
-
constructor(
|
|
19
|
-
this.dataType = dataType || 'data';
|
|
20
|
-
|
|
35
|
+
constructor() {
|
|
21
36
|
// some frameworks use global variable to store data, some use file storage
|
|
22
37
|
this.isFileStorage = true;
|
|
23
|
-
|
|
24
|
-
this.dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, this.dataType);
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
#stringToFilename(str) {
|
|
@@ -37,23 +50,28 @@ class DataStorage {
|
|
|
37
50
|
/**
|
|
38
51
|
* Puts any data to storage (file or global variable).
|
|
39
52
|
* If file: stores data as text, if global variable – stores as array of data.
|
|
53
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
40
54
|
* @param {*} data anything you want to store (string, object, array, etc)
|
|
41
55
|
* @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
|
|
42
56
|
* suite name + test name is used by default
|
|
43
57
|
* @returns
|
|
44
58
|
*/
|
|
45
|
-
putData(data, context = null) {
|
|
46
|
-
|
|
59
|
+
putData(dataType, data, context = null) {
|
|
60
|
+
if (!dataType || !data) return;
|
|
61
|
+
|
|
62
|
+
context = context || this.context || global.testomatioTestTitle || jestHelpers.getIdOfCurrentlyRunningTest();
|
|
47
63
|
if (!context) {
|
|
48
|
-
debug(`No context provided for "${
|
|
64
|
+
debug(`No context provided for "${dataType}" data:`, data);
|
|
49
65
|
return;
|
|
50
66
|
}
|
|
51
67
|
context = this.#stringToFilename(context);
|
|
52
68
|
|
|
53
69
|
if (this.isFileStorage) {
|
|
54
|
-
|
|
70
|
+
const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
|
|
71
|
+
fileSystem.createDir(dataDirPath);
|
|
72
|
+
this.#putDataToFile(dataType, data, context);
|
|
55
73
|
} else {
|
|
56
|
-
this.#putDataToGlobalVar(data, context);
|
|
74
|
+
this.#putDataToGlobalVar(dataType, data, context);
|
|
57
75
|
}
|
|
58
76
|
}
|
|
59
77
|
|
|
@@ -61,12 +79,16 @@ class DataStorage {
|
|
|
61
79
|
* Returns data, stored for specific test/context (or data which was stored without test id specified).
|
|
62
80
|
* This method will get data from global variable and/or from from file (previosly saved with put method).
|
|
63
81
|
*
|
|
82
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
64
83
|
* @param {string} context
|
|
65
|
-
* @returns {any []} of data (any type), null (if no data found for
|
|
84
|
+
* @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
|
|
66
85
|
*/
|
|
67
|
-
getData(context) {
|
|
86
|
+
getData(dataType, context) {
|
|
87
|
+
// TODO: think if it could be useful
|
|
88
|
+
// context = context || this.context || global.testomatioTestTitle || jestHelpers.getIdOfCurrentlyRunningTest();
|
|
89
|
+
|
|
68
90
|
if (!context) {
|
|
69
|
-
debug(`
|
|
91
|
+
debug(`Trying to get "${dataType}" data without context`);
|
|
70
92
|
return null;
|
|
71
93
|
}
|
|
72
94
|
|
|
@@ -76,36 +98,37 @@ class DataStorage {
|
|
|
76
98
|
let testDataFromGlobalVar = [];
|
|
77
99
|
|
|
78
100
|
if (global?.testomatioDataStore) {
|
|
79
|
-
testDataFromGlobalVar = this.#getDataFromGlobalVar(context);
|
|
101
|
+
testDataFromGlobalVar = this.#getDataFromGlobalVar(dataType, context);
|
|
80
102
|
if (testDataFromGlobalVar) {
|
|
81
103
|
if (testDataFromGlobalVar.length) return testDataFromGlobalVar;
|
|
82
104
|
}
|
|
83
105
|
// don't return nothing if no data in global variable
|
|
84
106
|
}
|
|
85
107
|
|
|
86
|
-
testDataFromFile = this.#getDataFromFile(context);
|
|
108
|
+
testDataFromFile = this.#getDataFromFile(dataType, context);
|
|
87
109
|
|
|
88
110
|
if (testDataFromFile.length) {
|
|
89
111
|
return testDataFromFile;
|
|
90
112
|
}
|
|
91
|
-
debug(`No "${
|
|
113
|
+
debug(`No "${dataType}" data for context "${context}" in both file and global variable`);
|
|
92
114
|
|
|
93
|
-
// in case no data found for
|
|
115
|
+
// in case no data found for context
|
|
94
116
|
return null;
|
|
95
117
|
}
|
|
96
118
|
|
|
97
119
|
/**
|
|
120
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
98
121
|
* @param {string} context
|
|
99
122
|
* @returns aray of data (any type)
|
|
100
123
|
*/
|
|
101
|
-
#getDataFromGlobalVar(context) {
|
|
124
|
+
#getDataFromGlobalVar(dataType, context) {
|
|
102
125
|
try {
|
|
103
|
-
if (global?.testomatioDataStore[
|
|
104
|
-
const testData = global.testomatioDataStore[
|
|
105
|
-
if (testData) debug(`"${
|
|
126
|
+
if (global?.testomatioDataStore[dataType]) {
|
|
127
|
+
const testData = global.testomatioDataStore[dataType][context];
|
|
128
|
+
if (testData) debug(`"${dataType}" data for constext "${context}":`, testData.join(', '));
|
|
106
129
|
return testData || [];
|
|
107
130
|
}
|
|
108
|
-
// debug(`No ${this.dataType} data for
|
|
131
|
+
// debug(`No ${this.dataType} data for context ${context} in <global> storage`);
|
|
109
132
|
return [];
|
|
110
133
|
} catch (e) {
|
|
111
134
|
// there could be no data, ignore
|
|
@@ -113,20 +136,21 @@ class DataStorage {
|
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
/**
|
|
116
|
-
*
|
|
139
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
117
140
|
* @param {*} context
|
|
118
141
|
* @returns array of data (any type)
|
|
119
142
|
*/
|
|
120
|
-
#getDataFromFile(context) {
|
|
143
|
+
#getDataFromFile(dataType, context) {
|
|
144
|
+
const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
|
|
121
145
|
try {
|
|
122
|
-
const filepath = join(
|
|
146
|
+
const filepath = join(dataDirPath, `${dataType}_${context}`);
|
|
123
147
|
if (fs.existsSync(filepath)) {
|
|
124
148
|
const testDataAsText = fs.readFileSync(filepath, 'utf-8');
|
|
125
|
-
if (testDataAsText) debug(`"${
|
|
149
|
+
if (testDataAsText) debug(`"${dataType}" data for context "${context}":`, testDataAsText);
|
|
126
150
|
const testDataArr = testDataAsText?.split(os.EOL) || [];
|
|
127
151
|
return testDataArr;
|
|
128
152
|
}
|
|
129
|
-
// debug(`No ${this.dataType} data for
|
|
153
|
+
// debug(`No ${this.dataType} data for ${context} in <file> storage`);
|
|
130
154
|
return [];
|
|
131
155
|
} catch (e) {
|
|
132
156
|
// there could be no data, ignore
|
|
@@ -136,24 +160,33 @@ class DataStorage {
|
|
|
136
160
|
|
|
137
161
|
/**
|
|
138
162
|
* Puts data to global variable. Unlike the file storage, stores data in array (file storage just append as string).
|
|
163
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
139
164
|
* @param {*} data
|
|
140
165
|
* @param {*} context
|
|
141
166
|
*/
|
|
142
|
-
#putDataToGlobalVar(data, context) {
|
|
167
|
+
#putDataToGlobalVar(dataType, data, context) {
|
|
143
168
|
debug('Saving data to global variable for ', context, ':', data);
|
|
144
169
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
145
|
-
if (!global.testomatioDataStore?.[
|
|
170
|
+
if (!global.testomatioDataStore?.[dataType]) global.testomatioDataStore[dataType] = {};
|
|
146
171
|
|
|
147
|
-
if (!global.testomatioDataStore?.[
|
|
148
|
-
global.testomatioDataStore[
|
|
172
|
+
if (!global.testomatioDataStore?.[dataType][context]) global.testomatioDataStore[dataType][context] = [];
|
|
173
|
+
global.testomatioDataStore[dataType][context].push(data);
|
|
149
174
|
}
|
|
150
175
|
|
|
151
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Puts data to file. Unlike the global variable storage, stores data as string
|
|
178
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
179
|
+
* @param {*} data
|
|
180
|
+
* @param {string} context
|
|
181
|
+
* @returns
|
|
182
|
+
*/
|
|
183
|
+
#putDataToFile(dataType, data, context) {
|
|
184
|
+
const dataDirPath = path.join(TESTOMAT_TMP_STORAGE_DIR, dataType);
|
|
152
185
|
if (typeof data !== 'string') data = JSON.stringify(data);
|
|
153
|
-
const filename = `${
|
|
154
|
-
const filepath = join(
|
|
155
|
-
if (!fs.existsSync(
|
|
156
|
-
debug(
|
|
186
|
+
const filename = `${dataType}_${context}`;
|
|
187
|
+
const filepath = join(dataDirPath, filename);
|
|
188
|
+
if (!fs.existsSync(dataDirPath)) fileSystem.createDir(dataDirPath);
|
|
189
|
+
debug(`Saving data to file for context "${context}" to ${filepath}. Data: ${JSON.stringify(data)}`);
|
|
157
190
|
|
|
158
191
|
// append new line if file already exists (in this case its definitely includes some data)
|
|
159
192
|
if (fs.existsSync(filepath)) {
|
|
@@ -164,7 +197,7 @@ class DataStorage {
|
|
|
164
197
|
}
|
|
165
198
|
}
|
|
166
199
|
|
|
167
|
-
module.exports = DataStorage;
|
|
200
|
+
module.exports.dataStorage = DataStorage.getInstance();
|
|
168
201
|
|
|
169
202
|
// TODO: consider using fs promises instead of writeSync/appendFileSync to
|
|
170
203
|
// prevent blocking and improve performance (probably queue usage will be required)
|
|
@@ -6,8 +6,7 @@ const RubyAdapter = require('./ruby');
|
|
|
6
6
|
const CSharpAdapter = require('./csharp');
|
|
7
7
|
|
|
8
8
|
function AdapterFactory(lang, opts) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
|
|
11
10
|
if (lang === 'java') {
|
|
12
11
|
return new JavaAdapter(opts);
|
|
13
12
|
}
|
|
@@ -23,6 +22,8 @@ function AdapterFactory(lang, opts) {
|
|
|
23
22
|
if (lang === 'c#' || lang === 'csharp') {
|
|
24
23
|
return new CSharpAdapter(opts);
|
|
25
24
|
}
|
|
25
|
+
|
|
26
|
+
return new Adapter(opts);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
module.exports = AdapterFactory;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -5,6 +5,7 @@ const JsonCycle = require('json-cycle');
|
|
|
5
5
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
6
6
|
const { isValidUrl, foundedTestLog } = require('../utils/utils');
|
|
7
7
|
const { parseFilterParams, generateFilterRequestParams, setS3Credentials, } = require('../utils/pipe_utils');
|
|
8
|
+
const { TESTOMATIO } = require('../config');
|
|
8
9
|
|
|
9
10
|
if (process.env.TESTOMATIO_RUN) {
|
|
10
11
|
process.env.runId = process.env.TESTOMATIO_RUN;
|
|
@@ -20,7 +21,7 @@ class TestomatioPipe {
|
|
|
20
21
|
constructor(params, store) {
|
|
21
22
|
this.isEnabled = false;
|
|
22
23
|
this.url = params.testomatioUrl || process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
this.apiKey = params.apiKey ||
|
|
24
|
+
this.apiKey = params.apiKey || TESTOMATIO;
|
|
24
25
|
debug('Testomatio Pipe: ', this.apiKey ? 'API KEY' : '*no api key*');
|
|
25
26
|
if (!this.apiKey) {
|
|
26
27
|
return;
|
|
@@ -175,13 +176,14 @@ class TestomatioPipe {
|
|
|
175
176
|
* @returns
|
|
176
177
|
*/
|
|
177
178
|
addTest(data) {
|
|
178
|
-
debug('Adding test...');
|
|
179
179
|
if (!this.isEnabled) return;
|
|
180
180
|
if (!this.runId) return;
|
|
181
181
|
data.api_key = this.apiKey;
|
|
182
182
|
data.create = this.createNewTests;
|
|
183
183
|
const json = JsonCycle.stringify(data);
|
|
184
184
|
|
|
185
|
+
debug('Adding test', json);
|
|
186
|
+
|
|
185
187
|
return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, {
|
|
186
188
|
maxContentLength: Infinity,
|
|
187
189
|
maxBodyLength: Infinity,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const {
|
|
3
|
-
|
|
1
|
+
const { services } = require('./services');
|
|
2
|
+
const { initPlaywrightForStorage } = require('./adapter/playwright');
|
|
3
|
+
|
|
4
|
+
if (process.env.PLAYWRIGHT_TEST_BASE_URL) {
|
|
5
|
+
initPlaywrightForStorage();
|
|
6
|
+
}
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Stores path to file as artifact and uploads it to the S3 storage
|
|
@@ -8,7 +11,7 @@ const { keyValueStorage } = require('./storages/key-value-storage');
|
|
|
8
11
|
*/
|
|
9
12
|
function saveArtifact(data, context = null) {
|
|
10
13
|
if (!data) return;
|
|
11
|
-
|
|
14
|
+
services.artifacts.put(data, context);
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
/**
|
|
@@ -16,7 +19,7 @@ function saveArtifact(data, context = null) {
|
|
|
16
19
|
* @param {...any} args
|
|
17
20
|
*/
|
|
18
21
|
function logMessage(...args) {
|
|
19
|
-
logger.
|
|
22
|
+
services.logger._templateLiteralLog(...args);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
/**
|
|
@@ -24,7 +27,7 @@ function logMessage(...args) {
|
|
|
24
27
|
* @param {*} message
|
|
25
28
|
*/
|
|
26
29
|
function addStep(message) {
|
|
27
|
-
logger.step(message);
|
|
30
|
+
services.logger.step(message);
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/**
|
|
@@ -32,17 +35,7 @@ function addStep(message) {
|
|
|
32
35
|
* @param {*} keyValue
|
|
33
36
|
*/
|
|
34
37
|
function setKeyValue(keyValue) {
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
* @param {string} context – test id or test title or suite title + test title
|
|
41
|
-
*/
|
|
42
|
-
function _setContext(context) {
|
|
43
|
-
logger.setContext(context);
|
|
44
|
-
keyValueStorage.setContext(context);
|
|
45
|
-
artifactStorage.setContext(context);
|
|
38
|
+
services.keyValues.put(keyValue);
|
|
46
39
|
}
|
|
47
40
|
|
|
48
41
|
module.exports = {
|
|
@@ -50,5 +43,4 @@ module.exports = {
|
|
|
50
43
|
log: logMessage,
|
|
51
44
|
step: addStep,
|
|
52
45
|
keyValue: setKeyValue,
|
|
53
|
-
_setContext,
|
|
54
46
|
};
|
package/lib/reporter.js
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
|
-
const { logger } = require('./storages/logger');
|
|
2
1
|
const TestomatClient = require('./client');
|
|
3
2
|
const TRConstants = require('./constants');
|
|
3
|
+
const { services } = require('./services');
|
|
4
4
|
|
|
5
|
-
const log = logger.templateLiteralLog.bind(logger);
|
|
6
|
-
const step = logger.step.bind(logger);
|
|
7
5
|
const reporterFunctions = require('./reporter-functions');
|
|
8
6
|
|
|
9
7
|
module.exports = {
|
|
10
|
-
|
|
11
|
-
testomatioLogger: logger,
|
|
12
|
-
log,
|
|
13
|
-
step,
|
|
14
|
-
TestomatClient,
|
|
15
|
-
TRConstants,
|
|
16
|
-
};
|
|
8
|
+
// TODO: deprecate in future; use log or testomat.log
|
|
9
|
+
testomatioLogger: services.logger,
|
|
17
10
|
|
|
18
|
-
module.exports.testomat = {
|
|
19
11
|
artifact: reporterFunctions.artifact,
|
|
20
12
|
log: reporterFunctions.log,
|
|
21
|
-
|
|
13
|
+
logger: services.logger,
|
|
22
14
|
meta: reporterFunctions.keyValue,
|
|
23
|
-
|
|
15
|
+
step: reporterFunctions.step,
|
|
16
|
+
|
|
17
|
+
TestomatClient,
|
|
18
|
+
TRConstants,
|
|
24
19
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const debug = require('debug')('@testomatio/reporter:
|
|
2
|
-
const
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:services-artifacts');
|
|
2
|
+
const { dataStorage } = require('../data-storage');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Artifact storage is supposed to store file paths
|
|
@@ -7,18 +7,6 @@ const DataStorage = require('./data-storage');
|
|
|
7
7
|
class ArtifactStorage {
|
|
8
8
|
static #instance;
|
|
9
9
|
|
|
10
|
-
#context;
|
|
11
|
-
|
|
12
|
-
// there is autocompletion for the class methods if implemented singleton this way
|
|
13
|
-
constructor() {
|
|
14
|
-
this.dataStorage = new DataStorage('artifact');
|
|
15
|
-
|
|
16
|
-
// singleton
|
|
17
|
-
if (!ArtifactStorage.#instance) {
|
|
18
|
-
ArtifactStorage.#instance = this;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
10
|
/**
|
|
23
11
|
* Singleton
|
|
24
12
|
* @returns {ArtifactStorage}
|
|
@@ -30,39 +18,28 @@ class ArtifactStorage {
|
|
|
30
18
|
return this.#instance;
|
|
31
19
|
}
|
|
32
20
|
|
|
33
|
-
/**
|
|
34
|
-
* @param {string} context - suite title + test title
|
|
35
|
-
*/
|
|
36
|
-
setContext(context) {
|
|
37
|
-
this.#context = context;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
21
|
/**
|
|
41
22
|
* Stores path to file as artifact and uploads it to the S3 storage
|
|
42
23
|
* @param {string | {path: string, type: string, name: string}} data - path to file or object with path, type and name
|
|
43
24
|
* @param {*} context testId or test title
|
|
44
25
|
*/
|
|
45
26
|
put(data, context = null) {
|
|
46
|
-
context = context || this.#context;
|
|
47
27
|
if (!data) return;
|
|
48
28
|
debug('Save artifact:', data);
|
|
49
|
-
|
|
29
|
+
dataStorage.putData('artifact', data, context);
|
|
50
30
|
}
|
|
51
31
|
|
|
52
32
|
/**
|
|
53
33
|
* Returns list of artifacts to upload
|
|
54
34
|
* @param {*} context testId or test context from test runner
|
|
55
|
-
* @returns {string | {path: string, type: string, name: string}[]}
|
|
35
|
+
* @returns {(string | {path: string, type: string, name: string})[]}
|
|
56
36
|
*/
|
|
57
37
|
get(context) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// array of any data
|
|
61
|
-
let artifacts = this.dataStorage.getData(context);
|
|
62
|
-
if (!artifacts || !artifacts.length) return null;
|
|
38
|
+
let artifacts = dataStorage.getData('artifact', context);
|
|
39
|
+
if (!artifacts || !artifacts.length) return [];
|
|
63
40
|
|
|
64
41
|
artifacts = artifacts.map(artifactData => {
|
|
65
|
-
// artifact could be an object ({type, path, name} props) or string
|
|
42
|
+
// artifact could be an object ({type, path, name} props) or string (just path)
|
|
66
43
|
let artifact;
|
|
67
44
|
try {
|
|
68
45
|
artifact = JSON.parse(artifactData);
|
|
@@ -72,7 +49,8 @@ class ArtifactStorage {
|
|
|
72
49
|
return artifact;
|
|
73
50
|
});
|
|
74
51
|
artifacts = artifacts.filter(artifact => !!artifact);
|
|
75
|
-
|
|
52
|
+
debug(`Artifacts for test ${context}:`, artifacts);
|
|
53
|
+
return artifacts.length ? artifacts : [];
|
|
76
54
|
}
|
|
77
55
|
}
|
|
78
56
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { logger } = require('./logger');
|
|
2
|
+
const { artifactStorage } = require('./artifacts');
|
|
3
|
+
const { keyValueStorage } = require('./key-values');
|
|
4
|
+
const { dataStorage } = require('../data-storage');
|
|
5
|
+
|
|
6
|
+
module.exports.services = {
|
|
7
|
+
logger,
|
|
8
|
+
artifacts: artifactStorage,
|
|
9
|
+
keyValues: keyValueStorage,
|
|
10
|
+
setContext: context => {
|
|
11
|
+
dataStorage.setContext(context);
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
const debug = require('debug')('@testomatio/reporter:key-value
|
|
2
|
-
const
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:services-key-value');
|
|
2
|
+
const { dataStorage } = require('../data-storage');
|
|
3
3
|
|
|
4
4
|
class KeyValueStorage {
|
|
5
5
|
static #instance;
|
|
6
6
|
|
|
7
|
-
#context;
|
|
8
|
-
|
|
9
|
-
constructor() {
|
|
10
|
-
this.dataStorage = new DataStorage('keyvalue');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
7
|
/**
|
|
14
|
-
*
|
|
8
|
+
*
|
|
15
9
|
* @returns {KeyValueStorage}
|
|
16
10
|
*/
|
|
17
11
|
static getInstance() {
|
|
@@ -21,20 +15,14 @@ class KeyValueStorage {
|
|
|
21
15
|
return this.#instance;
|
|
22
16
|
}
|
|
23
17
|
|
|
24
|
-
/**
|
|
25
|
-
* @param {string} context - suite title + test title
|
|
26
|
-
*/
|
|
27
|
-
setContext(context) {
|
|
28
|
-
this.#context = context;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
18
|
/**
|
|
32
19
|
* Stores key-value pair and passes it to reporter
|
|
33
20
|
* @param {{key: string}} keyValue - key-value pair(s) as object
|
|
21
|
+
* @param {*} context - full test title
|
|
34
22
|
*/
|
|
35
|
-
put(keyValue) {
|
|
23
|
+
put(keyValue, context = null) {
|
|
36
24
|
if (!keyValue) return;
|
|
37
|
-
|
|
25
|
+
dataStorage.putData('keyvalue', keyValue, context);
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
#isKeyValueObject(smth) {
|
|
@@ -44,14 +32,11 @@ class KeyValueStorage {
|
|
|
44
32
|
/**
|
|
45
33
|
* Returns key-values pairs for the test as object
|
|
46
34
|
* @param {*} context testId or test context from test runner
|
|
47
|
-
* @returns {
|
|
35
|
+
* @returns {{[key: string]: string} | {}} key-values pairs as object, e.g. {priority: 'high', browser: 'chrome'}
|
|
48
36
|
*/
|
|
49
37
|
get(context = null) {
|
|
50
|
-
|
|
51
|
-
if (!
|
|
52
|
-
|
|
53
|
-
const keyValuesList = this.dataStorage.getData(context);
|
|
54
|
-
if (!keyValuesList || !keyValuesList?.length) return null;
|
|
38
|
+
const keyValuesList = dataStorage.getData('keyvalue', context);
|
|
39
|
+
if (!keyValuesList || !keyValuesList?.length) return {};
|
|
55
40
|
|
|
56
41
|
const keyValues = {};
|
|
57
42
|
for (const keyValue of keyValuesList) {
|
|
@@ -66,7 +51,7 @@ class KeyValueStorage {
|
|
|
66
51
|
}
|
|
67
52
|
}
|
|
68
53
|
|
|
69
|
-
return
|
|
54
|
+
return keyValues;
|
|
70
55
|
}
|
|
71
56
|
}
|
|
72
57
|
|