@elisra-devops/docgen-data-provider 1.27.0 → 1.29.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,533 @@ 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
- });
52
-
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
- });
87
-
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
- });
132
-
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
- });
142
-
143
- describe('setRunStatus', () => {
144
- it('should return empty for shared step titles with Unspecified outcome', () => {
145
- // Arrange
146
- const actionResult = { outcome: 'Unspecified', isSharedStepTitle: true };
147
-
148
- // Act
149
- const result = (resultDataProvider as any).setRunStatus(actionResult);
150
-
151
- // Assert
152
- expect(result).toBe('');
153
- });
154
-
155
- it('should return "Not Run" for Unspecified outcome on regular steps', () => {
156
- // Arrange
157
- const actionResult = { outcome: 'Unspecified', isSharedStepTitle: false };
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
+ });
158
87
 
159
- // Act
160
- const result = (resultDataProvider as any).setRunStatus(actionResult);
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
+ });
161
130
 
162
- // Assert
163
- expect(result).toBe('Not Run');
164
- });
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
+ });
165
140
 
166
- it('should return the outcome for non-Unspecified outcomes', () => {
167
- // Arrange
168
- const actionResult = { outcome: 'Failed', isSharedStepTitle: false };
169
141
 
170
- // Act
171
- const result = (resultDataProvider as any).setRunStatus(actionResult);
172
142
 
173
- // Assert
174
- expect(result).toBe('Failed');
175
- });
176
- });
143
+ describe('compareActionResults', () => {
144
+ it('should compare version-like step positions correctly', () => {
145
+ // Act & Assert
146
+ const compare = (resultDataProvider as any).compareActionResults;
147
+ expect(compare('1', '2')).toBe(-1);
148
+ expect(compare('2', '1')).toBe(1);
149
+ expect(compare('1.1', '1.2')).toBe(-1);
150
+ expect(compare('1.2', '1.1')).toBe(1);
151
+ expect(compare('1.1', '1.1')).toBe(0);
152
+ expect(compare('1.1.1', '1.1')).toBe(1);
153
+ expect(compare('1.1', '1.1.1')).toBe(-1);
154
+ });
155
+ });
156
+ });
157
+
158
+ describe('Data fetching methods', () => {
159
+ describe('fetchTestSuites', () => {
160
+ it('should fetch and process test suites correctly', async () => {
161
+ // Arrange
162
+ const mockTestSuites = {
163
+ value: [
164
+ {
165
+ id: 1,
166
+ name: 'Root Suite',
167
+ children: [{ id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }],
168
+ },
169
+ ],
170
+ count: 1,
171
+ };
172
+
173
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestSuites);
174
+
175
+ // Act
176
+ const result = await (resultDataProvider as any).fetchTestSuites(mockTestPlanId, mockProjectName);
177
+
178
+ // Assert
179
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
180
+ `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites?asTreeView=true`,
181
+ mockToken
182
+ );
183
+ expect(result).toHaveLength(1);
184
+ expect(result[0]).toHaveProperty('testSuiteId', 2);
185
+ expect(result[0]).toHaveProperty('testGroupName');
186
+ });
187
+
188
+ it('should handle errors and return empty array', async () => {
189
+ // Arrange
190
+ const mockError = new Error('API error');
191
+ (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
192
+
193
+ // Act
194
+ const result = await (resultDataProvider as any).fetchTestSuites(mockTestPlanId, mockProjectName);
195
+
196
+ // Assert
197
+ expect(logger.error).toHaveBeenCalled();
198
+ expect(result).toEqual([]);
199
+ });
200
+ });
177
201
 
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
- });
202
+ describe('fetchTestPoints', () => {
203
+ it('should fetch and map test points correctly', async () => {
204
+ // Arrange
205
+ const mockSuiteId = '123';
206
+ const mockTestPoints = {
207
+ value: [
208
+ {
209
+ testCaseReference: { id: 1, name: 'Test Case 1' },
210
+ configuration: { name: 'Config 1' },
211
+ results: {
212
+ outcome: 'passed',
213
+ lastTestRunId: 100,
214
+ lastResultId: 200,
215
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
216
+ },
217
+ },
218
+ ],
219
+ count: 1,
220
+ };
221
+
222
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockTestPoints);
223
+
224
+ // Act
225
+ const result = await (resultDataProvider as any).fetchTestPoints(
226
+ mockProjectName,
227
+ mockTestPlanId,
228
+ mockSuiteId
229
+ );
230
+
231
+ // Assert
232
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
233
+ `${mockOrgUrl}${mockProjectName}/_apis/testplan/Plans/${mockTestPlanId}/Suites/${mockSuiteId}/TestPoint?includePointDetails=true`,
234
+ mockToken
235
+ );
236
+ expect(result).toHaveLength(1);
237
+ expect(result[0]).toEqual({
238
+ testCaseId: 1,
239
+ testCaseName: 'Test Case 1',
240
+ configurationName: 'Config 1',
241
+ outcome: 'passed',
242
+ lastRunId: 100,
243
+ lastResultId: 200,
244
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
245
+ testCaseUrl: 'https://dev.azure.com/organization/test-project/_workitems/edit/1',
190
246
  });
247
+ });
248
+
249
+ it('should handle errors and return empty array', async () => {
250
+ // Arrange
251
+ const mockSuiteId = '123';
252
+ const mockError = new Error('API error');
253
+ (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
254
+
255
+ // Act
256
+ const result = await (resultDataProvider as any).fetchTestPoints(
257
+ mockProjectName,
258
+ mockTestPlanId,
259
+ mockSuiteId
260
+ );
261
+
262
+ // Assert
263
+ expect(logger.error).toHaveBeenCalled();
264
+ expect(result).toEqual([]);
265
+ });
191
266
  });
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
- });
267
+ });
268
+
269
+ describe('Data transformation methods', () => {
270
+ describe('mapTestPoint', () => {
271
+ it('should transform test point data correctly', () => {
272
+ // Arrange
273
+ const testPoint = {
274
+ testCaseReference: { id: 1, name: 'Test Case 1' },
275
+ configuration: { name: 'Config 1' },
276
+ results: {
277
+ outcome: 'passed',
278
+ lastTestRunId: 100,
279
+ lastResultId: 200,
280
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
281
+ },
282
+ };
283
+
284
+ // Act
285
+ const result = (resultDataProvider as any).mapTestPoint(testPoint, mockProjectName);
286
+
287
+ // Assert
288
+ expect(result).toEqual({
289
+ testCaseId: 1,
290
+ testCaseName: 'Test Case 1',
291
+ configurationName: 'Config 1',
292
+ outcome: 'passed',
293
+ lastRunId: 100,
294
+ lastResultId: 200,
295
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
296
+ testCaseUrl: 'https://dev.azure.com/organization/test-project/_workitems/edit/1',
243
297
  });
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
- });
298
+ });
299
+
300
+ it('should handle missing fields', () => {
301
+ // Arrange
302
+ const testPoint = {
303
+ testCaseReference: { id: 1, name: 'Test Case 1' },
304
+ // No configuration or results
305
+ };
306
+
307
+ // Act
308
+ const result = (resultDataProvider as any).mapTestPoint(testPoint, mockProjectName);
309
+
310
+ // Assert
311
+ expect(result).toEqual({
312
+ testCaseId: 1,
313
+ testCaseName: 'Test Case 1',
314
+ configurationName: undefined,
315
+ outcome: 'Not Run',
316
+ lastRunId: undefined,
317
+ lastResultId: undefined,
318
+ lastResultDetails: undefined,
319
+ testCaseUrl: 'https://dev.azure.com/organization/test-project/_workitems/edit/1',
308
320
  });
321
+ });
309
322
  });
