@elisra-devops/docgen-data-provider 1.121.0 → 1.123.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/GitDataProvider.js +25 -0
- package/bin/modules/GitDataProvider.js.map +1 -1
- package/bin/modules/PipelinesDataProvider.d.ts +40 -3
- package/bin/modules/PipelinesDataProvider.js +160 -6
- package/bin/modules/PipelinesDataProvider.js.map +1 -1
- package/bin/tests/modules/gitDataProvider.test.js +122 -0
- package/bin/tests/modules/gitDataProvider.test.js.map +1 -1
- package/bin/tests/modules/pipelineDataProvider.test.js +98 -5
- package/bin/tests/modules/pipelineDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/GitDataProvider.ts +27 -0
- package/src/modules/PipelinesDataProvider.ts +207 -5
- package/src/tests/modules/gitDataProvider.test.ts +145 -0
- package/src/tests/modules/pipelineDataProvider.test.ts +152 -10
|
@@ -1558,6 +1558,151 @@ describe('GitDataProvider - GetCommitBatch', () => {
|
|
|
1558
1558
|
expect(result).toHaveLength(0);
|
|
1559
1559
|
});
|
|
1560
1560
|
|
|
1561
|
+
it('should enrich commit workItems with WIs linked from WI development section', async () => {
|
|
1562
|
+
// commitsbatch returns only the commit-side linked WI (e.g. from commit message "#277476").
|
|
1563
|
+
// WIs linked from the WI side (ADO development section) must be merged via the per-commit
|
|
1564
|
+
// /workItems endpoint.
|
|
1565
|
+
const gitUrl = 'https://dev.azure.com/org/project/_apis/git/repositories/repo-id';
|
|
1566
|
+
const itemVersion = { version: 'main', versionType: 'branch' };
|
|
1567
|
+
const compareVersion = { version: 'v1.0.0', versionType: 'tag' };
|
|
1568
|
+
|
|
1569
|
+
const mockCommitsResponse = {
|
|
1570
|
+
data: {
|
|
1571
|
+
count: 1,
|
|
1572
|
+
value: [
|
|
1573
|
+
{
|
|
1574
|
+
commitId: 'd465de7abc',
|
|
1575
|
+
committer: { name: 'Dev', date: '2024-01-01T00:00:00Z' },
|
|
1576
|
+
comment: 'fix: link WI-277476',
|
|
1577
|
+
workItems: [{ id: 277476, url: 'https://tfs/wi/277476' }],
|
|
1578
|
+
},
|
|
1579
|
+
],
|
|
1580
|
+
},
|
|
1581
|
+
};
|
|
1582
|
+
const mockEmptyPage = { data: { count: 0, value: [] } };
|
|
1583
|
+
// Per-commit endpoint returns the 2 WI-side linked items in addition to the commit-side one.
|
|
1584
|
+
const mockPerCommitWi = {
|
|
1585
|
+
value: [
|
|
1586
|
+
{ id: 277476, url: 'https://tfs/wi/277476' }, // duplicate — must be deduplicated
|
|
1587
|
+
{ id: 285957, url: 'https://tfs/wi/285957' },
|
|
1588
|
+
{ id: 284959, url: 'https://tfs/wi/284959' },
|
|
1589
|
+
],
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
(TFSServices as any).postRequest
|
|
1593
|
+
.mockResolvedValueOnce(mockCommitsResponse)
|
|
1594
|
+
.mockResolvedValueOnce(mockEmptyPage);
|
|
1595
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockPerCommitWi);
|
|
1596
|
+
|
|
1597
|
+
const result = await gitDataProvider.GetCommitBatch(gitUrl, itemVersion, compareVersion);
|
|
1598
|
+
|
|
1599
|
+
expect(result).toHaveLength(1);
|
|
1600
|
+
const workItems = result[0].commit.workItems;
|
|
1601
|
+
const ids = workItems.map((wi: any) => wi.id).sort((a: number, b: number) => a - b);
|
|
1602
|
+
expect(ids).toEqual([277476, 284959, 285957]);
|
|
1603
|
+
// Per-commit endpoint was called with correct URL
|
|
1604
|
+
expect(TFSServices.getItemContent).toHaveBeenCalledWith(
|
|
1605
|
+
`${gitUrl}/commits/d465de7abc/workItems?api-version=5.1`,
|
|
1606
|
+
mockToken
|
|
1607
|
+
);
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
it('should handle per-commit WI enrichment failure gracefully and log a warning', async () => {
|
|
1611
|
+
const gitUrl = 'https://dev.azure.com/org/project/_apis/git/repositories/repo-id';
|
|
1612
|
+
const itemVersion = { version: 'main', versionType: 'branch' };
|
|
1613
|
+
const compareVersion = { version: 'v1.0.0', versionType: 'tag' };
|
|
1614
|
+
|
|
1615
|
+
const mockCommitsResponse = {
|
|
1616
|
+
data: {
|
|
1617
|
+
count: 1,
|
|
1618
|
+
value: [
|
|
1619
|
+
{
|
|
1620
|
+
commitId: 'aabbcc',
|
|
1621
|
+
committer: { name: 'Dev', date: '2024-01-01T00:00:00Z' },
|
|
1622
|
+
comment: 'fix',
|
|
1623
|
+
workItems: [{ id: 100, url: 'https://tfs/wi/100' }],
|
|
1624
|
+
},
|
|
1625
|
+
],
|
|
1626
|
+
},
|
|
1627
|
+
};
|
|
1628
|
+
const mockEmptyPage = { data: { count: 0, value: [] } };
|
|
1629
|
+
|
|
1630
|
+
(TFSServices as any).postRequest
|
|
1631
|
+
.mockResolvedValueOnce(mockCommitsResponse)
|
|
1632
|
+
.mockResolvedValueOnce(mockEmptyPage);
|
|
1633
|
+
(TFSServices.getItemContent as jest.Mock).mockRejectedValueOnce(new Error('network error'));
|
|
1634
|
+
|
|
1635
|
+
const result = await gitDataProvider.GetCommitBatch(gitUrl, itemVersion, compareVersion);
|
|
1636
|
+
|
|
1637
|
+
expect(result).toHaveLength(1);
|
|
1638
|
+
expect(result[0].commit.workItems).toEqual([{ id: 100, url: 'https://tfs/wi/100' }]);
|
|
1639
|
+
expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('per-commit WI enrichment failed'));
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
it('should initialize workItems from absent field when per-commit endpoint returns WIs', async () => {
|
|
1643
|
+
// Commit arrives from commitsbatch with workItems absent (not [] but undefined).
|
|
1644
|
+
const gitUrl = 'https://dev.azure.com/org/project/_apis/git/repositories/repo-id';
|
|
1645
|
+
const itemVersion = { version: 'main', versionType: 'branch' };
|
|
1646
|
+
const compareVersion = { version: 'v1.0.0', versionType: 'tag' };
|
|
1647
|
+
|
|
1648
|
+
const mockCommitsResponse = {
|
|
1649
|
+
data: {
|
|
1650
|
+
count: 1,
|
|
1651
|
+
value: [
|
|
1652
|
+
{
|
|
1653
|
+
commitId: 'ccddee',
|
|
1654
|
+
committer: { name: 'Dev', date: '2024-01-01T00:00:00Z' },
|
|
1655
|
+
comment: 'no commit-side WI',
|
|
1656
|
+
// workItems intentionally absent
|
|
1657
|
+
},
|
|
1658
|
+
],
|
|
1659
|
+
},
|
|
1660
|
+
};
|
|
1661
|
+
const mockEmptyPage = { data: { count: 0, value: [] } };
|
|
1662
|
+
const mockPerCommitWi = { value: [{ id: 999, url: 'https://tfs/wi/999' }] };
|
|
1663
|
+
|
|
1664
|
+
(TFSServices as any).postRequest
|
|
1665
|
+
.mockResolvedValueOnce(mockCommitsResponse)
|
|
1666
|
+
.mockResolvedValueOnce(mockEmptyPage);
|
|
1667
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce(mockPerCommitWi);
|
|
1668
|
+
|
|
1669
|
+
const result = await gitDataProvider.GetCommitBatch(gitUrl, itemVersion, compareVersion);
|
|
1670
|
+
|
|
1671
|
+
expect(result).toHaveLength(1);
|
|
1672
|
+
expect(result[0].commit.workItems.map((wi: any) => wi.id)).toEqual([999]);
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
it('should leave workItems unchanged when per-commit endpoint returns empty list', async () => {
|
|
1676
|
+
const gitUrl = 'https://dev.azure.com/org/project/_apis/git/repositories/repo-id';
|
|
1677
|
+
const itemVersion = { version: 'main', versionType: 'branch' };
|
|
1678
|
+
const compareVersion = { version: 'v1.0.0', versionType: 'tag' };
|
|
1679
|
+
|
|
1680
|
+
const mockCommitsResponse = {
|
|
1681
|
+
data: {
|
|
1682
|
+
count: 1,
|
|
1683
|
+
value: [
|
|
1684
|
+
{
|
|
1685
|
+
commitId: 'ff0011',
|
|
1686
|
+
committer: { name: 'Dev', date: '2024-01-01T00:00:00Z' },
|
|
1687
|
+
comment: 'fix',
|
|
1688
|
+
workItems: [{ id: 42, url: 'https://tfs/wi/42' }],
|
|
1689
|
+
},
|
|
1690
|
+
],
|
|
1691
|
+
},
|
|
1692
|
+
};
|
|
1693
|
+
const mockEmptyPage = { data: { count: 0, value: [] } };
|
|
1694
|
+
|
|
1695
|
+
(TFSServices as any).postRequest
|
|
1696
|
+
.mockResolvedValueOnce(mockCommitsResponse)
|
|
1697
|
+
.mockResolvedValueOnce(mockEmptyPage);
|
|
1698
|
+
(TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce({ value: [] });
|
|
1699
|
+
|
|
1700
|
+
const result = await gitDataProvider.GetCommitBatch(gitUrl, itemVersion, compareVersion);
|
|
1701
|
+
|
|
1702
|
+
expect(result).toHaveLength(1);
|
|
1703
|
+
expect(result[0].commit.workItems.map((wi: any) => wi.id)).toEqual([42]);
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1561
1706
|
it('should include specificItemPath when provided', async () => {
|
|
1562
1707
|
// Arrange
|
|
1563
1708
|
const gitUrl = 'https://dev.azure.com/org/project/_apis/git/repositories/repo-id';
|
|
@@ -247,7 +247,7 @@ describe('PipelinesDataProvider', () => {
|
|
|
247
247
|
|
|
248
248
|
// Assert
|
|
249
249
|
expect(TFSServices.getItemContent).toHaveBeenCalledWith(
|
|
250
|
-
`${mockOrgUrl}${projectName}/_apis/pipelines/${pipelineId}/runs/${runId}`,
|
|
250
|
+
`${mockOrgUrl}${projectName}/_apis/pipelines/${pipelineId}/runs/${runId}?$expand=resources`,
|
|
251
251
|
mockToken
|
|
252
252
|
);
|
|
253
253
|
expect(result).toEqual(mockResponse);
|
|
@@ -1319,12 +1319,14 @@ describe('PipelinesDataProvider', () => {
|
|
|
1319
1319
|
data: { value: [] },
|
|
1320
1320
|
headers: {},
|
|
1321
1321
|
});
|
|
1322
|
-
(TFSServices.getItemContent as jest.Mock)
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1322
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1323
|
+
.mockResolvedValueOnce({}) // getRepoDefaultBranch (no defaultBranch → ancestry short-circuits)
|
|
1324
|
+
.mockResolvedValueOnce({
|
|
1325
|
+
value: [
|
|
1326
|
+
{ id: 100, result: 'succeeded' },
|
|
1327
|
+
{ id: 99, result: 'succeeded' },
|
|
1328
|
+
],
|
|
1329
|
+
});
|
|
1328
1330
|
|
|
1329
1331
|
jest.spyOn(pipelinesDataProvider as any, 'getPipelineRunDetails').mockResolvedValueOnce({
|
|
1330
1332
|
resources: {
|
|
@@ -1432,6 +1434,7 @@ describe('PipelinesDataProvider', () => {
|
|
|
1432
1434
|
headers: {},
|
|
1433
1435
|
});
|
|
1434
1436
|
(TFSServices.getItemContent as jest.Mock)
|
|
1437
|
+
.mockResolvedValueOnce({}) // getRepoDefaultBranch (no defaultBranch → ancestry short-circuits)
|
|
1435
1438
|
.mockResolvedValueOnce(mockRunHistory)
|
|
1436
1439
|
.mockResolvedValueOnce(mockPipelineDetails);
|
|
1437
1440
|
|
|
@@ -1500,7 +1503,9 @@ describe('PipelinesDataProvider', () => {
|
|
|
1500
1503
|
data: { value: [] },
|
|
1501
1504
|
headers: {},
|
|
1502
1505
|
});
|
|
1503
|
-
(TFSServices.getItemContent as jest.Mock)
|
|
1506
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1507
|
+
.mockResolvedValueOnce({}) // getRepoDefaultBranch — no defaultBranch, ancestry exits
|
|
1508
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1504
1509
|
|
|
1505
1510
|
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1506
1511
|
'project1',
|
|
@@ -1521,7 +1526,9 @@ describe('PipelinesDataProvider', () => {
|
|
|
1521
1526
|
data: { value: [buildCandidate(100, 'refs/heads/main'), buildCandidate(101, 'refs/heads/main')] },
|
|
1522
1527
|
headers: {},
|
|
1523
1528
|
});
|
|
1524
|
-
(TFSServices.getItemContent as jest.Mock)
|
|
1529
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1530
|
+
.mockResolvedValueOnce({}) // getRepoDefaultBranch — no defaultBranch, ancestry exits
|
|
1531
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1525
1532
|
|
|
1526
1533
|
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1527
1534
|
'project1',
|
|
@@ -1557,7 +1564,9 @@ describe('PipelinesDataProvider', () => {
|
|
|
1557
1564
|
data: { value: [] },
|
|
1558
1565
|
headers: {},
|
|
1559
1566
|
});
|
|
1560
|
-
(TFSServices.getItemContent as jest.Mock)
|
|
1567
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1568
|
+
.mockResolvedValueOnce({}) // getRepoDefaultBranch — no defaultBranch, ancestry exits
|
|
1569
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1561
1570
|
|
|
1562
1571
|
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1563
1572
|
'project1',
|
|
@@ -1595,6 +1604,139 @@ describe('PipelinesDataProvider', () => {
|
|
|
1595
1604
|
pipelinesDataProvider.findPreviousPipeline('project1', '123', 100, targetPipelineRun)
|
|
1596
1605
|
).rejects.toThrow('Pipeline discovery exceeded 50 pages');
|
|
1597
1606
|
});
|
|
1607
|
+
|
|
1608
|
+
describe('ancestry-walk fallback', () => {
|
|
1609
|
+
const featureTargetPipelineRun = {
|
|
1610
|
+
resources: {
|
|
1611
|
+
repositories: {
|
|
1612
|
+
self: {
|
|
1613
|
+
repository: { id: 'repo1' },
|
|
1614
|
+
version: 'C3-B',
|
|
1615
|
+
refName: 'refs/heads/feature/x',
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
},
|
|
1619
|
+
} as unknown as PipelineRun;
|
|
1620
|
+
|
|
1621
|
+
beforeEach(() => {
|
|
1622
|
+
jest.clearAllMocks();
|
|
1623
|
+
});
|
|
1624
|
+
|
|
1625
|
+
it('should return ancestry-resolved build when same-branch has no match', async () => {
|
|
1626
|
+
// same-branch search: no candidates (empty)
|
|
1627
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
1628
|
+
.mockResolvedValueOnce({ data: { value: [] }, headers: {} }) // same-branch
|
|
1629
|
+
.mockResolvedValueOnce({ // ancestry default-branch page
|
|
1630
|
+
data: {
|
|
1631
|
+
value: [
|
|
1632
|
+
buildCandidate(90, 'refs/heads/main'), // id=90, sourceVersion='sha-90'
|
|
1633
|
+
buildCandidate(70, 'refs/heads/main'), // id=70, sourceVersion='sha-70'
|
|
1634
|
+
],
|
|
1635
|
+
},
|
|
1636
|
+
headers: {},
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
// getRepoDefaultBranch
|
|
1640
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1641
|
+
.mockResolvedValueOnce({ defaultBranch: 'refs/heads/main' }) // getRepoDefaultBranch
|
|
1642
|
+
.mockResolvedValueOnce({ commonCommit: 'C3' }) // getMergeBase
|
|
1643
|
+
.mockResolvedValueOnce({ commonCommit: 'unrelated-sha' }) // isCommitAncestorOf sha-90 vs C3 => false
|
|
1644
|
+
.mockResolvedValueOnce({ commonCommit: 'sha-70' }); // isCommitAncestorOf sha-70 vs C3 => sha-70 == sha-70 -> true
|
|
1645
|
+
|
|
1646
|
+
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1647
|
+
'project1',
|
|
1648
|
+
'123',
|
|
1649
|
+
100,
|
|
1650
|
+
featureTargetPipelineRun
|
|
1651
|
+
);
|
|
1652
|
+
|
|
1653
|
+
expect(result).toBe(70);
|
|
1654
|
+
});
|
|
1655
|
+
|
|
1656
|
+
it('should return undefined when ancestry walk finds no ancestor match', async () => {
|
|
1657
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
1658
|
+
.mockResolvedValueOnce({ data: { value: [] }, headers: {} }) // same-branch
|
|
1659
|
+
.mockResolvedValueOnce({ // ancestry default-branch page
|
|
1660
|
+
data: { value: [buildCandidate(90, 'refs/heads/main')] },
|
|
1661
|
+
headers: {},
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1665
|
+
.mockResolvedValueOnce({ defaultBranch: 'refs/heads/main' }) // getRepoDefaultBranch
|
|
1666
|
+
.mockResolvedValueOnce({ commonCommit: 'C3' }) // getMergeBase
|
|
1667
|
+
.mockResolvedValueOnce({ commonCommit: 'unrelated-sha' }) // isCommitAncestorOf => false
|
|
1668
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1669
|
+
|
|
1670
|
+
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1671
|
+
'project1',
|
|
1672
|
+
'123',
|
|
1673
|
+
100,
|
|
1674
|
+
featureTargetPipelineRun
|
|
1675
|
+
);
|
|
1676
|
+
|
|
1677
|
+
expect(result).toBeUndefined();
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1680
|
+
it('should return undefined (not throw) when getRepoDefaultBranch fails', async () => {
|
|
1681
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
1682
|
+
.mockResolvedValueOnce({ data: { value: [] }, headers: {} }); // same-branch
|
|
1683
|
+
|
|
1684
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1685
|
+
.mockRejectedValueOnce(new Error('repo not found')) // getRepoDefaultBranch throws
|
|
1686
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1687
|
+
|
|
1688
|
+
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1689
|
+
'project1',
|
|
1690
|
+
'123',
|
|
1691
|
+
100,
|
|
1692
|
+
featureTargetPipelineRun
|
|
1693
|
+
);
|
|
1694
|
+
|
|
1695
|
+
expect(result).toBeUndefined();
|
|
1696
|
+
});
|
|
1697
|
+
|
|
1698
|
+
it('should not invoke ancestry walk when same-branch search finds a match', async () => {
|
|
1699
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock).mockResolvedValueOnce({
|
|
1700
|
+
data: { value: [buildCandidate(90, 'refs/heads/feature/x')] },
|
|
1701
|
+
headers: {},
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
const getItemContentSpy = jest.spyOn(TFSServices, 'getItemContent');
|
|
1705
|
+
|
|
1706
|
+
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1707
|
+
'project1',
|
|
1708
|
+
'123',
|
|
1709
|
+
100,
|
|
1710
|
+
featureTargetPipelineRun
|
|
1711
|
+
);
|
|
1712
|
+
|
|
1713
|
+
expect(result).toBe(90);
|
|
1714
|
+
// getRepoDefaultBranch / getMergeBase are called via getItemContent — none should be called
|
|
1715
|
+
const ancestryCalls = getItemContentSpy.mock.calls.filter(
|
|
1716
|
+
([url]) => typeof url === 'string' && (url.includes('git/repositories') && !url.includes('_apis/build'))
|
|
1717
|
+
);
|
|
1718
|
+
expect(ancestryCalls).toHaveLength(0);
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
it('should return undefined (not throw) when getMergeBase returns no commonCommit', async () => {
|
|
1722
|
+
(TFSServices.getItemContentWithHeaders as jest.Mock)
|
|
1723
|
+
.mockResolvedValueOnce({ data: { value: [] }, headers: {} }); // same-branch
|
|
1724
|
+
|
|
1725
|
+
(TFSServices.getItemContent as jest.Mock)
|
|
1726
|
+
.mockResolvedValueOnce({ defaultBranch: 'refs/heads/main' }) // getRepoDefaultBranch
|
|
1727
|
+
.mockResolvedValueOnce({}) // getMergeBase returns no commonCommit
|
|
1728
|
+
.mockResolvedValueOnce({ value: [] }); // GetPipelineRunHistory (legacy path)
|
|
1729
|
+
|
|
1730
|
+
const result = await pipelinesDataProvider.findPreviousPipeline(
|
|
1731
|
+
'project1',
|
|
1732
|
+
'123',
|
|
1733
|
+
100,
|
|
1734
|
+
featureTargetPipelineRun
|
|
1735
|
+
);
|
|
1736
|
+
|
|
1737
|
+
expect(result).toBeUndefined();
|
|
1738
|
+
});
|
|
1739
|
+
});
|
|
1598
1740
|
});
|
|
1599
1741
|
|
|
1600
1742
|
describe('private helper methods', () => {
|