@elisra-devops/docgen-data-provider 1.84.0 → 1.86.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.
@@ -1078,6 +1078,67 @@ describe('ResultDataProvider', () => {
1078
1078
  expect(esuk).toBe('ESUK');
1079
1079
  expect(il).toBe('IL');
1080
1080
  });
1081
+ it('should derive responsibility from Area Path alias when System.AreaPath is missing', () => {
1082
+ const esuk = resultDataProvider.deriveMewpResponsibility({
1083
+ 'Custom.SAPWBS': '',
1084
+ 'Area Path': 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK',
1085
+ });
1086
+ const il = resultDataProvider.deriveMewpResponsibility({
1087
+ 'Custom.SAPWBS': '',
1088
+ 'Area Path': 'MEWP\\Customer Requirements\\Level 2\\ATP',
1089
+ });
1090
+ expect(esuk).toBe('ESUK');
1091
+ expect(il).toBe('IL');
1092
+ });
1093
+ it('should derive test-case responsibility from testCasesItems area-path fields', async () => {
1094
+ const fetchByIdsSpy = jest
1095
+ .spyOn(resultDataProvider, 'fetchWorkItemsByIds')
1096
+ .mockResolvedValue([]);
1097
+ const map = await resultDataProvider.buildMewpTestCaseResponsibilityMap([
1098
+ {
1099
+ testCasesItems: [
1100
+ {
1101
+ workItem: {
1102
+ id: 101,
1103
+ workItemFields: [{ name: 'Area Path', value: 'MEWP\\Customer Requirements\\Level 2\\ATP' }],
1104
+ },
1105
+ },
1106
+ {
1107
+ workItem: {
1108
+ id: 102,
1109
+ workItemFields: [
1110
+ { referenceName: 'System.AreaPath', value: 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK' },
1111
+ ],
1112
+ },
1113
+ },
1114
+ ],
1115
+ testPointsItems: [],
1116
+ },
1117
+ ], mockProjectName);
1118
+ expect(map.get(101)).toBe('IL');
1119
+ expect(map.get(102)).toBe('ESUK');
1120
+ expect(fetchByIdsSpy).not.toHaveBeenCalled();
1121
+ });
1122
+ it('should derive test-case responsibility from AreaPath even when SAPWBS exists on test-case payload', async () => {
1123
+ jest.spyOn(resultDataProvider, 'fetchWorkItemsByIds').mockResolvedValue([]);
1124
+ const map = await resultDataProvider.buildMewpTestCaseResponsibilityMap([
1125
+ {
1126
+ testCasesItems: [
1127
+ {
1128
+ workItem: {
1129
+ id: 201,
1130
+ workItemFields: [
1131
+ { referenceName: 'Custom.SAPWBS', value: 'ESUK' },
1132
+ { referenceName: 'System.AreaPath', value: 'MEWP\\Customer Requirements\\Level 2\\ATP' },
1133
+ ],
1134
+ },
1135
+ },
1136
+ ],
1137
+ testPointsItems: [],
1138
+ },
1139
+ ], mockProjectName);
1140
+ expect(map.get(201)).toBe('IL');
1141
+ });
1081
1142
  it('should zip bug rows with L3/L4 pairs and avoid cross-product duplication', () => {
1082
1143
  const requirements = [
1083
1144
  {
@@ -1309,6 +1370,62 @@ describe('ResultDataProvider', () => {
1309
1370
  expect(rows[0]['Bug ID']).toBe(99001);
1310
1371
  expect(rows[0]['Bug Responsibility']).toBe('Elisra');
1311
1372
  });
1373
+ it('should fallback bug responsibility from test case mapping when external bug row is unknown', () => {
1374
+ const requirements = [
1375
+ {
1376
+ requirementId: 'SR5310',
1377
+ baseKey: 'SR5310',
1378
+ title: 'Req 5310',
1379
+ subSystem: 'Power',
1380
+ responsibility: '',
1381
+ linkedTestCaseIds: [101],
1382
+ },
1383
+ ];
1384
+ const requirementIndex = new Map([
1385
+ [
1386
+ 'SR5310',
1387
+ new Map([
1388
+ [
1389
+ 101,
1390
+ {
1391
+ passed: 0,
1392
+ failed: 1,
1393
+ notRun: 0,
1394
+ },
1395
+ ],
1396
+ ]),
1397
+ ],
1398
+ ]);
1399
+ const observedTestCaseIdsByRequirement = new Map([
1400
+ ['SR5310', new Set([101])],
1401
+ ]);
1402
+ const linkedRequirementsByTestCase = new Map([
1403
+ [
1404
+ 101,
1405
+ {
1406
+ baseKeys: new Set(['SR5310']),
1407
+ fullCodes: new Set(['SR5310']),
1408
+ bugIds: new Set([10003]),
1409
+ },
1410
+ ],
1411
+ ]);
1412
+ const externalBugsByTestCase = new Map([
1413
+ [
1414
+ 101,
1415
+ [
1416
+ {
1417
+ id: 10003,
1418
+ title: 'Bug 10003',
1419
+ responsibility: 'Unknown',
1420
+ requirementBaseKey: 'SR5310',
1421
+ },
1422
+ ],
1423
+ ],
1424
+ ]);
1425
+ const rows = resultDataProvider.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, new Map(), externalBugsByTestCase, new Map(), new Map([[101, 'IL']]));
1426
+ expect(rows).toHaveLength(1);
1427
+ expect(rows[0]['Bug Responsibility']).toBe('Elisra');
1428
+ });
1312
1429
  });
1313
1430
  describe('getMewpInternalValidationFlatResults', () => {
1314
1431
  it('should skip test cases with no in-scope expected requirements and no linked requirements', async () => {
@@ -1486,6 +1603,63 @@ describe('ResultDataProvider', () => {
1486
1603
  }),
1487
1604
  ]));
1488
1605
  });
1606
+ it('should report only base SR when an entire mentioned family is uncovered', async () => {
1607
+ jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
1608
+ jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
1609
+ jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
1610
+ {
1611
+ testPointsItems: [{ testCaseId: 401, testCaseName: 'TC 401 - Family uncovered' }],
1612
+ testCasesItems: [
1613
+ {
1614
+ workItem: {
1615
+ id: 401,
1616
+ workItemFields: [{ key: 'Steps', value: '<steps id=\"mock-steps-tc-401\"></steps>' }],
1617
+ },
1618
+ },
1619
+ ],
1620
+ },
1621
+ ]);
1622
+ jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
1623
+ {
1624
+ workItemId: 9001,
1625
+ requirementId: 'SR0054-1',
1626
+ baseKey: 'SR0054',
1627
+ title: 'SR0054 child 1',
1628
+ responsibility: 'ESUK',
1629
+ linkedTestCaseIds: [],
1630
+ areaPath: 'MEWP\\Customer Requirements\\Level 2',
1631
+ },
1632
+ {
1633
+ workItemId: 9002,
1634
+ requirementId: 'SR0054-2',
1635
+ baseKey: 'SR0054',
1636
+ title: 'SR0054 child 2',
1637
+ responsibility: 'ESUK',
1638
+ linkedTestCaseIds: [],
1639
+ areaPath: 'MEWP\\Customer Requirements\\Level 2',
1640
+ },
1641
+ ]);
1642
+ jest.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(new Map([[401, { baseKeys: new Set(), fullCodes: new Set() }]]));
1643
+ jest.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
1644
+ {
1645
+ stepId: '3',
1646
+ stepPosition: '3',
1647
+ action: 'Validate family root',
1648
+ expected: 'SR0054',
1649
+ isSharedStepTitle: false,
1650
+ },
1651
+ ]);
1652
+ const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
1653
+ expect(result.rows).toHaveLength(1);
1654
+ expect(result.rows[0]).toEqual(expect.objectContaining({
1655
+ 'Test Case ID': 401,
1656
+ 'Mentioned but Not Linked': 'Step 3: SR0054',
1657
+ 'Linked but Not Mentioned': '',
1658
+ 'Validation Status': 'Fail',
1659
+ }));
1660
+ expect(String(result.rows[0]['Mentioned but Not Linked'] || '')).not.toContain('SR0054-1');
1661
+ expect(String(result.rows[0]['Mentioned but Not Linked'] || '')).not.toContain('SR0054-2');
1662
+ });
1489
1663
  it('should not duplicate Direction A discrepancy when same requirement is repeated in multiple steps', async () => {
1490
1664
  jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
1491
1665
  jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
@@ -2091,6 +2265,101 @@ describe('ResultDataProvider', () => {
2091
2265
  }));
2092
2266
  });
