@elisra-devops/docgen-data-provider 1.22.0 → 1.23.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.
Files changed (49) hide show
  1. package/bin/helpers/helper.js.map +1 -1
  2. package/bin/helpers/test/tfs.test.d.ts +1 -0
  3. package/bin/helpers/test/tfs.test.js +613 -0
  4. package/bin/helpers/test/tfs.test.js.map +1 -0
  5. package/bin/helpers/tfs.js +1 -1
  6. package/bin/helpers/tfs.js.map +1 -1
  7. package/bin/modules/GitDataProvider.js.map +1 -1
  8. package/bin/modules/PipelinesDataProvider.js +3 -2
  9. package/bin/modules/PipelinesDataProvider.js.map +1 -1
  10. package/bin/modules/ResultDataProvider.d.ts +199 -17
  11. package/bin/modules/ResultDataProvider.js +611 -195
  12. package/bin/modules/ResultDataProvider.js.map +1 -1
  13. package/bin/modules/TestDataProvider.js +7 -7
  14. package/bin/modules/TestDataProvider.js.map +1 -1
  15. package/bin/modules/TicketsDataProvider.d.ts +1 -1
  16. package/bin/modules/TicketsDataProvider.js +3 -2
  17. package/bin/modules/TicketsDataProvider.js.map +1 -1
  18. package/bin/modules/test/JfrogDataProvider.test.d.ts +1 -0
  19. package/bin/modules/test/JfrogDataProvider.test.js +110 -0
  20. package/bin/modules/test/JfrogDataProvider.test.js.map +1 -0
  21. package/bin/modules/test/ResultDataProvider.test.d.ts +1 -0
  22. package/bin/modules/test/ResultDataProvider.test.js +478 -0
  23. package/bin/modules/test/ResultDataProvider.test.js.map +1 -0
  24. package/bin/modules/test/gitDataProvider.test.js +424 -120
  25. package/bin/modules/test/gitDataProvider.test.js.map +1 -1
  26. package/bin/modules/test/managmentDataProvider.test.js +283 -28
  27. package/bin/modules/test/managmentDataProvider.test.js.map +1 -1
  28. package/bin/modules/test/pipelineDataProvider.test.js +229 -45
  29. package/bin/modules/test/pipelineDataProvider.test.js.map +1 -1
  30. package/bin/modules/test/testDataProvider.test.js +225 -81
  31. package/bin/modules/test/testDataProvider.test.js.map +1 -1
  32. package/bin/modules/test/ticketsDataProvider.test.js +310 -82
  33. package/bin/modules/test/ticketsDataProvider.test.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/helpers/helper.ts +16 -14
  36. package/src/helpers/test/tfs.test.ts +748 -0
  37. package/src/helpers/tfs.ts +1 -1
  38. package/src/modules/GitDataProvider.ts +10 -10
  39. package/src/modules/PipelinesDataProvider.ts +2 -2
  40. package/src/modules/ResultDataProvider.ts +815 -260
  41. package/src/modules/TestDataProvider.ts +8 -8
  42. package/src/modules/TicketsDataProvider.ts +5 -9
  43. package/src/modules/test/JfrogDataProvider.test.ts +171 -0
  44. package/src/modules/test/ResultDataProvider.test.ts +581 -0
  45. package/src/modules/test/gitDataProvider.test.ts +671 -187
  46. package/src/modules/test/managmentDataProvider.test.ts +386 -26
  47. package/src/modules/test/pipelineDataProvider.test.ts +281 -52
  48. package/src/modules/test/testDataProvider.test.ts +307 -105
  49. package/src/modules/test/ticketsDataProvider.test.ts +425 -129
