@testrevolution/bugbug-cli 12.0.0 → 12.20.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 +7 -7
- package/src/__tests__/helper.spec.js +230 -0
- package/src/utils/helper.js +19 -11
- package/src/utils/testReports.js +4 -4
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.20.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"automation",
|
|
7
7
|
"cli",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@sentry/node": "^8.55.0",
|
|
22
|
-
"axios": "^1.
|
|
22
|
+
"axios": "^1.10.0",
|
|
23
23
|
"console-table-printer": "^2.12.1",
|
|
24
24
|
"dotenv": "^16.4.7",
|
|
25
25
|
"jest-junit": "^16.0.0",
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
"node": ">= 20.12.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@babel/eslint-parser": "^7.
|
|
45
|
-
"@babel/plugin-proposal-throw-expressions": "^7.
|
|
44
|
+
"@babel/eslint-parser": "^7.27.1",
|
|
45
|
+
"@babel/plugin-proposal-throw-expressions": "^7.27.1",
|
|
46
46
|
"babel-plugin-rewire": "^1.2.0",
|
|
47
47
|
"cross-env": "^7.0.3",
|
|
48
48
|
"eslint": "^8.57.1",
|
|
49
49
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
50
|
-
"eslint-plugin-import": "^2.
|
|
51
|
-
"eslint-plugin-jest": "^
|
|
50
|
+
"eslint-plugin-import": "^2.32.0",
|
|
51
|
+
"eslint-plugin-jest": "^29.0.1",
|
|
52
52
|
"eslint-plugin-node": "^11.1.0",
|
|
53
|
-
"jest": "^
|
|
53
|
+
"jest": "^30.0.3"
|
|
54
54
|
},
|
|
55
55
|
"author": "TestRevolution sp. z o.o."
|
|
56
56
|
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
const { parseTimeFromDuration, getSecondsFromDuration } = require('../utils/helper');
|
|
2
|
+
|
|
3
|
+
describe('timeParser', () => {
|
|
4
|
+
describe('parseTimeFromDuration', () => {
|
|
5
|
+
it('should parse full duration with microseconds', () => {
|
|
6
|
+
// Arrange
|
|
7
|
+
const duration = '00:01:15.704622';
|
|
8
|
+
|
|
9
|
+
// Act
|
|
10
|
+
const result = parseTimeFromDuration(duration);
|
|
11
|
+
|
|
12
|
+
// Assert
|
|
13
|
+
expect(result).toEqual({
|
|
14
|
+
hours: '00',
|
|
15
|
+
minutes: '01',
|
|
16
|
+
secondsWithMs: '15.704622',
|
|
17
|
+
seconds: '15',
|
|
18
|
+
microseconds: '704622',
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should parse duration without microseconds', () => {
|
|
23
|
+
// Arrange
|
|
24
|
+
const duration = '01:30:45';
|
|
25
|
+
|
|
26
|
+
// Act
|
|
27
|
+
const result = parseTimeFromDuration(duration);
|
|
28
|
+
|
|
29
|
+
// Assert
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
hours: '01',
|
|
32
|
+
minutes: '30',
|
|
33
|
+
secondsWithMs: '45',
|
|
34
|
+
seconds: '45',
|
|
35
|
+
microseconds: '0',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should parse duration with zero microseconds', () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const duration = '00:00:30.0';
|
|
42
|
+
|
|
43
|
+
// Act
|
|
44
|
+
const result = parseTimeFromDuration(duration);
|
|
45
|
+
|
|
46
|
+
// Assert
|
|
47
|
+
expect(result).toEqual({
|
|
48
|
+
hours: '00',
|
|
49
|
+
minutes: '00',
|
|
50
|
+
secondsWithMs: '30.0',
|
|
51
|
+
seconds: '30',
|
|
52
|
+
microseconds: '0',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should handle empty string', () => {
|
|
57
|
+
// Arrange
|
|
58
|
+
const duration = '';
|
|
59
|
+
|
|
60
|
+
// Act
|
|
61
|
+
const result = parseTimeFromDuration(duration);
|
|
62
|
+
|
|
63
|
+
// Assert
|
|
64
|
+
expect(result).toEqual({
|
|
65
|
+
hours: '00',
|
|
66
|
+
minutes: '0',
|
|
67
|
+
secondsWithMs: '0.0',
|
|
68
|
+
seconds: '0',
|
|
69
|
+
microseconds: '0',
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle null or undefined', () => {
|
|
74
|
+
// Arrange & Act
|
|
75
|
+
const resultNull = parseTimeFromDuration(null);
|
|
76
|
+
const resultUndefined = parseTimeFromDuration(undefined);
|
|
77
|
+
|
|
78
|
+
// Assert
|
|
79
|
+
expect(resultNull).toEqual({
|
|
80
|
+
hours: '00',
|
|
81
|
+
minutes: '0',
|
|
82
|
+
secondsWithMs: '0.0',
|
|
83
|
+
seconds: '0',
|
|
84
|
+
microseconds: '0',
|
|
85
|
+
});
|
|
86
|
+
expect(resultUndefined).toEqual({
|
|
87
|
+
hours: '00',
|
|
88
|
+
minutes: '0',
|
|
89
|
+
secondsWithMs: '0.0',
|
|
90
|
+
seconds: '0',
|
|
91
|
+
microseconds: '0',
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle short microseconds', () => {
|
|
96
|
+
// Arrange
|
|
97
|
+
const duration = '00:01:15.5';
|
|
98
|
+
|
|
99
|
+
// Act
|
|
100
|
+
const result = parseTimeFromDuration(duration);
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(result).toEqual({
|
|
104
|
+
hours: '00',
|
|
105
|
+
minutes: '01',
|
|
106
|
+
secondsWithMs: '15.5',
|
|
107
|
+
seconds: '15',
|
|
108
|
+
microseconds: '5',
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('getSecondsFromDuration', () => {
|
|
114
|
+
it('should convert duration with microseconds to total float seconds', () => {
|
|
115
|
+
// Arrange
|
|
116
|
+
const duration = '00:01:15.704622';
|
|
117
|
+
|
|
118
|
+
// Act
|
|
119
|
+
const result = getSecondsFromDuration(duration);
|
|
120
|
+
|
|
121
|
+
// Assert
|
|
122
|
+
// 1 minute = 60 seconds, + 15 seconds = 75 seconds
|
|
123
|
+
// 75 * 1,000,000 = 75,000,000 microseconds
|
|
124
|
+
// + 704,622 microseconds = 75,704,622
|
|
125
|
+
expect(result).toBe(75.704622);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should convert duration without microseconds', () => {
|
|
129
|
+
// Arrange
|
|
130
|
+
const duration = '01:30:45';
|
|
131
|
+
|
|
132
|
+
// Act
|
|
133
|
+
const result = getSecondsFromDuration(duration);
|
|
134
|
+
|
|
135
|
+
// Assert
|
|
136
|
+
expect(result).toBe(5445);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should handle zero duration', () => {
|
|
140
|
+
// Arrange
|
|
141
|
+
const duration = '00:00:00.0';
|
|
142
|
+
|
|
143
|
+
// Act
|
|
144
|
+
const result = getSecondsFromDuration(duration);
|
|
145
|
+
|
|
146
|
+
// Assert
|
|
147
|
+
expect(result).toBe(0);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should pad short microseconds to 6 digits', () => {
|
|
151
|
+
// Arrange
|
|
152
|
+
const duration = '00:00:01.5';
|
|
153
|
+
|
|
154
|
+
// Act
|
|
155
|
+
const result = getSecondsFromDuration(duration);
|
|
156
|
+
|
|
157
|
+
// Assert
|
|
158
|
+
expect(result).toBe(1.5);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should truncate microseconds longer than 6 digits', () => {
|
|
162
|
+
// Arrange
|
|
163
|
+
const duration = '00:00:01.1234567890';
|
|
164
|
+
|
|
165
|
+
// Act
|
|
166
|
+
const result = getSecondsFromDuration(duration);
|
|
167
|
+
|
|
168
|
+
// Assert
|
|
169
|
+
expect(result).toBe(1.123456);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should handle missing microseconds with default padding', () => {
|
|
173
|
+
// Arrange
|
|
174
|
+
const duration = '00:00:05';
|
|
175
|
+
|
|
176
|
+
// Act
|
|
177
|
+
const result = getSecondsFromDuration(duration);
|
|
178
|
+
|
|
179
|
+
// Assert
|
|
180
|
+
expect(result).toBe(5);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should handle large durations', () => {
|
|
184
|
+
// Arrange
|
|
185
|
+
const duration = '23:59:59.999999';
|
|
186
|
+
|
|
187
|
+
// Act
|
|
188
|
+
const result = getSecondsFromDuration(duration);
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
expect(result).toBe(86399.999999);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should handle empty string', () => {
|
|
195
|
+
// Arrange
|
|
196
|
+
const duration = '';
|
|
197
|
+
|
|
198
|
+
// Act
|
|
199
|
+
const result = getSecondsFromDuration(duration);
|
|
200
|
+
|
|
201
|
+
// Assert
|
|
202
|
+
expect(result).toBe(0);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should handle null or undefined', () => {
|
|
206
|
+
// Arrange & Act
|
|
207
|
+
const resultNull = getSecondsFromDuration(null);
|
|
208
|
+
const resultUndefined = getSecondsFromDuration(undefined);
|
|
209
|
+
|
|
210
|
+
// Assert
|
|
211
|
+
expect(resultNull).toBe(0);
|
|
212
|
+
expect(resultUndefined).toBe(0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should handle malformed duration strings gracefully', () => {
|
|
216
|
+
// Arrange
|
|
217
|
+
const duration = 'invalid:time:format';
|
|
218
|
+
|
|
219
|
+
// Act
|
|
220
|
+
const parseResult = parseTimeFromDuration(duration);
|
|
221
|
+
const millisecondsResult = getSecondsFromDuration(duration);
|
|
222
|
+
|
|
223
|
+
// Assert
|
|
224
|
+
expect(parseResult.hours).toBe('invalid');
|
|
225
|
+
expect(parseResult.minutes).toBe('time');
|
|
226
|
+
expect(parseResult.seconds).toBe('format');
|
|
227
|
+
expect(millisecondsResult).toBeNaN();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
package/src/utils/helper.js
CHANGED
|
@@ -28,7 +28,7 @@ const getUnknownOptions = async (args, knownKeys) => {
|
|
|
28
28
|
const printErrorResponse = async (spinner, error) => {
|
|
29
29
|
if (
|
|
30
30
|
error.response?.status === 400
|
|
31
|
-
|
|
31
|
+
&& error.response?.data[0]?.message) {
|
|
32
32
|
spinner.fail(error.response.data[0].message);
|
|
33
33
|
} else {
|
|
34
34
|
spinner.fail(error.toString());
|
|
@@ -45,28 +45,36 @@ const overrideSettings = async (args) => {
|
|
|
45
45
|
|
|
46
46
|
const parseTimeFromDuration = (duration) => {
|
|
47
47
|
const [hours, minutes = '0', secondsWithMs = '0.0'] = (duration || '').split(':');
|
|
48
|
-
const [seconds,
|
|
48
|
+
const [seconds, microseconds = '0'] = secondsWithMs.split('.');
|
|
49
49
|
return {
|
|
50
|
-
hours: hours || '00',
|
|
50
|
+
hours: hours || '00',
|
|
51
|
+
minutes,
|
|
52
|
+
secondsWithMs,
|
|
53
|
+
seconds,
|
|
54
|
+
microseconds,
|
|
51
55
|
};
|
|
52
56
|
};
|
|
53
57
|
|
|
54
|
-
const
|
|
58
|
+
const getSecondsFromDuration = (duration) => {
|
|
55
59
|
const {
|
|
56
|
-
hours, minutes, seconds,
|
|
60
|
+
hours, minutes, seconds, microseconds,
|
|
57
61
|
} = parseTimeFromDuration(duration);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
const totalSeconds = parseInt(hours, 10) * 3600
|
|
63
|
+
+ parseInt(minutes, 10) * 60
|
|
64
|
+
+ parseInt(seconds, 10);
|
|
65
|
+
|
|
66
|
+
const paddedMicroseconds = (microseconds || '0').padEnd(6, '0').substring(0, 6);
|
|
67
|
+
const fractionalSeconds = parseInt(paddedMicroseconds, 10) / 1_000_000;
|
|
68
|
+
|
|
69
|
+
return totalSeconds + fractionalSeconds;
|
|
63
70
|
};
|
|
64
71
|
|
|
65
72
|
module.exports = {
|
|
66
73
|
getExitCode,
|
|
74
|
+
getSecondsFromDuration,
|
|
67
75
|
getUnknownOptions,
|
|
68
76
|
overrideSettings,
|
|
77
|
+
parseTimeFromDuration,
|
|
69
78
|
parseVariables,
|
|
70
79
|
printErrorResponse,
|
|
71
|
-
getMillisecondsFromDuration,
|
|
72
80
|
};
|
package/src/utils/testReports.js
CHANGED
|
@@ -3,7 +3,7 @@ const fs = require('node:fs');
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
|
|
5
5
|
const settings = require('../settings');
|
|
6
|
-
const {
|
|
6
|
+
const { getSecondsFromDuration } = require('./helper');
|
|
7
7
|
|
|
8
8
|
const getTestInJunitFormat = (testRun) => {
|
|
9
9
|
const errorDetails = testRun.errorCode ? {
|
|
@@ -14,7 +14,7 @@ const getTestInJunitFormat = (testRun) => {
|
|
|
14
14
|
return {
|
|
15
15
|
id: testRun.id,
|
|
16
16
|
name: testRun.name,
|
|
17
|
-
time:
|
|
17
|
+
time: getSecondsFromDuration(testRun.duration),
|
|
18
18
|
classname: null,
|
|
19
19
|
errors: testRun.status === settings.STATUS_ERROR ? [errorDetails] : [],
|
|
20
20
|
failures: testRun.status === settings.STATUS_FAILED ? [errorDetails] : [],
|
|
@@ -24,14 +24,14 @@ const getTestInJunitFormat = (testRun) => {
|
|
|
24
24
|
const getSuiteInJunitFormat = (suiteRun) => ({
|
|
25
25
|
id: suiteRun.id,
|
|
26
26
|
name: suiteRun.name,
|
|
27
|
-
time:
|
|
27
|
+
time: getSecondsFromDuration(suiteRun.duration),
|
|
28
28
|
testCases: suiteRun.details.map((testRun) => getTestInJunitFormat(testRun)),
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
const generateJunitReport = async (type, result, outputPath) => {
|
|
32
32
|
const testReport = {};
|
|
33
33
|
testReport.name = 'BugBug Report';
|
|
34
|
-
testReport.time =
|
|
34
|
+
testReport.time = getSecondsFromDuration(result.duration);
|
|
35
35
|
testReport.suites = [];
|
|
36
36
|
|
|
37
37
|
if (type === settings.TYPE_TEST) {
|