@elisra-devops/docgen-data-provider 1.63.12 → 1.67.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/.github/workflows/ci.yml +26 -9
- package/.github/workflows/release.yml +9 -10
- package/bin/helpers/tfs.d.ts +3 -0
- package/bin/helpers/tfs.js +44 -7
- package/bin/helpers/tfs.js.map +1 -1
- package/bin/modules/GitDataProvider.d.ts +10 -0
- package/bin/modules/GitDataProvider.js +10 -0
- package/bin/modules/GitDataProvider.js.map +1 -1
- package/bin/modules/MangementDataProvider.js +7 -1
- package/bin/modules/MangementDataProvider.js.map +1 -1
- package/bin/modules/TestDataProvider.js +0 -1
- package/bin/modules/TestDataProvider.js.map +1 -1
- package/bin/modules/TicketsDataProvider.d.ts +63 -27
- package/bin/modules/TicketsDataProvider.js +226 -122
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/helpers/helper.test.js +279 -0
- package/bin/tests/helpers/helper.test.js.map +1 -0
- package/bin/{helpers/test → tests/helpers}/tfs.test.js +312 -49
- package/bin/tests/helpers/tfs.test.js.map +1 -0
- package/bin/tests/index.test.js +25 -0
- package/bin/tests/index.test.js.map +1 -0
- package/bin/tests/models/tfs-data.test.js +160 -0
- package/bin/tests/models/tfs-data.test.js.map +1 -0
- package/bin/{modules/test → tests/modules}/JfrogDataProvider.test.js +9 -9
- package/bin/tests/modules/JfrogDataProvider.test.js.map +1 -0
- package/bin/tests/modules/ResultDataProvider.test.js +1942 -0
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -0
- package/bin/tests/modules/gitDataProvider.test.js +1888 -0
- package/bin/tests/modules/gitDataProvider.test.js.map +1 -0
- package/bin/{modules/test → tests/modules}/managmentDataProvider.test.js +39 -31
- package/bin/tests/modules/managmentDataProvider.test.js.map +1 -0
- package/bin/tests/modules/pipelineDataProvider.test.d.ts +1 -0
- package/bin/tests/modules/pipelineDataProvider.test.js +783 -0
- package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -0
- package/bin/tests/modules/testDataProvider.test.d.ts +1 -0
- package/bin/tests/modules/testDataProvider.test.js +717 -0
- package/bin/tests/modules/testDataProvider.test.js.map +1 -0
- package/bin/tests/modules/ticketsDataProvider.test.d.ts +1 -0
- package/bin/tests/modules/ticketsDataProvider.test.js +1681 -0
- package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -0
- package/bin/tests/utils/DataProviderUtils.test.d.ts +1 -0
- package/bin/tests/utils/DataProviderUtils.test.js +61 -0
- package/bin/tests/utils/DataProviderUtils.test.js.map +1 -0
- package/bin/tests/utils/testStepParserHelper.test.d.ts +1 -0
- package/bin/tests/utils/testStepParserHelper.test.js +359 -0
- package/bin/tests/utils/testStepParserHelper.test.js.map +1 -0
- package/package.json +10 -1
- package/src/helpers/tfs.ts +51 -7
- package/src/modules/GitDataProvider.ts +10 -0
- package/src/modules/MangementDataProvider.ts +6 -1
- package/src/modules/TestDataProvider.ts +0 -1
- package/src/modules/TicketsDataProvider.ts +311 -151
- package/src/tests/helpers/helper.test.ts +337 -0
- package/src/tests/helpers/tfs.test.ts +1092 -0
- package/src/tests/index.test.ts +28 -0
- package/src/tests/models/tfs-data.test.ts +203 -0
- package/src/tests/modules/JfrogDataProvider.test.ts +167 -0
- package/src/tests/modules/ResultDataProvider.test.ts +2571 -0
- package/src/tests/modules/gitDataProvider.test.ts +2628 -0
- package/src/{modules/test → tests/modules}/managmentDataProvider.test.ts +63 -32
- package/src/tests/modules/pipelineDataProvider.test.ts +1038 -0
- package/src/tests/modules/testDataProvider.test.ts +1046 -0
- package/src/tests/modules/ticketsDataProvider.test.ts +2204 -0
- package/src/tests/utils/DataProviderUtils.test.ts +76 -0
- package/src/tests/utils/testStepParserHelper.test.ts +437 -0
- package/tsconfig.json +1 -0
- package/bin/helpers/test/tfs.test.js.map +0 -1
- package/bin/modules/test/JfrogDataProvider.test.js.map +0 -1
- package/bin/modules/test/ResultDataProvider.test.js +0 -444
- package/bin/modules/test/ResultDataProvider.test.js.map +0 -1
- package/bin/modules/test/gitDataProvider.test.js +0 -433
- package/bin/modules/test/gitDataProvider.test.js.map +0 -1
- package/bin/modules/test/managmentDataProvider.test.js.map +0 -1
- package/bin/modules/test/pipelineDataProvider.test.js +0 -237
- package/bin/modules/test/pipelineDataProvider.test.js.map +0 -1
- package/bin/modules/test/testDataProvider.test.js +0 -234
- package/bin/modules/test/testDataProvider.test.js.map +0 -1
- package/bin/modules/test/ticketsDataProvider.test.js +0 -322
- package/bin/modules/test/ticketsDataProvider.test.js.map +0 -1
- package/src/helpers/test/tfs.test.ts +0 -748
- package/src/modules/test/JfrogDataProvider.test.ts +0 -171
- package/src/modules/test/ResultDataProvider.test.ts +0 -542
- package/src/modules/test/gitDataProvider.test.ts +0 -691
- package/src/modules/test/pipelineDataProvider.test.ts +0 -292
- package/src/modules/test/testDataProvider.test.ts +0 -318
- package/src/modules/test/ticketsDataProvider.test.ts +0 -434
- /package/bin/{helpers/test/tfs.test.d.ts → tests/helpers/helper.test.d.ts} +0 -0
- /package/bin/{modules/test/JfrogDataProvider.test.d.ts → tests/helpers/tfs.test.d.ts} +0 -0
- /package/bin/{modules/test/ResultDataProvider.test.d.ts → tests/index.test.d.ts} +0 -0
- /package/bin/{modules/test/gitDataProvider.test.d.ts → tests/models/tfs-data.test.d.ts} +0 -0
- /package/bin/{modules/test/managmentDataProvider.test.d.ts → tests/modules/JfrogDataProvider.test.d.ts} +0 -0
- /package/bin/{modules/test/pipelineDataProvider.test.d.ts → tests/modules/ResultDataProvider.test.d.ts} +0 -0
- /package/bin/{modules/test/testDataProvider.test.d.ts → tests/modules/gitDataProvider.test.d.ts} +0 -0
- /package/bin/{modules/test/ticketsDataProvider.test.d.ts → tests/modules/managmentDataProvider.test.d.ts} +0 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tfs_1 = require("../../helpers/tfs");
|
|
4
|
+
const helper_1 = require("../../helpers/helper");
|
|
5
|
+
const TestDataProvider_1 = require("../../modules/TestDataProvider");
|
|
6
|
+
const logger_1 = require("../../utils/logger");
|
|
7
|
+
jest.mock('../../helpers/tfs');
|
|
8
|
+
jest.mock('../../utils/logger');
|
|
9
|
+
jest.mock('../../helpers/helper');
|
|
10
|
+
jest.mock('../../utils/testStepParserHelper', () => {
|
|
11
|
+
return {
|
|
12
|
+
__esModule: true,
|
|
13
|
+
default: jest.fn().mockImplementation(() => ({
|
|
14
|
+
parseTestSteps: jest.fn(),
|
|
15
|
+
})),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
jest.mock('p-limit', () => jest.fn(() => (fn) => fn()));
|
|
19
|
+
describe('TestDataProvider', () => {
|
|
20
|
+
let testDataProvider;
|
|
21
|
+
const mockOrgUrl = 'https://dev.azure.com/orgname/';
|
|
22
|
+
const mockToken = 'mock-token';
|
|
23
|
+
const mockProject = 'project-123';
|
|
24
|
+
const mockPlanId = '456';
|
|
25
|
+
const mockSuiteId = '789';
|
|
26
|
+
const mockTestCaseId = '101112';
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
testDataProvider = new TestDataProvider_1.default(mockOrgUrl, mockToken);
|
|
30
|
+
});
|
|
31
|
+
// Helper to access private method for testing
|
|
32
|
+
const invokeFetchWithCache = async (instance, url, ttl = 60000) => {
|
|
33
|
+
return instance.fetchWithCache.call(instance, url, ttl);
|
|
34
|
+
};
|
|
35
|
+
describe('fetchWithCache', () => {
|
|
36
|
+
it('should return cached data when available and not expired', async () => {
|
|
37
|
+
// Arrange
|
|
38
|
+
const mockUrl = `${mockOrgUrl}_apis/test/endpoint`;
|
|
39
|
+
const mockData = { value: 'test data' };
|
|
40
|
+
const cache = new Map();
|
|
41
|
+
cache.set(mockUrl, {
|
|
42
|
+
data: mockData,
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
});
|
|
45
|
+
testDataProvider.cache = cache;
|
|
46
|
+
// Act
|
|
47
|
+
const result = await invokeFetchWithCache(testDataProvider, mockUrl);
|
|
48
|
+
// Assert
|
|
49
|
+
expect(result).toEqual(mockData);
|
|
50
|
+
expect(tfs_1.TFSServices.getItemContent).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
it('should fetch new data when cache is expired', async () => {
|
|
53
|
+
// Arrange
|
|
54
|
+
const mockUrl = `${mockOrgUrl}_apis/test/endpoint`;
|
|
55
|
+
const mockData = { value: 'old data' };
|
|
56
|
+
const newData = { value: 'new data' };
|
|
57
|
+
const cache = new Map();
|
|
58
|
+
cache.set(mockUrl, {
|
|
59
|
+
data: mockData,
|
|
60
|
+
timestamp: Date.now() - 70000, // Expired (default TTL is 60000ms)
|
|
61
|
+
});
|
|
62
|
+
testDataProvider.cache = cache;
|
|
63
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(newData);
|
|
64
|
+
// Act
|
|
65
|
+
const result = await invokeFetchWithCache(testDataProvider, mockUrl);
|
|
66
|
+
// Assert
|
|
67
|
+
expect(result).toEqual(newData);
|
|
68
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(mockUrl, mockToken);
|
|
69
|
+
});
|
|
70
|
+
it('should fetch and cache new data when not in cache', async () => {
|
|
71
|
+
// Arrange
|
|
72
|
+
const mockUrl = `${mockOrgUrl}_apis/test/endpoint`;
|
|
73
|
+
const mockData = { value: 'new data' };
|
|
74
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
75
|
+
// Act
|
|
76
|
+
const result = await invokeFetchWithCache(testDataProvider, mockUrl);
|
|
77
|
+
// Assert
|
|
78
|
+
expect(result).toEqual(mockData);
|
|
79
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(mockUrl, mockToken);
|
|
80
|
+
expect(testDataProvider.cache.has(mockUrl)).toBeTruthy();
|
|
81
|
+
expect(testDataProvider.cache.get(mockUrl).data).toEqual(mockData);
|
|
82
|
+
});
|
|
83
|
+
it('should throw and log error when fetch fails', async () => {
|
|
84
|
+
// Arrange
|
|
85
|
+
const mockUrl = `${mockOrgUrl}_apis/test/endpoint`;
|
|
86
|
+
const mockError = new Error('API call failed');
|
|
87
|
+
tfs_1.TFSServices.getItemContent.mockRejectedValueOnce(mockError);
|
|
88
|
+
// Act & Assert
|
|
89
|
+
await expect(invokeFetchWithCache(testDataProvider, mockUrl)).rejects.toThrow('API call failed');
|
|
90
|
+
expect(logger_1.default.error).toHaveBeenCalledWith(`Error fetching ${mockUrl}: API call failed`);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('GetTestSuiteByTestCase', () => {
|
|
94
|
+
it('should return test suites for a given test case ID', async () => {
|
|
95
|
+
// Arrange
|
|
96
|
+
const mockData = { value: [{ id: '123', name: 'Test Suite 1' }] };
|
|
97
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
98
|
+
// Act
|
|
99
|
+
const result = await testDataProvider.GetTestSuiteByTestCase(mockTestCaseId);
|
|
100
|
+
// Assert
|
|
101
|
+
expect(result).toEqual(mockData);
|
|
102
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}_apis/testplan/suites?testCaseId=${mockTestCaseId}`, mockToken);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('GetTestPlans', () => {
|
|
106
|
+
it('should return test plans for a given project', async () => {
|
|
107
|
+
// Arrange
|
|
108
|
+
const mockData = {
|
|
109
|
+
value: [
|
|
110
|
+
{ id: '456', name: 'Test Plan 1' },
|
|
111
|
+
{ id: '789', name: 'Test Plan 2' },
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
115
|
+
// Act
|
|
116
|
+
const result = await testDataProvider.GetTestPlans(mockProject);
|
|
117
|
+
// Assert
|
|
118
|
+
expect(result).toEqual(mockData);
|
|
119
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/plans`, mockToken);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('GetTestSuites', () => {
|
|
123
|
+
it('should return test suites for a given project and plan ID', async () => {
|
|
124
|
+
// Arrange
|
|
125
|
+
const mockData = {
|
|
126
|
+
value: [
|
|
127
|
+
{ id: '123', name: 'Test Suite 1' },
|
|
128
|
+
{ id: '456', name: 'Test Suite 2' },
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
132
|
+
// Act
|
|
133
|
+
const result = await testDataProvider.GetTestSuites(mockProject, mockPlanId);
|
|
134
|
+
// Assert
|
|
135
|
+
expect(result).toEqual(mockData);
|
|
136
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/Plans/${mockPlanId}/suites`, mockToken);
|
|
137
|
+
});
|
|
138
|
+
it('should return null and log error if fetching test suites fails', async () => {
|
|
139
|
+
// Arrange
|
|
140
|
+
const mockError = new Error('Failed to get test suites');
|
|
141
|
+
tfs_1.TFSServices.getItemContent.mockRejectedValueOnce(mockError);
|
|
142
|
+
// Act
|
|
143
|
+
const result = await testDataProvider.GetTestSuites(mockProject, mockPlanId);
|
|
144
|
+
// Assert
|
|
145
|
+
expect(result).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('GetTestSuitesForPlan', () => {
|
|
149
|
+
it('should throw error when project is not provided', async () => {
|
|
150
|
+
// Act & Assert
|
|
151
|
+
await expect(testDataProvider.GetTestSuitesForPlan('', mockPlanId)).rejects.toThrow('Project not selected');
|
|
152
|
+
});
|
|
153
|
+
it('should throw error when plan ID is not provided', async () => {
|
|
154
|
+
// Act & Assert
|
|
155
|
+
await expect(testDataProvider.GetTestSuitesForPlan(mockProject, '')).rejects.toThrow('Plan not selected');
|
|
156
|
+
});
|
|
157
|
+
it('should return test suites for a plan', async () => {
|
|
158
|
+
// Arrange
|
|
159
|
+
const mockData = {
|
|
160
|
+
testSuites: [
|
|
161
|
+
{ id: '123', name: 'Test Suite 1' },
|
|
162
|
+
{ id: '456', name: 'Test Suite 2' },
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
166
|
+
// Act
|
|
167
|
+
const result = await testDataProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
168
|
+
// Assert
|
|
169
|
+
expect(result).toEqual(mockData);
|
|
170
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}/${mockProject}/_api/_testManagement/GetTestSuitesForPlan?__v=5&planId=${mockPlanId}`, mockToken);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('GetTestSuiteById', () => {
|
|
174
|
+
it('should call GetTestSuitesForPlan and Helper.findSuitesRecursive with correct params', async () => {
|
|
175
|
+
// Arrange
|
|
176
|
+
const mockTestSuites = { testSuites: [{ id: '123', name: 'Test Suite 1' }] };
|
|
177
|
+
const mockSuiteData = [new helper_1.suiteData('Test Suite 1', '123', '456', 1)];
|
|
178
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockTestSuites);
|
|
179
|
+
helper_1.Helper.findSuitesRecursive.mockReturnValueOnce(mockSuiteData);
|
|
180
|
+
// Act
|
|
181
|
+
const result = await testDataProvider.GetTestSuiteById(mockProject, mockPlanId, mockSuiteId, true);
|
|
182
|
+
// Assert
|
|
183
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}/${mockProject}/_api/_testManagement/GetTestSuitesForPlan?__v=5&planId=${mockPlanId}`, mockToken);
|
|
184
|
+
expect(helper_1.Helper.findSuitesRecursive).toHaveBeenCalledWith(mockPlanId, mockOrgUrl, mockProject, mockTestSuites.testSuites, mockSuiteId, true);
|
|
185
|
+
expect(result).toEqual(mockSuiteData);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('GetTestCases', () => {
|
|
189
|
+
it('should return test cases for a given project, plan ID, and suite ID', async () => {
|
|
190
|
+
// Arrange
|
|
191
|
+
const mockData = {
|
|
192
|
+
count: 2,
|
|
193
|
+
value: [
|
|
194
|
+
{ testCase: { id: '101', name: 'Test Case 1', url: 'url1' } },
|
|
195
|
+
{ testCase: { id: '102', name: 'Test Case 2', url: 'url2' } },
|
|
196
|
+
],
|
|
197
|
+
};
|
|
198
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce(mockData);
|
|
199
|
+
// Act
|
|
200
|
+
const result = await testDataProvider.GetTestCases(mockProject, mockPlanId, mockSuiteId);
|
|
201
|
+
// Assert
|
|
202
|
+
expect(result).toEqual(mockData);
|
|
203
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/Plans/${mockPlanId}/suites/${mockSuiteId}/testcases/`, mockToken);
|
|
204
|
+
expect(logger_1.default.debug).toHaveBeenCalledWith(`test cases for plan ${mockPlanId} and ${mockSuiteId} were found`);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('clearCache', () => {
|
|
208
|
+
it('should clear the cache', () => {
|
|
209
|
+
// Arrange
|
|
210
|
+
const mockUrl = `${mockOrgUrl}_apis/test/endpoint`;
|
|
211
|
+
const mockData = { value: 'test data' };
|
|
212
|
+
const cache = new Map();
|
|
213
|
+
cache.set(mockUrl, {
|
|
214
|
+
data: mockData,
|
|
215
|
+
timestamp: Date.now(),
|
|
216
|
+
});
|
|
217
|
+
testDataProvider.cache = cache;
|
|
218
|
+
// Act
|
|
219
|
+
testDataProvider.clearCache();
|
|
220
|
+
// Assert
|
|
221
|
+
expect(testDataProvider.cache.size).toBe(0);
|
|
222
|
+
expect(logger_1.default.debug).toHaveBeenCalledWith('Cache cleared');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('UpdateTestRun', () => {
|
|
226
|
+
it('should update a test run with the correct state', async () => {
|
|
227
|
+
// Arrange
|
|
228
|
+
const mockRunId = '12345';
|
|
229
|
+
const mockState = 'Completed';
|
|
230
|
+
const mockResponse = { id: mockRunId, state: mockState };
|
|
231
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
232
|
+
// Act
|
|
233
|
+
const result = await testDataProvider.UpdateTestRun(mockProject, mockRunId, mockState);
|
|
234
|
+
// Assert
|
|
235
|
+
expect(result).toEqual(mockResponse);
|
|
236
|
+
expect(tfs_1.TFSServices.postRequest).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/Runs/${mockRunId}?api-version=5.0`, mockToken, 'PATCH', { state: mockState }, null);
|
|
237
|
+
expect(logger_1.default.info).toHaveBeenCalledWith(`Update runId : ${mockRunId} to state : ${mockState}`);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
describe('GetTestSuitesByPlan', () => {
|
|
241
|
+
it('should return suites without filter using default suiteId', async () => {
|
|
242
|
+
// Arrange
|
|
243
|
+
const mockTestSuites = { testSuites: [{ id: 457, name: 'Suite 1', parentSuiteId: 0 }] };
|
|
244
|
+
const mockSuiteData = [new helper_1.suiteData('Suite 1', '457', '456', 1)];
|
|
245
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValue(mockTestSuites);
|
|
246
|
+
helper_1.Helper.findSuitesRecursive.mockReturnValue(mockSuiteData);
|
|
247
|
+
// Act
|
|
248
|
+
const result = await testDataProvider.GetTestSuitesByPlan(mockProject, mockPlanId, true);
|
|
249
|
+
// Assert
|
|
250
|
+
expect(helper_1.Helper.findSuitesRecursive).toHaveBeenCalled();
|
|
251
|
+
});
|
|
252
|
+
it('should process multiple top-level suite hierarchies and combine results', async () => {
|
|
253
|
+
jest.spyOn(testDataProvider, 'GetTestSuitesForPlan').mockResolvedValueOnce({
|
|
254
|
+
testSuites: [
|
|
255
|
+
{ id: 1, parentSuiteId: 0 },
|
|
256
|
+
{ id: 2, parentSuiteId: 0 },
|
|
257
|
+
{ id: 10, parentSuiteId: 1 },
|
|
258
|
+
{ id: 11, parentSuiteId: 2 },
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
const suiteIdsFilter = [10, 11];
|
|
262
|
+
const getByIdSpy = jest
|
|
263
|
+
.spyOn(testDataProvider, 'GetTestSuiteById')
|
|
264
|
+
.mockResolvedValueOnce([{ id: '10' }])
|
|
265
|
+
.mockResolvedValueOnce([{ id: '11' }]);
|
|
266
|
+
const res = await testDataProvider.GetTestSuitesByPlan(mockProject, mockPlanId, true, suiteIdsFilter);
|
|
267
|
+
expect(getByIdSpy).toHaveBeenCalledTimes(2);
|
|
268
|
+
expect(res).toEqual([{ id: '10' }, { id: '11' }]);
|
|
269
|
+
});
|
|
270
|
+
it('should fallback to first suite when no top-level suites can be determined', async () => {
|
|
271
|
+
jest.spyOn(testDataProvider, 'GetTestSuitesForPlan').mockResolvedValueOnce({
|
|
272
|
+
testSuites: [{ id: 1, parentSuiteId: 0 }],
|
|
273
|
+
});
|
|
274
|
+
const getByIdSpy = jest
|
|
275
|
+
.spyOn(testDataProvider, 'GetTestSuiteById')
|
|
276
|
+
.mockResolvedValueOnce([{ id: '1' }]);
|
|
277
|
+
const res = await testDataProvider.GetTestSuitesByPlan(mockProject, mockPlanId, true, [1]);
|
|
278
|
+
expect(getByIdSpy).toHaveBeenCalledWith(mockProject, mockPlanId, '1', true, [1]);
|
|
279
|
+
expect(res).toEqual([{ id: '1' }]);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
describe('createNewRequirement', () => {
|
|
283
|
+
it('should pick customer id from any supported customer fields when enabled', () => {
|
|
284
|
+
const rel = testDataProvider.createNewRequirement(true, {
|
|
285
|
+
id: '123',
|
|
286
|
+
fields: {
|
|
287
|
+
'System.Title': 'Req title',
|
|
288
|
+
'Custom.CustomerID': 'CID-1',
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
expect(rel).toEqual({ type: 'requirement', id: '123', title: 'Req title', customerId: 'CID-1' });
|
|
292
|
+
});
|
|
293
|
+
it('should default customerId to a single space when enabled and no field exists', () => {
|
|
294
|
+
const rel = testDataProvider.createNewRequirement(true, {
|
|
295
|
+
id: '123',
|
|
296
|
+
fields: {
|
|
297
|
+
'System.Title': 'Req title',
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
expect(rel).toEqual({ type: 'requirement', id: '123', title: 'Req title', customerId: ' ' });
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
describe('addToMap', () => {
|
|
304
|
+
it('should create array for missing key and append values', () => {
|
|
305
|
+
const map = new Map();
|
|
306
|
+
testDataProvider.addToMap(map, 'k', 'v1');
|
|
307
|
+
testDataProvider.addToMap(map, 'k', 'v2');
|
|
308
|
+
expect(map.get('k')).toEqual(['v1', 'v2']);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
describe('GetTestSuiteById with filtering', () => {
|
|
312
|
+
it('should filter suites when suiteIdsFilter is provided', async () => {
|
|
313
|
+
// Arrange
|
|
314
|
+
const suiteIdsFilter = [123, 456];
|
|
315
|
+
const mockTestSuites = {
|
|
316
|
+
testSuites: [
|
|
317
|
+
{ id: 123, name: 'Suite 1', parentSuiteId: 100 },
|
|
318
|
+
{ id: 456, name: 'Suite 2', parentSuiteId: 100 },
|
|
319
|
+
],
|
|
320
|
+
};
|
|
321
|
+
const mockSuiteData = [new helper_1.suiteData('Suite 1', '123', '100', 1)];
|
|
322
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValue(mockTestSuites);
|
|
323
|
+
helper_1.Helper.findSuitesRecursive.mockReturnValue(mockSuiteData);
|
|
324
|
+
// Act
|
|
325
|
+
const result = await testDataProvider.GetTestSuiteById(mockProject, mockPlanId, '123', true, suiteIdsFilter);
|
|
326
|
+
// Assert
|
|
327
|
+
expect(helper_1.Helper.findSuitesRecursive).toHaveBeenCalled();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
describe('GetTestPoint', () => {
|
|
331
|
+
it('should return test points for a test case', async () => {
|
|
332
|
+
// Arrange
|
|
333
|
+
const mockResponse = { value: [{ id: 1, testCaseId: mockTestCaseId }] };
|
|
334
|
+
tfs_1.TFSServices.getItemContent.mockResolvedValue(mockResponse);
|
|
335
|
+
// Act
|
|
336
|
+
const result = await testDataProvider.GetTestPoint(mockProject, mockPlanId, mockSuiteId, mockTestCaseId);
|
|
337
|
+
// Assert
|
|
338
|
+
expect(result).toEqual(mockResponse);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
describe('CreateTestRun', () => {
|
|
342
|
+
it('should create a test run successfully', async () => {
|
|
343
|
+
// Arrange
|
|
344
|
+
const testRunName = 'Test Run 1';
|
|
345
|
+
const testPointId = '12345';
|
|
346
|
+
const mockResponse = { data: { id: 1, name: testRunName } };
|
|
347
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
348
|
+
// Act
|
|
349
|
+
const result = await testDataProvider.CreateTestRun(mockProject, testRunName, mockPlanId, testPointId);
|
|
350
|
+
// Assert
|
|
351
|
+
expect(result).toEqual(mockResponse);
|
|
352
|
+
expect(tfs_1.TFSServices.postRequest).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/runs`, mockToken, 'Post', {
|
|
353
|
+
name: testRunName,
|
|
354
|
+
plan: { id: mockPlanId },
|
|
355
|
+
pointIds: [testPointId],
|
|
356
|
+
}, null);
|
|
357
|
+
});
|
|
358
|
+
it('should throw error when creation fails', async () => {
|
|
359
|
+
// Arrange
|
|
360
|
+
const testRunName = 'Test Run 1';
|
|
361
|
+
const testPointId = '12345';
|
|
362
|
+
const mockError = new Error('Creation failed');
|
|
363
|
+
tfs_1.TFSServices.postRequest.mockRejectedValueOnce(mockError);
|
|
364
|
+
// Act & Assert
|
|
365
|
+
await expect(testDataProvider.CreateTestRun(mockProject, testRunName, mockPlanId, testPointId)).rejects.toThrow('Error: Creation failed');
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
describe('UpdateTestCase', () => {
|
|
369
|
+
it('should update test case to Active state (0)', async () => {
|
|
370
|
+
// Arrange
|
|
371
|
+
const mockRunId = '12345';
|
|
372
|
+
const mockResponse = { data: { outcome: '0' } };
|
|
373
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
374
|
+
// Act
|
|
375
|
+
const result = await testDataProvider.UpdateTestCase(mockProject, mockRunId, 0);
|
|
376
|
+
// Assert
|
|
377
|
+
expect(result).toEqual(mockResponse);
|
|
378
|
+
expect(logger_1.default.info).toHaveBeenCalledWith('Reset test case to Active state ');
|
|
379
|
+
});
|
|
380
|
+
it('should update test case to Completed state (1)', async () => {
|
|
381
|
+
// Arrange
|
|
382
|
+
const mockRunId = '12345';
|
|
383
|
+
const mockResponse = { data: { state: 'Completed', outcome: '1' } };
|
|
384
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
385
|
+
// Act
|
|
386
|
+
const result = await testDataProvider.UpdateTestCase(mockProject, mockRunId, 1);
|
|
387
|
+
// Assert
|
|
388
|
+
expect(result).toEqual(mockResponse);
|
|
389
|
+
expect(logger_1.default.info).toHaveBeenCalledWith('Update test case to complite state ');
|
|
390
|
+
});
|
|
391
|
+
it('should update test case to Passed state (2)', async () => {
|
|
392
|
+
// Arrange
|
|
393
|
+
const mockRunId = '12345';
|
|
394
|
+
const mockResponse = { data: { state: 'Completed', outcome: '2' } };
|
|
395
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
396
|
+
// Act
|
|
397
|
+
const result = await testDataProvider.UpdateTestCase(mockProject, mockRunId, 2);
|
|
398
|
+
// Assert
|
|
399
|
+
expect(result).toEqual(mockResponse);
|
|
400
|
+
expect(logger_1.default.info).toHaveBeenCalledWith('Update test case to passed state ');
|
|
401
|
+
});
|
|
402
|
+
it('should update test case to Failed state (3)', async () => {
|
|
403
|
+
// Arrange
|
|
404
|
+
const mockRunId = '12345';
|
|
405
|
+
const mockResponse = { data: { state: 'Completed', outcome: '3' } };
|
|
406
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
407
|
+
// Act
|
|
408
|
+
const result = await testDataProvider.UpdateTestCase(mockProject, mockRunId, 3);
|
|
409
|
+
// Assert
|
|
410
|
+
expect(result).toEqual(mockResponse);
|
|
411
|
+
expect(logger_1.default.info).toHaveBeenCalledWith('Update test case to failed state ');
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
describe('UploadTestAttachment', () => {
|
|
415
|
+
it('should upload attachment to test run', async () => {
|
|
416
|
+
// Arrange
|
|
417
|
+
const runId = '12345';
|
|
418
|
+
const stream = 'base64encodeddata';
|
|
419
|
+
const fileName = 'test.png';
|
|
420
|
+
const comment = 'Test attachment';
|
|
421
|
+
const attachmentType = 'GeneralAttachment';
|
|
422
|
+
const mockResponse = { data: { id: 1 } };
|
|
423
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
424
|
+
// Act
|
|
425
|
+
const result = await testDataProvider.UploadTestAttachment(runId, mockProject, stream, fileName, comment, attachmentType);
|
|
426
|
+
// Assert
|
|
427
|
+
expect(result).toEqual(mockResponse);
|
|
428
|
+
expect(tfs_1.TFSServices.postRequest).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/Runs/${runId}/attachments?api-version=5.0-preview.1`, mockToken, 'Post', { stream, fileName, comment, attachmentType }, null);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
describe('GetTestRunById', () => {
|
|
432
|
+
it('should return test run by ID', async () => {
|
|
433
|
+
// Arrange
|
|
434
|
+
const runId = '12345';
|
|
435
|
+
const mockResponse = { id: runId, name: 'Test Run 1' };
|
|
436
|
+
// Act
|
|
437
|
+
const result = await testDataProvider.GetTestRunById(mockProject, runId);
|
|
438
|
+
// Assert
|
|
439
|
+
expect(tfs_1.TFSServices.getItemContent).toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
describe('GetTestPointByTestCaseId', () => {
|
|
443
|
+
it('should return test points for a test case ID', async () => {
|
|
444
|
+
// Arrange
|
|
445
|
+
const mockResponse = { data: { value: [{ id: 1 }] } };
|
|
446
|
+
tfs_1.TFSServices.postRequest.mockResolvedValueOnce(mockResponse);
|
|
447
|
+
// Act
|
|
448
|
+
const result = await testDataProvider.GetTestPointByTestCaseId(mockProject, mockTestCaseId);
|
|
449
|
+
// Assert
|
|
450
|
+
expect(result).toEqual(mockResponse);
|
|
451
|
+
expect(tfs_1.TFSServices.postRequest).toHaveBeenCalledWith(`${mockOrgUrl}${mockProject}/_apis/test/points`, mockToken, 'Post', { PointsFilter: { TestcaseIds: [mockTestCaseId] } }, null);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
describe('GetTestCasesBySuites', () => {
|
|
455
|
+
it('should return test cases for suites', async () => {
|
|
456
|
+
// Arrange
|
|
457
|
+
const mockSuiteData = [new helper_1.suiteData('Suite 1', '123', '456', 1)];
|
|
458
|
+
const mockTestCases = {
|
|
459
|
+
count: 1,
|
|
460
|
+
value: [{ testCase: { id: '101', url: 'http://test.com/101' } }],
|
|
461
|
+
};
|
|
462
|
+
const mockTestCaseDetails = {
|
|
463
|
+
id: 101,
|
|
464
|
+
fields: {
|
|
465
|
+
'System.Title': 'Test Case 1',
|
|
466
|
+
'System.AreaPath': 'Area/Path',
|
|
467
|
+
'System.Description': 'Description',
|
|
468
|
+
'Microsoft.VSTS.TCM.Steps': null,
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
tfs_1.TFSServices.getItemContent
|
|
472
|
+
.mockResolvedValueOnce({ testSuites: mockSuiteData })
|
|
473
|
+
.mockResolvedValueOnce(mockTestCases)
|
|
474
|
+
.mockResolvedValueOnce(mockTestCaseDetails);
|
|
475
|
+
helper_1.Helper.findSuitesRecursive.mockReturnValueOnce(mockSuiteData);
|
|
476
|
+
// Act
|
|
477
|
+
const result = await testDataProvider.GetTestCasesBySuites(mockProject, mockPlanId, mockSuiteId, false, false, false, false);
|
|
478
|
+
// Assert
|
|
479
|
+
expect(result.testCasesList).toBeDefined();
|
|
480
|
+
expect(result.requirementToTestCaseTraceMap).toBeDefined();
|
|
481
|
+
expect(result.testCaseToRequirementsTraceMap).toBeDefined();
|
|
482
|
+
});
|
|
483
|
+
it('should use pre-filtered suites when provided', async () => {
|
|
484
|
+
// Arrange
|
|
485
|
+
const preFilteredSuites = [new helper_1.suiteData('Suite 1', '123', '456', 1)];
|
|
486
|
+
const mockTestCases = {
|
|
487
|
+
count: 1,
|
|
488
|
+
value: [{ testCase: { id: '101', url: 'http://test.com/101' } }],
|
|
489
|
+
};
|
|
490
|
+
const mockTestCaseDetails = {
|
|
491
|
+
id: 101,
|
|
492
|
+
fields: {
|
|
493
|
+
'System.Title': 'Test Case 1',
|
|
494
|
+
'System.AreaPath': 'Area/Path',
|
|
495
|
+
'System.Description': 'Description',
|
|
496
|
+
'Microsoft.VSTS.TCM.Steps': null,
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
tfs_1.TFSServices.getItemContent
|
|
500
|
+
.mockResolvedValueOnce(mockTestCases)
|
|
501
|
+
.mockResolvedValueOnce(mockTestCaseDetails);
|
|
502
|
+
// Act
|
|
503
|
+
const result = await testDataProvider.GetTestCasesBySuites(mockProject, mockPlanId, mockSuiteId, false, false, false, false, undefined, undefined, preFilteredSuites);
|
|
504
|
+
// Assert
|
|
505
|
+
expect(result.testCasesList).toBeDefined();
|
|
506
|
+
});
|
|
507
|
+
it('should handle errors in suite processing', async () => {
|
|
508
|
+
// Arrange
|
|
509
|
+
const preFilteredSuites = [new helper_1.suiteData('Suite 1', '123', '456', 1)];
|
|
510
|
+
tfs_1.TFSServices.getItemContent.mockRejectedValueOnce(new Error('API Error'));
|
|
511
|
+
// Act
|
|
512
|
+
const result = await testDataProvider.GetTestCasesBySuites(mockProject, mockPlanId, mockSuiteId, false, false, false, false, undefined, undefined, preFilteredSuites);
|
|
513
|
+
// Assert
|
|
514
|
+
expect(result.testCasesList).toEqual([]);
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
describe('StructureTestCase', () => {
|
|
518
|
+
it('should return empty array when no test cases', async () => {
|
|
519
|
+
// Arrange
|
|
520
|
+
const suite = new helper_1.suiteData('Suite 1', '123', '456', 1);
|
|
521
|
+
const testCases = { count: 0, value: [] };
|
|
522
|
+
// Act
|
|
523
|
+
const result = await testDataProvider.StructureTestCase(mockProject, testCases, suite, false, false, false, new Map(), new Map());
|
|
524
|
+
// Assert
|
|
525
|
+
expect(result).toEqual([]);
|
|
526
|
+
expect(logger_1.default.warn).toHaveBeenCalled();
|
|
527
|
+
});
|
|
528
|
+
it('should warn and return [] when no testCases are provided', async () => {
|
|
529
|
+
const res = await testDataProvider.StructureTestCase(mockProject, { value: [], count: 0 }, { id: '1', name: 'Suite 1' }, true, true, false, new Map(), new Map());
|
|
530
|
+
expect(res).toEqual([]);
|
|
531
|
+
expect(logger_1.default.warn).toHaveBeenCalledWith('No test cases found for suite: 1');
|
|
532
|
+
});
|
|
533
|
+
it('should parse steps, add requirement relation when enabled, and add mom relation when enabled', async () => {
|
|
534
|
+
const UtilsMock = require('../../utils/testStepParserHelper').default;
|
|
535
|
+
const parserInstance = UtilsMock.mock.results[0].value;
|
|
536
|
+
parserInstance.parseTestSteps.mockResolvedValueOnce([{ stepId: '1' }]);
|
|
537
|
+
const suite = { id: '1', name: 'Suite 1' };
|
|
538
|
+
const testCases = {
|
|
539
|
+
count: 1,
|
|
540
|
+
value: [{ testCase: { id: 123, url: 'https://example.com/testcase/123' } }],
|
|
541
|
+
};
|
|
542
|
+
jest.spyOn(testDataProvider, 'fetchWithCache').mockImplementation(async (...args) => {
|
|
543
|
+
const url = String(args[0] || '');
|
|
544
|
+
if (url.includes('testcase/123') && url.includes('?$expand=All')) {
|
|
545
|
+
return {
|
|
546
|
+
id: 123,
|
|
547
|
+
fields: {
|
|
548
|
+
'System.Title': 'TC 123',
|
|
549
|
+
'System.AreaPath': 'A',
|
|
550
|
+
'System.Description': 'D',
|
|
551
|
+
'Microsoft.VSTS.TCM.Steps': '<steps></steps>',
|
|
552
|
+
},
|
|
553
|
+
relations: [
|
|
554
|
+
{ url: 'https://example.com/_apis/wit/workItems/200' },
|
|
555
|
+
{ url: 'https://example.com/not-work-items/ignore' },
|
|
556
|
+
{ url: 'https://example.com/_apis/wit/workItems/201' },
|
|
557
|
+
],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
if (url.includes('/workItems/200')) {
|
|
561
|
+
return {
|
|
562
|
+
id: 200,
|
|
563
|
+
fields: {
|
|
564
|
+
'System.WorkItemType': 'Requirement',
|
|
565
|
+
'System.Title': 'REQ 200',
|
|
566
|
+
'Custom.CustomerRequirementId': 'C-200',
|
|
567
|
+
},
|
|
568
|
+
_links: { html: { href: 'http://example.com/200' } },
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (url.includes('/workItems/201')) {
|
|
572
|
+
return {
|
|
573
|
+
id: 201,
|
|
574
|
+
fields: {
|
|
575
|
+
'System.WorkItemType': 'Bug',
|
|
576
|
+
'System.Title': 'BUG 201',
|
|
577
|
+
'System.State': 'Active',
|
|
578
|
+
},
|
|
579
|
+
_links: { html: { href: 'http://example.com/201' } },
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
throw new Error(`unexpected url ${url}`);
|
|
583
|
+
});
|
|
584
|
+
const requirementToTestCaseTraceMap = new Map();
|
|
585
|
+
const testCaseToRequirementsTraceMap = new Map();
|
|
586
|
+
const res = await testDataProvider.StructureTestCase(mockProject, testCases, suite, true, true, true, requirementToTestCaseTraceMap, testCaseToRequirementsTraceMap);
|
|
587
|
+
expect(res).toHaveLength(1);
|
|
588
|
+
expect(res[0].steps).toEqual([{ stepId: '1' }]);
|
|
589
|
+
expect(res[0].relations).toEqual(expect.arrayContaining([
|
|
590
|
+
expect.objectContaining({ type: 'requirement', id: 200, customerId: 'C-200' }),
|
|
591
|
+
]));
|
|
592
|
+
expect(res[0].relations).toEqual(expect.arrayContaining([expect.objectContaining({ type: 'Bug', id: 201 })]));
|
|
593
|
+
expect(requirementToTestCaseTraceMap.size).toBe(1);
|
|
594
|
+
expect(testCaseToRequirementsTraceMap.size).toBe(1);
|
|
595
|
+
});
|
|
596
|
+
it('should use stepResultDetailsMap when provided and skip parseTestSteps', async () => {
|
|
597
|
+
const UtilsMock = require('../../utils/testStepParserHelper').default;
|
|
598
|
+
const parserInstance = UtilsMock.mock.results[0].value;
|
|
599
|
+
parserInstance.parseTestSteps.mockClear();
|
|
600
|
+
const suite = { id: '1', name: 'Suite 1' };
|
|
601
|
+
const testCases = {
|
|
602
|
+
count: 1,
|
|
603
|
+
value: [{ testCase: { id: 123, url: 'https://example.com/testcase/123' } }],
|
|
604
|
+
};
|
|
605
|
+
const stepResultDetailsMap = new Map();
|
|
606
|
+
stepResultDetailsMap.set('123', {
|
|
607
|
+
testCaseRevision: 7,
|
|
608
|
+
stepList: [{ stepId: 'from-cache' }],
|
|
609
|
+
caseEvidenceAttachments: [{ id: 1 }],
|
|
610
|
+
});
|
|
611
|
+
jest.spyOn(testDataProvider, 'fetchWithCache').mockResolvedValueOnce({
|
|
612
|
+
id: 123,
|
|
613
|
+
fields: {
|
|
614
|
+
'System.Title': 'TC 123',
|
|
615
|
+
'System.AreaPath': 'A',
|
|
616
|
+
'System.Description': 'D',
|
|
617
|
+
'Microsoft.VSTS.TCM.Steps': '<steps></steps>',
|
|
618
|
+
},
|
|
619
|
+
relations: [],
|
|
620
|
+
});
|
|
621
|
+
const res = await testDataProvider.StructureTestCase(mockProject, testCases, suite, true, false, false, new Map(), new Map(), stepResultDetailsMap);
|
|
622
|
+
expect(res).toHaveLength(1);
|
|
623
|
+
expect(res[0].steps).toEqual([{ stepId: 'from-cache' }]);
|
|
624
|
+
expect(res[0].caseEvidenceAttachments).toEqual([{ id: 1 }]);
|
|
625
|
+
expect(parserInstance.parseTestSteps).not.toHaveBeenCalled();
|
|
626
|
+
});
|
|
627
|
+
it('should log error and continue when fetching relation content fails', async () => {
|
|
628
|
+
const suite = { id: '1', name: 'Suite 1' };
|
|
629
|
+
const testCases = {
|
|
630
|
+
count: 1,
|
|
631
|
+
value: [{ testCase: { id: 123, url: 'https://example.com/testcase/123' } }],
|
|
632
|
+
};
|
|
633
|
+
jest.spyOn(testDataProvider, 'fetchWithCache').mockImplementation(async (...args) => {
|
|
634
|
+
const url = String(args[0] || '');
|
|
635
|
+
if (url.includes('testcase/123') && url.includes('?$expand=All')) {
|
|
636
|
+
return {
|
|
637
|
+
id: 123,
|
|
638
|
+
fields: {
|
|
639
|
+
'System.Title': 'TC 123',
|
|
640
|
+
'System.AreaPath': 'A',
|
|
641
|
+
'System.Description': 'D',
|
|
642
|
+
'Microsoft.VSTS.TCM.Steps': null,
|
|
643
|
+
},
|
|
644
|
+
relations: [{ url: 'https://example.com/_apis/wit/workItems/200' }],
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (url.includes('/workItems/200')) {
|
|
648
|
+
throw new Error('boom');
|
|
649
|
+
}
|
|
650
|
+
return null;
|
|
651
|
+
});
|
|
652
|
+
const res = await testDataProvider.StructureTestCase(mockProject, testCases, suite, true, true, false, new Map(), new Map());
|
|
653
|
+
expect(res).toHaveLength(1);
|
|
654
|
+
expect(logger_1.default.error).toHaveBeenCalledWith(expect.stringContaining('Failed to fetch relation content for URL https://example.com/_apis/wit/workItems/200'));
|
|
655
|
+
});
|
|
656
|
+
it('should append linked MOMs from testCaseToLinkedMomLookup when provided', async () => {
|
|
657
|
+
const suite = { id: '1', name: 'Suite 1' };
|
|
658
|
+
const testCases = {
|
|
659
|
+
count: 1,
|
|
660
|
+
value: [{ testCase: { id: 123, url: 'https://example.com/testcase/123' } }],
|
|
661
|
+
};
|
|
662
|
+
jest.spyOn(testDataProvider, 'fetchWithCache').mockResolvedValueOnce({
|
|
663
|
+
id: 123,
|
|
664
|
+
fields: {
|
|
665
|
+
'System.Title': 'TC 123',
|
|
666
|
+
'System.AreaPath': 'A',
|
|
667
|
+
'System.Description': 'D',
|
|
668
|
+
'Microsoft.VSTS.TCM.Steps': null,
|
|
669
|
+
},
|
|
670
|
+
relations: [],
|
|
671
|
+
});
|
|
672
|
+
const testCaseToLinkedMomLookup = new Map();
|
|
673
|
+
testCaseToLinkedMomLookup.set(123, new Set([
|
|
674
|
+
{
|
|
675
|
+
id: 900,
|
|
676
|
+
fields: {
|
|
677
|
+
'System.WorkItemType': 'Task',
|
|
678
|
+
'System.Title': 'Task 900',
|
|
679
|
+
'System.State': 'Active',
|
|
680
|
+
},
|
|
681
|
+
_links: { html: { href: 'http://example.com/900' } },
|
|
682
|
+
},
|
|
683
|
+
]));
|
|
684
|
+
const res = await testDataProvider.StructureTestCase(mockProject, testCases, suite, false, false, false, new Map(), new Map(), undefined, testCaseToLinkedMomLookup);
|
|
685
|
+
expect(res).toHaveLength(1);
|
|
686
|
+
expect(res[0].relations).toEqual(expect.arrayContaining([expect.objectContaining({ type: 'Task', id: 900 })]));
|
|
687
|
+
});
|
|
688
|
+
it('should return empty list when test case fetch fails', async () => {
|
|
689
|
+
const suite = { id: '1', name: 'Suite 1' };
|
|
690
|
+
const testCases = {
|
|
691
|
+
count: 1,
|
|
692
|
+
value: [{ testCase: { id: 123, url: 'https://example.com/testcase/123' } }],
|
|
693
|
+
};
|
|
694
|
+
jest.spyOn(testDataProvider, 'fetchWithCache').mockRejectedValueOnce(new Error('boom'));
|
|
695
|
+
const res = await testDataProvider.StructureTestCase(mockProject, testCases, suite, false, false, false, new Map(), new Map());
|
|
696
|
+
expect(res).toEqual([]);
|
|
697
|
+
expect(logger_1.default.error).toHaveBeenCalledWith('Error: ran into an issue while retrieving testCase 123');
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
describe('ParseSteps', () => {
|
|
701
|
+
it('should parse XML steps correctly', () => {
|
|
702
|
+
// Arrange
|
|
703
|
+
const stepsXml = `<steps>
|
|
704
|
+
<step>
|
|
705
|
+
<parameterizedString>Action 1</parameterizedString>
|
|
706
|
+
<parameterizedString>Expected 1</parameterizedString>
|
|
707
|
+
</step>
|
|
708
|
+
</steps>`;
|
|
709
|
+
// Act
|
|
710
|
+
const result = testDataProvider.ParseSteps(stepsXml);
|
|
711
|
+
// Assert - ParseSteps uses xml2js.parseString which is synchronous callback-based
|
|
712
|
+
// The result may be empty if parsing fails or steps format doesn't match
|
|
713
|
+
expect(Array.isArray(result)).toBe(true);
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
//# sourceMappingURL=testDataProvider.test.js.map
|