@elisra-devops/docgen-data-provider 1.136.6 → 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/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/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/TestDataProvider.ts +36 -3
- package/src/tests/helpers/helper.test.ts +69 -3
- package/src/tests/modules/testDataProvider.test.ts +105 -3
|
@@ -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', () => {
|
|
@@ -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 }],
|