310
323
 
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
- });
324
+ describe('calculateGroupResultSummary', () => {
325
+ it('should return empty strings when includeHardCopyRun is true', () => {
326
+ // Arrange
327
+ const testPoints = [{ outcome: 'passed' }, { outcome: 'failed' }];
328
+
329
+ // Act
330
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, true);
331
+
332
+ // Assert
333
+ expect(result).toEqual({
334
+ passed: '',
335
+ failed: '',
336
+ notApplicable: '',
337
+ blocked: '',
338
+ notRun: '',
339
+ total: '',
340
+ successPercentage: '',
362
341
  });
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
- });
342
+ });
343
+
344
+ it('should calculate summary statistics correctly', () => {
345
+ // Arrange
346
+ const testPoints = [
347
+ { outcome: 'passed' },
348
+ { outcome: 'passed' },
349
+ { outcome: 'failed' },
350
+ { outcome: 'notApplicable' },
351
+ { outcome: 'blocked' },
352
+ { outcome: 'something else' },
353
+ ];
354
+
355
+ // Act
356
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
357
+
358
+ // Assert
359
+ expect(result).toEqual({
360
+ passed: 2,
361
+ failed: 1,
362
+ notApplicable: 1,
363
+ blocked: 1,
364
+ notRun: 1,
365
+ total: 6,
366
+ successPercentage: '33.33%',
431
367
  });
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
- });
368
+ });
369
+
370
+ it('should handle empty array', () => {
371
+ // Arrange
372
+ const testPoints: any[] = [];
373
+
374
+ // Act
375
+ const result = (resultDataProvider as any).calculateGroupResultSummary(testPoints, false);
376
+
377
+ // Assert
378
+ expect(result).toEqual({
379
+ passed: 0,
380
+ failed: 0,
381
+ notApplicable: 0,
382
+ blocked: 0,
383
+ notRun: 0,
384
+ total: 0,
385
+ successPercentage: '0.00%',
488
386
  });
387
+ });
489
388
  });
