@testrevolution/bugbug-cli 7.8.1 → 7.10.1
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 +4 -2
- package/package.json +4 -2
- package/src/__tests__/commands.spec.js +36 -0
- package/src/commands/remote.js +31 -8
- package/src/settings.js +9 -0
- package/src/utils/helper.js +20 -0
- package/src/utils/testReports.js +57 -0
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ bugbug remote <option>
|
|
|
34
34
|
|
|
35
35
|
options:
|
|
36
36
|
* list [test|suite|profile] [--no-wait] [--no-progress] [--debug]
|
|
37
|
-
* run [test|suite] <string:testId|suiteId> [--no-wait] [--no-progress] [--debug] [--with-details] [--profile] [--variable]
|
|
37
|
+
* run [test|suite] <string:testId|suiteId> [--no-wait] [--no-progress] [--debug] [--with-details] [--profile] [--variable] [--reporter] [--output-path]
|
|
38
38
|
* status [test|suite] <string:testRunId|suiteRunId> [--no-progress] [--debug]
|
|
39
39
|
* result [test|suite] <string:testRunId|suiteRunId> [--no-progress] [--debug] [--with-details]
|
|
40
40
|
|
|
@@ -43,8 +43,10 @@ optional flags:
|
|
|
43
43
|
* --no-progress - don't show progress spinner
|
|
44
44
|
* --no-wait - exit immediately, don't wait for result
|
|
45
45
|
* --profile <string:"profile name"> - run with specific profile
|
|
46
|
-
* --variable <string:"varName=varValue"> -
|
|
46
|
+
* --variable <string:"varName=varValue"> - override variable during single run
|
|
47
47
|
* --with-details - show result with details
|
|
48
|
+
* --reporter <"inline"|"junit"> - the name of the reporter to use (default: "inline")
|
|
49
|
+
* --output-path - the path to save the test report; relative to the current working directory
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
Run test:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrevolution/bugbug-cli",
|
|
3
3
|
"description": "BugBug CLI",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.10.1",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
7
7
|
"cli",
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
"axios": "^1.6.2",
|
|
23
23
|
"console-table-printer": "^2.11.2",
|
|
24
24
|
"dotenv": "^16.3.1",
|
|
25
|
+
"junit-xml": "^1.2.0",
|
|
25
26
|
"minimist": "^1.2.8",
|
|
26
27
|
"ora": "^5.4.1",
|
|
27
28
|
"path": "^0.12.7",
|
|
28
29
|
"validator": "^13.11.0"
|
|
29
30
|
},
|
|
30
31
|
"scripts": {
|
|
31
|
-
"start": "NODE_ENV=development node ./bin/bugbug",
|
|
32
|
+
"start": "cross-env NODE_ENV=development node ./bin/bugbug",
|
|
32
33
|
"test": "jest --coverage",
|
|
33
34
|
"lint": "eslint --report-unused-disable-directives ./src/**/*.js",
|
|
34
35
|
"lint:fix": "eslint ./src/**/*.js --fix",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"@babel/eslint-parser": "^7.23.3",
|
|
43
44
|
"@babel/plugin-proposal-throw-expressions": "^7.23.3",
|
|
44
45
|
"babel-plugin-rewire": "^1.2.0",
|
|
46
|
+
"cross-env": "^7.0.3",
|
|
45
47
|
"eslint": "^8.53.0",
|
|
46
48
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
47
49
|
"eslint-plugin-import": "^2.29.0",
|
|
@@ -179,6 +179,8 @@ describe('commands module', () => {
|
|
|
179
179
|
noprogress: true,
|
|
180
180
|
nowait: false,
|
|
181
181
|
withDetails: false,
|
|
182
|
+
reporter: 'inline',
|
|
183
|
+
outputPath: 'test-report.xml',
|
|
182
184
|
});
|
|
183
185
|
});
|
|
184
186
|
|
|
@@ -196,6 +198,8 @@ describe('commands module', () => {
|
|
|
196
198
|
noprogress: true,
|
|
197
199
|
nowait: false,
|
|
198
200
|
withDetails: false,
|
|
201
|
+
reporter: 'inline',
|
|
202
|
+
outputPath: 'test-report.xml',
|
|
199
203
|
});
|
|
200
204
|
});
|
|
201
205
|
|
|
@@ -213,6 +217,8 @@ describe('commands module', () => {
|
|
|
213
217
|
noprogress: true,
|
|
214
218
|
nowait: false,
|
|
215
219
|
withDetails: false,
|
|
220
|
+
reporter: 'inline',
|
|
221
|
+
outputPath: 'test-report.xml',
|
|
216
222
|
});
|
|
217
223
|
});
|
|
218
224
|
|
|
@@ -230,6 +236,8 @@ describe('commands module', () => {
|
|
|
230
236
|
noprogress: true,
|
|
231
237
|
nowait: false,
|
|
232
238
|
withDetails: false,
|
|
239
|
+
reporter: 'inline',
|
|
240
|
+
outputPath: 'test-report.xml',
|
|
233
241
|
});
|
|
234
242
|
});
|
|
235
243
|
|
|
@@ -247,6 +255,8 @@ describe('commands module', () => {
|
|
|
247
255
|
noprogress: true,
|
|
248
256
|
nowait: false,
|
|
249
257
|
withDetails: false,
|
|
258
|
+
reporter: 'inline',
|
|
259
|
+
outputPath: 'test-report.xml',
|
|
250
260
|
});
|
|
251
261
|
});
|
|
252
262
|
|
|
@@ -266,6 +276,8 @@ describe('commands module', () => {
|
|
|
266
276
|
noprogress: true,
|
|
267
277
|
nowait: false,
|
|
268
278
|
withDetails: false,
|
|
279
|
+
reporter: 'inline',
|
|
280
|
+
outputPath: 'test-report.xml',
|
|
269
281
|
});
|
|
270
282
|
});
|
|
271
283
|
|
|
@@ -284,6 +296,8 @@ describe('commands module', () => {
|
|
|
284
296
|
noprogress: true,
|
|
285
297
|
nowait: false,
|
|
286
298
|
withDetails: false,
|
|
299
|
+
reporter: 'inline',
|
|
300
|
+
outputPath: 'test-report.xml',
|
|
287
301
|
});
|
|
288
302
|
});
|
|
289
303
|
|
|
@@ -302,6 +316,8 @@ describe('commands module', () => {
|
|
|
302
316
|
noprogress: true,
|
|
303
317
|
nowait: true,
|
|
304
318
|
withDetails: false,
|
|
319
|
+
reporter: 'inline',
|
|
320
|
+
outputPath: 'test-report.xml',
|
|
305
321
|
});
|
|
306
322
|
});
|
|
307
323
|
|
|
@@ -320,6 +336,8 @@ describe('commands module', () => {
|
|
|
320
336
|
noprogress: true,
|
|
321
337
|
nowait: true,
|
|
322
338
|
withDetails: false,
|
|
339
|
+
reporter: 'inline',
|
|
340
|
+
outputPath: 'test-report.xml',
|
|
323
341
|
});
|
|
324
342
|
});
|
|
325
343
|
|
|
@@ -338,6 +356,8 @@ describe('commands module', () => {
|
|
|
338
356
|
noprogress: true,
|
|
339
357
|
nowait: true,
|
|
340
358
|
withDetails: false,
|
|
359
|
+
reporter: 'inline',
|
|
360
|
+
outputPath: 'test-report.xml',
|
|
341
361
|
});
|
|
342
362
|
});
|
|
343
363
|
|
|
@@ -355,6 +375,8 @@ describe('commands module', () => {
|
|
|
355
375
|
noprogress: false,
|
|
356
376
|
nowait: false,
|
|
357
377
|
withDetails: true,
|
|
378
|
+
reporter: 'inline',
|
|
379
|
+
outputPath: 'test-report.xml',
|
|
358
380
|
});
|
|
359
381
|
});
|
|
360
382
|
|
|
@@ -372,6 +394,8 @@ describe('commands module', () => {
|
|
|
372
394
|
noprogress: true,
|
|
373
395
|
nowait: false,
|
|
374
396
|
withDetails: false,
|
|
397
|
+
reporter: 'inline',
|
|
398
|
+
outputPath: 'test-report.xml',
|
|
375
399
|
});
|
|
376
400
|
});
|
|
377
401
|
|
|
@@ -390,6 +414,8 @@ describe('commands module', () => {
|
|
|
390
414
|
noprogress: true,
|
|
391
415
|
nowait: false,
|
|
392
416
|
withDetails: false,
|
|
417
|
+
reporter: 'inline',
|
|
418
|
+
outputPath: 'test-report.xml',
|
|
393
419
|
});
|
|
394
420
|
});
|
|
395
421
|
|
|
@@ -408,6 +434,8 @@ describe('commands module', () => {
|
|
|
408
434
|
noprogress: true,
|
|
409
435
|
nowait: true,
|
|
410
436
|
withDetails: false,
|
|
437
|
+
reporter: 'inline',
|
|
438
|
+
outputPath: 'test-report.xml',
|
|
411
439
|
});
|
|
412
440
|
});
|
|
413
441
|
|
|
@@ -425,6 +453,8 @@ describe('commands module', () => {
|
|
|
425
453
|
noprogress: true,
|
|
426
454
|
nowait: false,
|
|
427
455
|
withDetails: false,
|
|
456
|
+
reporter: 'inline',
|
|
457
|
+
outputPath: 'test-report.xml',
|
|
428
458
|
});
|
|
429
459
|
});
|
|
430
460
|
|
|
@@ -442,6 +472,8 @@ describe('commands module', () => {
|
|
|
442
472
|
noprogress: true,
|
|
443
473
|
nowait: false,
|
|
444
474
|
withDetails: false,
|
|
475
|
+
reporter: 'inline',
|
|
476
|
+
outputPath: 'test-report.xml',
|
|
445
477
|
});
|
|
446
478
|
});
|
|
447
479
|
|
|
@@ -459,6 +491,8 @@ describe('commands module', () => {
|
|
|
459
491
|
noprogress: true,
|
|
460
492
|
nowait: false,
|
|
461
493
|
withDetails: false,
|
|
494
|
+
reporter: 'inline',
|
|
495
|
+
outputPath: 'test-report.xml',
|
|
462
496
|
});
|
|
463
497
|
});
|
|
464
498
|
|
|
@@ -476,6 +510,8 @@ describe('commands module', () => {
|
|
|
476
510
|
noprogress: true,
|
|
477
511
|
nowait: false,
|
|
478
512
|
withDetails: false,
|
|
513
|
+
reporter: 'inline',
|
|
514
|
+
outputPath: 'test-report.xml',
|
|
479
515
|
});
|
|
480
516
|
});
|
|
481
517
|
|
package/src/commands/remote.js
CHANGED
|
@@ -14,6 +14,9 @@ const {
|
|
|
14
14
|
printTestRunInfo,
|
|
15
15
|
printSuiteRunInfo,
|
|
16
16
|
} = require('../utils/print');
|
|
17
|
+
const {
|
|
18
|
+
generateJunitReport,
|
|
19
|
+
} = require('../utils/testReports');
|
|
17
20
|
const help = require('./help');
|
|
18
21
|
|
|
19
22
|
const OBJECT_TYPES = [settings.TYPE_TEST, settings.TYPE_SUITE, settings.TYPE_PROFILE];
|
|
@@ -37,7 +40,10 @@ const getList = async (type, query, extraParams) => {
|
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
const getResult = async (type, id, extraParams) => {
|
|
40
|
-
const {
|
|
43
|
+
const {
|
|
44
|
+
noprogress, withDetails, reporter, outputPath,
|
|
45
|
+
} = extraParams;
|
|
46
|
+
|
|
41
47
|
const spinner = getSpinner(noprogress);
|
|
42
48
|
spinner.start('Waiting for result...');
|
|
43
49
|
const route = settings.API_ROUTING[`${type}Result`];
|
|
@@ -45,10 +51,19 @@ const getResult = async (type, id, extraParams) => {
|
|
|
45
51
|
try {
|
|
46
52
|
const result = await apiCall(path, route.method, {}, {});
|
|
47
53
|
if (result) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
switch (reporter) {
|
|
55
|
+
case settings.REPORTER_TYPE.junit: {
|
|
56
|
+
await generateJunitReport(type, result.data, outputPath);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default: {
|
|
60
|
+
if (type === settings.TYPE_TEST) {
|
|
61
|
+
printTestRunInfo(spinner, result.data, withDetails);
|
|
62
|
+
} else {
|
|
63
|
+
printSuiteRunInfo(spinner, result.data, withDetails);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
52
67
|
}
|
|
53
68
|
return getExitCode(result.data.status);
|
|
54
69
|
}
|
|
@@ -60,7 +75,9 @@ const getResult = async (type, id, extraParams) => {
|
|
|
60
75
|
};
|
|
61
76
|
|
|
62
77
|
const checkStatus = async (type, id, extraParams) => {
|
|
63
|
-
const {
|
|
78
|
+
const {
|
|
79
|
+
nowait, noprogress, withDetails, reporter,
|
|
80
|
+
} = extraParams;
|
|
64
81
|
const spinner = getSpinner(noprogress);
|
|
65
82
|
spinner.start('Waiting for result...');
|
|
66
83
|
const route = settings.API_ROUTING[`${type}Status`];
|
|
@@ -73,7 +90,11 @@ const checkStatus = async (type, id, extraParams) => {
|
|
|
73
90
|
} else {
|
|
74
91
|
response = await apiCallPoll(path, route.method, {}, spinner, 0);
|
|
75
92
|
}
|
|
76
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
reporter !== settings.REPORTER_TYPE.inline
|
|
95
|
+
|| withDetails
|
|
96
|
+
|| settings.FAILED_STATUS.includes(response.data.status)
|
|
97
|
+
) {
|
|
77
98
|
await getResult(type, id, extraParams);
|
|
78
99
|
} else {
|
|
79
100
|
printStatus(spinner, response.data.status);
|
|
@@ -138,6 +159,8 @@ const parseArgs = async (args) => {
|
|
|
138
159
|
nowait: args.wait === false || args.nowait || false,
|
|
139
160
|
noprogress: args.progress === false || args.noprogress || false,
|
|
140
161
|
withDetails: args['with-details'] || false,
|
|
162
|
+
reporter: args.reporter || settings.REPORTER_TYPE.inline,
|
|
163
|
+
outputPath: args['output-path'] || 'test-report.xml',
|
|
141
164
|
};
|
|
142
165
|
|
|
143
166
|
return {
|
|
@@ -152,7 +175,7 @@ const validateArgs = async (args) => {
|
|
|
152
175
|
return false;
|
|
153
176
|
}
|
|
154
177
|
|
|
155
|
-
const knownKeys = ['_', 'wait', 'nowait', 'noprogress', 'progress', 'with-details', 'debug', 'profile', 'variable', 'result-timeout'];
|
|
178
|
+
const knownKeys = ['_', 'wait', 'nowait', 'noprogress', 'progress', 'with-details', 'debug', 'profile', 'variable', 'result-timeout', 'reporter', 'output-path'];
|
|
156
179
|
const unknownKeys = await getUnknownOptions(args, knownKeys);
|
|
157
180
|
if (unknownKeys.length > 0) {
|
|
158
181
|
console.error(`Unknown options: ${unknownKeys.join(', ')}`);
|
package/src/settings.js
CHANGED
|
@@ -11,6 +11,7 @@ const ENV_FILE_PATH = path.resolve(__dirname, '..', `.env.${NODE_ENV}`);
|
|
|
11
11
|
const USER_AGENT = `BugBug CLI ${VERSION}`;
|
|
12
12
|
dotenv.config({ path: ENV_FILE_PATH });
|
|
13
13
|
|
|
14
|
+
// TODO: Fix this path due to Mac OS restrictions
|
|
14
15
|
const CONFIG_DIR_PATH = path.join(os.homedir(), '.bugbug');
|
|
15
16
|
const CONFIG_FILE_PATH = path.join(CONFIG_DIR_PATH, 'settings.json');
|
|
16
17
|
|
|
@@ -68,6 +69,11 @@ const API_ERROR_SUBSCRIPTION_EXCEPTION = 'subscriptionException';
|
|
|
68
69
|
const API_ERROR_RUNNING_QUEUE_IS_FULL = 'runningQueueIsFullError';
|
|
69
70
|
const API_ERROR_QUOTA_EXCEEDED = 'quotaExceeded';
|
|
70
71
|
|
|
72
|
+
const REPORTER_TYPE = {
|
|
73
|
+
junit: 'junit',
|
|
74
|
+
inline: 'inline',
|
|
75
|
+
};
|
|
76
|
+
|
|
71
77
|
module.exports = {
|
|
72
78
|
ACTION_HELP,
|
|
73
79
|
ACTION_LIST,
|
|
@@ -95,4 +101,7 @@ module.exports = {
|
|
|
95
101
|
TYPE_TEST,
|
|
96
102
|
USER_AGENT,
|
|
97
103
|
VERSION,
|
|
104
|
+
STATUS_ERROR,
|
|
105
|
+
STATUS_FAILED,
|
|
106
|
+
REPORTER_TYPE,
|
|
98
107
|
};
|
package/src/utils/helper.js
CHANGED
|
@@ -43,10 +43,30 @@ const overrideSettings = async (args) => {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
const parseTimeFromDuration = (duration) => {
|
|
47
|
+
const [hours, minutes = '0', secondsWithMs = '0.0'] = (duration || '').split(':');
|
|
48
|
+
const [seconds, milliSeconds] = secondsWithMs.split('.');
|
|
49
|
+
return {
|
|
50
|
+
hours: hours || '00', minutes, secondsWithMs, seconds, milliSeconds,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getMillisecondsFromDuration = (duration) => {
|
|
55
|
+
const {
|
|
56
|
+
hours, minutes, seconds, milliSeconds,
|
|
57
|
+
} = parseTimeFromDuration(duration);
|
|
58
|
+
return (
|
|
59
|
+
parseInt(hours, 10) * 3600
|
|
60
|
+
+ parseInt(minutes, 10) * 60
|
|
61
|
+
+ parseInt(seconds, 10)
|
|
62
|
+
) * 1000 + parseInt(milliSeconds, 10);
|
|
63
|
+
};
|
|
64
|
+
|
|
46
65
|
module.exports = {
|
|
47
66
|
getExitCode,
|
|
48
67
|
getUnknownOptions,
|
|
49
68
|
overrideSettings,
|
|
50
69
|
parseVariables,
|
|
51
70
|
printErrorResponse,
|
|
71
|
+
getMillisecondsFromDuration,
|
|
52
72
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { getJunitXml } = require('junit-xml');
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const settings = require('../settings');
|
|
6
|
+
const { getMillisecondsFromDuration } = require('./helper');
|
|
7
|
+
|
|
8
|
+
const getTestInJunitFormat = (testRun) => {
|
|
9
|
+
const errorDetails = testRun.errorCode ? {
|
|
10
|
+
message: `Error ${testRun.errorCode} occurred in step: ${testRun.details[0].step.type} (${testRun.details[0].step.id})`,
|
|
11
|
+
stack: testRun.errorCode,
|
|
12
|
+
} : undefined;
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
id: testRun.id,
|
|
16
|
+
name: testRun.name,
|
|
17
|
+
time: getMillisecondsFromDuration(testRun.duration),
|
|
18
|
+
classname: null,
|
|
19
|
+
errors: testRun.status === settings.STATUS_ERROR ? [errorDetails] : [],
|
|
20
|
+
failures: testRun.status === settings.STATUS_FAILED ? [errorDetails] : [],
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getSuiteInJunitFormat = (suiteRun) => ({
|
|
25
|
+
id: suiteRun.id,
|
|
26
|
+
name: suiteRun.name,
|
|
27
|
+
time: getMillisecondsFromDuration(suiteRun.duration),
|
|
28
|
+
testCases: suiteRun.details.map((testRun) => getTestInJunitFormat(testRun)),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const generateJunitReport = async (type, result, outputPath) => {
|
|
32
|
+
const testReport = {};
|
|
33
|
+
testReport.name = 'BugBug Report';
|
|
34
|
+
testReport.time = getMillisecondsFromDuration(result.duration);
|
|
35
|
+
testReport.suites = [];
|
|
36
|
+
|
|
37
|
+
if (type === settings.TYPE_TEST) {
|
|
38
|
+
testReport.suites.push(
|
|
39
|
+
getSuiteInJunitFormat({
|
|
40
|
+
name: 'Single Test Run',
|
|
41
|
+
duration: result.duration,
|
|
42
|
+
details: [result],
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (type === settings.TYPE_SUITE) {
|
|
48
|
+
testReport.suites.push(getSuiteInJunitFormat(result));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const junitReport = getJunitXml(testReport);
|
|
52
|
+
fs.writeFileSync(path.join(process.cwd(), outputPath), junitReport, { flag: 'w+' });
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
generateJunitReport,
|
|
57
|
+
};
|