2093
2267
  });
2268
+ describe('buildLinkedRequirementsByTestCase', () => {
2269
+ it('should map linked requirements only for supported test-case requirement relation types', async () => {
2270
+ var _a, _b, _c, _d;
2271
+ const requirements = [
2272
+ {
2273
+ workItemId: 7001,
2274
+ requirementId: 'SR0054-1',
2275
+ baseKey: 'SR0054',
2276
+ linkedTestCaseIds: [],
2277
+ },
2278
+ ];
2279
+ const testData = [
2280
+ {
2281
+ testCasesItems: [{ workItem: { id: 1001 } }],
2282
+ testPointsItems: [],
2283
+ },
2284
+ ];
2285
+ const fetchByIdsSpy = jest
2286
+ .spyOn(resultDataProvider, 'fetchWorkItemsByIds')
2287
+ .mockImplementation(async (...args) => {
2288
+ const ids = Array.isArray(args === null || args === void 0 ? void 0 : args[1]) ? args[1] : [];
2289
+ const includeRelations = !!(args === null || args === void 0 ? void 0 : args[2]);
2290
+ if (includeRelations) {
2291
+ return [
2292
+ {
2293
+ id: 1001,
2294
+ relations: [
2295
+ {
2296
+ rel: 'Microsoft.VSTS.Common.TestedBy-Reverse',
2297
+ url: 'https://dev.azure.com/org/project/_apis/wit/workItems/7001',
2298
+ },
2299
+ {
2300
+ rel: 'System.LinkTypes.Related',
2301
+ url: 'https://dev.azure.com/org/project/_apis/wit/workItems/7001',
2302
+ },
2303
+ ],
2304
+ },
2305
+ ];
2306
+ }
2307
+ return ids.map((id) => ({
2308
+ id,
2309
+ fields: {
2310
+ 'System.WorkItemType': id === 7001 ? 'Requirement' : 'Test Case',
2311
+ },
2312
+ }));
2313
+ });
2314
+ const linked = await resultDataProvider.buildLinkedRequirementsByTestCase(requirements, testData, mockProjectName);
2315
+ expect(fetchByIdsSpy).toHaveBeenCalled();
2316
+ expect((_b = (_a = linked.get(1001)) === null || _a === void 0 ? void 0 : _a.baseKeys) === null || _b === void 0 ? void 0 : _b.has('SR0054')).toBe(true);
2317
+ expect((_d = (_c = linked.get(1001)) === null || _c === void 0 ? void 0 : _c.fullCodes) === null || _d === void 0 ? void 0 : _d.has('SR0054-1')).toBe(true);
2318
+ });
2319
+ it('should ignore unsupported relation types when linking test case to requirements', async () => {
2320
+ var _a, _b, _c, _d;
2321
+ const requirements = [
2322
+ {
2323
+ workItemId: 7002,
2324
+ requirementId: 'SR0099-1',
2325
+ baseKey: 'SR0099',
2326
+ linkedTestCaseIds: [],
2327
+ },
2328
+ ];
2329
+ const testData = [
2330
+ {
2331
+ testCasesItems: [{ workItem: { id: 1002 } }],
2332
+ testPointsItems: [],
2333
+ },
2334
+ ];
2335
+ jest.spyOn(resultDataProvider, 'fetchWorkItemsByIds').mockImplementation(async (...args) => {
2336
+ const ids = Array.isArray(args === null || args === void 0 ? void 0 : args[1]) ? args[1] : [];
2337
+ const includeRelations = !!(args === null || args === void 0 ? void 0 : args[2]);
2338
+ if (includeRelations) {
2339
+ return [
2340
+ {
2341
+ id: 1002,
2342
+ relations: [
2343
+ {
2344
+ rel: 'System.LinkTypes.Related',
2345
+ url: 'https://dev.azure.com/org/project/_apis/wit/workItems/7002',
2346
+ },
2347
+ ],
2348
+ },
2349
+ ];
2350
+ }
2351
+ return ids.map((id) => ({
2352
+ id,
2353
+ fields: {
2354
+ 'System.WorkItemType': id === 7002 ? 'Requirement' : 'Test Case',
2355
+ },
2356
+ }));
2357
+ });
2358
+ const linked = await resultDataProvider.buildLinkedRequirementsByTestCase(requirements, testData, mockProjectName);
2359
+ expect((_b = (_a = linked.get(1002)) === null || _a === void 0 ? void 0 : _a.baseKeys) === null || _b === void 0 ? void 0 : _b.has('SR0099')).toBe(false);
2360
+ expect((_d = (_c = linked.get(1002)) === null || _c === void 0 ? void 0 : _c.fullCodes) === null || _d === void 0 ? void 0 : _d.has('SR0099-1')).toBe(false);
2361
+ });
2362
+ });
2094
2363
  describe('MEWP rel fallback scoping', () => {
2095
2364
  it('should fallback to previous Rel run when latest selected Rel has no run for a test case', async () => {
2096
2365
  const suites = [