@testrevolution/bugbug-cli 12.34.1 → 13.5.3
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/.env +1 -1
- package/package.json +9 -8
- package/src/__tests__/commands.spec.js +42 -6
- package/src/commands/config.js +5 -1
- package/src/commands/help.js +45 -27
- package/src/commands/remote.js +25 -9
- package/src/index.js +2 -1
- package/src/settings.js +3 -1
- package/test-report.xml +37 -0
- package/src/utils/testReports.js +0 -65
package/.env
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
SENTRY_DSN=https://
|
|
1
|
+
SENTRY_DSN=https://c850ce209b19d8558600d0ecbb5a160f@sentry.bugbug.io/52
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testrevolution/bugbug-cli",
|
|
3
3
|
"description": "BugBug CLI",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "13.5.3",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
7
7
|
"cli",
|
|
@@ -19,16 +19,17 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@sentry/node": "^10.5.0",
|
|
22
|
-
"axios": "^1.
|
|
23
|
-
"
|
|
24
|
-
"
|
|
22
|
+
"axios": "^1.13.4",
|
|
23
|
+
"chalk": "^4.1.2",
|
|
24
|
+
"console-table-printer": "^2.15.0",
|
|
25
|
+
"dotenv": "^17.2.3",
|
|
25
26
|
"jest-junit": "^16.0.0",
|
|
26
27
|
"junit-xml": "^1.2.0",
|
|
27
28
|
"minimist": "^1.2.8",
|
|
28
|
-
"nanoid": "^3.3.
|
|
29
|
+
"nanoid": "^3.3.11",
|
|
29
30
|
"ora": "^5.4.1",
|
|
30
31
|
"path": "^0.12.7",
|
|
31
|
-
"validator": "^13.15.
|
|
32
|
+
"validator": "^13.15.26"
|
|
32
33
|
},
|
|
33
34
|
"scripts": {
|
|
34
35
|
"start": "cross-env NODE_ENV=development node ./bin/bugbug",
|
|
@@ -49,9 +50,9 @@
|
|
|
49
50
|
"eslint": "^8.57.1",
|
|
50
51
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
51
52
|
"eslint-plugin-import": "^2.32.0",
|
|
52
|
-
"eslint-plugin-jest": "^29.
|
|
53
|
+
"eslint-plugin-jest": "^29.12.2",
|
|
53
54
|
"eslint-plugin-node": "^11.1.0",
|
|
54
|
-
"jest": "^30.0
|
|
55
|
+
"jest": "^30.2.0"
|
|
55
56
|
},
|
|
56
57
|
"author": "TestRevolution sp. z o.o."
|
|
57
58
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
const axios = require('axios');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const chalk = require('chalk');
|
|
2
4
|
|
|
5
|
+
chalk.level = 0;
|
|
6
|
+
|
|
7
|
+
const { join } = require('path');
|
|
3
8
|
const {
|
|
4
9
|
config: configCommand,
|
|
5
10
|
help: helpCommand,
|
|
@@ -36,7 +41,7 @@ describe('commands module', () => {
|
|
|
36
41
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
37
42
|
await helpCommand(args);
|
|
38
43
|
expect(consoleLogSpy).toHaveBeenCalled();
|
|
39
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('bugbug
|
|
44
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('bugbug <command> [options]'));
|
|
40
45
|
});
|
|
41
46
|
|
|
42
47
|
it('should console log config help menu', async () => {
|
|
@@ -46,7 +51,7 @@ describe('commands module', () => {
|
|
|
46
51
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
47
52
|
await helpCommand(args);
|
|
48
53
|
expect(consoleLogSpy).toHaveBeenCalled();
|
|
49
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('
|
|
54
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('config <command>'));
|
|
50
55
|
});
|
|
51
56
|
|
|
52
57
|
it('should console log remote help menu', async () => {
|
|
@@ -56,7 +61,7 @@ describe('commands module', () => {
|
|
|
56
61
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
57
62
|
await helpCommand(args);
|
|
58
63
|
expect(consoleLogSpy).toHaveBeenCalled();
|
|
59
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('
|
|
64
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('remote <command> [options]'));
|
|
60
65
|
});
|
|
61
66
|
});
|
|
62
67
|
|
|
@@ -297,7 +302,10 @@ describe('commands module', () => {
|
|
|
297
302
|
variable: ['var=val', 'var2=val2'],
|
|
298
303
|
};
|
|
299
304
|
await remoteCommand(args);
|
|
300
|
-
expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, {
|
|
305
|
+
expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, {
|
|
306
|
+
key: 'var2',
|
|
307
|
+
value: 'val2',
|
|
308
|
+
}], {
|
|
301
309
|
noprogress: true,
|
|
302
310
|
nowait: false,
|
|
303
311
|
withDetails: false,
|
|
@@ -421,7 +429,10 @@ describe('commands module', () => {
|
|
|
421
429
|
variable: ['var=val', 'var2=val2'],
|
|
422
430
|
};
|
|
423
431
|
await remoteCommand(args);
|
|
424
|
-
expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, {
|
|
432
|
+
expect(runFunMock).toHaveBeenCalledWith(id, undefined, [{ key: 'var', value: 'val' }, {
|
|
433
|
+
key: 'var2',
|
|
434
|
+
value: 'val2',
|
|
435
|
+
}], {
|
|
425
436
|
noprogress: true,
|
|
426
437
|
nowait: false,
|
|
427
438
|
withDetails: false,
|
|
@@ -581,6 +592,31 @@ describe('commands module', () => {
|
|
|
581
592
|
});
|
|
582
593
|
});
|
|
583
594
|
|
|
595
|
+
it.each(['test', 'suite'])('result %s command should call API and generate JUnit report', async (type) => {
|
|
596
|
+
// Arrange
|
|
597
|
+
const reportFile = 'report data';
|
|
598
|
+
jest.spyOn(fs, 'writeFileSync').mockImplementation();
|
|
599
|
+
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
600
|
+
axios
|
|
601
|
+
.mockResolvedValueOnce(apiMocks.getResultTestRunSuccess)
|
|
602
|
+
.mockResolvedValueOnce({ data: reportFile });
|
|
603
|
+
const id = '95a37d6a-a7a5-42ae-8fe3-336a120b807b';
|
|
604
|
+
const args = {
|
|
605
|
+
_: ['remote', 'result', type, id],
|
|
606
|
+
noprogress: true,
|
|
607
|
+
'with-details': true,
|
|
608
|
+
reporter: settings.REPORTER_TYPE.junit,
|
|
609
|
+
'output-path': 'test-report.xml',
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// Act
|
|
613
|
+
await remoteCommand(args);
|
|
614
|
+
|
|
615
|
+
// Assert
|
|
616
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(join(process.cwd(), 'test-report.xml'), reportFile, expect.any(Object));
|
|
617
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(`JUnit report generated at ${join(process.cwd(), 'test-report.xml')}`);
|
|
618
|
+
});
|
|
619
|
+
|
|
584
620
|
it('result test command should call API and print errorCode when failed', async () => {
|
|
585
621
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
586
622
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
@@ -789,7 +825,7 @@ describe('commands module', () => {
|
|
|
789
825
|
test_id: id, profile_name: undefined, variables: [], triggeredBy: 'cli',
|
|
790
826
|
|
|
791
827
|
},
|
|
792
|
-
params: {
|
|
828
|
+
params: {},
|
|
793
829
|
headers: { Authorization: `Token ${configValue.token}`, 'User-Agent': settings.USER_AGENT },
|
|
794
830
|
});
|
|
795
831
|
expect(axios).toHaveBeenNthCalledWith(2, {
|
package/src/commands/config.js
CHANGED
|
@@ -27,7 +27,7 @@ const validateArgs = async (args) => {
|
|
|
27
27
|
help({ _: ['config'] });
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
-
const knownKeys = ['_'];
|
|
30
|
+
const knownKeys = ['_', 'color', 'no-color'];
|
|
31
31
|
const unknownKeys = await getUnknownOptions(args, knownKeys);
|
|
32
32
|
if (unknownKeys.length > 0) {
|
|
33
33
|
console.error(`Unknown options: ${unknownKeys.join(', ')}`);
|
|
@@ -43,6 +43,10 @@ module.exports = async (args) => {
|
|
|
43
43
|
}
|
|
44
44
|
const { subCmd, token } = await parseArgs(args);
|
|
45
45
|
switch (subCmd) {
|
|
46
|
+
case settings.ACTION_HELP:
|
|
47
|
+
help({ _: ['config'] });
|
|
48
|
+
exitCode = await getExitCode(true);
|
|
49
|
+
break;
|
|
46
50
|
case settings.ACTION_SET_TOKEN:
|
|
47
51
|
exitCode = await setToken(token);
|
|
48
52
|
break;
|
package/src/commands/help.js
CHANGED
|
@@ -1,40 +1,58 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
1
2
|
const settings = require('../settings');
|
|
2
3
|
|
|
3
4
|
const help = {
|
|
4
5
|
main: `
|
|
5
|
-
|
|
6
|
+
${chalk.bold('Usage:')}
|
|
7
|
+
${chalk.green('bugbug')} <command> [options]
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
${chalk.bold('Commands:')}
|
|
10
|
+
${chalk.cyan('config')} Configure CLI settings
|
|
11
|
+
${chalk.cyan('remote')} Run and manage tests on remote
|
|
12
|
+
${chalk.cyan('version')} Show version information
|
|
13
|
+
${chalk.cyan('help <command>')} Show help menu for a command
|
|
14
|
+
|
|
15
|
+
${chalk.bold('Global flags:')}
|
|
16
|
+
${chalk.yellow('--debug')} Show raw API responses
|
|
17
|
+
${chalk.yellow('--no-color')} Disable colored output`,
|
|
11
18
|
|
|
12
19
|
config: `
|
|
13
|
-
|
|
20
|
+
${chalk.bold('Usage:')}
|
|
21
|
+
${chalk.green('bugbug config')} <command>
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
${chalk.bold('Commands:')}
|
|
24
|
+
${chalk.cyan('set-token <token>')} Set BugBug API token`,
|
|
17
25
|
|
|
18
26
|
remote: `
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
${chalk.bold('Usage:')}
|
|
28
|
+
${chalk.green('bugbug remote')} <command> [options]
|
|
29
|
+
|
|
30
|
+
${chalk.bold('Commands:')}
|
|
31
|
+
${chalk.cyan('list')} <test|suite|profile> List tests, suites or profiles
|
|
32
|
+
${chalk.cyan('run')} <test|suite> <id> Run a test or suite
|
|
33
|
+
${chalk.cyan('status')} <test|suite> <id> Check run status
|
|
34
|
+
${chalk.cyan('result')} <test|suite> <id> Get run result
|
|
35
|
+
${chalk.cyan('stop')} <test|suite> <id> Stop a running test or suite
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
${chalk.bold('Global flags:')}
|
|
39
|
+
${chalk.yellow('--debug')} Show raw API responses
|
|
40
|
+
${chalk.yellow('--no-progress')} Hide progress spinner
|
|
41
|
+
${chalk.yellow('--no-color')} Disable colored output
|
|
42
|
+
|
|
43
|
+
${chalk.bold('Command-specific flags:')}
|
|
44
|
+
${chalk.white.underline('run, status')}
|
|
45
|
+
${chalk.yellow('--no-wait')} Exit immediately, don't wait for result
|
|
46
|
+
${chalk.yellow('--result-timeout <min>')} Max waiting time (default: 60)
|
|
47
|
+
|
|
48
|
+
${chalk.white.underline('run')}
|
|
49
|
+
${chalk.yellow('--profile <name>')} Run with specific profile
|
|
50
|
+
${chalk.yellow('--variable <key=val>')} Override variable (can be used multiple times)
|
|
51
|
+
|
|
52
|
+
${chalk.white.underline('run, status, result')}
|
|
53
|
+
${chalk.yellow('--with-details')} Include detailed step information
|
|
54
|
+
${chalk.yellow('--reporter <type>')} "inline" or "junit" (default: "inline")
|
|
55
|
+
${chalk.yellow('--output-path <path>')} Path for JUnit report (default: test-report.xml)
|
|
38
56
|
`,
|
|
39
57
|
};
|
|
40
58
|
|
package/src/commands/remote.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const { join } = require('node:path');
|
|
1
3
|
const { format } = require('util');
|
|
2
4
|
const validator = require('validator');
|
|
3
5
|
|
|
@@ -14,9 +16,6 @@ const {
|
|
|
14
16
|
printTestRunInfo,
|
|
15
17
|
printSuiteRunInfo,
|
|
16
18
|
} = require('../utils/print');
|
|
17
|
-
const {
|
|
18
|
-
generateJunitReport,
|
|
19
|
-
} = require('../utils/testReports');
|
|
20
19
|
const help = require('./help');
|
|
21
20
|
const { sentry } = require('../utils/sentry');
|
|
22
21
|
|
|
@@ -54,8 +53,16 @@ const getResult = async (type, id, extraParams) => {
|
|
|
54
53
|
if (result) {
|
|
55
54
|
switch (reporter) {
|
|
56
55
|
case settings.REPORTER_TYPE.junit: {
|
|
57
|
-
|
|
58
|
-
await
|
|
56
|
+
spinner.info('Generating JUnit report...');
|
|
57
|
+
const report = await apiCall(
|
|
58
|
+
format(settings.API_ROUTING[`${type}RunReport`].path, id),
|
|
59
|
+
settings.API_ROUTING[`${type}RunReport`].method,
|
|
60
|
+
{},
|
|
61
|
+
{},
|
|
62
|
+
extraParams.triggeredBy,
|
|
63
|
+
);
|
|
64
|
+
fs.writeFileSync(join(process.cwd(), outputPath), report.data, { flag: 'w+' });
|
|
65
|
+
spinner.info(`JUnit report generated at ${join(process.cwd(), outputPath)}`);
|
|
59
66
|
break;
|
|
60
67
|
}
|
|
61
68
|
default: {
|
|
@@ -185,8 +192,8 @@ const checkTokenConfig = async () => {
|
|
|
185
192
|
};
|
|
186
193
|
|
|
187
194
|
const parseArgs = async (args) => {
|
|
188
|
-
const subCmd = args._[1].toLowerCase();
|
|
189
|
-
const objectType = args._[2].toLowerCase();
|
|
195
|
+
const subCmd = (args._[1] || '').toLowerCase();
|
|
196
|
+
const objectType = (args._[2] || '').toLowerCase();
|
|
190
197
|
const id = args._[3];
|
|
191
198
|
const variables = await parseVariables(args.variable);
|
|
192
199
|
|
|
@@ -208,19 +215,24 @@ const parseArgs = async (args) => {
|
|
|
208
215
|
};
|
|
209
216
|
|
|
210
217
|
const validateArgs = async (args) => {
|
|
218
|
+
const subCmd = (args._[1] || '').toLowerCase();
|
|
219
|
+
if (subCmd === settings.ACTION_HELP) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
211
223
|
if (args._.length < 3) {
|
|
212
224
|
console.error('Missing parameters!');
|
|
213
225
|
help({ _: ['remote'] });
|
|
214
226
|
return false;
|
|
215
227
|
}
|
|
216
228
|
|
|
217
|
-
const knownKeys = ['_', 'wait', 'nowait', 'noprogress', 'progress', 'with-details', 'debug', 'profile', 'variable', 'result-timeout', 'reporter', 'output-path', 'triggered-by'];
|
|
229
|
+
const knownKeys = ['_', 'wait', 'nowait', 'noprogress', 'progress', 'with-details', 'debug', 'profile', 'variable', 'result-timeout', 'reporter', 'output-path', 'triggered-by', 'color', 'no-color'];
|
|
218
230
|
const unknownKeys = await getUnknownOptions(args, knownKeys);
|
|
219
231
|
if (unknownKeys.length > 0) {
|
|
220
232
|
console.error(`Unknown options: ${unknownKeys.join(', ')}`);
|
|
221
233
|
return false;
|
|
222
234
|
}
|
|
223
|
-
const {
|
|
235
|
+
const { objectType, id } = await parseArgs(args);
|
|
224
236
|
|
|
225
237
|
if (!OBJECT_TYPES.includes(objectType)) {
|
|
226
238
|
console.error(`Incorrect object type ${objectType}. Available types: ${OBJECT_TYPES}.`);
|
|
@@ -260,6 +272,10 @@ module.exports = async (args) => {
|
|
|
260
272
|
let exitCode;
|
|
261
273
|
await checkTokenConfig();
|
|
262
274
|
switch (subCmd) {
|
|
275
|
+
case settings.ACTION_HELP:
|
|
276
|
+
help({ _: ['remote'] });
|
|
277
|
+
exitCode = await getExitCode(true);
|
|
278
|
+
break;
|
|
263
279
|
case settings.ACTION_LIST:
|
|
264
280
|
exitCode = await getList(objectType, id, extraParams);
|
|
265
281
|
break;
|
package/src/index.js
CHANGED
|
@@ -3,9 +3,10 @@ const minimist = require('minimist');
|
|
|
3
3
|
const { nanoid } = require('nanoid');
|
|
4
4
|
const { sentry, init: initSentry } = require('./utils/sentry');
|
|
5
5
|
const settings = require('./settings');
|
|
6
|
-
require('dotenv').config();
|
|
6
|
+
require('dotenv').config({ quiet: true });
|
|
7
7
|
require('dotenv').config({
|
|
8
8
|
path: path.resolve(process.cwd(), `.env.${settings.NODE_ENV}`),
|
|
9
|
+
quiet: true,
|
|
9
10
|
});
|
|
10
11
|
// Initialize Sentry first
|
|
11
12
|
initSentry();
|
package/src/settings.js
CHANGED
|
@@ -9,7 +9,7 @@ util.inspect.styles.string = 'white';
|
|
|
9
9
|
const NODE_ENV = process.env.NODE_ENV || 'production';
|
|
10
10
|
const ENV_FILE_PATH = path.resolve(__dirname, '..', `.env.${NODE_ENV}`);
|
|
11
11
|
const USER_AGENT = `BugBug CLI ${VERSION}`;
|
|
12
|
-
dotenv.config({ path: ENV_FILE_PATH });
|
|
12
|
+
dotenv.config({ path: ENV_FILE_PATH, quiet: true });
|
|
13
13
|
|
|
14
14
|
// TODO: Fix this path due to Mac OS restrictions
|
|
15
15
|
const CONFIG_DIR_PATH = path.join(os.homedir(), '.bugbug');
|
|
@@ -42,6 +42,8 @@ const API_ROUTING = {
|
|
|
42
42
|
testRun: { method: 'POST', path: `${API_VERSION}/testruns/` },
|
|
43
43
|
testStatus: { method: 'GET', path: `${API_VERSION}/testruns/%s/status/` },
|
|
44
44
|
testStop: { method: 'POST', path: `${API_VERSION}/testruns/%s/stop/` },
|
|
45
|
+
testRunReport: { method: 'GET', path: `${API_VERSION}/testruns/%s/report/junit/` },
|
|
46
|
+
suiteRunReport: { method: 'GET', path: `${API_VERSION}/suiteruns/%s/report/junit/` },
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
const API_POLLING_MODIFIED_INTERVAL = 120000; // 120 seconds
|
package/test-report.xml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<?xml version='1.0' encoding='utf-8'?>
|
|
2
|
+
<testsuites>
|
|
3
|
+
<testsuite errors="0" failures="0" id="08deed80-07d1-4987-aaa3-e14a1f906a29" name="Test_Tic Tac Toe simple [OK]" skipped="0" tests="1" time="7.00" sequence="#3" timestamp="2025-10-28T13:08:49.533959+00:00">
|
|
4
|
+
<properties>
|
|
5
|
+
<property name="report_id" value="ca971f23-d2e1-4401-bf12-5cd2e968ce3a" />
|
|
6
|
+
<property name="generated_at" value="2025-10-28T13:08:57.329443+00:00" />
|
|
7
|
+
<property name="generated_by" value="admin@testrevolution.io" />
|
|
8
|
+
<property name="auto_retry_attempt" value="0" />
|
|
9
|
+
<property name="browser_height" value="786" />
|
|
10
|
+
<property name="browser_name" value="Chrome" />
|
|
11
|
+
<property name="browser_version" value="136.0.0.0" />
|
|
12
|
+
<property name="browser_width" value="1366" />
|
|
13
|
+
<property name="extension_version" value="12.33.2" />
|
|
14
|
+
<property name="finished" value="2025-10-28T13:08:56.534381+00:00" />
|
|
15
|
+
<property name="os_name" value="Linux" />
|
|
16
|
+
<property name="profile_name" value="Default" />
|
|
17
|
+
<property name="run_mode" value="cloud" />
|
|
18
|
+
<property name="sequence" value="#3" />
|
|
19
|
+
<property name="screen_size_type" value="desktop" />
|
|
20
|
+
<property name="test_id" value="d998ade4-93bc-4d94-bab8-a73a290f8490" />
|
|
21
|
+
<property name="triggered_by" value="cli" />
|
|
22
|
+
<property name="report_format_version" value="1.0" />
|
|
23
|
+
</properties>
|
|
24
|
+
<testcase name="Tic Tac Toe simple [OK]" classname="TestSuite_Single" time="7.00" test_run_id="08deed80-07d1-4987-aaa3-e14a1f906a29" sequence="#3">
|
|
25
|
+
<system-out>=== Test Steps Execution (summary) ===
|
|
26
|
+
Total steps: 9
|
|
27
|
+
Passed: 9
|
|
28
|
+
Failed: 0
|
|
29
|
+
|
|
30
|
+
First 3 steps:
|
|
31
|
+
1. OK goto (2.36s)
|
|
32
|
+
2. OK click (1.22s)
|
|
33
|
+
3. OK click (0.27s)
|
|
34
|
+
</system-out>
|
|
35
|
+
</testcase>
|
|
36
|
+
</testsuite>
|
|
37
|
+
</testsuites>
|
package/src/utils/testReports.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
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 { getSecondsFromDuration } = require('./helper');
|
|
7
|
-
|
|
8
|
-
const getTestInJunitFormat = (testRun) => {
|
|
9
|
-
const errorCode = testRun.errorCode ?? 'MISSING_ERROR_CODE';
|
|
10
|
-
const stepInfo = testRun.details?.[0]?.step;
|
|
11
|
-
const errorDetails = stepInfo
|
|
12
|
-
? {
|
|
13
|
-
message: `Error ${errorCode} occurred in step: ${stepInfo.type} (${stepInfo.id})`,
|
|
14
|
-
stack: errorCode,
|
|
15
|
-
}
|
|
16
|
-
: {
|
|
17
|
-
message: `Error ${errorCode} occurred`,
|
|
18
|
-
stack: errorCode,
|
|
19
|
-
};
|
|
20
|
-
const errorsList = errorDetails ? [errorDetails] : [];
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
id: testRun.id,
|
|
24
|
-
name: testRun.name,
|
|
25
|
-
time: getSecondsFromDuration(testRun.duration),
|
|
26
|
-
classname: null,
|
|
27
|
-
errors: testRun.status === settings.STATUS_ERROR ? errorsList : [],
|
|
28
|
-
failures: testRun.status === settings.STATUS_FAILED ? errorsList : [],
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const getSuiteInJunitFormat = (suiteRun) => ({
|
|
33
|
-
id: suiteRun.id,
|
|
34
|
-
name: suiteRun.name,
|
|
35
|
-
time: getSecondsFromDuration(suiteRun.duration),
|
|
36
|
-
testCases: suiteRun.details.map((testRun) => getTestInJunitFormat(testRun)),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const generateJunitReport = async (type, result, outputPath) => {
|
|
40
|
-
const testReport = {};
|
|
41
|
-
testReport.name = 'BugBug Report';
|
|
42
|
-
testReport.time = getSecondsFromDuration(result.duration);
|
|
43
|
-
testReport.suites = [];
|
|
44
|
-
|
|
45
|
-
if (type === settings.TYPE_TEST) {
|
|
46
|
-
testReport.suites.push(
|
|
47
|
-
getSuiteInJunitFormat({
|
|
48
|
-
name: 'Single Test Run',
|
|
49
|
-
duration: result.duration,
|
|
50
|
-
details: [result],
|
|
51
|
-
}),
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (type === settings.TYPE_SUITE) {
|
|
56
|
-
testReport.suites.push(getSuiteInJunitFormat(result));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const junitReport = getJunitXml(testReport);
|
|
60
|
-
fs.writeFileSync(path.join(process.cwd(), outputPath), junitReport, { flag: 'w+' });
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
module.exports = {
|
|
64
|
-
generateJunitReport,
|
|
65
|
-
};
|