@elisra-devops/docgen-data-provider 1.78.0 → 1.80.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/models/mewp-reporting.d.ts +1 -0
- package/bin/modules/ResultDataProvider.d.ts +2 -1
- package/bin/modules/ResultDataProvider.js +208 -60
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +6 -6
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/models/mewp-reporting.ts +1 -0
- package/src/modules/ResultDataProvider.ts +225 -54
- package/src/tests/modules/ResultDataProvider.test.ts +6 -7
|
@@ -86,7 +86,6 @@ export default class ResultDataProvider {
|
|
|
86
86
|
private createEmptyMewpCoverageBugCell;
|
|
87
87
|
private createEmptyMewpCoverageL3L4Cell;
|
|
88
88
|
private buildMewpCoverageL3L4Rows;
|
|
89
|
-
private formatMewpCustomerId;
|
|
90
89
|
private buildMewpCoverageRows;
|
|
91
90
|
private resolveCoverageBugResponsibility;
|
|
92
91
|
private resolveMewpL2RunStatus;
|
|
@@ -119,6 +118,8 @@ export default class ResultDataProvider {
|
|
|
119
118
|
private fetchMewpL2Requirements;
|
|
120
119
|
private isMewpL2AreaPath;
|
|
121
120
|
private collapseMewpRequirementFamilies;
|
|
121
|
+
private buildMewpL2ToLinkedL1BaseKeys;
|
|
122
|
+
private buildMewpExternalJoinKeysByL2Requirement;
|
|
122
123
|
private buildRequirementFamilyMap;
|
|
123
124
|
private buildLinkedRequirementsByTestCase;
|
|
124
125
|
private resolveMewpRequirementScopeKeysFromQuery;
|
|
@@ -304,6 +304,7 @@ class ResultDataProvider {
|
|
|
304
304
|
const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
|
|
305
305
|
const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
|
|
306
306
|
const requirements = this.collapseMewpRequirementFamilies(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
|
|
307
|
+
const l2ToLinkedL1BaseKeys = await this.buildMewpL2ToLinkedL1BaseKeys(allRequirements, projectName, testData);
|
|
307
308
|
const requirementSapWbsByBaseKey = this.buildRequirementSapWbsByBaseKey(allRequirements);
|
|
308
309
|
const externalBugsByTestCase = await this.loadExternalBugsByTestCase(options === null || options === void 0 ? void 0 : options.externalBugsFile);
|
|
309
310
|
const externalL3L4ByBaseKey = await this.loadExternalL3L4ByBaseKey(options === null || options === void 0 ? void 0 : options.externalL3L4File, requirementSapWbsByBaseKey);
|
|
@@ -333,6 +334,7 @@ class ResultDataProvider {
|
|
|
333
334
|
if (requirements.length === 0) {
|
|
334
335
|
return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
|
|
335
336
|
}
|
|
337
|
+
const externalJoinKeysByL2 = this.buildMewpExternalJoinKeysByL2Requirement(requirements, l2ToLinkedL1BaseKeys);
|
|
336
338
|
const requirementIndex = new Map();
|
|
337
339
|
const observedTestCaseIdsByRequirement = new Map();
|
|
338
340
|
const requirementKeys = new Set();
|
|
@@ -382,8 +384,15 @@ class ResultDataProvider {
|
|
|
382
384
|
this.accumulateRequirementCountsFromActionResults(fallbackActionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
383
385
|
}
|
|
384
386
|
const requirementBaseKeys = new Set(requirements.map((item) => String((item === null || item === void 0 ? void 0 : item.baseKey) || '').trim()).filter((item) => !!item));
|
|
387
|
+
const externalJoinKeyUniverse = new Set();
|
|
388
|
+
for (const keySet of externalJoinKeysByL2.values()) {
|
|
389
|
+
for (const key of keySet) {
|
|
390
|
+
if (key)
|
|
391
|
+
externalJoinKeyUniverse.add(key);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
385
394
|
const externalL3L4BaseKeys = new Set([...externalL3L4ByBaseKey.keys()]);
|
|
386
|
-
const externalL3L4OverlapKeys = [...externalL3L4BaseKeys].filter((key) =>
|
|
395
|
+
const externalL3L4OverlapKeys = [...externalL3L4BaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
|
|
387
396
|
const failedRequirementBaseKeys = new Set();
|
|
388
397
|
const failedTestCaseIds = new Set();
|
|
389
398
|
for (const [requirementBaseKey, byTestCase] of requirementIndex.entries()) {
|
|
@@ -404,9 +413,10 @@ class ResultDataProvider {
|
|
|
404
413
|
externalBugBaseKeys.add(key);
|
|
405
414
|
}
|
|
406
415
|
}
|
|
407
|
-
const externalBugRequirementOverlap = [...externalBugBaseKeys].filter((key) =>
|
|
408
|
-
const externalBugFailedRequirementOverlap = [...externalBugBaseKeys].filter((key) =>
|
|
416
|
+
const externalBugRequirementOverlap = [...externalBugBaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
|
|
417
|
+
const externalBugFailedRequirementOverlap = [...externalBugBaseKeys].filter((key) => externalJoinKeyUniverse.has(key));
|
|
409
418
|
logger_1.default.info(`MEWP coverage join diagnostics: requirementBaseKeys=${requirementBaseKeys.size} ` +
|
|
419
|
+
`externalJoinKeys=${externalJoinKeyUniverse.size} ` +
|
|
410
420
|
`failedRequirementBaseKeys=${failedRequirementBaseKeys.size} failedTestCases=${failedTestCaseIds.size}; ` +
|
|
411
421
|
`externalL3L4BaseKeys=${externalL3L4BaseKeys.size} externalL3L4Overlap=${externalL3L4OverlapKeys.length}; ` +
|
|
412
422
|
`externalBugTestCases=${externalBugTestCaseIds.size} externalBugFailedTestCaseOverlap=${externalBugFailedTestCaseOverlap.length}; ` +
|
|
@@ -414,21 +424,25 @@ class ResultDataProvider {
|
|
|
414
424
|
`externalBugFailedRequirementOverlap=${externalBugFailedRequirementOverlap.length}`);
|
|
415
425
|
if (externalL3L4BaseKeys.size > 0 && externalL3L4OverlapKeys.length === 0) {
|
|
416
426
|
const sampleReq = [...requirementBaseKeys].slice(0, 5);
|
|
427
|
+
const sampleJoin = [...externalJoinKeyUniverse].slice(0, 5);
|
|
417
428
|
const sampleExt = [...externalL3L4BaseKeys].slice(0, 5);
|
|
418
429
|
logger_1.default.warn(`MEWP coverage join diagnostics: no L3/L4 key overlap found. ` +
|
|
419
|
-
`sampleRequirementKeys=${sampleReq.join(', ')}
|
|
430
|
+
`sampleRequirementKeys=${sampleReq.join(', ')} sampleJoinKeys=${sampleJoin.join(', ')} ` +
|
|
431
|
+
`sampleExternalL3L4Keys=${sampleExt.join(', ')}`);
|
|
420
432
|
}
|
|
421
433
|
if (externalBugBaseKeys.size > 0 && externalBugRequirementOverlap.length === 0) {
|
|
422
434
|
const sampleReq = [...requirementBaseKeys].slice(0, 5);
|
|
435
|
+
const sampleJoin = [...externalJoinKeyUniverse].slice(0, 5);
|
|
423
436
|
const sampleExt = [...externalBugBaseKeys].slice(0, 5);
|
|
424
437
|
logger_1.default.warn(`MEWP coverage join diagnostics: no bug requirement-key overlap found. ` +
|
|
425
|
-
`sampleRequirementKeys=${sampleReq.join(', ')}
|
|
438
|
+
`sampleRequirementKeys=${sampleReq.join(', ')} sampleJoinKeys=${sampleJoin.join(', ')} ` +
|
|
439
|
+
`sampleExternalBugKeys=${sampleExt.join(', ')}`);
|
|
426
440
|
}
|
|
427
441
|
if (externalBugTestCaseIds.size > 0 && externalBugFailedTestCaseOverlap.length === 0) {
|
|
428
442
|
logger_1.default.warn(`MEWP coverage join diagnostics: no overlap between external bug test cases and failed test cases. ` +
|
|
429
443
|
`Bug rows remain empty because bugs are shown only for failed L2s.`);
|
|
430
444
|
}
|
|
431
|
-
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase);
|
|
445
|
+
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase, externalJoinKeysByL2);
|
|
432
446
|
const coverageRowStats = rows.reduce((acc, row) => {
|
|
433
447
|
const hasBug = Number((row === null || row === void 0 ? void 0 : row['Bug ID']) || 0) > 0;
|
|
434
448
|
const hasL3 = String((row === null || row === void 0 ? void 0 : row['L3 REQ ID']) || '').trim() !== '';
|
|
@@ -493,12 +507,24 @@ class ResultDataProvider {
|
|
|
493
507
|
}
|
|
494
508
|
}
|
|
495
509
|
const validL2BaseKeys = new Set([...requirementFamilies.keys()]);
|
|
510
|
+
const diagnostics = {
|
|
511
|
+
totalTestCases: 0,
|
|
512
|
+
totalParsedSteps: 0,
|
|
513
|
+
totalStepsWithMentions: 0,
|
|
514
|
+
totalMentionedCustomerIds: 0,
|
|
515
|
+
testCasesWithoutMentionedCustomerIds: 0,
|
|
516
|
+
failingRows: 0,
|
|
517
|
+
};
|
|
496
518
|
for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
|
|
519
|
+
diagnostics.totalTestCases += 1;
|
|
497
520
|
const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
|
|
498
521
|
const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
|
|
499
522
|
? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
|
|
500
523
|
: [];
|
|
524
|
+
const executableSteps = parsedSteps.filter((step) => !(step === null || step === void 0 ? void 0 : step.isSharedStepTitle));
|
|
525
|
+
diagnostics.totalParsedSteps += executableSteps.length;
|
|
501
526
|
const mentionEntries = this.extractRequirementMentionsFromExpectedSteps(parsedSteps, true);
|
|
527
|
+
diagnostics.totalStepsWithMentions += mentionEntries.length;
|
|
502
528
|
const mentionedL2Only = new Set();
|
|
503
529
|
const mentionedCodeFirstStep = new Map();
|
|
504
530
|
const mentionedBaseFirstStep = new Map();
|
|
@@ -521,6 +547,10 @@ class ResultDataProvider {
|
|
|
521
547
|
}
|
|
522
548
|
}
|
|
523
549
|
}
|
|
550
|
+
diagnostics.totalMentionedCustomerIds += mentionedL2Only.size;
|
|
551
|
+
if (mentionedL2Only.size === 0) {
|
|
552
|
+
diagnostics.testCasesWithoutMentionedCustomerIds += 1;
|
|
553
|
+
}
|
|
524
554
|
const mentionedBaseKeys = new Set([...mentionedL2Only].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
525
555
|
const expectedFamilyCodes = new Set();
|
|
526
556
|
for (const baseKey of mentionedBaseKeys) {
|
|
@@ -597,6 +627,14 @@ class ResultDataProvider {
|
|
|
597
627
|
.join('; ');
|
|
598
628
|
const linkedButNotMentioned = sortedExtraLinked.join('; ');
|
|
599
629
|
const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
|
|
630
|
+
if (validationStatus === 'Fail')
|
|
631
|
+
diagnostics.failingRows += 1;
|
|
632
|
+
logger_1.default.debug(`MEWP internal validation parse diagnostics: ` +
|
|
633
|
+
`testCaseId=${testCaseId} parsedSteps=${executableSteps.length} ` +
|
|
634
|
+
`stepsWithMentions=${mentionEntries.length} customerIdsFound=${mentionedL2Only.size} ` +
|
|
635
|
+
`linkedRequirements=${linkedFullCodes.size} mentionedButNotLinked=${sortedMissingMentioned.length + sortedMissingFamily.length} ` +
|
|
636
|
+
`linkedButNotMentioned=${sortedExtraLinked.length} status=${validationStatus} ` +
|
|
637
|
+
`customerIdSample='${[...mentionedL2Only].slice(0, 5).join(', ')}'`);
|
|
600
638
|
rows.push({
|
|
601
639
|
'Test Case ID': testCaseId,
|
|
602
640
|
'Test Case Title': String(testCaseTitleMap.get(testCaseId) || '').trim(),
|
|
@@ -605,6 +643,11 @@ class ResultDataProvider {
|
|
|
605
643
|
'Validation Status': validationStatus,
|
|
606
644
|
});
|
|
607
645
|
}
|
|
646
|
+
logger_1.default.info(`MEWP internal validation summary: testCases=${diagnostics.totalTestCases} ` +
|
|
647
|
+
`parsedSteps=${diagnostics.totalParsedSteps} stepsWithMentions=${diagnostics.totalStepsWithMentions} ` +
|
|
648
|
+
`totalCustomerIdsFound=${diagnostics.totalMentionedCustomerIds} ` +
|
|
649
|
+
`testCasesWithoutCustomerIds=${diagnostics.testCasesWithoutMentionedCustomerIds} ` +
|
|
650
|
+
`failingRows=${diagnostics.failingRows}`);
|
|
608
651
|
return {
|
|
609
652
|
sheetName: this.buildInternalValidationSheetName(planName, testPlanId),
|
|
610
653
|
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
@@ -694,7 +737,8 @@ class ResultDataProvider {
|
|
|
694
737
|
return `MEWP Internal Validation - ${suffix}`;
|
|
695
738
|
}
|
|
696
739
|
createMewpCoverageRow(requirement, runStatus, bug, linkedL3L4) {
|
|
697
|
-
const
|
|
740
|
+
const l2ReqIdNumeric = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
|
|
741
|
+
const l2ReqId = l2ReqIdNumeric > 0 ? String(l2ReqIdNumeric) : '';
|
|
698
742
|
const l2ReqTitle = this.toMewpComparableText(requirement.title);
|
|
699
743
|
const l2SubSystem = this.toMewpComparableText(requirement.subSystem);
|
|
700
744
|
return {
|
|
@@ -718,7 +762,22 @@ class ResultDataProvider {
|
|
|
718
762
|
return { l3Id: '', l3Title: '', l4Id: '', l4Title: '' };
|
|
719
763
|
}
|
|
720
764
|
buildMewpCoverageL3L4Rows(links) {
|
|
721
|
-
const
|
|
765
|
+
const deduped = new Map();
|
|
766
|
+
for (const item of links || []) {
|
|
767
|
+
const level = (item === null || item === void 0 ? void 0 : item.level) === 'L4' ? 'L4' : 'L3';
|
|
768
|
+
const id = String((item === null || item === void 0 ? void 0 : item.id) || '').trim();
|
|
769
|
+
if (!id)
|
|
770
|
+
continue;
|
|
771
|
+
const key = `${level}:${id}`;
|
|
772
|
+
if (!deduped.has(key)) {
|
|
773
|
+
deduped.set(key, {
|
|
774
|
+
id,
|
|
775
|
+
level,
|
|
776
|
+
title: String((item === null || item === void 0 ? void 0 : item.title) || '').trim(),
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const sorted = [...deduped.values()].sort((a, b) => {
|
|
722
781
|
if (a.level !== b.level)
|
|
723
782
|
return a.level === 'L3' ? -1 : 1;
|
|
724
783
|
return String(a.id || '').localeCompare(String(b.id || ''));
|
|
@@ -735,21 +794,14 @@ class ResultDataProvider {
|
|
|
735
794
|
}
|
|
736
795
|
return rows;
|
|
737
796
|
}
|
|
738
|
-
|
|
739
|
-
const normalized = this.normalizeMewpRequirementCode(this.toMewpComparableText(rawValue));
|
|
740
|
-
if (normalized)
|
|
741
|
-
return normalized;
|
|
742
|
-
const onlyDigits = String(rawValue || '').replace(/\D/g, '');
|
|
743
|
-
if (onlyDigits)
|
|
744
|
-
return `SR${onlyDigits}`;
|
|
745
|
-
return '';
|
|
746
|
-
}
|
|
747
|
-
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase) {
|
|
797
|
+
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase, externalJoinKeysByL2) {
|
|
748
798
|
var _a;
|
|
749
799
|
const rows = [];
|
|
750
800
|
const linkedByRequirement = this.invertBaseRequirementLinks(linkedRequirementsByTestCase);
|
|
801
|
+
const joinKeysByRequirement = externalJoinKeysByL2 || new Map();
|
|
751
802
|
for (const requirement of requirements) {
|
|
752
803
|
const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || this.toRequirementKey(requirement.requirementId) || '').trim();
|
|
804
|
+
const externalJoinKeys = joinKeysByRequirement.get(key) || new Set([key]);
|
|
753
805
|
const linkedTestCaseIds = ((requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []).filter((id) => Number.isFinite(id) && Number(id) > 0);
|
|
754
806
|
const linkedByTestCase = key ? Array.from(linkedByRequirement.get(key) || []) : [];
|
|
755
807
|
const observedTestCaseIds = key
|
|
@@ -771,7 +823,7 @@ class ResultDataProvider {
|
|
|
771
823
|
const externalBugs = externalBugsByTestCase.get(testCaseId) || [];
|
|
772
824
|
for (const bug of externalBugs) {
|
|
773
825
|
const bugBaseKey = String((bug === null || bug === void 0 ? void 0 : bug.requirementBaseKey) || '').trim();
|
|
774
|
-
if (bugBaseKey && bugBaseKey
|
|
826
|
+
if (bugBaseKey && !externalJoinKeys.has(bugBaseKey))
|
|
775
827
|
continue;
|
|
776
828
|
const bugId = Number((bug === null || bug === void 0 ? void 0 : bug.id) || 0);
|
|
777
829
|
if (!Number.isFinite(bugId) || bugId <= 0)
|
|
@@ -789,7 +841,7 @@ class ResultDataProvider {
|
|
|
789
841
|
const bugsForRows = runStatus === 'Fail'
|
|
790
842
|
? Array.from(aggregatedBugs.values()).sort((a, b) => a.id - b.id)
|
|
791
843
|
: [];
|
|
792
|
-
const l3l4ForRows = [...(l3l4ByBaseKey.get(
|
|
844
|
+
const l3l4ForRows = [...externalJoinKeys].flatMap((joinKey) => l3l4ByBaseKey.get(joinKey) || []);
|
|
793
845
|
const bugRows = bugsForRows.length > 0
|
|
794
846
|
? bugsForRows
|
|
795
847
|
: [];
|
|
@@ -1369,7 +1421,7 @@ class ResultDataProvider {
|
|
|
1369
1421
|
const workItems = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
|
|
1370
1422
|
const requirements = workItems.map((wi) => {
|
|
1371
1423
|
const fields = (wi === null || wi === void 0 ? void 0 : wi.fields) || {};
|
|
1372
|
-
const requirementId = this.extractMewpRequirementIdentifier(fields
|
|
1424
|
+
const requirementId = this.extractMewpRequirementIdentifier(fields);
|
|
1373
1425
|
const areaPath = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
1374
1426
|
return {
|
|
1375
1427
|
workItemId: Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0),
|
|
@@ -1451,18 +1503,132 @@ class ResultDataProvider {
|
|
|
1451
1503
|
}
|
|
1452
1504
|
return [...families.entries()]
|
|
1453
1505
|
.map(([baseKey, family]) => {
|
|
1454
|
-
var _a, _b, _c, _d;
|
|
1506
|
+
var _a, _b, _c, _d, _e;
|
|
1455
1507
|
return ({
|
|
1456
|
-
|
|
1508
|
+
workItemId: Number(((_a = family === null || family === void 0 ? void 0 : family.representative) === null || _a === void 0 ? void 0 : _a.workItemId) || 0),
|
|
1509
|
+
requirementId: String(((_b = family === null || family === void 0 ? void 0 : family.representative) === null || _b === void 0 ? void 0 : _b.requirementId) || baseKey),
|
|
1457
1510
|
baseKey,
|
|
1458
|
-
title: String(((
|
|
1459
|
-
subSystem: String(((
|
|
1460
|
-
responsibility: String(((
|
|
1511
|
+
title: String(((_c = family === null || family === void 0 ? void 0 : family.representative) === null || _c === void 0 ? void 0 : _c.title) || ''),
|
|
1512
|
+
subSystem: String(((_d = family === null || family === void 0 ? void 0 : family.representative) === null || _d === void 0 ? void 0 : _d.subSystem) || ''),
|
|
1513
|
+
responsibility: String(((_e = family === null || family === void 0 ? void 0 : family.representative) === null || _e === void 0 ? void 0 : _e.responsibility) || ''),
|
|
1461
1514
|
linkedTestCaseIds: [...family.linkedTestCaseIds].sort((a, b) => a - b),
|
|
1462
1515
|
});
|
|
1463
1516
|
})
|
|
1464
1517
|
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
1465
1518
|
}
|
|
1519
|
+
async buildMewpL2ToLinkedL1BaseKeys(requirements, projectName, testData) {
|
|
1520
|
+
const out = new Map();
|
|
1521
|
+
const relatedIds = new Set();
|
|
1522
|
+
const linkedTestCaseIdsByL2 = new Map();
|
|
1523
|
+
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
1524
|
+
for (const requirement of requirements || []) {
|
|
1525
|
+
const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1526
|
+
if (!l2BaseKey)
|
|
1527
|
+
continue;
|
|
1528
|
+
if (!out.has(l2BaseKey))
|
|
1529
|
+
out.set(l2BaseKey, new Set());
|
|
1530
|
+
if (!linkedTestCaseIdsByL2.has(l2BaseKey))
|
|
1531
|
+
linkedTestCaseIdsByL2.set(l2BaseKey, new Set());
|
|
1532
|
+
for (const testCaseId of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
|
|
1533
|
+
const numeric = Number(testCaseId);
|
|
1534
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
1535
|
+
linkedTestCaseIdsByL2.get(l2BaseKey).add(numeric);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
for (const relatedId of (requirement === null || requirement === void 0 ? void 0 : requirement.relatedWorkItemIds) || []) {
|
|
1539
|
+
const id = Number(relatedId);
|
|
1540
|
+
if (Number.isFinite(id) && id > 0)
|
|
1541
|
+
relatedIds.add(id);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
let titleDerivedCount = 0;
|
|
1545
|
+
for (const [l2BaseKey, testCaseIds] of linkedTestCaseIdsByL2.entries()) {
|
|
1546
|
+
const targetSet = out.get(l2BaseKey) || new Set();
|
|
1547
|
+
for (const testCaseId of testCaseIds) {
|
|
1548
|
+
const title = String(testCaseTitleMap.get(testCaseId) || '').trim();
|
|
1549
|
+
if (!title)
|
|
1550
|
+
continue;
|
|
1551
|
+
const fromTitleCodes = this.extractRequirementCodesFromExpectedText(title, false);
|
|
1552
|
+
if (fromTitleCodes.size > 0) {
|
|
1553
|
+
for (const code of fromTitleCodes) {
|
|
1554
|
+
const normalized = this.toRequirementKey(code);
|
|
1555
|
+
if (normalized)
|
|
1556
|
+
targetSet.add(normalized);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
else {
|
|
1560
|
+
const normalized = this.toRequirementKey(title);
|
|
1561
|
+
if (normalized)
|
|
1562
|
+
targetSet.add(normalized);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
if (targetSet.size > 0) {
|
|
1566
|
+
titleDerivedCount += 1;
|
|
1567
|
+
}
|
|
1568
|
+
out.set(l2BaseKey, targetSet);
|
|
1569
|
+
}
|
|
1570
|
+
if (relatedIds.size === 0) {
|
|
1571
|
+
const linkedL1Count = [...out.values()].reduce((sum, set) => sum + set.size, 0);
|
|
1572
|
+
logger_1.default.info(`MEWP L2->L1 mapping summary: l2Families=${out.size} ` +
|
|
1573
|
+
`fromTitle=${titleDerivedCount} fallbackFromLinkedL1=0 linkedL1Keys=${linkedL1Count}`);
|
|
1574
|
+
return out;
|
|
1575
|
+
}
|
|
1576
|
+
const relatedWorkItems = await this.fetchWorkItemsByIds(projectName, [...relatedIds], false);
|
|
1577
|
+
const l1BaseByWorkItemId = new Map();
|
|
1578
|
+
for (const workItem of relatedWorkItems || []) {
|
|
1579
|
+
const workItemId = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1580
|
+
if (!Number.isFinite(workItemId) || workItemId <= 0)
|
|
1581
|
+
continue;
|
|
1582
|
+
const fields = (workItem === null || workItem === void 0 ? void 0 : workItem.fields) || {};
|
|
1583
|
+
const customerId = this.extractMewpRequirementIdentifier(fields);
|
|
1584
|
+
const baseKey = this.toRequirementKey(customerId);
|
|
1585
|
+
if (!baseKey)
|
|
1586
|
+
continue;
|
|
1587
|
+
l1BaseByWorkItemId.set(workItemId, baseKey);
|
|
1588
|
+
}
|
|
1589
|
+
let linkedFallbackCount = 0;
|
|
1590
|
+
for (const requirement of requirements || []) {
|
|
1591
|
+
const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1592
|
+
if (!l2BaseKey)
|
|
1593
|
+
continue;
|
|
1594
|
+
if (!out.has(l2BaseKey))
|
|
1595
|
+
out.set(l2BaseKey, new Set());
|
|
1596
|
+
const targetSet = out.get(l2BaseKey);
|
|
1597
|
+
if (targetSet.size > 0) {
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
for (const relatedId of (requirement === null || requirement === void 0 ? void 0 : requirement.relatedWorkItemIds) || []) {
|
|
1601
|
+
const baseKey = l1BaseByWorkItemId.get(Number(relatedId));
|
|
1602
|
+
if (baseKey)
|
|
1603
|
+
targetSet.add(baseKey);
|
|
1604
|
+
}
|
|
1605
|
+
if (targetSet.size > 0) {
|
|
1606
|
+
linkedFallbackCount += 1;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
const l2Families = out.size;
|
|
1610
|
+
const withLinkedL1 = [...out.values()].filter((set) => set.size > 0).length;
|
|
1611
|
+
const linkedL1Count = [...out.values()].reduce((sum, set) => sum + set.size, 0);
|
|
1612
|
+
logger_1.default.info(`MEWP L2->L1 mapping summary: l2Families=${l2Families} withLinkedL1=${withLinkedL1} ` +
|
|
1613
|
+
`fromTitle=${titleDerivedCount} fallbackFromLinkedL1=${linkedFallbackCount} linkedL1Keys=${linkedL1Count}`);
|
|
1614
|
+
return out;
|
|
1615
|
+
}
|
|
1616
|
+
buildMewpExternalJoinKeysByL2Requirement(requirements, l2ToLinkedL1BaseKeys) {
|
|
1617
|
+
const out = new Map();
|
|
1618
|
+
for (const requirement of requirements || []) {
|
|
1619
|
+
const l2BaseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1620
|
+
if (!l2BaseKey)
|
|
1621
|
+
continue;
|
|
1622
|
+
const joinKeys = new Set([l2BaseKey]);
|
|
1623
|
+
for (const l1Key of l2ToLinkedL1BaseKeys.get(l2BaseKey) || []) {
|
|
1624
|
+
const normalized = String(l1Key || '').trim();
|
|
1625
|
+
if (normalized)
|
|
1626
|
+
joinKeys.add(normalized);
|
|
1627
|
+
}
|
|
1628
|
+
out.set(l2BaseKey, joinKeys);
|
|
1629
|
+
}
|
|
1630
|
+
return out;
|
|
1631
|
+
}
|
|
1466
1632
|
buildRequirementFamilyMap(requirements, scopedRequirementKeys) {
|
|
1467
1633
|
const familyMap = new Map();
|
|
1468
1634
|
for (const requirement of requirements || []) {
|
|
@@ -1713,48 +1879,30 @@ class ResultDataProvider {
|
|
|
1713
1879
|
}
|
|
1714
1880
|
return [...out].sort((a, b) => a - b);
|
|
1715
1881
|
}
|
|
1716
|
-
extractMewpRequirementIdentifier(fields
|
|
1882
|
+
extractMewpRequirementIdentifier(fields) {
|
|
1717
1883
|
const entries = Object.entries(fields || {});
|
|
1718
|
-
|
|
1719
|
-
|
|
1884
|
+
const normalizeFieldKey = (value) => String(value || '')
|
|
1885
|
+
.toLowerCase()
|
|
1886
|
+
.replace(/[^a-z0-9]/g, '');
|
|
1887
|
+
// Strict MEWP mode: only explicit MEWP customer-id fields are accepted.
|
|
1888
|
+
// API display name: "Customer ID"
|
|
1889
|
+
// API reference name: "Custom.CustomerID"
|
|
1890
|
+
const customerIdFieldKeys = new Set([
|
|
1720
1891
|
'customerid',
|
|
1721
|
-
'
|
|
1722
|
-
|
|
1723
|
-
'requirementid',
|
|
1724
|
-
'externalid',
|
|
1725
|
-
'srid',
|
|
1726
|
-
'sapwbsid',
|
|
1727
|
-
];
|
|
1892
|
+
'customcustomerid',
|
|
1893
|
+
]);
|
|
1728
1894
|
for (const [key, value] of entries) {
|
|
1729
|
-
const normalizedKey =
|
|
1730
|
-
if (!
|
|
1895
|
+
const normalizedKey = normalizeFieldKey(key);
|
|
1896
|
+
if (!customerIdFieldKeys.has(normalizedKey))
|
|
1731
1897
|
continue;
|
|
1732
1898
|
const valueAsString = this.toMewpComparableText(value);
|
|
1733
1899
|
if (!valueAsString)
|
|
1734
1900
|
continue;
|
|
1735
|
-
const
|
|
1736
|
-
if (
|
|
1737
|
-
return
|
|
1901
|
+
const normalizedRequirementId = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
1902
|
+
if (normalizedRequirementId)
|
|
1903
|
+
return normalizedRequirementId;
|
|
1738
1904
|
}
|
|
1739
|
-
|
|
1740
|
-
const looseHints = ['customer', 'requirement', 'external', 'sapwbs', 'sr'];
|
|
1741
|
-
for (const [key, value] of entries) {
|
|
1742
|
-
const normalizedKey = String(key || '').toLowerCase();
|
|
1743
|
-
if (!looseHints.some((hint) => normalizedKey.includes(hint)))
|
|
1744
|
-
continue;
|
|
1745
|
-
const valueAsString = this.toMewpComparableText(value);
|
|
1746
|
-
if (!valueAsString)
|
|
1747
|
-
continue;
|
|
1748
|
-
const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
1749
|
-
if (normalized)
|
|
1750
|
-
return normalized;
|
|
1751
|
-
}
|
|
1752
|
-
// Optional fallback from title only (avoid scanning all fields and accidental SR matches).
|
|
1753
|
-
const title = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
1754
|
-
const titleCode = this.normalizeMewpRequirementCodeWithSuffix(title);
|
|
1755
|
-
if (titleCode)
|
|
1756
|
-
return titleCode;
|
|
1757
|
-
return fallbackWorkItemId ? `SR${fallbackWorkItemId}` : '';
|
|
1905
|
+
return '';
|
|
1758
1906
|
}
|
|
1759
1907
|
deriveMewpResponsibility(fields) {
|
|
1760
1908
|
const explicitSapWbs = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['Custom.SAPWBS']);
|