@testrevolution/bugbug-cli 12.23.5 → 12.35.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@testrevolution/bugbug-cli",
3
3
  "description": "BugBug CLI",
4
- "version": "12.23.5",
4
+ "version": "12.35.0",
5
5
  "keywords": [
6
6
  "automation",
7
7
  "cli",
@@ -1,5 +1,7 @@
1
1
  const axios = require('axios');
2
+ const fs = require('fs');
2
3
 
4
+ const { join } = require('path');
3
5
  const {
4
6
  config: configCommand,
5
7
  help: helpCommand,
@@ -581,6 +583,31 @@ describe('commands module', () => {
581
583
  });
582
584
  });
583
585
 
586
+ it.each(['test', 'suite'])('result %s command should call API and generate JUnit report', async (type) => {
587
+ // Arrange
588
+ const reportFile = 'report data';
589
+ jest.spyOn(fs, 'writeFileSync').mockImplementation();
590
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
591
+ axios
592
+ .mockResolvedValueOnce(apiMocks.getResultTestRunSuccess)
593
+ .mockResolvedValueOnce({ data: reportFile });
594
+ const id = '95a37d6a-a7a5-42ae-8fe3-336a120b807b';
595
+ const args = {
596
+ _: ['remote', 'result', type, id],
597
+ noprogress: true,
598
+ 'with-details': true,
599
+ reporter: settings.REPORTER_TYPE.junit,
600
+ 'output-path': 'test-report.xml',
601
+ };
602
+
603
+ // Act
604
+ await remoteCommand(args);
605
+
606
+ // Assert
607
+ expect(fs.writeFileSync).toHaveBeenCalledWith(join(process.cwd(), 'test-report.xml'), reportFile, expect.any(Object));
608
+ expect(consoleLogSpy).toHaveBeenCalledWith(`JUnit report generated at ${join(process.cwd(), 'test-report.xml')}`);
609
+ });
610
+
584
611
  it('result test command should call API and print errorCode when failed', async () => {
585
612
  const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
586
613
  const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
@@ -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
- console.log('Generating JUnit report...');
58
- await generateJunitReport(type, result.data, outputPath);
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: {
package/src/settings.js CHANGED
@@ -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
@@ -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>
@@ -1,59 +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 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] : [];
15
-
16
- return {
17
- id: testRun.id,
18
- name: testRun.name,
19
- time: getSecondsFromDuration(testRun.duration),
20
- classname: null,
21
- errors: testRun.status === settings.STATUS_ERROR ? errorsList : [],
22
- failures: testRun.status === settings.STATUS_FAILED ? errorsList : [],
23
- };
24
- };
25
-
26
- const getSuiteInJunitFormat = (suiteRun) => ({
27
- id: suiteRun.id,
28
- name: suiteRun.name,
29
- time: getSecondsFromDuration(suiteRun.duration),
30
- testCases: suiteRun.details.map((testRun) => getTestInJunitFormat(testRun)),
31
- });
32
-
33
- const generateJunitReport = async (type, result, outputPath) => {
34
- const testReport = {};
35
- testReport.name = 'BugBug Report';
36
- testReport.time = getSecondsFromDuration(result.duration);
37
- testReport.suites = [];
38
-
39
- if (type === settings.TYPE_TEST) {
40
- testReport.suites.push(
41
- getSuiteInJunitFormat({
42
- name: 'Single Test Run',
43
- duration: result.duration,
44
- details: [result],
45
- }),
46
- );
47
- }
48
-
49
- if (type === settings.TYPE_SUITE) {
50
- testReport.suites.push(getSuiteInJunitFormat(result));
51
- }
52
-
53
- const junitReport = getJunitXml(testReport);
54
- fs.writeFileSync(path.join(process.cwd(), outputPath), junitReport, { flag: 'w+' });
55
- };
56
-
57
- module.exports = {
58
- generateJunitReport,
59
- };