@testrevolution/bugbug-cli 12.20.0 → 12.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/__mocks__/@sentry/node.js +8 -0
- package/src/__mocks__/nanoid.js +3 -0
- package/src/commands/remote.js +21 -6
- package/src/index.js +15 -8
- package/src/utils/api.js +15 -10
- package/src/utils/helper.js +18 -8
- package/src/utils/sentry.js +3 -0
- package/src/utils/testReports.js +8 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrevolution/bugbug-cli",
|
|
3
3
|
"description": "BugBug CLI",
|
|
4
|
-
"version": "12.
|
|
4
|
+
"version": "12.21.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
7
7
|
"cli",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"bugbug": "bin/bugbug"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@sentry/node": "^8.55.0",
|
|
22
21
|
"axios": "^1.10.0",
|
|
23
22
|
"console-table-printer": "^2.12.1",
|
|
24
23
|
"dotenv": "^16.4.7",
|
|
25
24
|
"jest-junit": "^16.0.0",
|
|
26
25
|
"junit-xml": "^1.2.0",
|
|
27
26
|
"minimist": "^1.2.8",
|
|
27
|
+
"nanoid": "^5.1.5",
|
|
28
28
|
"ora": "^5.4.1",
|
|
29
29
|
"path": "^0.12.7",
|
|
30
30
|
"validator": "^13.15.0"
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@babel/eslint-parser": "^7.27.1",
|
|
45
45
|
"@babel/plugin-proposal-throw-expressions": "^7.27.1",
|
|
46
|
+
"@sentry/node": "^10.1.0",
|
|
46
47
|
"babel-plugin-rewire": "^1.2.0",
|
|
47
48
|
"cross-env": "^7.0.3",
|
|
48
49
|
"eslint": "^8.57.1",
|
package/src/commands/remote.js
CHANGED
|
@@ -5,7 +5,7 @@ const settings = require('../settings');
|
|
|
5
5
|
const { getSpinner } = require('../utils/spinner');
|
|
6
6
|
const { getConfigValue } = require('../utils/config');
|
|
7
7
|
const {
|
|
8
|
-
getExitCode, parseVariables,
|
|
8
|
+
getExitCode, parseVariables, handleResponseError, getUnknownOptions,
|
|
9
9
|
} = require('../utils/helper');
|
|
10
10
|
const { apiCall, apiCallPoll } = require('../utils/api');
|
|
11
11
|
const {
|
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
generateJunitReport,
|
|
19
19
|
} = require('../utils/testReports');
|
|
20
20
|
const help = require('./help');
|
|
21
|
+
const { sentry } = require('../utils/sentry');
|
|
21
22
|
|
|
22
23
|
const OBJECT_TYPES = [settings.TYPE_TEST, settings.TYPE_SUITE, settings.TYPE_PROFILE];
|
|
23
24
|
|
|
@@ -34,7 +35,7 @@ const getList = async (type, query, extraParams) => {
|
|
|
34
35
|
return getExitCode(true);
|
|
35
36
|
}
|
|
36
37
|
} catch (error) {
|
|
37
|
-
await
|
|
38
|
+
await handleResponseError(spinner, error);
|
|
38
39
|
}
|
|
39
40
|
return getExitCode(false);
|
|
40
41
|
};
|
|
@@ -53,6 +54,7 @@ const getResult = async (type, id, extraParams) => {
|
|
|
53
54
|
if (result) {
|
|
54
55
|
switch (reporter) {
|
|
55
56
|
case settings.REPORTER_TYPE.junit: {
|
|
57
|
+
console.log('Generating JUnit report...');
|
|
56
58
|
await generateJunitReport(type, result.data, outputPath);
|
|
57
59
|
break;
|
|
58
60
|
}
|
|
@@ -68,7 +70,7 @@ const getResult = async (type, id, extraParams) => {
|
|
|
68
70
|
return getExitCode(result.data.status);
|
|
69
71
|
}
|
|
70
72
|
} catch (error) {
|
|
71
|
-
await
|
|
73
|
+
await handleResponseError(spinner, error);
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
return getExitCode(false);
|
|
@@ -101,7 +103,7 @@ const checkStatus = async (type, id, extraParams) => {
|
|
|
101
103
|
}
|
|
102
104
|
exitCode = getExitCode(response.data.status);
|
|
103
105
|
} catch (error) {
|
|
104
|
-
await
|
|
106
|
+
await handleResponseError(spinner, error);
|
|
105
107
|
await getResult(type, id, extraParams);
|
|
106
108
|
exitCode = getExitCode(false);
|
|
107
109
|
}
|
|
@@ -123,7 +125,7 @@ const run = async (type, data, extraParams) => {
|
|
|
123
125
|
return getExitCode(true);
|
|
124
126
|
}
|
|
125
127
|
} catch (error) {
|
|
126
|
-
await
|
|
128
|
+
await handleResponseError(spinner, error);
|
|
127
129
|
}
|
|
128
130
|
return getExitCode(false);
|
|
129
131
|
};
|
|
@@ -149,7 +151,7 @@ const stop = async (type, { id }, extraParams) => {
|
|
|
149
151
|
if (error.response?.status === 404) {
|
|
150
152
|
spinner.fail(`No ${type}Run with id ${id} found.`);
|
|
151
153
|
} else {
|
|
152
|
-
await
|
|
154
|
+
await handleResponseError(spinner, error);
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
return getExitCode(false);
|
|
@@ -178,6 +180,7 @@ const checkTokenConfig = async () => {
|
|
|
178
180
|
}
|
|
179
181
|
} catch (err) {
|
|
180
182
|
console.error(err);
|
|
183
|
+
sentry.captureException(err);
|
|
181
184
|
}
|
|
182
185
|
};
|
|
183
186
|
|
|
@@ -242,6 +245,18 @@ module.exports = async (args) => {
|
|
|
242
245
|
subCmd, objectType, id, variables, extraParams,
|
|
243
246
|
} = await parseArgs(args);
|
|
244
247
|
|
|
248
|
+
sentry.setTags({
|
|
249
|
+
Command: 'remote',
|
|
250
|
+
'Sub command': subCmd,
|
|
251
|
+
'Command object type': objectType,
|
|
252
|
+
'Triggered by': extraParams.triggeredBy,
|
|
253
|
+
Reporter: extraParams.reporter,
|
|
254
|
+
});
|
|
255
|
+
sentry.addBreadcrumb({
|
|
256
|
+
message: `Run "${subCmd}" remote command`,
|
|
257
|
+
category: 'Command',
|
|
258
|
+
});
|
|
259
|
+
|
|
245
260
|
let exitCode;
|
|
246
261
|
await checkTokenConfig();
|
|
247
262
|
switch (subCmd) {
|
package/src/index.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const minimist = require('minimist');
|
|
3
|
-
const
|
|
4
|
-
const sentry = require('./utils/sentry');
|
|
5
|
-
const commands = require('./commands');
|
|
3
|
+
const { nanoid } = require('nanoid');
|
|
4
|
+
const { sentry, init: initSentry } = require('./utils/sentry');
|
|
6
5
|
const settings = require('./settings');
|
|
7
|
-
const config = require('./utils/config');
|
|
8
|
-
const helper = require('./utils/helper');
|
|
9
|
-
|
|
10
6
|
require('dotenv').config();
|
|
11
7
|
require('dotenv').config({
|
|
12
8
|
path: path.resolve(process.cwd(), `.env.${settings.NODE_ENV}`),
|
|
13
9
|
});
|
|
10
|
+
// Initialize Sentry first
|
|
11
|
+
initSentry();
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
const api = require('./utils/api');
|
|
14
|
+
const commands = require('./commands');
|
|
15
|
+
const config = require('./utils/config');
|
|
16
|
+
const helper = require('./utils/helper');
|
|
16
17
|
|
|
17
18
|
module.exports = async () => {
|
|
18
19
|
await config.initConfigDir();
|
|
@@ -21,7 +22,13 @@ module.exports = async () => {
|
|
|
21
22
|
let exitCode;
|
|
22
23
|
|
|
23
24
|
await helper.overrideSettings(args);
|
|
24
|
-
|
|
25
|
+
const processId = nanoid(8);
|
|
26
|
+
api.mountAxiosInterceptors({
|
|
27
|
+
processId,
|
|
28
|
+
debug: `${args.debug}` === 'true',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
sentry.setTag('ProcessId', processId);
|
|
25
32
|
|
|
26
33
|
switch (cmd) {
|
|
27
34
|
case 'config':
|
package/src/utils/api.js
CHANGED
|
@@ -4,8 +4,13 @@ const config = require('./config');
|
|
|
4
4
|
const print = require('./print');
|
|
5
5
|
const { getSpinner } = require('./spinner');
|
|
6
6
|
|
|
7
|
-
const interceptRequest = (
|
|
8
|
-
|
|
7
|
+
const interceptRequest = ({ processId, debug }) => (request) => {
|
|
8
|
+
request.params = {
|
|
9
|
+
...(request.params || {}),
|
|
10
|
+
reqId: processId,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (debug) {
|
|
9
14
|
print.printObject({
|
|
10
15
|
url: request.url,
|
|
11
16
|
headers: request.headers,
|
|
@@ -26,16 +31,16 @@ const printResponseData = (response) => {
|
|
|
26
31
|
});
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
const interceptResponseSuccess = (
|
|
30
|
-
if (
|
|
34
|
+
const interceptResponseSuccess = ({ debug }) => (response) => {
|
|
35
|
+
if (debug) {
|
|
31
36
|
printResponseData(response);
|
|
32
37
|
}
|
|
33
38
|
return response;
|
|
34
39
|
};
|
|
35
40
|
|
|
36
|
-
const interceptResponseError = (spinner,
|
|
41
|
+
const interceptResponseError = (spinner, { debug }) => (error) => {
|
|
37
42
|
const { response } = error;
|
|
38
|
-
if (response &&
|
|
43
|
+
if (response && debug) {
|
|
39
44
|
printResponseData(response);
|
|
40
45
|
}
|
|
41
46
|
if (response && response.status === 400) {
|
|
@@ -66,13 +71,13 @@ const interceptResponseError = (spinner, isDebugEnabled = false) => (error) => {
|
|
|
66
71
|
return Promise.reject(error);
|
|
67
72
|
};
|
|
68
73
|
|
|
69
|
-
const mountAxiosInterceptors = (
|
|
74
|
+
const mountAxiosInterceptors = ({ processId, debug }) => {
|
|
70
75
|
const spinner = getSpinner(false);
|
|
71
76
|
axios.defaults.headers.common['Content-Type'] = 'application/json';
|
|
72
|
-
axios.interceptors.request.use(interceptRequest(
|
|
77
|
+
axios.interceptors.request.use(interceptRequest({ processId, debug }));
|
|
73
78
|
axios.interceptors.response.use(
|
|
74
|
-
interceptResponseSuccess(
|
|
75
|
-
interceptResponseError(spinner,
|
|
79
|
+
interceptResponseSuccess({ debug }),
|
|
80
|
+
interceptResponseError(spinner, { debug }),
|
|
76
81
|
);
|
|
77
82
|
};
|
|
78
83
|
|
package/src/utils/helper.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
const { nanoid } = require('nanoid');
|
|
1
2
|
const settings = require('../settings');
|
|
3
|
+
const { sentry } = require('./sentry');
|
|
2
4
|
|
|
3
5
|
const getExitCode = async (status) => {
|
|
4
6
|
const exitCode = settings.EXIT_CODES[status];
|
|
7
|
+
|
|
8
|
+
// We need to flush the sentry buffer
|
|
9
|
+
// to ensure the error is sent to Sentry before the process exits
|
|
10
|
+
await sentry.flush(2000);
|
|
11
|
+
|
|
5
12
|
return exitCode === undefined ? 1 : exitCode;
|
|
6
13
|
};
|
|
7
14
|
|
|
@@ -25,14 +32,17 @@ const getUnknownOptions = async (args, knownKeys) => {
|
|
|
25
32
|
return unknownKeys;
|
|
26
33
|
};
|
|
27
34
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const handleResponseError = async (spinner, error) => {
|
|
36
|
+
const returnedMessage = error.response?.data[0]?.message;
|
|
37
|
+
const errorId = error.request?.params?.reqId ?? nanoid(8);
|
|
38
|
+
|
|
39
|
+
if (returnedMessage) {
|
|
40
|
+
spinner.fail(returnedMessage);
|
|
41
|
+
return;
|
|
35
42
|
}
|
|
43
|
+
|
|
44
|
+
sentry.captureException(error);
|
|
45
|
+
spinner.fail(`Unexpected error occurred (id: ${errorId})`);
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
const overrideSettings = async (args) => {
|
|
@@ -76,5 +86,5 @@ module.exports = {
|
|
|
76
86
|
overrideSettings,
|
|
77
87
|
parseTimeFromDuration,
|
|
78
88
|
parseVariables,
|
|
79
|
-
|
|
89
|
+
handleResponseError,
|
|
80
90
|
};
|
package/src/utils/sentry.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-extraneous-dependencies, node/no-unpublished-require
|
|
1
2
|
const Sentry = require('@sentry/node');
|
|
2
3
|
const pkgJson = require('../../package.json');
|
|
3
4
|
|
|
@@ -7,9 +8,11 @@ const init = () => {
|
|
|
7
8
|
environment: process.env.ENV,
|
|
8
9
|
release: `${pkgJson.version}-${process.env.ENV}`,
|
|
9
10
|
normalizeDepth: 8,
|
|
11
|
+
sendDefaultPii: true,
|
|
10
12
|
});
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
module.exports = {
|
|
14
16
|
init,
|
|
17
|
+
sentry: Sentry,
|
|
15
18
|
};
|
package/src/utils/testReports.js
CHANGED
|
@@ -6,18 +6,20 @@ const settings = require('../settings');
|
|
|
6
6
|
const { getSecondsFromDuration } = require('./helper');
|
|
7
7
|
|
|
8
8
|
const getTestInJunitFormat = (testRun) => {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const errorCode = testRun.errorCode ?? 'MISSING_ERROR_CODE';
|
|
10
|
+
const errorDetails = {
|
|
11
|
+
message: `Error ${errorCode} occurred in step: ${testRun.details[0].step.type} (${testRun.details[0].step.id})`,
|
|
12
|
+
stack: errorCode,
|
|
13
|
+
};
|
|
14
|
+
const errorsList = errorDetails ? [errorDetails] : [];
|
|
13
15
|
|
|
14
16
|
return {
|
|
15
17
|
id: testRun.id,
|
|
16
18
|
name: testRun.name,
|
|
17
19
|
time: getSecondsFromDuration(testRun.duration),
|
|
18
20
|
classname: null,
|
|
19
|
-
errors: testRun.status === settings.STATUS_ERROR ?
|
|
20
|
-
failures: testRun.status === settings.STATUS_FAILED ?
|
|
21
|
+
errors: testRun.status === settings.STATUS_ERROR ? errorsList : [],
|
|
22
|
+
failures: testRun.status === settings.STATUS_FAILED ? errorsList : [],
|
|
21
23
|
};
|
|
22
24
|
};
|
|
23
25
|
|