490
389
 
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
-
390
+ describe('mapAttachmentsUrl', () => {
391
+ it('should map attachment URLs correctly', () => {
392
+ // Arrange
393
+ const mockRunResults = [
394
+ {
395
+ testCaseId: 1,
396
+ lastRunId: 100,
397
+ lastResultId: 200,
398
+ iteration: {
399
+ attachments: [{ id: 1, name: 'attachment1.png', actionPath: 'path1' }],
400
+ actionResults: [{ actionPath: 'path1', stepPosition: '1.1' }],
401
+ },
402
+ analysisAttachments: [{ id: 2, fileName: 'analysis1.txt' }],
403
+ },
404
+ ];
405
+
406
+ // Act
407
+ const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
408
+
409
+ // Assert
410
+ expect(result[0].iteration.attachments[0].downloadUrl).toBe(
411
+ `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/1/attachment1.png`
412
+ );
413
+ expect(result[0].iteration.attachments[0].stepNo).toBe('1.1');
414
+ expect(result[0].analysisAttachments[0].downloadUrl).toBe(
415
+ `${mockOrgUrl}${mockProjectName}/_apis/test/runs/100/results/200/attachments/2/analysis1.txt`
416
+ );
417
+ });
418
+
419
+ it('should handle missing iteration', () => {
420
+ // Arrange
421
+ const mockRunResults = [
422
+ {
423
+ testCaseId: 1,
424
+ lastRunId: 100,
425
+ lastResultId: 200,
426
+ // No iteration
427
+ analysisAttachments: [{ id: 2, fileName: 'analysis1.txt' }],
428
+ },
429
+ ];
430
+
431
+ // Act
432
+ const result = resultDataProvider.mapAttachmentsUrl(mockRunResults, mockProjectName);
433
+
434
+ // Assert
435
+ expect(result[0]).toEqual(mockRunResults[0]);
436
+ });
437
+ });
438
+ });
439
+
440
+ describe('getCombinedResultsSummary', () => {
441
+ it('should combine all results into expected format', async () => {
442
+ // Arrange
443
+ const mockTestSuites = {
444
+ value: [
445
+ {
446
+ id: 1,
447
+ name: 'Root Suite',
448
+ children: [{ id: 2, name: 'Child Suite 1', parentSuite: { id: 1 } }],
449
+ },
450
+ ],
451
+ count: 1,
452
+ };
453
+
454
+ const mockTestPoints = {
455
+ value: [
456
+ {
457
+ testCaseReference: { id: 1, name: 'Test Case 1' },
458
+ configuration: { name: 'Config 1' },
459
+ results: {
460
+ outcome: 'passed',
461
+ lastTestRunId: 100,
462
+ lastResultId: 200,
463
+ lastResultDetails: { dateCompleted: '2023-01-01', runBy: { displayName: 'Test User' } },
464
+ },
465
+ },
466
+ ],
467
+ count: 1,
468
+ };
469
+
470
+ const mockTestCases = {
471
+ value: [
472
+ {
473
+ workItem: {
474
+ id: 1,
475
+ workItemFields: [{ key: 'Steps', value: '<steps>...</steps>' }],
476
+ },
477
+ },
478
+ ],
479
+ };
480
+
481
+ const mockResult = {
482
+ testCase: { id: 1, name: 'Test Case 1' },
483
+ testSuite: { id: 2, name: 'Child Suite 1' },
484
+ iterationDetails: [
485
+ {
486
+ actionResults: [
487
+ { stepIdentifier: '1', outcome: 'Passed', errorMessage: '', actionPath: 'path1' },
488
+ ],
489
+ attachments: [],
490
+ },
491
+ ],
492
+ testCaseRevision: 1,
493
+ failureType: null,
494
+ resolutionState: null,
495
+ comment: null,
496
+ };
497
+
498
+ // Setup mocks for API calls
499
+ (TFSServices.getItemContent as jest.Mock)
500
+ .mockResolvedValueOnce(mockTestSuites) // fetchTestSuites
501
+ .mockResolvedValueOnce(mockTestPoints) // fetchTestPoints
502
+ .mockResolvedValueOnce(mockTestCases) // fetchTestCasesBySuiteId
503
+ .mockResolvedValueOnce(mockResult) // fetchResult
504
+ .mockResolvedValueOnce({ value: [] }) // fetchResult - attachments
505
+ .mockResolvedValueOnce({ fields: {} }); // fetchResult - wiByRevision
506
+
507
+ const mockTestStepParserHelper = (resultDataProvider as any).testStepParserHelper;
508
+ mockTestStepParserHelper.parseTestSteps.mockResolvedValueOnce([
509
+ {
510
+ stepId: 1,
511
+ stepPosition: '1',
512
+ action: 'Do something',
513
+ expected: 'Something happens',
514
+ isSharedStepTitle: false,
515
+ },
516
+ ]);
517
+
518
+ // Act
519
+ const result = await resultDataProvider.getCombinedResultsSummary(
520
+ mockTestPlanId,
521
+ mockProjectName,
522
+ undefined,
523
+ true
524
+ );
525
+
526
+ // Assert
527
+ expect(result.combinedResults.length).toBeGreaterThan(0);
528
+ expect(result.combinedResults[0]).toHaveProperty(
529
+ 'contentControl',
530
+ 'test-group-summary-content-control'
531
+ );
532
+ expect(result.combinedResults[1]).toHaveProperty(
533
+ 'contentControl',
534
+ 'test-result-summary-content-control'
535
+ );
536
+ expect(result.combinedResults[2]).toHaveProperty(
537
+ 'contentControl',
538
+ 'detailed-test-result-content-control'
539
+ );
580
540
  });
581
- });
541
+ });
542
+ });