@elisra-devops/docgen-data-provider 1.136.5 → 1.136.7
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/helpers/helper.d.ts +9 -7
- package/bin/helpers/helper.js +50 -57
- package/bin/helpers/helper.js.map +1 -1
- package/bin/modules/PipelinesDataProvider.d.ts +1 -1
- package/bin/modules/PipelinesDataProvider.js +11 -14
- package/bin/modules/PipelinesDataProvider.js.map +1 -1
- package/bin/modules/TestDataProvider.d.ts +1 -0
- package/bin/modules/TestDataProvider.js +45 -4
- package/bin/modules/TestDataProvider.js.map +1 -1
- package/bin/tests/helpers/helper.test.js +58 -3
- package/bin/tests/helpers/helper.test.js.map +1 -1
- package/bin/tests/modules/pipelineDataProvider.test.js +34 -10
- package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -1
- package/bin/tests/modules/testDataProvider.test.js +92 -3
- package/bin/tests/modules/testDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/helpers/helper.ts +59 -85
- package/src/modules/PipelinesDataProvider.ts +11 -14
- package/src/modules/TestDataProvider.ts +36 -3
- package/src/tests/helpers/helper.test.ts +69 -3
- package/src/tests/modules/pipelineDataProvider.test.ts +43 -10
- package/src/tests/modules/testDataProvider.test.ts +105 -3
|
@@ -115,20 +115,45 @@ export default class TestDataProvider {
|
|
|
115
115
|
return descriptions;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// DFS pre-order flatten of a suite payload that may be a nested tree (asTreeView=true),
|
|
119
|
+
// a flat list whose elements also carry populated `children[]`, or a plain flat list.
|
|
120
|
+
// The `seen` guard means a suite is only emitted once even if it appears both in the
|
|
121
|
+
// top-level array and nested under a `children` array, so tree order always wins when
|
|
122
|
+
// available, with no duplicates and no change in behavior when `children` is absent.
|
|
123
|
+
private flattenSuitesTreeOrder(suites: any[]): any[] {
|
|
124
|
+
const flat: any[] = [];
|
|
125
|
+
const seen = new Set<any>();
|
|
126
|
+
|
|
127
|
+
const visit = (suite: any) => {
|
|
128
|
+
if (!suite || seen.has(suite.id)) return;
|
|
129
|
+
seen.add(suite.id);
|
|
130
|
+
const { children, ...rest } = suite;
|
|
131
|
+
flat.push(rest);
|
|
132
|
+
if (Array.isArray(children)) {
|
|
133
|
+
children.forEach(visit);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
suites.forEach(visit);
|
|
138
|
+
return flat;
|
|
139
|
+
}
|
|
140
|
+
|
|
118
141
|
private async normalizeAndEnrichSuitesResponse(project: string, data: any): Promise<any> {
|
|
119
|
-
const
|
|
142
|
+
const rawSuites = Array.isArray(data?.testSuites)
|
|
120
143
|
? data.testSuites
|
|
121
144
|
: Array.isArray(data?.value)
|
|
122
145
|
? data.value
|
|
123
146
|
: Array.isArray(data)
|
|
124
147
|
? data
|
|
125
148
|
: [];
|
|
149
|
+
const suites = this.flattenSuitesTreeOrder(rawSuites);
|
|
126
150
|
|
|
127
|
-
const mapped = suites.map((suite: any) => ({
|
|
151
|
+
const mapped = suites.map((suite: any, index: number) => ({
|
|
128
152
|
...suite,
|
|
129
153
|
title: suite?.title || suite?.name || '',
|
|
130
154
|
parentSuiteId: suite?.parentSuiteId ?? suite?.parentSuite?.id ?? 0,
|
|
131
155
|
description: this.getSuiteDescription(suite),
|
|
156
|
+
treeOrder: index,
|
|
132
157
|
}));
|
|
133
158
|
|
|
134
159
|
const missingDescriptionIds = mapped
|
|
@@ -190,7 +215,7 @@ export default class TestDataProvider {
|
|
|
190
215
|
|
|
191
216
|
// Bearer branch — follow x-ms-continuationtoken until the server stops returning it.
|
|
192
217
|
// Doc: https://learn.microsoft.com/en-us/rest/api/azure/devops/testplan/test-suites/get-test-suites-for-plan?view=azure-devops-rest-7.0
|
|
193
|
-
const base = `${this.joinOrgProject(project)}/_apis/testplan/Plans/${planid}/suites?expand=children&api-version=7.0`;
|
|
218
|
+
const base = `${this.joinOrgProject(project)}/_apis/testplan/Plans/${planid}/suites?expand=children&asTreeView=true&api-version=7.0`;
|
|
194
219
|
const cacheKey = `${base}__all_pages`;
|
|
195
220
|
|
|
196
221
|
if (this.cache.has(cacheKey)) {
|
|
@@ -277,6 +302,14 @@ export default class TestDataProvider {
|
|
|
277
302
|
}
|
|
278
303
|
}
|
|
279
304
|
|
|
305
|
+
// Multiple hierarchies must be emitted in tree order, not in the order the
|
|
306
|
+
// caller happened to list suiteIdsFilter.
|
|
307
|
+
topLevelSuites.sort((a, b) => {
|
|
308
|
+
const orderA = suiteMap.get(a)?.treeOrder ?? suiteMap.get(parseInt(a))?.treeOrder ?? 0;
|
|
309
|
+
const orderB = suiteMap.get(b)?.treeOrder ?? suiteMap.get(parseInt(b))?.treeOrder ?? 0;
|
|
310
|
+
return orderA - orderB;
|
|
311
|
+
});
|
|
312
|
+
|
|
280
313
|
if (topLevelSuites.length === 0) {
|
|
281
314
|
// Fallback: use first suite if no clear top-level suites found
|
|
282
315
|
const selectedSuiteId = suiteIdsFilter[0].toString();
|
|
@@ -4,9 +4,6 @@ import { Query, Workitem } from '../../models/tfs-data';
|
|
|
4
4
|
describe('Helper', () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
// Reset static state before each test
|
|
7
|
-
Helper.suitList = [];
|
|
8
|
-
Helper.level = 1;
|
|
9
|
-
Helper.first = true;
|
|
10
7
|
Helper.levelList = [];
|
|
11
8
|
});
|
|
12
9
|
|
|
@@ -241,6 +238,75 @@ describe('Helper', () => {
|
|
|
241
238
|
// Assert
|
|
242
239
|
expect(result.length).toBe(5);
|
|
243
240
|
});
|
|
241
|
+
|
|
242
|
+
it('should emit siblings in array (tree) order even when their ids are not ascending', () => {
|
|
243
|
+
// Arrange - array order mirrors a manually reordered ADO tree: D, B, A, C
|
|
244
|
+
const suits = [
|
|
245
|
+
{ id: '1', title: 'Root', parentSuiteId: 0 },
|
|
246
|
+
{ id: '40', title: 'Suite D', parentSuiteId: '1' },
|
|
247
|
+
{ id: '20', title: 'Suite B', parentSuiteId: '1' },
|
|
248
|
+
{ id: '10', title: 'Suite A', parentSuiteId: '1' },
|
|
249
|
+
{ id: '30', title: 'Suite C', parentSuiteId: '1' },
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
// Act
|
|
253
|
+
const result = Helper.findSuitesRecursive(mockPlanId, mockUrl, mockProject, suits, '1', true);
|
|
254
|
+
|
|
255
|
+
// Assert
|
|
256
|
+
expect(result.map((s) => s.name)).toEqual(['Suite D', 'Suite B', 'Suite A', 'Suite C']);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should emit the selected nested suite first even when it appears last in the source array', () => {
|
|
260
|
+
// Arrange
|
|
261
|
+
const suits = [
|
|
262
|
+
{ id: '3', title: 'Grandchild Suite', parentSuiteId: '2' },
|
|
263
|
+
{ id: '1', title: 'Root Suite', parentSuiteId: 0 },
|
|
264
|
+
{ id: '2', title: 'Child Suite 1', parentSuiteId: '1' },
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
// Act
|
|
268
|
+
const result = Helper.findSuitesRecursive(mockPlanId, mockUrl, mockProject, suits, '2', true);
|
|
269
|
+
|
|
270
|
+
// Assert
|
|
271
|
+
expect(result.map((s) => s.name)).toEqual(['Child Suite 1', 'Grandchild Suite']);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should compute levels correctly regardless of source array position', () => {
|
|
275
|
+
// Arrange - grandchild appears before its parent in the array
|
|
276
|
+
const suits = [
|
|
277
|
+
{ id: '3', title: 'Level 2', parentSuiteId: '2' },
|
|
278
|
+
{ id: '1', title: 'Root', parentSuiteId: 0 },
|
|
279
|
+
{ id: '2', title: 'Level 1', parentSuiteId: '1' },
|
|
280
|
+
];
|
|
281
|
+
|
|
282
|
+
// Act
|
|
283
|
+
const result = Helper.findSuitesRecursive(mockPlanId, mockUrl, mockProject, suits, '1', true);
|
|
284
|
+
|
|
285
|
+
// Assert
|
|
286
|
+
expect(result.find((s) => s.name === 'Level 1')?.level).toBe(1);
|
|
287
|
+
expect(result.find((s) => s.name === 'Level 2')?.level).toBe(2);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should not leak state between sequential calls', () => {
|
|
291
|
+
// Arrange
|
|
292
|
+
const suitsA = [
|
|
293
|
+
{ id: '1', title: 'Root A', parentSuiteId: 0 },
|
|
294
|
+
{ id: '2', title: 'Child A', parentSuiteId: '1' },
|
|
295
|
+
];
|
|
296
|
+
const suitsB = [
|
|
297
|
+
{ id: '10', title: 'Root B', parentSuiteId: 0 },
|
|
298
|
+
{ id: '20', title: 'Child B1', parentSuiteId: '10' },
|
|
299
|
+
{ id: '30', title: 'Child B2', parentSuiteId: '10' },
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
// Act
|
|
303
|
+
const resultA = Helper.findSuitesRecursive(mockPlanId, mockUrl, mockProject, suitsA, '1', true);
|
|
304
|
+
const resultB = Helper.findSuitesRecursive(mockPlanId, mockUrl, mockProject, suitsB, '10', true);
|
|
305
|
+
|
|
306
|
+
// Assert - resultB is unaffected by resultA's traversal
|
|
307
|
+
expect(resultA.map((s) => s.name)).toEqual(['Child A']);
|
|
308
|
+
expect(resultB.map((s) => s.name)).toEqual(['Child B1', 'Child B2']);
|
|
309
|
+
});
|
|
244
310
|
});
|
|
245
311
|
|
|
246
312
|
describe('LevelBuilder', () => {
|
|
@@ -951,23 +951,28 @@ describe('PipelinesDataProvider', () => {
|
|
|
951
951
|
});
|
|
952
952
|
|
|
953
953
|
describe('GetRecentReleaseArtifactInfo', () => {
|
|
954
|
-
it('should return empty array when no
|
|
954
|
+
it('should return empty array when release response has no artifacts', async () => {
|
|
955
955
|
// Arrange
|
|
956
956
|
const projectName = 'project1';
|
|
957
|
-
const
|
|
958
|
-
|
|
957
|
+
const releaseId = 123;
|
|
958
|
+
const mockReleaseResponse = { artifacts: [] };
|
|
959
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
|
|
959
960
|
|
|
960
961
|
// Act
|
|
961
|
-
const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName);
|
|
962
|
+
const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
|
|
962
963
|
|
|
963
964
|
// Assert
|
|
965
|
+
expect(TFSServices.getItemContent).toHaveBeenCalledWith(
|
|
966
|
+
expect.stringContaining(`releases/${releaseId}`),
|
|
967
|
+
mockToken
|
|
968
|
+
);
|
|
964
969
|
expect(result).toEqual([]);
|
|
965
970
|
});
|
|
966
971
|
|
|
967
|
-
it('should return artifact info
|
|
972
|
+
it('should return artifact info for the specified release', async () => {
|
|
968
973
|
// Arrange
|
|
969
974
|
const projectName = 'project1';
|
|
970
|
-
const
|
|
975
|
+
const releaseId = 123;
|
|
971
976
|
const mockReleaseResponse = {
|
|
972
977
|
artifacts: [
|
|
973
978
|
{
|
|
@@ -978,16 +983,44 @@ describe('PipelinesDataProvider', () => {
|
|
|
978
983
|
},
|
|
979
984
|
],
|
|
980
985
|
};
|
|
981
|
-
(TFSServices.getItemContent as jest.Mock)
|
|
982
|
-
.mockResolvedValueOnce(mockReleasesResponse)
|
|
983
|
-
.mockResolvedValueOnce(mockReleaseResponse);
|
|
986
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
|
|
984
987
|
|
|
985
988
|
// Act
|
|
986
|
-
const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName);
|
|
989
|
+
const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
|
|
987
990
|
|
|
988
991
|
// Assert
|
|
989
992
|
expect(result).toEqual([{ artifactName: 'artifact1', artifactVersion: '1.0.0' }]);
|
|
990
993
|
});
|
|
994
|
+
|
|
995
|
+
it('should return empty array without calling the API when releaseId is invalid', async () => {
|
|
996
|
+
// Arrange
|
|
997
|
+
const projectName = 'project1';
|
|
998
|
+
|
|
999
|
+
// Act
|
|
1000
|
+
const resultForZero = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, 0);
|
|
1001
|
+
const resultForNaN = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, NaN);
|
|
1002
|
+
const resultForNegative = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, -5);
|
|
1003
|
+
|
|
1004
|
+
// Assert
|
|
1005
|
+
expect(resultForZero).toEqual([]);
|
|
1006
|
+
expect(resultForNaN).toEqual([]);
|
|
1007
|
+
expect(resultForNegative).toEqual([]);
|
|
1008
|
+
expect(TFSServices.getItemContent).not.toHaveBeenCalled();
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
it('should return empty array when release response has no artifacts field at all', async () => {
|
|
1012
|
+
// Arrange
|
|
1013
|
+
const projectName = 'project1';
|
|
1014
|
+
const releaseId = 123;
|
|
1015
|
+
const mockReleaseResponse = { id: releaseId, name: 'Release-1' };
|
|
1016
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockReleaseResponse);
|
|
1017
|
+
|
|
1018
|
+
// Act
|
|
1019
|
+
const result = await pipelinesDataProvider.GetRecentReleaseArtifactInfo(projectName, releaseId);
|
|
1020
|
+
|
|
1021
|
+
// Assert
|
|
1022
|
+
expect(result).toEqual([]);
|
|
1023
|
+
});
|
|
991
1024
|
});
|
|
992
1025
|
|
|
993
1026
|
describe('getPipelineResourcePipelinesFromObject', () => {
|
|
@@ -326,7 +326,7 @@ describe('TestDataProvider', () => {
|
|
|
326
326
|
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
327
327
|
|
|
328
328
|
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
329
|
-
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
329
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&asTreeView=true&api-version=7.0`,
|
|
330
330
|
mockBearerToken,
|
|
331
331
|
'get',
|
|
332
332
|
{},
|
|
@@ -368,7 +368,7 @@ describe('TestDataProvider', () => {
|
|
|
368
368
|
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
369
369
|
|
|
370
370
|
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
371
|
-
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
371
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&asTreeView=true&api-version=7.0`,
|
|
372
372
|
mockBearerToken,
|
|
373
373
|
'get',
|
|
374
374
|
{},
|
|
@@ -407,6 +407,7 @@ describe('TestDataProvider', () => {
|
|
|
407
407
|
const calledUrl = (TFSServices.getItemContentWithHeaders as jest.Mock).mock.calls[0][0] as string;
|
|
408
408
|
expect(calledUrl).toContain(`/_apis/testplan/Plans/${mockPlanId}/suites`);
|
|
409
409
|
expect(calledUrl).toContain('expand=children');
|
|
410
|
+
expect(calledUrl).toContain('asTreeView=true');
|
|
410
411
|
expect(calledUrl).toContain('api-version=7.0');
|
|
411
412
|
expect(calledUrl).not.toContain('includeChildren');
|
|
412
413
|
});
|
|
@@ -480,6 +481,82 @@ describe('TestDataProvider', () => {
|
|
|
480
481
|
expect(callCount).toBeGreaterThan(0);
|
|
481
482
|
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('reached MAX_PAGES'));
|
|
482
483
|
});
|
|
484
|
+
|
|
485
|
+
it('should flatten a nested asTreeView=true payload into DFS pre-order', async () => {
|
|
486
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
487
|
+
const treePayload = {
|
|
488
|
+
value: [
|
|
489
|
+
{
|
|
490
|
+
id: 1,
|
|
491
|
+
name: 'Root',
|
|
492
|
+
children: [
|
|
493
|
+
{ id: 20, name: 'Suite B', parentSuite: { id: 1 } },
|
|
494
|
+
{
|
|
495
|
+
id: 10,
|
|
496
|
+
name: 'Suite A',
|
|
497
|
+
parentSuite: { id: 1 },
|
|
498
|
+
children: [{ id: 11, name: 'Suite A.1', parentSuite: { id: 10 } }],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
count: 1,
|
|
504
|
+
};
|
|
505
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({
|
|
506
|
+
data: treePayload,
|
|
507
|
+
headers: {},
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
511
|
+
|
|
512
|
+
expect(result.testSuites.map((s: any) => s.id)).toEqual([1, 20, 10, 11]);
|
|
513
|
+
expect(result.testSuites.map((s: any) => s.treeOrder)).toEqual([0, 1, 2, 3]);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should derive tree order from a flat list whose elements carry populated children[], without duplicating suites', async () => {
|
|
517
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
518
|
+
// Server ignored/does-not-support asTreeView and returned a flat, id-ordered list,
|
|
519
|
+
// but each suite still carries its own children[] in display order.
|
|
520
|
+
const flatWithChildren = {
|
|
521
|
+
value: [
|
|
522
|
+
{ id: 10, name: 'Suite A', parentSuite: { id: 1 }, children: [{ id: 11, name: 'Suite A.1' }] },
|
|
523
|
+
{ id: 11, name: 'Suite A.1', parentSuite: { id: 10 } },
|
|
524
|
+
{ id: 1, name: 'Root', children: [{ id: 10, name: 'Suite A' }, { id: 20, name: 'Suite B' }] },
|
|
525
|
+
{ id: 20, name: 'Suite B', parentSuite: { id: 1 } },
|
|
526
|
+
],
|
|
527
|
+
count: 4,
|
|
528
|
+
};
|
|
529
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({
|
|
530
|
+
data: flatWithChildren,
|
|
531
|
+
headers: {},
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
535
|
+
|
|
536
|
+
expect(result.testSuites.map((s: any) => s.id)).toEqual([10, 11, 1, 20]);
|
|
537
|
+
expect(result.testSuites).toHaveLength(4);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
it('should leave order unchanged for a flat list with no children field (regression guard)', async () => {
|
|
541
|
+
const bearerProvider = new TestDataProvider(mockOrgUrl, mockBearerToken);
|
|
542
|
+
const flatPayload = {
|
|
543
|
+
value: [
|
|
544
|
+
{ id: 40, name: 'Suite D' },
|
|
545
|
+
{ id: 20, name: 'Suite B' },
|
|
546
|
+
{ id: 10, name: 'Suite A' },
|
|
547
|
+
],
|
|
548
|
+
count: 3,
|
|
549
|
+
};
|
|
550
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({
|
|
551
|
+
data: flatPayload,
|
|
552
|
+
headers: {},
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const result = await bearerProvider.GetTestSuitesForPlan(mockProject, mockPlanId);
|
|
556
|
+
|
|
557
|
+
expect(result.testSuites.map((s: any) => s.id)).toEqual([40, 20, 10]);
|
|
558
|
+
expect(result.testSuites.map((s: any) => s.treeOrder)).toEqual([0, 1, 2]);
|
|
559
|
+
});
|
|
483
560
|
});
|
|
484
561
|
|
|
485
562
|
describe('PAT-token branch — regression guard', () => {
|
|
@@ -548,7 +625,7 @@ describe('TestDataProvider', () => {
|
|
|
548
625
|
const result = await bearerProvider.GetTestSuiteById(mockProject, mockPlanId, mockSuiteId, true);
|
|
549
626
|
|
|
550
627
|
expect(TFSServices.getItemContentWithHeaders).toHaveBeenCalledWith(
|
|
551
|
-
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&api-version=7.0`,
|
|
628
|
+
`${mockOrgUrl.replace(/\/+$/, '')}/${mockProject}/_apis/testplan/Plans/${mockPlanId}/suites?expand=children&asTreeView=true&api-version=7.0`,
|
|
552
629
|
mockBearerToken,
|
|
553
630
|
'get',
|
|
554
631
|
{},
|
|
@@ -672,6 +749,31 @@ describe('TestDataProvider', () => {
|
|
|
672
749
|
expect(res).toEqual([{ id: '10' }, { id: '11' }]);
|
|
673
750
|
});
|
|
674
751
|
|
|
752
|
+
it('should emit multiple hierarchies in tree order, not in suiteIdsFilter order', async () => {
|
|
753
|
+
jest.spyOn(testDataProvider, 'GetTestSuitesForPlan').mockResolvedValueOnce({
|
|
754
|
+
testSuites: [
|
|
755
|
+
{ id: 1, parentSuiteId: 0, treeOrder: 0 },
|
|
756
|
+
{ id: 2, parentSuiteId: 0, treeOrder: 1 },
|
|
757
|
+
{ id: 10, parentSuiteId: 1, treeOrder: 2 },
|
|
758
|
+
{ id: 11, parentSuiteId: 2, treeOrder: 3 },
|
|
759
|
+
],
|
|
760
|
+
} as any);
|
|
761
|
+
|
|
762
|
+
// Caller lists the second hierarchy's suite (11) before the first (10),
|
|
763
|
+
// but suite 10 (treeOrder 2) precedes suite 11 (treeOrder 3) in the tree.
|
|
764
|
+
const suiteIdsFilter = [11, 10];
|
|
765
|
+
const getByIdSpy = jest
|
|
766
|
+
.spyOn(testDataProvider, 'GetTestSuiteById')
|
|
767
|
+
.mockResolvedValueOnce([{ id: '10' }])
|
|
768
|
+
.mockResolvedValueOnce([{ id: '11' }]);
|
|
769
|
+
|
|
770
|
+
const res = await testDataProvider.GetTestSuitesByPlan(mockProject, mockPlanId, true, suiteIdsFilter);
|
|
771
|
+
|
|
772
|
+
expect(getByIdSpy).toHaveBeenNthCalledWith(1, mockProject, mockPlanId, '10', true, suiteIdsFilter);
|
|
773
|
+
expect(getByIdSpy).toHaveBeenNthCalledWith(2, mockProject, mockPlanId, '11', true, suiteIdsFilter);
|
|
774
|
+
expect(res).toEqual([{ id: '10' }, { id: '11' }]);
|
|
775
|
+
});
|
|
776
|
+
|
|
675
777
|
it('should fallback to first suite when no top-level suites can be determined', async () => {
|
|
676
778
|
jest.spyOn(testDataProvider, 'GetTestSuitesForPlan').mockResolvedValueOnce({
|
|
677
779
|
testSuites: [{ id: 1, parentSuiteId: 0 }],
|