@@ -1,138 +1,434 @@
1
- import DgDataProviderAzureDevOps from "../..";
2
-
3
- require("dotenv").config();
4
- jest.setTimeout(60000);
5
-
6
-
7
- const orgUrl = process.env.ORG_URL;
8
- const token = process.env.PAT;
9
- const dgDataProviderAzureDevOps = new DgDataProviderAzureDevOps(orgUrl,token);
10
-
11
- const wiql =
12
- "SELECT [System.Id],[System.WorkItemType],[System.Title],[System.AssignedTo],[System.State],[System.Tags] FROM workitems WHERE [System.TeamProject]=@project";
13
-
14
- describe("ticket module - tests", () => {
15
- test("should create a new work item", async () => {
16
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
17
- let body =
18
- [{
19
- op: "add",
20
- path: "/fields/System.IterationPath",
21
- value: "tests"
22
- },
23
- {
24
- op: "add",
25
- path: "/fields/System.State",
26
- value: "New"
27
- },
28
- {
29
- op: "add",
30
- path: "/fields/System.AreaPath",
31
- value: "tests"
32
- },
33
- {
34
- op: "add",
35
- path: "/fields/System.Title",
36
- value: "new-test-title2"
37
- },
38
- {
39
- op: "add",
40
- path: "/fields/System.Tags",
41
- value: "tag-test"
42
- },
43
- {
44
- op: "add",
45
- path: "/fields/System.AssignedTo",
46
- value: "denispankove"
47
- }];
48
- let res = await TicketDataProvider.CreateNewWorkItem(
49
- "tests",
50
- body,
51
- "Epic",
52
- true
53
- );
54
- expect(typeof res.id).toBe("number");
55
- body[3].value = "edited";
56
- let updatedWI = await TicketDataProvider.UpdateWorkItem(
57
- "tests",
58
- body,
59
- res.id,
60
- true
61
- );
62
- expect(updatedWI.fields["System.Title"]).toBe("edited");
1
+ import { TFSServices } from '../../helpers/tfs';
2
+ import TicketsDataProvider from '../TicketsDataProvider';
3
+ import logger from '../../utils/logger';
4
+ import { Helper } from '../../helpers/helper';
5
+ import { QueryType } from '../../models/tfs-data';
6
+
7
+ jest.mock('../../helpers/tfs');
8
+ jest.mock('../../utils/logger');
9
+ jest.mock('../../helpers/helper');
10
+
11
+ describe('TicketsDataProvider', () => {
12
+ let ticketsDataProvider: TicketsDataProvider;
13
+ const mockOrgUrl = 'https://dev.azure.com/organization/';
14
+ const mockToken = 'mock-token';
15
+ const mockProject = 'test-project';
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ ticketsDataProvider = new TicketsDataProvider(mockOrgUrl, mockToken);
63
20
  });
64
- test("should return shared queires", async () => {
65
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
66
- let json = await TicketDataProvider.GetSharedQueries(
67
- "tests",
68
- "",
69
- );
70
- expect(json.length).toBeGreaterThan(1);
21
+
22
+ describe('FetchImageAsBase64', () => {
23
+ it('should fetch image as base64', async () => {
24
+ // Arrange
25
+ const mockUrl = 'https://example.com/image.jpg';
26
+ const mockBase64 = 'base64-encoded-image';
27
+ (TFSServices.fetchAzureDevOpsImageAsBase64 as jest.Mock).mockResolvedValueOnce(mockBase64);
28
+
29
+ // Act
30
+ const result = await ticketsDataProvider.FetchImageAsBase64(mockUrl);
31
+
32
+ // Assert
33
+ expect(TFSServices.fetchAzureDevOpsImageAsBase64).toHaveBeenCalledWith(
34
+ mockUrl, mockToken, 'get', null
35
+ );
36
+ expect(result).toBe(mockBase64);
37
+ });
71
38
  });
72
- test("should return query results", async () => {
73
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
74
- let json = await TicketDataProvider.GetSharedQueries(
75
- "tests",
76
- ""
77
- );
78
- let query = json.find((o: { wiql: undefined }) => o.wiql != undefined);
79
- let result = await TicketDataProvider.GetQueryResultsByWiqlHref(
80
- query.wiql.href,
81
- "tests"
82
- );
83
- expect(result.length).toBeGreaterThanOrEqual(1);
39
+
40
+ describe('GetWorkItem', () => {
41
+ it('should fetch work item with correct URL', async () => {
42
+ // Arrange
43
+ const mockId = '123';
44
+ const mockWorkItem = { id: 123, fields: { 'System.Title': 'Test Work Item' } };
45
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockWorkItem);
46
+
47
+ // Act
48
+ const result = await ticketsDataProvider.GetWorkItem(mockProject, mockId);
49
+
50
+ // Assert
51
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
52
+ `${mockOrgUrl}${mockProject}/_apis/wit/workitems/${mockId}?$expand=All`,
53
+ mockToken
54
+ );
55
+ expect(result).toEqual(mockWorkItem);
56
+ });
84
57
  });
85
- test("should return query results by query id", async () => {
86
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
87
- let result = await TicketDataProvider.GetQueryResultById(
88
- "08e044be-b9bc-4962-99c9-ffebb47ff95a",
89
- "tests"
90
- );
91
- expect(result.length).toBeGreaterThanOrEqual(1);
58
+
59
+ describe('GetLinksByIds', () => {
60
+ it('should retrieve links by ids', async () => {
61
+ // Arrange
62
+ const mockIds = [1, 2];
63
+ const mockWorkItems = [
64
+ { id: 1, fields: { 'System.Title': 'Item 1' } },
65
+ { id: 2, fields: { 'System.Title': 'Item 2' } }
66
+ ];
67
+ const mockLinksMap = new Map();
68
+ mockLinksMap.set('1', { id: '1', rels: ['3'] });
69
+ mockLinksMap.set('2', { id: '2', rels: [] });
70
+
71
+ const mockRelatedItems = [{ id: 3, fields: { 'System.Title': 'Related Item' } }];
72
+ const mockTraceItem = { id: '1', title: 'Item 1', url: 'url', customerId: 'customer', links: [] };
73
+
74
+ jest.spyOn(ticketsDataProvider, 'PopulateWorkItemsByIds').mockResolvedValueOnce(mockWorkItems);
75
+ jest.spyOn(ticketsDataProvider, 'GetRelationsIds').mockResolvedValueOnce(mockLinksMap);
76
+ jest.spyOn(ticketsDataProvider, 'GetParentLink')
77
+ .mockResolvedValueOnce(mockTraceItem)
78
+ .mockResolvedValueOnce({ id: '2', title: 'Item 2', url: 'url', customerId: 'customer', links: [] });
79
+ jest.spyOn(ticketsDataProvider, 'PopulateWorkItemsByIds').mockResolvedValueOnce(mockRelatedItems);
80
+ jest.spyOn(ticketsDataProvider, 'GetLinks').mockResolvedValueOnce([]);
81
+
82
+ // Act
83
+ const result = await ticketsDataProvider.GetLinksByIds(mockProject, mockIds);
84
+
85
+ // Assert
86
+ expect(result.length).toBe(2);
87
+ expect(ticketsDataProvider.PopulateWorkItemsByIds).toHaveBeenCalledWith(mockIds, mockProject);
88
+ expect(ticketsDataProvider.GetRelationsIds).toHaveBeenCalledWith(mockWorkItems);
89
+ });
90
+ });
91
+
92
+ describe('GetSharedQueries', () => {
93
+ it('should fetch STD shared queries with correct URL', async () => {
94
+ // Arrange
95
+ const mockPath = '';
96
+ const mockDocType = 'STD';
97
+ const mockQueries = { name: 'Query 1' };
98
+ const mockResponse = { reqTestTree: {}, testReqTree: {} };
99
+
100
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockQueries);
101
+ jest.spyOn(ticketsDataProvider as any, 'fetchLinkedQueries').mockResolvedValueOnce(mockResponse);
102
+
103
+ // Act
104
+ const result = await ticketsDataProvider.GetSharedQueries(mockProject, mockPath, mockDocType);
105
+
106
+ // Assert
107
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
108
+ `${mockOrgUrl}${mockProject}/_apis/wit/queries/Shared%20Queries?$depth=2&$expand=all`,
109
+ mockToken
110
+ );
111
+ expect(result).toEqual(mockResponse);
112
+ });
113
+
114
+ it('should fetch SVD shared queries and call fetchAnyQueries', async () => {
115
+ // Arrange
116
+ const mockPath = 'Custom Path';
117
+ const mockDocType = 'SVD';
118
+ const mockQueries = { name: 'Query 1' };
119
+ const mockResponse = { systemOverviewQueryTree: {}, knownBugsQueryTree: {} };
120
+
121
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockQueries);
122
+ jest.spyOn(ticketsDataProvider as any, 'fetchAnyQueries').mockResolvedValueOnce(mockResponse);
123
+
124
+ // Act
125
+ const result = await ticketsDataProvider.GetSharedQueries(mockProject, mockPath, mockDocType);
126
+
127
+ // Assert
128
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
129
+ `${mockOrgUrl}${mockProject}/_apis/wit/queries/${mockPath}?$depth=2&$expand=all`,
130
+ mockToken
131
+ );
132
+ expect(result).toEqual(mockResponse);
133
+ });
134
+
135
+ it('should handle errors', async () => {
136
+ // Arrange
137
+ const mockPath = '';
138
+ const mockError = new Error('API error');
139
+
140
+ (TFSServices.getItemContent as jest.Mock).mockImplementationOnce(() => {
141
+ return Promise.reject(mockError);
142
+ });
143
+
144
+ // Act & Assert
145
+ await expect(ticketsDataProvider.GetSharedQueries(mockProject, mockPath))
146
+ .rejects.toThrow('API error');
147
+ expect(logger.error).toHaveBeenCalled();
148
+ });
149
+ });
150
+
151
+ describe('GetQueryResultsFromWiql', () => {
152
+ it('should handle OneHop query with table format', async () => {
153
+ // Arrange
154
+ const mockWiqlHref = 'https://example.com/wiql';
155
+ const mockTestCaseMap = new Map<number, Set<any>>();
156
+ const mockQueryResult = {
157
+ queryType: QueryType.OneHop,
158
+ columns: [],
159
+ workItemRelations: [{ source: null, target: { id: 1, url: 'url' } }]
160
+ };
161
+ const mockTableResult = {
162
+ sourceTargetsMap: new Map(),
163
+ sortingSourceColumnsMap: new Map(),
164
+ sortingTargetsColumnsMap: new Map()
165
+ };
166
+
167
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockQueryResult);
168
+ jest.spyOn(ticketsDataProvider as any, 'parseDirectLinkedQueryResultForTableFormat')
169
+ .mockResolvedValueOnce(mockTableResult);
170
+
171
+ // Act
172
+ const result = await ticketsDataProvider.GetQueryResultsFromWiql(
173
+ mockWiqlHref, true, mockTestCaseMap
174
+ );
175
+
176
+ // Assert
177
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(mockWiqlHref, mockToken);
178
+ expect(result).toEqual(mockTableResult);
179
+ });
180
+
181
+ it('should throw error when wiqlHref is empty', async () => {
182
+ // Arrange
183
+ const mockTestCaseMap = new Map<number, Set<any>>();
184
+
185
+ // Act & Assert
186
+ const result = await ticketsDataProvider.GetQueryResultsFromWiql('', false, mockTestCaseMap);
187
+ expect(logger.error).toHaveBeenCalled();
188
+ expect(result).toBeUndefined();
189
+ });
190
+ });
191
+
192
+ describe('GetModeledQuery', () => {
193
+ it('should structure query list correctly', () => {
194
+ // Arrange
195
+ const mockQueryList = [
196
+ {
197
+ name: 'Query 1',
198
+ _links: { wiql: 'http://example.com/wiql1' },
199
+ id: 'q1'
200
+ },
201
+ {
202
+ name: 'Query 2',
203
+ _links: { wiql: null },
204
+ id: 'q2'
205
+ }
206
+ ];
207
+
208
+ // Act
209
+ const result = ticketsDataProvider.GetModeledQuery(mockQueryList);
210
+
211
+ // Assert
212
+ expect(result).toEqual([
213
+ { queryName: 'Query 1', wiql: 'http://example.com/wiql1', id: 'q1' },
214
+ { queryName: 'Query 2', wiql: null, id: 'q2' }
215
+ ]);
216
+ });
92
217
  });
93
- test("should return wi base on wiql string", async () => {
94
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
95
- let result = await TicketDataProvider.GetQueryResultsByWiqlString(
96
- wiql,
97
- "tests"
98
- );
99
- expect(result.workItems.length).toBeGreaterThanOrEqual(1);
218
+
219
+ describe('PopulateWorkItemsByIds', () => {
220
+ it('should fetch work items in batches of 200', async () => {
221
+ // Arrange
222
+ const mockIds = Array.from({ length: 250 }, (_, i) => i + 1);
223
+ const mockResponse1 = { value: mockIds.slice(0, 200).map(id => ({ id })) };
224
+ const mockResponse2 = { value: mockIds.slice(200).map(id => ({ id })) };
225
+
226
+ (TFSServices.getItemContent as jest.Mock)
227
+ .mockResolvedValueOnce(mockResponse1)
228
+ .mockResolvedValueOnce(mockResponse2);
229
+
230
+ // Act
231
+ const result = await ticketsDataProvider.PopulateWorkItemsByIds(mockIds, mockProject);
232
+
233
+ // Assert
234
+ expect(TFSServices.getItemContent).toHaveBeenCalledTimes(2);
235
+ expect(result.length).toBe(250);
236
+ });
237
+
238
+ it('should handle errors and return empty array', async () => {
239
+ // Arrange
240
+ const mockIds = [1, 2, 3];
241
+ const mockError = new Error('API error');
242
+
243
+ (TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(mockError);
244
+
245
+ // Act
246
+ const result = await ticketsDataProvider.PopulateWorkItemsByIds(mockIds, mockProject);
247
+
248
+ // Assert
249
+ expect(result).toEqual([]);
250
+ expect(logger.error).toHaveBeenCalled();
251
+ });
100
252
  });
101
- test("should return populated work items array", async () => {
102
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
103
- let result = await TicketDataProvider.GetQueryResultsByWiqlString(
104
- wiql,
105
- "tests"
106
- );
107
- let wiarray = result.workItems.map((o: any) => o.id);
108
- let res = await TicketDataProvider.PopulateWorkItemsByIds(
109
- wiarray,
110
- "tests"
111
- );
112
- expect(res.length).toBeGreaterThanOrEqual(1);
253
+
254
+ describe('GetIterationsByTeamName', () => {
255
+ it('should fetch iterations with team name specified', async () => {
256
+ // Arrange
257
+ const mockTeamName = 'test-team';
258
+ const mockIterations = ['iteration1', 'iteration2'];
259
+
260
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockIterations);
261
+
262
+ // Act
263
+ const result = await ticketsDataProvider.GetIterationsByTeamName(mockProject, mockTeamName);
264
+
265
+ // Assert
266
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
267
+ `${mockOrgUrl}${mockProject}/${mockTeamName}/_apis/work/teamsettings/iterations`,
268
+ mockToken,
269
+ 'get'
270
+ );
271
+ expect(result).toEqual(mockIterations);
272
+ });
273
+
274
+ it('should fetch iterations without team name', async () => {
275
+ // Arrange
276
+ const mockTeamName = '';
277
+ const mockIterations = ['iteration1', 'iteration2'];
278
+
279
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockIterations);
280
+
281
+ // Act
282
+ const result = await ticketsDataProvider.GetIterationsByTeamName(mockProject, mockTeamName);
283
+
284
+ // Assert
285
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
286
+ `${mockOrgUrl}${mockProject}/_apis/work/teamsettings/iterations`,
287
+ mockToken,
288
+ 'get'
289
+ );
290
+ expect(result).toEqual(mockIterations);
291
+ });
113
292
  });
114
- test("should return list of attachments", async () => {
115
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
116
- let attachList = await TicketDataProvider.GetWorkitemAttachments(
117
- "tests",
118
- "538"
119
- );
120
- expect(attachList.length > 0).toBeDefined();
293
+
294
+ describe('CreateNewWorkItem', () => {
295
+ it('should create work item with correct parameters', async () => {
296
+ // Arrange
297
+ const mockWiBody = [{ op: 'add', path: '/fields/System.Title', value: 'New Item' }];
298
+ const mockWiType = 'Bug';
299
+ const mockByPass = true;
300
+ const mockResponse = { id: 123, fields: { 'System.Title': 'New Item' } };
301
+
302
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockResponse);
303
+
304
+ // Act
305
+ const result = await ticketsDataProvider.CreateNewWorkItem(
306
+ mockProject, mockWiBody, mockWiType, mockByPass
307
+ );
308
+
309
+ // Assert
310
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
311
+ `${mockOrgUrl}${mockProject}/_apis/wit/workitems/$${mockWiType}?bypassRules=true`,
312
+ mockToken,
313
+ 'POST',
314
+ mockWiBody,
315
+ {
316
+ 'Content-Type': 'application/json-patch+json'
317
+ }
318
+ );
319
+ expect(result).toEqual(mockResponse);
320
+ });
121
321
  });
122
- test("should return Json data of the attachment", async () => {
123
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
124
- let attachedData = await TicketDataProvider.GetWorkitemAttachmentsJSONData(
125
- "tests",
126
- "14933c55-6d84-499c-88db-55202f16dd46"
127
- );
128
- expect(JSON.stringify(attachedData).length > 0).toBeDefined();
322
+
323
+ describe('UpdateWorkItem', () => {
324
+ it('should update work item with correct parameters', async () => {
325
+ // Arrange
326
+ const mockWiBody = [{ op: 'add', path: '/fields/System.Title', value: 'Updated Item' }];
327
+ const mockWorkItemId = 123;
328
+ const mockByPass = true;
329
+ const mockResponse = { id: 123, fields: { 'System.Title': 'Updated Item' } };
330
+
331
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockResponse);
332
+
333
+ // Act
334
+ const result = await ticketsDataProvider.UpdateWorkItem(
335
+ mockProject, mockWiBody, mockWorkItemId, mockByPass
336
+ );
337
+
338
+ // Assert
339
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
340
+ `${mockOrgUrl}${mockProject}/_apis/wit/workitems/${mockWorkItemId}?bypassRules=true`,
341
+ mockToken,
342
+ 'patch',
343
+ mockWiBody,
344
+ {
345
+ 'Content-Type': 'application/json-patch+json'
346
+ }
347
+ );
348
+ expect(result).toEqual(mockResponse);
349
+ });
129
350
  });
130
- test("should return list of id & link object", async () => {
131
- let TicketDataProvider = await dgDataProviderAzureDevOps.getTicketsDataProvider();
132
- let attachList = await TicketDataProvider.GetLinksByIds(
133
- "tests",
134
- [535]
135
- );
136
- expect(attachList.length).toBeGreaterThan(0);
351
+
352
+ describe('GetWorkitemAttachments', () => {
353
+ it('should return attachments for work item', async () => {
354
+ // Arrange
355
+ const mockId = '123';
356
+ const mockWorkItem = {
357
+ relations: [
358
+ {
359
+ rel: 'AttachedFile',
360
+ url: 'https://example.com/attachment/1',
361
+ attributes: { name: 'file.txt' }
362
+ }
363
+ ]
364
+ };
365
+
366
+ // Mock the TFSServices.getItemContent directly to return our mock data
367
+ // This is what will be called by the new TicketsDataProvider instance
368
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockWorkItem);
369
+
370
+ // Act
371
+ const result = await ticketsDataProvider.GetWorkitemAttachments(mockProject, mockId);
372
+
373
+ // Assert
374
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
375
+ `${mockOrgUrl}${mockProject}/_apis/wit/workitems/${mockId}?$expand=All`,
376
+ mockToken
377
+ );
378
+ expect(result.length).toBe(1);
379
+
380
+ // Check that the downloadUrl was added correctly
381
+ expect(result[0]).toHaveProperty('downloadUrl', 'https://example.com/attachment/1/file.txt');
382
+ expect(result[0].rel).toBe('AttachedFile');
383
+ });
384
+
385
+ it('should handle work item with no relations', async () => {
386
+ // Arrange
387
+ const mockId = '123';
388
+ const mockWorkItem = { relations: null };
389
+
390
+ jest.spyOn(ticketsDataProvider, 'GetWorkItem').mockResolvedValueOnce(mockWorkItem);
391
+
392
+ // Act
393
+ const result = await ticketsDataProvider.GetWorkitemAttachments(mockProject, mockId);
394
+
395
+ // Assert
396
+ expect(result).toEqual([]);
397
+ });
398
+
399
+ it('should filter out non-attachment relations', async () => {
400
+ // Arrange
401
+ const mockId = '123';
402
+ const mockWorkItem = {
403
+ relations: [
404
+ {
405
+ rel: 'Parent',
406
+ url: 'https://example.com/parent/1',
407
+ attributes: { name: 'parent' }
408
+ },
409
+ {
410
+ rel: 'AttachedFile',
411
+ url: 'https://example.com/attachment/1',
412
+ attributes: { name: 'file.txt' }
413
+ }
414
+ ]
415
+ };
416
+
417
+ // Mock TFSServices.getItemContent directly instead of GetWorkItem
418
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockWorkItem);
419
+
420
+ // Act
421
+ const result = await ticketsDataProvider.GetWorkitemAttachments(mockProject, mockId);
422
+
423
+ // Assert
424
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
425
+ `${mockOrgUrl}${mockProject}/_apis/wit/workitems/${mockId}?$expand=All`,
426
+ mockToken
427
+ );
428
+ expect(result.length).toBe(1);
429
+ expect(result[0].rel).toBe('AttachedFile');
430
+ // Only the AttachedFile relation should be in the result
431
+ expect(result.some(item => item.rel === 'Parent')).toBe(false);
432
+ });
137
433
  });
138
- }); //describe
434
+ });