@elisra-devops/docgen-data-provider 1.26.6 → 1.28.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.
@@ -1,7 +1,7 @@
1
1
  import { TFSServices } from '../../helpers/tfs';
2
2
  import ResultDataProvider from '../ResultDataProvider';
3
3
  import logger from '../../utils/logger';
4
- import TestStepParserHelper from '../../utils/testStepParserHelper';
4
+ import Utils from '../../utils/testStepParserHelper';
5
5
 
6
6
  // Mock dependencies
7
7
  jest.mock('../../helpers/tfs');
@@ -10,572 +10,563 @@ jest.mock('../../utils/testStepParserHelper');
10
10
  jest.mock('p-limit', () => () => (fn: Function) => fn());
11
11
 
12
12
  describe('ResultDataProvider', () => {
13
- let resultDataProvider: ResultDataProvider;
14
- const mockOrgUrl = 'https://dev.azure.com/organization/';
15
- const mockToken = 'mock-token';
16
- const mockProjectName = 'test-project';
17
- const mockTestPlanId = '12345';
18
-
19
- beforeEach(() => {
20
- jest.clearAllMocks();
21
- resultDataProvider = new ResultDataProvider(mockOrgUrl, mockToken);
13
+ let resultDataProvider: ResultDataProvider;
14
+ const mockOrgUrl = 'https://dev.azure.com/organization/';
15
+ const mockToken = 'mock-token';
16
+ const mockProjectName = 'test-project';
17
+ const mockTestPlanId = '12345';
18
+
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ resultDataProvider = new ResultDataProvider(mockOrgUrl, mockToken);
22
+ });
23
+
24
+ describe('Utility methods', () => {
25
+ describe('flattenSuites', () => {
26
+ it('should flatten a hierarchical suite structure into a single-level array', () => {
27
+ // Arrange
28
+ const suites = [
29
+ {
30
+ id: 1,
31
+ name: 'Parent 1',
32
+ children: [
33
+ { id: 2, name: 'Child 1' },
34
+ {
35
+ id: 3,
36
+ name: 'Child 2',
37
+ children: [{ id: 4, name: 'Grandchild 1' }],
38
+ },
39
+ ],
40
+ },
41
+ { id: 5, name: 'Parent 2' },
42
+ ];
43
+
44
+ // Act
45
+ const result: any[] = (resultDataProvider as any).flattenSuites(suites);
46
+
47
+ // Assert
48
+ expect(result).toHaveLength(5);
49
+ expect(result.map((s) => s.id)).toEqual([1, 2, 3, 4, 5]);
50
+ });
22
51
  });
23
52
 
24
- describe('Utility methods', () => {
25
- describe('flattenSuites', () => {
26
- it('should flatten a hierarchical suite structure into a single-level array', () => {
27
- // Arrange
28
- const suites = [
29
- {
30
- id: 1,
31
- name: 'Parent 1',
32
- children: [
33
- { id: 2, name: 'Child 1' },
34
- {
35
- id: 3,
36
- name: 'Child 2',
37
- children: [{ id: 4, name: 'Grandchild 1' }]
38
- }
39
- ]
40
- },
41
- { id: 5, name: 'Parent 2' }
42
- ];
43
-
44
- // Act
45
- const result: any[] = (resultDataProvider as any).flattenSuites(suites);
46
-
47
- // Assert
48
- expect(result).toHaveLength(5);
49
- expect(result.map(s => s.id)).toEqual([1, 2, 3, 4, 5]);
50
- });
51
- });
53
+ describe('filterSuites', () => {
54
+ it('should filter suites based on selected suite IDs', () => {
55
+ // Arrange
56
+ const testSuites = [
57
+ { id: 1, name: 'Suite 1', parentSuite: { id: 0 } },
58
+ { id: 2, name: 'Suite 2', parentSuite: { id: 1 } },
59
+ { id: 3, name: 'Suite 3', parentSuite: { id: 1 } },
60
+ ];
61
+ const selectedSuiteIds = [1, 3];
62
+
63
+ // Act
64
+ const result: any[] = (resultDataProvider as any).filterSuites(testSuites, selectedSuiteIds);
65
+
66
+ // Assert
67
+ expect(result).toHaveLength(2);
68
+ expect(result.map((s) => s.id)).toEqual([1, 3]);
69
+ });
70
+
71
+ it('should return all suites with parent when no suite IDs are selected', () => {
72
+ // Arrange
73
+ const testSuites = [
74
+ { id: 1, name: 'Suite 1', parentSuite: { id: 0 } },
75
+ { id: 2, name: 'Suite 2', parentSuite: { id: 1 } },
76
+ { id: 3, name: 'Suite 3', parentSuite: null },
77
+ ];
78
+
79
+ // Act
80
+ const result: any[] = (resultDataProvider as any).filterSuites(testSuites);
81
+
82
+ // Assert
83
+ expect(result).toHaveLength(2);
84
+ expect(result.map((s) => s.id)).toEqual([1, 2]);
85
+ });
86
+ });
52
87
 
53
- describe('filterSuites', () => {
54
- it('should filter suites based on selected suite IDs', () => {
55
- // Arrange
56
- const testSuites = [
57
- { id: 1, name: 'Suite 1', parentSuite: { id: 0 } },
58
- { id: 2, name: 'Suite 2', parentSuite: { id: 1 } },
59
- { id: 3, name: 'Suite 3', parentSuite: { id: 1 } }
60
- ];
61
- const selectedSuiteIds = [1, 3];
62
-
63
- // Act
64
- const result: any[] = (resultDataProvider as any).filterSuites(testSuites, selectedSuiteIds);
65
-
66
- // Assert
67
- expect(result).toHaveLength(2);
68
- expect(result.map(s => s.id)).toEqual([1, 3]);
69
- });
70
-
71
- it('should return all suites with parent when no suite IDs are selected', () => {
72
- // Arrange
73
- const testSuites = [
74
- { id: 1, name: 'Suite 1', parentSuite: { id: 0 } },
75
- { id: 2, name: 'Suite 2', parentSuite: { id: 1 } },
76
- { id: 3, name: 'Suite 3', parentSuite: null }
77
- ];
78
-
79
- // Act
80
- const result: any[] = (resultDataProvider as any).filterSuites(testSuites);
81
-
82
- // Assert
83
- expect(result).toHaveLength(2);
84
- expect(result.map(s => s.id)).toEqual([1, 2]);
85
- });
86
- });
88
+ describe('buildTestGroupName', () => {
89
+ it('should return simple suite name when hierarchy is disabled', () => {
90
+ // Arrange
91
+ const suiteMap = new Map([[1, { id: 1, name: 'Suite 1', parentSuite: { id: 0 } }]]);
92
+
93
+ // Act
94
+ const result = (resultDataProvider as any).buildTestGroupName(1, suiteMap, false);
95
+
96
+ // Assert
97
+ expect(result).toBe('Suite 1');
98
+ });
99
+
100
+ it('should build hierarchical name with parent info', () => {
101
+ // Arrange
102
+ const suiteMap = new Map([
103
+ [1, { id: 1, name: 'Parent', parentSuite: null }],
104
+ [2, { id: 2, name: 'Child', parentSuite: { id: 1 } }],
105
+ ]);
106
+
107
+ // Act
108
+ const result = (resultDataProvider as any).buildTestGroupName(2, suiteMap, true);
109
+
110
+ // Assert
111
+ expect(result).toBe('Child');
112
+ });
113
+
114
+ it('should abbreviate deep hierarchies', () => {
115
+ // Arrange
116
+ const suiteMap = new Map([
117
+ [1, { id: 1, name: 'Root', parentSuite: null }],
118
+ [2, { id: 2, name: 'Level1', parentSuite: { id: 1 } }],
119
+ [3, { id: 3, name: 'Level2', parentSuite: { id: 2 } }],
120
+ [4, { id: 4, name: 'Level3', parentSuite: { id: 3 } }],
121
+ ]);
122
+
123
+ // Act
124
+ const result = (resultDataProvider as any).buildTestGroupName(4, suiteMap, true);
125
+
126
+ // Assert
127
+ expect(result).toBe('Level1/.../Level3');
128
+ });
129
+ });
87
130
 
88
- describe('buildTestGroupName', () => {
89
- it('should return simple suite name when hierarchy is disabled', () => {
90
- // Arrange
91
- const suiteMap = new Map([
92
- [1, { id: 1, name: 'Suite 1', parentSuite: { id: 0 } }]
93
- ]);
94
-
95
- // Act
96
- const result = (resultDataProvider as any).buildTestGroupName(1, suiteMap, false);
97
-
98
- // Assert
99
- expect(result).toBe('Suite 1');
100
- });
101
-
102
- it('should build hierarchical name with parent info', () => {
103
- // Arrange
104
- const suiteMap = new Map([
105
- [1, { id: 1, name: 'Parent', parentSuite: null }],
106
- [2, { id: 2, name: 'Child', parentSuite: { id: 1 } }]
107
- ]);
108
-
109
- // Act
110
- const result = (resultDataProvider as any).buildTestGroupName(2, suiteMap, true);
111
-
112
- // Assert
113
- expect(result).toBe('Child');
114
- });
115
-
116
- it('should abbreviate deep hierarchies', () => {
117
- // Arrange
118
- const suiteMap = new Map([
119
- [1, { id: 1, name: 'Root', parentSuite: null }],
120
- [2, { id: 2, name: 'Level1', parentSuite: { id: 1 } }],
121
- [3, { id: 3, name: 'Level2', parentSuite: { id: 2 } }],
122
- [4, { id: 4, name: 'Level3', parentSuite: { id: 3 } }]
123
- ]);
124
-
125
- // Act
126
- const result = (resultDataProvider as any).buildTestGroupName(4, suiteMap, true);
127
-
128
- // Assert
129
- expect(result).toBe('Level1/.../Level3');
130
- });
131
- });
131
+ describe('convertRunStatus', () => {
132
+ it('should convert API status to readable format', () => {
133
+ // Arrange & Act & Assert
134
+ expect((resultDataProvider as any).convertRunStatus('passed')).toBe('Passed');
135
+ expect((resultDataProvider as any).convertRunStatus('failed')).toBe('Failed');
136
+ expect((resultDataProvider as any).convertRunStatus('notApplicable')).toBe('Not Applicable');
137
+ expect((resultDataProvider as any).convertRunStatus('unknown')).toBe('Not Run');
138
+ });
139
+ });
132
140
 
133
- describe('convertRunStatus', () => {
134
- it('should convert API status to readable format', () => {
135
- // Arrange & Act & Assert
136
- expect((resultDataProvider as any).convertRunStatus('passed')).toBe('Passed');
137
- expect((resultDataProvider as any).convertRunStatus('failed')).toBe('Failed');
138
- expect((resultDataProvider as any).convertRunStatus('notApplicable')).toBe('Not Applicable');
139
- expect((resultDataProvider as any).convertRunStatus('unknown')).toBe('Not Run');
140
- });
141
- });
141
+ describe('setRunStatus', () => {
142
+ it('should return empty for shared step titles with Unspecified outcome', () => {
143
+ // Arrange
144
+ const actionResult = { outcome: 'Unspecified', isSharedStepTitle: true };
142
145
 
143
- describe('setRunStatus', () => {
144
- it('should return empty for shared step titles with Unspecified outcome', () => {
145
- // Arrange
146
- const actionResult = { outcome: 'Unspecified', isSharedStepTitle: true };
146
+ // Act
147
+ const result = (resultDataProvider as any).setRunStatus(actionResult);
147
148
 
148
- // Act
149
- const result = (resultDataProvider as any).setRunStatus(actionResult);
149
+ // Assert
150
+ expect(result).toBe('');
151
+ });
150
152
 
151
- // Assert
152
- expect(result).toBe('');
153
- });
153
+ it('should return "Not Run" for Unspecified outcome on regular steps', () => {
154
+ // Arrange
155
+ const actionResult = { outcome: 'Unspecified', isSharedStepTitle: false };
154
156
 
155
- it('should return "Not Run" for Unspecified outcome on regular steps', () => {
156
- // Arrange
157
- const actionResult = { outcome: 'Unspecified', isSharedStepTitle: false };
157
+ // Act
158
+ const result = (resultDataProvider as any).setRunStatus(actionResult);
158
159
 
159
- // Act
160
- const result = (resultDataProvider as any).setRunStatus(actionResult);
160
+ // Assert
161
+ expect(result).toBe('Not Run');
162
+ });
161
163
 
162
- // Assert
163
- expect(result).toBe('Not Run');
164
- });
164
+ it('should return the outcome for non-Unspecified outcomes', () => {
165
+ // Arrange
166
+ const actionResult = { outcome: 'Failed', isSharedStepTitle: false };
165
167
 
166
- it('should return the outcome for non-Unspecified outcomes', () => {
167
- // Arrange
168
- const actionResult = { outcome: 'Failed', isSharedStepTitle: false };
168
+ // Act
169
+ const result = (resultDataProvider as any).setRunStatus(actionResult);
169
170
 
170
- // Act
171
- const result = (resultDataProvider as any).setRunStatus(actionResult);
171
+ // Assert
172
+ expect(result).toBe('Failed');
173
+ });
174
+ });
172
175
 
173
- // Assert
174
- expect(result).toBe('Failed');
175
- });
176
- });
176
+ describe('compareActionResults', () => {
177
+ it('should compare version-like step positions correctly', () => {
178
+ // Act & Assert
179
+ const compare = (resultDataProvider as any).compareActionResults;
180
+ expect(compare('1', '2')).toBe(-1);
181
+ expect(compare('2', '1')).toBe(1);
182
+ expect(compare('1.1', '1.2')).toBe(-1);
183
+ expect(compare('1.2', '1.1')).toBe(1);
184
+ expect(compare('1.1', '1.1')).toBe(0);
185
+ expect(compare('1.1.1', '1.1')).toBe(1);
186
+ expect(compare('1.1', '1.1.1')).toBe(-1);
187
+ });
188
+ });
189
+ });
190
+
191
+ describe('Data fetching methods', () => {
192
+ describe('fetchTestSuites', () => {
193
+ it('should fetch and process test suites correctly', async () => {
194
+ // Arrange
195
+ const mockTestSuites = {
196
+ value: [
197
+ {
198
+ id: 1,
199
+ name: 'Root Suite',
200
+ children: [{ id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }],
201
+ },
202
+ ],
203
+ count: 1,
204
+ };
205
+
206
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestSuites);
207
+
208
+ // Act
209
+ const result = await (resultDataProvider as any).fetchTestSuites(mockTestPlanId, mockProjectName);
210
+
211
+ // Assert
212
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
213
+ `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites?asTreeView=true`,
214
+ mockToken
215
+ );
216
+ expect(result).toHaveLength(1);
217
+ expect(result[0]).toHaveProperty('testSuiteId', 2);
218
+ expect(result[0]).toHaveProperty('testGroupName');
219
+ });
220
+
221
+ it('should handle errors and return empty array', async () => {
222
+ // Arrange
223
+ const mockError = new Error('API error');
224
+ (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
225
+
226
+ // Act
227
+ const result = await (resultDataProvider as any).fetchTestSuites(mockTestPlanId, mockProjectName);
228
+
229
+ // Assert
230
+ expect(logger.error).toHaveBeenCalled();
231
+ expect(result).toEqual([]);
232
+ });
233
+ });
177
234
 
178
- describe('compareActionResults', () => {
179
- it('should compare version-like step positions correctly', () => {
180
- // Act & Assert
181
- const compare = (resultDataProvider as any).compareActionResults;
182
- expect(compare('1', '2')).toBe(-1);
183
- expect(compare('2', '1')).toBe(1);
184
- expect(compare('1.1', '1.2')).toBe(-1);
185
- expect(compare('1.2', '1.1')).toBe(1);
186
- expect(compare('1.1', '1.1')).toBe(0);
187
- expect(compare('1.1.1', '1.1')).toBe(1);
188
- expect(compare('1.1', '1.1.1')).toBe(-1);
189
- });
235
+ describe('fetchTestPoints', () => {
236
+ it('should fetch and map test points correctly', async () => {
237
+ // Arrange
238
+ const mockSuiteId = '123';
239
+ const mockTestPoints = {
240
+ value: [
241
+ {
242
+ testCaseReference: { id: 1, name: 'Test Case 1' },
243
+ configuration: { name: 'Config 1' },
244
+ results: {
245
+ outcome: 'passed',
246
+ lastTestRunId: 100,
247
+ lastResultId: 200,
248
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
249
+ },
250
+ },
251
+ ],
252
+ count: 1,
253
+ };
254
+
255
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestPoints);
256
+
257
+ // Act
258
+ const result = await (resultDataProvider as any).fetchTestPoints(
259
+ mockProjectName,
260
+ mockTestPlanId,
261
+ mockSuiteId
262
+ );
263
+
264
+ // Assert
265
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
266
+ `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites/${mockSuiteId}/TestPoint?includePointDetails=true`,
267
+ mockToken
268
+ );
269
+ expect(result).toHaveLength(1);
270
+ expect(result[0]).toEqual({
271
+ testCaseId: 1,
272
+ testCaseName: 'Test Case 1',
273
+ configurationName: 'Config 1',
274
+ outcome: 'passed',
275
+ lastRunId: 100,
276
+ lastResultId: 200,
277
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
190
278
  });
279
+ });
280
+
281
+ it('should handle errors and return empty array', async () => {
282
+ // Arrange
283
+ const mockSuiteId = '123';
284
+ const mockError = new Error('API error');
285
+ (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
286
+
287
+ // Act
288
+ const result = await (resultDataProvider as any).fetchTestPoints(
289
+ mockProjectName,
290
+ mockTestPlanId,
291
+ mockSuiteId
292
+ );
293
+
294
+ // Assert
295
+ expect(logger.error).toHaveBeenCalled();
296
+ expect(result).toEqual([]);
297
+ });
191
298
  });
192
-
193
- describe('Data fetching methods', () => {
194
- describe('fetchTestSuites', () => {
195
- it('should fetch and process test suites correctly', async () => {
196
- // Arrange
197
- const mockTestSuites = {
198
- value: [
199
- {
200
- id: 1,
201
- name: 'Root Suite',
202
- children: [
203
- { id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }
204
- ]
205
- }
206
- ],
207
- count: 1
208
- };
209
-
210
- (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestSuites);
211
-
212
- // Act
213
- const result = await (resultDataProvider as any).fetchTestSuites(
214
- mockTestPlanId,
215
- mockProjectName
216
- );
217
-
218
- // Assert
219
- expect(TFSServices.getItemContent).toHaveBeenCalledWith(
220
- `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites?asTreeView=true`,
221
- mockToken
222
- );
223
- expect(result).toHaveLength(1);
224
- expect(result[0]).toHaveProperty('testSuiteId', 2);
225
- expect(result[0]).toHaveProperty('testGroupName');
226
- });
227
-
228
- it('should handle errors and return empty array', async () => {
229
- // Arrange
230
- const mockError = new Error('API error');
231
- (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
232
-
233
- // Act
234
- const result = await (resultDataProvider as any).fetchTestSuites(
235
- mockTestPlanId,
236
- mockProjectName
237
- );
238
-
239
- // Assert
240
- expect(logger.error).toHaveBeenCalled();
241
- expect(result).toEqual([]);
242
- });
299
+ });
300
+
301
+ describe('Data transformation methods', () => {
302
+ describe('mapTestPoint', () => {
303
+ it('should transform test point data correctly', () => {
304
+ // Arrange
305
+ const testPoint = {
306
+ testCaseReference: { id: 1, name: 'Test Case 1' },
307
+ configuration: { name: 'Config 1' },
308
+ results: {
309
+ outcome: 'passed',
310
+ lastTestRunId: 100,
311
+ lastResultId: 200,
312
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
313
+ },
314
+ };
315
+
316
+ // Act
317
+ const result = (resultDataProvider as any).mapTestPoint(testPoint);
318
+
319
+ // Assert
320
+ expect(result).toEqual({
321
+ testCaseId: 1,
322
+ testCaseName: 'Test Case 1',
323
+ configurationName: 'Config 1',
324
+ outcome: 'passed',
325
+ lastRunId: 100,
326
+ lastResultId: 200,
327
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
243
328
  });
244
-
245
- describe('fetchTestPoints', () => {
246
- it('should fetch and map test points correctly', async () => {
247
- // Arrange
248
- const mockSuiteId = '123';
249
- const mockTestPoints = {
250
- value: [
251
- {
252
- testCaseReference: { id: 1, name: 'Test Case 1' },
253
- configuration: { name: 'Config 1' },
254
- results: {
255
- outcome: 'passed',
256
- lastTestRunId: 100,
257
- lastResultId: 200,
258
- lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } }
259
- }
260
- }
261
- ],
262
- count: 1
263
- };
264
-
265
- (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestPoints);
266
-
267
- // Act
268
- const result = await (resultDataProvider as any).fetchTestPoints(
269
- mockProjectName,
270
- mockTestPlanId,
271
- mockSuiteId
272
- );
273
-
274
- // Assert
275
- expect(TFSServices.getItemContent).toHaveBeenCalledWith(
276
- `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites/${mockSuiteId}/TestPoint?includePointDetails=true`,
277
- mockToken
278
- );
279
- expect(result).toHaveLength(1);
280
- expect(result[0]).toEqual({
281
- testCaseId: 1,
282
- testCaseName: 'Test Case 1',
283
- configurationName: 'Config 1',
284
- outcome: 'passed',
285
- lastRunId: 100,
286
- lastResultId: 200,
287
- lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } }
288
- });
289
- });
290
-
291
- it('should handle errors and return empty array', async () => {
292
- // Arrange
293
- const mockSuiteId = '123';
294
- const mockError = new Error('API error');
295
- (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
296
-
297
- // Act
298
- const result = await (resultDataProvider as any).fetchTestPoints(
299
- mockProjectName,
300
- mockTestPlanId,
301
- mockSuiteId
302
- );
303
-
304
- // Assert
305
- expect(logger.error).toHaveBeenCalled();
306
- expect(result).toEqual([]);
307
- });
329
+ });
330
+
331
+ it('should handle missing fields', () => {
332
+ // Arrange
333
+ const testPoint = {
334
+ testCaseReference: { id: 1, name: 'Test Case 1' },
335
+ // No configuration or results
336
+ };
337
+
338
+ // Act
339
+ const result = (resultDataProvider as any).mapTestPoint(testPoint);
340
+
341
+ // Assert
342
+ expect(result).toEqual({
343
+ testCaseId: 1,
344
+ testCaseName: 'Test Case 1',
345
+ configurationName: undefined,
346
+ outcome: 'Not Run',
347
+ lastRunId: undefined,
348
+ lastResultId: undefined,
349
+ lastResultDetails: undefined,
308
350
  });
351
+ });
309
352
  });
310
353
 
311
- describe('Data transformation methods', () => {
312
- describe('mapTestPoint', () => {
313
- it('should transform test point data correctly', () => {
314
- // Arrange
315
- const testPoint = {
316
- testCaseReference: { id: 1, name: 'Test Case 1' },
317
- configuration: { name: 'Config 1' },
318
- results: {
319
- outcome: 'passed',
320
- lastTestRunId: 100,
321
- lastResultId: 200,
322
- lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } }
323
- }
324
- };
325
-
326
- // Act
327
- const result = (resultDataProvider as any).mapTestPoint(testPoint);
328
-
329
- // Assert
330
- expect(result).toEqual({
331
- testCaseId: 1,
332
- testCaseName: 'Test Case 1',
333
- configurationName: 'Config 1',
334
- outcome: 'passed',
335
- lastRunId: 100,
336
- lastResultId: 200,
337
- lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } }
338
- });
339
- });
340
-
341
- it('should handle missing fields', () => {
342
- // Arrange
343
- const testPoint = {
344
- testCaseReference: { id: 1, name: 'Test Case 1' }
345
- // No configuration or results
346
- };
347
-
348
- // Act
349
- const result = (resultDataProvider as any).mapTestPoint(testPoint);
350
-
351
- // Assert
352
- expect(result).toEqual({
353
- testCaseId: 1,
354
- testCaseName: 'Test Case 1',
355
- configurationName: undefined,
356
- outcome: 'Not Run',
357
- lastRunId: undefined,
358
- lastResultId: undefined,
359
- lastResultDetails: undefined
360
- });
361
- });
354
+ describe('calculateGroupResultSummary', () => {
355
+ it('should return empty strings when includeHardCopyRun is true', () => {
356
+ // Arrange
357
+ const testPoints = [{ outcome: 'passed' }, { outcome: 'failed' }];
358
+
359
+ // Act
360
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, true);
361
+
362
+ // Assert
363
+ expect(result).toEqual({
364
+ passed: '',
365
+ failed: '',
366
+ notApplicable: '',
367
+ blocked: '',
368
+ notRun: '',
369
+ total: '',
370
+ successPercentage: '',
362
371
  });
363
-
364
- describe('calculateGroupResultSummary', () => {
365
- it('should return empty strings when includeHardCopyRun is true', () => {
366
- // Arrange
367
- const testPoints = [
368
- { outcome: 'passed' },
369
- { outcome: 'failed' }
370
- ];
371
-
372
- // Act
373
- const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, true);
374
-
375
- // Assert
376
- expect(result).toEqual({
377
- passed: '',
378
- failed: '',
379
- notApplicable: '',
380
- blocked: '',
381
- notRun: '',
382
- total: '',
383
- successPercentage: ''
384
- });
385
- });
386
-
387
- it('should calculate summary statistics correctly', () => {
388
- // Arrange
389
- const testPoints = [
390
- { outcome: 'passed' },
391
- { outcome: 'passed' },
392
- { outcome: 'failed' },
393
- { outcome: 'notApplicable' },
394
- { outcome: 'blocked' },
395
- { outcome: 'something else' }
396
- ];
397
-
398
- // Act
399
- const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
400
-
401
- // Assert
402
- expect(result).toEqual({
403
- passed: 2,
404
- failed: 1,
405
- notApplicable: 1,
406
- blocked: 1,
407
- notRun: 1,
408
- total: 6,
409
- successPercentage: '33.33%'
410
- });
411
- });
412
-
413
- it('should handle empty array', () => {
414
- // Arrange
415
- const testPoints: any[] = [];
416
-
417
- // Act
418
- const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
419
-
420
- // Assert
421
- expect(result).toEqual({
422
- passed: 0,
423
- failed: 0,
424
- notApplicable: 0,
425
- blocked: 0,
426
- notRun: 0,
427
- total: 0,
428
- successPercentage: '0.00%'
429
- });
430
- });
372
+ });
373
+
374
+ it('should calculate summary statistics correctly', () => {
375
+ // Arrange
376
+ const testPoints = [
377
+ { outcome: 'passed' },
378
+ { outcome: 'passed' },
379
+ { outcome: 'failed' },
380
+ { outcome: 'notApplicable' },
381
+ { outcome: 'blocked' },
382
+ { outcome: 'something else' },
383
+ ];
384
+
385
+ // Act
386
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
387
+
388
+ // Assert
389
+ expect(result).toEqual({
390
+ passed: 2,
391
+ failed: 1,
392
+ notApplicable: 1,
393
+ blocked: 1,
394
+ notRun: 1,
395
+ total: 6,
396
+ successPercentage: '33.33%',
431
397
  });
432
-
433
- describe('mapAttachmentsUrl', () => {
434
- it('should map attachment URLs correctly', () => {
435
- // Arrange
436
- const mockRunResults = [
437
- {
438
- testCaseId: 1,
439
- lastRunId: 100,
440
- lastResultId: 200,
441
- iteration: {
442
- attachments: [
443
- { id: 1, name: 'attachment1.png', actionPath: 'path1' }
444
- ],
445
- actionResults: [
446
- { actionPath: 'path1', stepPosition: '1.1' }
447
- ]
448
- },
449
- analysisAttachments: [
450
- { id: 2, fileName: 'analysis1.txt' }
451
- ]
452
- }
453
- ];
454
-
455
- // Act
456
- const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
457
-
458
- // Assert
459
- expect(result[0].iteration.attachments[0].downloadUrl).toBe(
460
- `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/1/attachment1.png`
461
- );
462
- expect(result[0].iteration.attachments[0].stepNo).toBe('1.1');
463
- expect(result[0].analysisAttachments[0].downloadUrl).toBe(
464
- `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/2/analysis1.txt`
465
- );
466
- });
467
-
468
- it('should handle missing iteration', () => {
469
- // Arrange
470
- const mockRunResults = [
471
- {
472
- testCaseId: 1,
473
- lastRunId: 100,
474
- lastResultId: 200,
475
- // No iteration
476
- analysisAttachments: [
477
- { id: 2, fileName: 'analysis1.txt' }
478
- ]
479
- }
480
- ];
481
-
482
- // Act
483
- const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
484
-
485
- // Assert
486
- expect(result[0]).toEqual(mockRunResults[0]);
487
- });
398
+ });
399
+
400
+ it('should handle empty array', () => {
401
+ // Arrange
402
+ const testPoints: any[] = [];
403
+
404
+ // Act
405
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
406
+
407
+ // Assert
408
+ expect(result).toEqual({
409
+ passed: 0,
410
+ failed: 0,
411
+ notApplicable: 0,
412
+ blocked: 0,
413
+ notRun: 0,
414
+ total: 0,
415
+ successPercentage: '0.00%',
488
416
  });
417
+ });
489
418
  });
490
419
 
491
- describe('getCombinedResultsSummary', () => {
492
- it('should combine all results into expected format', async () => {
493
- // Arrange
494
- const mockTestSuites = {
495
- value: [
496
- {
497
- id: 1,
498
- name: 'Root Suite',
499
- children: [
500
- { id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }
501
- ]
502
- }
503
- ],
504
- count: 1
505
- };
506
-
507
- const mockTestPoints = {
508
- value: [
509
- {
510
- testCaseReference: { id: 1, name: 'Test Case 1' },
511
- configuration: { name: 'Config 1' },
512
- results: {
513
- outcome: 'passed',
514
- lastTestRunId: 100,
515
- lastResultId: 200,
516
- lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } }
517
- }
518
- }
519
- ],
520
- count: 1
521
- };
522
-
523
- const mockTestCases = {
524
- value: [
525
- {
526
- workItem: {
527
- id: 1,
528
- workItemFields: [{ key: 'Steps', value: '<steps>...</steps>' }]
529
- }
530
- }
531
- ]
532
- };
533
-
534
- const mockResult = {
535
- testCase: { id: 1, name: 'Test Case 1' },
536
- testSuite: { id: 2, name: 'Child Suite 1' },
537
- iterationDetails: [
538
- {
539
- actionResults: [
540
- { stepIdentifier: '1', outcome: 'Passed', errorMessage: '', actionPath: 'path1' }
541
- ],
542
- attachments: []
543
- }
544
- ],
545
- testCaseRevision: 1,
546
- failureType: null,
547
- resolutionState: null,
548
- comment: null
549
- };
550
-
551
- // Setup mocks for API calls
552
- (TFSServices.getItemContent as jest.Mock)
553
- .mockResolvedValueOnce(mockTestSuites) // fetchTestSuites
554
- .mockResolvedValueOnce(mockTestPoints) // fetchTestPoints
555
- .mockResolvedValueOnce(mockTestCases) // fetchTestCasesBySuiteId
556
- .mockResolvedValueOnce(mockResult) // fetchResult
557
- .mockResolvedValueOnce({ value: [] }) // fetchResult - attachments
558
- .mockResolvedValueOnce({ fields: {} }); // fetchResult - wiByRevision
559
-
560
- const mockTestStepParserHelper = (resultDataProvider as any).testStepParserHelper;
561
- mockTestStepParserHelper.parseTestSteps.mockResolvedValueOnce([
562
- { stepId: 1, stepPosition: '1', action: 'Do something', expected: 'Something happens', isSharedStepTitle: false }
563
- ]);
564
-
565
- // Act
566
- const result = await resultDataProvider.getCombinedResultsSummary(
567
- mockTestPlanId,
568
- mockProjectName,
569
- undefined,
570
- true
571
- );
572
-
573
- // Assert
574
- expect(result.length).toBeGreaterThan(0);
575
- expect(result[0]).toHaveProperty('contentControl', 'test-group-summary-content-control');
576
- expect(result[1]).toHaveProperty('contentControl', 'test-result-summary-content-control');
577
- expect(result[2]).toHaveProperty('contentControl', 'detailed-test-result-content-control');
578
- });
579
-
420
+ describe('mapAttachmentsUrl', () => {
421
+ it('should map attachment URLs correctly', () => {
422
+ // Arrange
423
+ const mockRunResults = [
424
+ {
425
+ testCaseId: 1,
426
+ lastRunId: 100,
427
+ lastResultId: 200,
428
+ iteration: {
429
+ attachments: [{ id: 1, name: 'attachment1.png', actionPath: 'path1' }],
430
+ actionResults: [{ actionPath: 'path1', stepPosition: '1.1' }],
431
+ },
432
+ analysisAttachments: [{ id: 2, fileName: 'analysis1.txt' }],
433
+ },
434
+ ];
435
+
436
+ // Act
437
+ const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
438
+
439
+ // Assert
440
+ expect(result[0].iteration.attachments[0].downloadUrl).toBe(
441
+ `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/1/attachment1.png`
442
+ );
443
+ expect(result[0].iteration.attachments[0].stepNo).toBe('1.1');
444
+ expect(result[0].analysisAttachments[0].downloadUrl).toBe(
445
+ `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/2/analysis1.txt`
446
+ );
447
+ });
448
+
449
+ it('should handle missing iteration', () => {
450
+ // Arrange
451
+ const mockRunResults = [
452
+ {
453
+ testCaseId: 1,
454
+ lastRunId: 100,
455
+ lastResultId: 200,
456
+ // No iteration
457
+ analysisAttachments: [{ id: 2, fileName: 'analysis1.txt' }],
458
+ },
459
+ ];
460
+
461
+ // Act
462
+ const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
463
+
464
+ // Assert
465
+ expect(result[0]).toEqual(mockRunResults[0]);
466
+ });
467
+ });
468
+ });
469
+
470
+ describe('getCombinedResultsSummary', () => {
471
+ it('should combine all results into expected format', async () => {
472
+ // Arrange
473
+ const mockTestSuites = {
474
+ value: [
475
+ {
476
+ id: 1,
477
+ name: 'Root Suite',
478
+ children: [{ id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }],
479
+ },
480
+ ],
481
+ count: 1,
482
+ };
483
+
484
+ const mockTestPoints = {
485
+ value: [
486
+ {
487
+ testCaseReference: { id: 1, name: 'Test Case 1' },
488
+ configuration: { name: 'Config 1' },
489
+ results: {
490
+ outcome: 'passed',
491
+ lastTestRunId: 100,
492
+ lastResultId: 200,
493
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
494
+ },
495
+ },
496
+ ],
497
+ count: 1,
498
+ };
499
+
500
+ const mockTestCases = {
501
+ value: [
502
+ {
503
+ workItem: {
504
+ id: 1,
505
+ workItemFields: [{ key: 'Steps', value: '<steps>...</steps>' }],
506
+ },
507
+ },
508
+ ],
509
+ };
510
+
511
+ const mockResult = {
512
+ testCase: { id: 1, name: 'Test Case 1' },
513
+ testSuite: { id: 2, name: 'Child Suite 1' },
514
+ iterationDetails: [
515
+ {
516
+ actionResults: [
517
+ { stepIdentifier: '1', outcome: 'Passed', errorMessage: '', actionPath: 'path1' },
518
+ ],
519
+ attachments: [],
520
+ },
521
+ ],
522
+ testCaseRevision: 1,
523
+ failureType: null,
524
+ resolutionState: null,
525
+ comment: null,
526
+ };
527
+
528
+ // Setup mocks for API calls
529
+ (TFSServices.getItemContent as jest.Mock)
530
+ .mockResolvedValueOnce(mockTestSuites) // fetchTestSuites
531
+ .mockResolvedValueOnce(mockTestPoints) // fetchTestPoints
532
+ .mockResolvedValueOnce(mockTestCases) // fetchTestCasesBySuiteId
533
+ .mockResolvedValueOnce(mockResult) // fetchResult
534
+ .mockResolvedValueOnce({ value: [] }) // fetchResult - attachments
535
+ .mockResolvedValueOnce({ fields: {} }); // fetchResult - wiByRevision
536
+
537
+ const mockTestStepParserHelper = (resultDataProvider as any).testStepParserHelper;
538
+ mockTestStepParserHelper.parseTestSteps.mockResolvedValueOnce([
539
+ {
540
+ stepId: 1,
541
+ stepPosition: '1',
542
+ action: 'Do something',
543
+ expected: 'Something happens',
544
+ isSharedStepTitle: false,
545
+ },
546
+ ]);
547
+
548
+ // Act
549
+ const result = await resultDataProvider.getCombinedResultsSummary(
550
+ mockTestPlanId,
551
+ mockProjectName,
552
+ undefined,
553
+ true
554
+ );
555
+
556
+ // Assert
557
+ expect(result.combinedResults.length).toBeGreaterThan(0);
558
+ expect(result.combinedResults[0]).toHaveProperty(
559
+ 'contentControl',
560
+ 'test-group-summary-content-control'
561
+ );
562
+ expect(result.combinedResults[1]).toHaveProperty(
563
+ 'contentControl',
564
+ 'test-result-summary-content-control'
565
+ );
566
+ expect(result.combinedResults[2]).toHaveProperty(
567
+ 'contentControl',
568
+ 'detailed-test-result-content-control'
569
+ );
580
570
  });
581
- });
571
+ });
572
+ });