@elisra-devops/docgen-data-provider 1.109.1 → 1.110.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/bin/modules/TicketsDataProvider.d.ts +2 -0
- package/bin/modules/TicketsDataProvider.js +72 -43
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/tests/modules/ticketsDataProvider.test.js +150 -34
- package/bin/tests/modules/ticketsDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/TicketsDataProvider.ts +94 -51
- package/src/tests/modules/ticketsDataProvider.test.ts +172 -39
|
@@ -88,11 +88,109 @@ describe('TicketsDataProvider', () => {
|
|
|
88
88
|
expect(result).toEqual({ tree1: null, tree2: null });
|
|
89
89
|
});
|
|
90
90
|
});
|
|
91
|
+
describe('fetchCustomerRequirementQueries', () => {
|
|
92
|
+
it('should return flat, tree, and oneHop query nodes without WIQL type filtering', async () => {
|
|
93
|
+
const root = {
|
|
94
|
+
id: 'root',
|
|
95
|
+
name: 'SYSRS',
|
|
96
|
+
isFolder: true,
|
|
97
|
+
hasChildren: true,
|
|
98
|
+
children: [
|
|
99
|
+
{
|
|
100
|
+
id: 'flat-query',
|
|
101
|
+
name: 'Flat Customer Pull',
|
|
102
|
+
isFolder: false,
|
|
103
|
+
hasChildren: false,
|
|
104
|
+
queryType: 'flat',
|
|
105
|
+
wiql: "SELECT * FROM WorkItems WHERE [System.WorkItemType] = 'Task'",
|
|
106
|
+
columns: [],
|
|
107
|
+
_links: { wiql: { href: 'flat-url' } },
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'tree-query',
|
|
111
|
+
name: 'Tree Customer Pull',
|
|
112
|
+
isFolder: false,
|
|
113
|
+
hasChildren: false,
|
|
114
|
+
queryType: 'tree',
|
|
115
|
+
wiql: "SELECT * FROM WorkItemLinks WHERE [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'",
|
|
116
|
+
columns: [],
|
|
117
|
+
_links: { wiql: { href: 'tree-url' } },
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'onehop-query',
|
|
121
|
+
name: 'OneHop Customer Pull',
|
|
122
|
+
isFolder: false,
|
|
123
|
+
hasChildren: false,
|
|
124
|
+
queryType: 'oneHop',
|
|
125
|
+
wiql: "SELECT * FROM WorkItemLinks WHERE [System.Links.LinkType] = 'System.LinkTypes.Related'",
|
|
126
|
+
columns: [],
|
|
127
|
+
_links: { wiql: { href: 'onehop-url' } },
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'unsupported-query',
|
|
131
|
+
name: 'Unsupported',
|
|
132
|
+
isFolder: false,
|
|
133
|
+
hasChildren: false,
|
|
134
|
+
queryType: 'invalid',
|
|
135
|
+
_links: { wiql: { href: 'unsupported-url' } },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
const result = await ticketsDataProvider.fetchCustomerRequirementQueries(root, []);
|
|
140
|
+
expect(result.systemRequirementsQueryTree.children.map((child) => child.queryType)).toEqual([
|
|
141
|
+
'flat',
|
|
142
|
+
'tree',
|
|
143
|
+
'oneHop',
|
|
144
|
+
]);
|
|
145
|
+
expect(result.systemRequirementsQueryTree.children.map((child) => child.wiql.href)).toEqual([
|
|
146
|
+
'flat-url',
|
|
147
|
+
'tree-url',
|
|
148
|
+
'onehop-url',
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
it('should include system-to-subsystem queries because customer discovery has no folder exclusions', async () => {
|
|
152
|
+
const root = {
|
|
153
|
+
id: 'root',
|
|
154
|
+
name: 'SYSRS',
|
|
155
|
+
isFolder: true,
|
|
156
|
+
hasChildren: true,
|
|
157
|
+
children: [
|
|
158
|
+
{
|
|
159
|
+
id: 'excluded-folder',
|
|
160
|
+
name: 'System To Subsystem',
|
|
161
|
+
isFolder: true,
|
|
162
|
+
hasChildren: true,
|
|
163
|
+
children: [
|
|
164
|
+
{
|
|
165
|
+
id: 'system-to-subsystem-query',
|
|
166
|
+
name: 'System To Subsystem Query',
|
|
167
|
+
isFolder: false,
|
|
168
|
+
hasChildren: false,
|
|
169
|
+
queryType: 'tree',
|
|
170
|
+
_links: { wiql: { href: 'system-to-subsystem-url' } },
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'included-query',
|
|
176
|
+
name: 'Included Query',
|
|
177
|
+
isFolder: false,
|
|
178
|
+
hasChildren: false,
|
|
179
|
+
queryType: 'oneHop',
|
|
180
|
+
_links: { wiql: { href: 'included-url' } },
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
const result = await ticketsDataProvider.fetchCustomerRequirementQueries(root);
|
|
185
|
+
expect(result.systemRequirementsQueryTree.children).toHaveLength(2);
|
|
186
|
+
expect(result.systemRequirementsQueryTree.children[0].children[0]).toEqual(expect.objectContaining({ id: 'system-to-subsystem-query', queryType: 'tree' }));
|
|
187
|
+
expect(result.systemRequirementsQueryTree.children[1]).toEqual(expect.objectContaining({ id: 'included-query', queryType: 'oneHop' }));
|
|
188
|
+
});
|
|
189
|
+
});
|
|
91
190
|
describe('fetchSysRsQueries', () => {
|
|
92
|
-
it('should
|
|
191
|
+
it('should scan the dedicated sysrs folder for customer queries regardless of query type', async () => {
|
|
93
192
|
const rootQueries = { id: 'root' };
|
|
94
193
|
const sysRsRoot = { id: 'sysrs-root', name: 'SYSRS' };
|
|
95
|
-
const subsystemToSystemFolder = { id: 'fwd-folder', name: 'System To Customer' };
|
|
96
194
|
const systemToSubsystemFolder = { id: 'rev-folder', name: 'System To Subsystem' };
|
|
97
195
|
const getDocTypeRootSpy = jest
|
|
98
196
|
.spyOn(ticketsDataProvider, 'getDocTypeRoot')
|
|
@@ -100,42 +198,39 @@ describe('TicketsDataProvider', () => {
|
|
|
100
198
|
const fetchSystemRequirementQueriesSpy = jest
|
|
101
199
|
.spyOn(ticketsDataProvider, 'fetchSystemRequirementQueries')
|
|
102
200
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
|
|
103
|
-
const
|
|
104
|
-
.spyOn(ticketsDataProvider, '
|
|
201
|
+
const fetchCustomerRequirementQueriesSpy = jest
|
|
202
|
+
.spyOn(ticketsDataProvider, 'fetchCustomerRequirementQueries')
|
|
105
203
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'customer-tree' } });
|
|
106
204
|
const findChildFolderByPossibleNamesSpy = jest
|
|
107
205
|
.spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames')
|
|
108
|
-
.mockResolvedValueOnce(subsystemToSystemFolder)
|
|
109
206
|
.mockResolvedValueOnce(systemToSubsystemFolder);
|
|
110
207
|
const fetchRequirementsTraceQueriesForFolderSpy = jest
|
|
111
208
|
.spyOn(ticketsDataProvider, 'fetchRequirementsTraceQueriesForFolder')
|
|
112
|
-
.mockResolvedValueOnce({ id: 'fwd-trace-tree' })
|
|
113
209
|
.mockResolvedValueOnce({ id: 'rev-trace-tree' });
|
|
114
210
|
const result = await ticketsDataProvider.fetchSysRsQueries(rootQueries);
|
|
115
211
|
expect(getDocTypeRootSpy).toHaveBeenCalledWith(rootQueries, 'sysrs');
|
|
212
|
+
// fetchSystemRequirementQueries only excludes the sub-system trace folder;
|
|
213
|
+
// the legacy System-to-Customer exclusion list is gone.
|
|
116
214
|
expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(sysRsRoot, [
|
|
117
|
-
'system to customer',
|
|
118
|
-
'system-to-customer',
|
|
119
|
-
'system customer',
|
|
120
|
-
'subsystem to system',
|
|
121
|
-
'customer to system',
|
|
122
215
|
'system to subsystem',
|
|
123
216
|
'system-to-subsystem',
|
|
124
217
|
'system subsystem',
|
|
125
218
|
]);
|
|
126
|
-
|
|
127
|
-
expect(findChildFolderByPossibleNamesSpy).
|
|
128
|
-
expect(
|
|
129
|
-
expect(fetchRequirementsTraceQueriesForFolderSpy).
|
|
130
|
-
expect(
|
|
219
|
+
// Only one folder lookup remains (sub-system trace); no System-to-Customer lookup.
|
|
220
|
+
expect(findChildFolderByPossibleNamesSpy).toHaveBeenCalledTimes(1);
|
|
221
|
+
expect(findChildFolderByPossibleNamesSpy).toHaveBeenCalledWith(sysRsRoot, expect.arrayContaining(['system to subsystem', 'system-to-subsystem']));
|
|
222
|
+
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledTimes(1);
|
|
223
|
+
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledWith(systemToSubsystemFolder);
|
|
224
|
+
// Customer picker scans the whole sysRsRoot with no query/folder exclusions.
|
|
225
|
+
expect(fetchCustomerRequirementQueriesSpy).toHaveBeenCalledWith(sysRsRoot);
|
|
131
226
|
expect(result).toEqual({
|
|
132
227
|
systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
|
|
133
228
|
customerRequirementsQueries: { systemRequirementsQueryTree: { id: 'customer-tree' } },
|
|
134
|
-
subsystemToSystemRequirementsQueries:
|
|
229
|
+
subsystemToSystemRequirementsQueries: null,
|
|
135
230
|
systemToSubsystemRequirementsQueries: { id: 'rev-trace-tree' },
|
|
136
231
|
});
|
|
137
232
|
});
|
|
138
|
-
it('should return null customer/trace trees when
|
|
233
|
+
it('should return null customer/trace trees when the dedicated sysrs folder is missing (no fallback scan)', async () => {
|
|
139
234
|
const rootQueries = { id: 'root' };
|
|
140
235
|
jest
|
|
141
236
|
.spyOn(ticketsDataProvider, 'getDocTypeRoot')
|
|
@@ -143,32 +238,24 @@ describe('TicketsDataProvider', () => {
|
|
|
143
238
|
const fetchSystemRequirementQueriesSpy = jest
|
|
144
239
|
.spyOn(ticketsDataProvider, 'fetchSystemRequirementQueries')
|
|
145
240
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'system-tree' } });
|
|
146
|
-
const
|
|
147
|
-
.spyOn(ticketsDataProvider, '
|
|
241
|
+
const fetchCustomerRequirementQueriesSpy = jest
|
|
242
|
+
.spyOn(ticketsDataProvider, 'fetchCustomerRequirementQueries')
|
|
148
243
|
.mockResolvedValue({ systemRequirementsQueryTree: { id: 'should-not-be-used' } });
|
|
149
|
-
jest
|
|
150
|
-
.spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames')
|
|
151
|
-
.mockResolvedValueOnce(null)
|
|
152
|
-
.mockResolvedValueOnce(null);
|
|
244
|
+
jest.spyOn(ticketsDataProvider, 'findChildFolderByPossibleNames').mockResolvedValueOnce(null);
|
|
153
245
|
const fetchRequirementsTraceQueriesForFolderSpy = jest
|
|
154
246
|
.spyOn(ticketsDataProvider, 'fetchRequirementsTraceQueriesForFolder')
|
|
155
|
-
.mockResolvedValueOnce(null)
|
|
156
247
|
.mockResolvedValueOnce(null);
|
|
157
248
|
const result = await ticketsDataProvider.fetchSysRsQueries(rootQueries);
|
|
158
249
|
expect(fetchSystemRequirementQueriesSpy).toHaveBeenCalledWith(rootQueries, [
|
|
159
|
-
'system to customer',
|
|
160
|
-
'system-to-customer',
|
|
161
|
-
'system customer',
|
|
162
|
-
'subsystem to system',
|
|
163
|
-
'customer to system',
|
|
164
250
|
'system to subsystem',
|
|
165
251
|
'system-to-subsystem',
|
|
166
252
|
'system subsystem',
|
|
167
253
|
]);
|
|
168
|
-
expect(fetchRequirementsTraceQueriesForFolderSpy).
|
|
169
|
-
expect(fetchRequirementsTraceQueriesForFolderSpy).
|
|
170
|
-
// No fallback scan
|
|
171
|
-
|
|
254
|
+
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledTimes(1);
|
|
255
|
+
expect(fetchRequirementsTraceQueriesForFolderSpy).toHaveBeenCalledWith(null);
|
|
256
|
+
// No fallback scan when the dedicated sysrs folder is absent — otherwise
|
|
257
|
+
// unrelated flat queries from the Shared Queries root would leak in.
|
|
258
|
+
expect(fetchCustomerRequirementQueriesSpy).not.toHaveBeenCalled();
|
|
172
259
|
expect(result).toEqual({
|
|
173
260
|
systemRequirementsQueries: { systemRequirementsQueryTree: { id: 'system-tree' } },
|
|
174
261
|
customerRequirementsQueries: null,
|
|
@@ -1087,6 +1174,35 @@ describe('TicketsDataProvider', () => {
|
|
|
1087
1174
|
// Assert
|
|
1088
1175
|
expect(result).toBeDefined();
|
|
1089
1176
|
});
|
|
1177
|
+
// Regression: WI_DEFAULT_FIELDS must include System.WorkItemType so that
|
|
1178
|
+
// downstream Requirement-type filters (e.g. the customer-coverage table)
|
|
1179
|
+
// do not drop every row because workItemType resolves to empty string.
|
|
1180
|
+
it('should request System.WorkItemType in default fields for flat queries and expose workItemType on parsed items', async () => {
|
|
1181
|
+
const mockWiqlHref = 'https://example.com/wiql';
|
|
1182
|
+
const mockQueryResult = {
|
|
1183
|
+
queryType: 'flat',
|
|
1184
|
+
workItems: [{ id: 42, url: 'https://example.com/wi/42' }],
|
|
1185
|
+
};
|
|
1186
|
+
const mockWiResponse = {
|
|
1187
|
+
fields: {
|
|
1188
|
+
'System.Title': 'Customer Requirement',
|
|
1189
|
+
'System.WorkItemType': 'Requirement',
|
|
1190
|
+
'System.Description': 'Desc',
|
|
1191
|
+
},
|
|
1192
|
+
_links: { html: { href: 'https://example.com/wi/42' } },
|
|
1193
|
+
};
|
|
1194
|
+
tfs_1.TFSServices.getItemContent
|
|
1195
|
+
.mockResolvedValueOnce(mockQueryResult)
|
|
1196
|
+
.mockResolvedValueOnce(mockWiResponse);
|
|
1197
|
+
const result = await ticketsDataProvider.GetQueryResultsFromWiql(mockWiqlHref, false, new Map());
|
|
1198
|
+
// The per-item fetch URL must request System.WorkItemType.
|
|
1199
|
+
const perItemCall = tfs_1.TFSServices.getItemContent.mock.calls.find((call) => typeof call[0] === 'string' && call[0].startsWith('https://example.com/wi/42'));
|
|
1200
|
+
expect(perItemCall).toBeDefined();
|
|
1201
|
+
expect(perItemCall[0]).toContain('System.WorkItemType');
|
|
1202
|
+
// The parsed item must expose workItemType so downstream filters work.
|
|
1203
|
+
expect(Array.isArray(result)).toBe(true);
|
|
1204
|
+
expect(result[0]).toMatchObject({ id: 42, workItemType: 'Requirement' });
|
|
1205
|
+
});
|
|
1090
1206
|
it('should return undefined when queryType is not supported', async () => {
|
|
1091
1207
|
tfs_1.TFSServices.getItemContent.mockResolvedValueOnce({ queryType: 'Unknown' });
|
|
1092
1208
|
const res = await ticketsDataProvider.GetQueryResultsFromWiql('https://example.com/wiql', false, new Map());
|