@elisra-devops/docgen-data-provider 1.90.0 → 1.92.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/ResultDataProvider.d.ts +1 -0
- package/bin/modules/ResultDataProvider.js +105 -50
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +157 -4
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +121 -57
- package/src/tests/modules/ResultDataProvider.test.ts +181 -4
|
@@ -138,6 +138,7 @@ export default class ResultDataProvider {
|
|
|
138
138
|
private isExcludedL3L4BySapWbs;
|
|
139
139
|
private normalizeMewpRequirementCode;
|
|
140
140
|
private normalizeMewpRequirementCodeWithSuffix;
|
|
141
|
+
private compareMewpRequirementCodes;
|
|
141
142
|
private formatRequirementCodesGroupedByFamily;
|
|
142
143
|
private toMewpComparableText;
|
|
143
144
|
private fetchTestPlanName;
|
|
@@ -476,7 +476,7 @@ class ResultDataProvider {
|
|
|
476
476
|
}
|
|
477
477
|
}
|
|
478
478
|
async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest) {
|
|
479
|
-
var _a, _b
|
|
479
|
+
var _a, _b;
|
|
480
480
|
const defaultPayload = {
|
|
481
481
|
sheetName: `MEWP Internal Validation - Plan ${testPlanId}`,
|
|
482
482
|
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
@@ -489,6 +489,31 @@ class ResultDataProvider {
|
|
|
489
489
|
const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
|
|
490
490
|
const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
|
|
491
491
|
const requirementFamilies = this.buildRequirementFamilyMap(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
|
|
492
|
+
const linkedFullCodesByTestCase = new Map();
|
|
493
|
+
const linkedFamilyCodesAcrossTestCases = new Map();
|
|
494
|
+
const linkedFullCodesAcrossTestCases = new Set();
|
|
495
|
+
const linkedBaseKeysAcrossTestCases = new Set();
|
|
496
|
+
for (const [linkedTestCaseId, links] of linkedRequirementsByTestCase.entries()) {
|
|
497
|
+
const rawFullCodes = (links === null || links === void 0 ? void 0 : links.fullCodes) || new Set();
|
|
498
|
+
const filteredFullCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && rawFullCodes.size > 0
|
|
499
|
+
? new Set([...rawFullCodes].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code))))
|
|
500
|
+
: rawFullCodes;
|
|
501
|
+
linkedFullCodesByTestCase.set(linkedTestCaseId, filteredFullCodes);
|
|
502
|
+
for (const code of filteredFullCodes) {
|
|
503
|
+
const normalizedCode = this.normalizeMewpRequirementCodeWithSuffix(code);
|
|
504
|
+
if (!normalizedCode)
|
|
505
|
+
continue;
|
|
506
|
+
linkedFullCodesAcrossTestCases.add(normalizedCode);
|
|
507
|
+
const baseKey = this.toRequirementKey(normalizedCode);
|
|
508
|
+
if (!baseKey)
|
|
509
|
+
continue;
|
|
510
|
+
linkedBaseKeysAcrossTestCases.add(baseKey);
|
|
511
|
+
if (!linkedFamilyCodesAcrossTestCases.has(baseKey)) {
|
|
512
|
+
linkedFamilyCodesAcrossTestCases.set(baseKey, new Set());
|
|
513
|
+
}
|
|
514
|
+
linkedFamilyCodesAcrossTestCases.get(baseKey).add(normalizedCode);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
492
517
|
const rows = [];
|
|
493
518
|
const stepsXmlByTestCase = this.buildTestCaseStepsXmlMap(testData);
|
|
494
519
|
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
@@ -558,11 +583,7 @@ class ResultDataProvider {
|
|
|
558
583
|
diagnostics.testCasesWithoutMentionedCustomerIds += 1;
|
|
559
584
|
}
|
|
560
585
|
const mentionedBaseKeys = new Set([...mentionedL2Only].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
561
|
-
const
|
|
562
|
-
const linkedFullCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && linkedFullCodesRaw.size > 0
|
|
563
|
-
? new Set([...linkedFullCodesRaw].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code))))
|
|
564
|
-
: linkedFullCodesRaw;
|
|
565
|
-
const linkedBaseKeys = new Set([...linkedFullCodes].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
586
|
+
const linkedFullCodes = linkedFullCodesByTestCase.get(testCaseId) || new Set();
|
|
566
587
|
const mentionedCodesByBase = new Map();
|
|
567
588
|
for (const code of mentionedL2Only) {
|
|
568
589
|
const baseKey = this.toRequirementKey(code);
|
|
@@ -572,42 +593,36 @@ class ResultDataProvider {
|
|
|
572
593
|
mentionedCodesByBase.set(baseKey, new Set());
|
|
573
594
|
mentionedCodesByBase.get(baseKey).add(code);
|
|
574
595
|
}
|
|
575
|
-
//
|
|
576
|
-
// 1)
|
|
577
|
-
//
|
|
578
|
-
//
|
|
596
|
+
// Direction A logic:
|
|
597
|
+
// 1) Base mention ("SR0054") is parent-level and considered covered only when
|
|
598
|
+
// the whole family is covered across scoped test cases:
|
|
599
|
+
// - if family has children, all children must be linked;
|
|
600
|
+
// - if family has no children, the base item itself must be linked.
|
|
601
|
+
// 2) Child mention ("SR0054-1") is exact-match and checked across scoped test cases.
|
|
579
602
|
const missingBaseWhenFamilyUncovered = new Set();
|
|
580
603
|
const missingSpecificMentionedNoFamily = new Set();
|
|
581
|
-
const missingFamilyMembers = new Set();
|
|
582
604
|
for (const [baseKey, mentionedCodes] of mentionedCodesByBase.entries()) {
|
|
583
605
|
const familyCodes = requirementFamilies.get(baseKey);
|
|
584
606
|
const mentionedCodesList = [...mentionedCodes];
|
|
585
607
|
const hasBaseMention = mentionedCodesList.some((code) => !/-\d+$/.test(code));
|
|
586
608
|
const mentionedSpecificMembers = mentionedCodesList.filter((code) => /-\d+$/.test(code));
|
|
587
609
|
if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
|
|
588
|
-
|
|
589
|
-
//
|
|
610
|
+
const familyLinkedCodes = linkedFamilyCodesAcrossTestCases.get(baseKey) || new Set();
|
|
611
|
+
// Base mention ("SR0054") requires full family coverage across selected test cases.
|
|
590
612
|
if (hasBaseMention) {
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
for (const code of missingInTargetFamily) {
|
|
602
|
-
missingFamilyMembers.add(code);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
613
|
+
const normalizedFamilyMembers = [...familyCodes]
|
|
614
|
+
.map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
|
|
615
|
+
.filter((code) => !!code);
|
|
616
|
+
const specificFamilyMembers = normalizedFamilyMembers.filter((code) => /-\d+$/.test(code));
|
|
617
|
+
const requiredFamilyMembers = specificFamilyMembers.length > 0 ? specificFamilyMembers : normalizedFamilyMembers;
|
|
618
|
+
const isWholeFamilyCovered = requiredFamilyMembers.every((memberCode) => familyLinkedCodes.has(memberCode));
|
|
619
|
+
if (!isWholeFamilyCovered) {
|
|
620
|
+
missingBaseWhenFamilyUncovered.add(baseKey);
|
|
605
621
|
}
|
|
606
|
-
continue;
|
|
607
622
|
}
|
|
608
|
-
// Specific mention ("SR0054-1") validates as exact-match only.
|
|
623
|
+
// Specific mention ("SR0054-1") validates as exact-match only across scoped test cases.
|
|
609
624
|
for (const code of mentionedSpecificMembers) {
|
|
610
|
-
if (!
|
|
625
|
+
if (!familyLinkedCodes.has(code)) {
|
|
611
626
|
missingSpecificMentionedNoFamily.add(code);
|
|
612
627
|
}
|
|
613
628
|
}
|
|
@@ -617,11 +632,11 @@ class ResultDataProvider {
|
|
|
617
632
|
for (const code of mentionedCodes) {
|
|
618
633
|
const hasSpecificSuffix = /-\d+$/.test(code);
|
|
619
634
|
if (hasSpecificSuffix) {
|
|
620
|
-
if (!
|
|
635
|
+
if (!linkedFullCodesAcrossTestCases.has(code)) {
|
|
621
636
|
missingSpecificMentionedNoFamily.add(code);
|
|
622
637
|
}
|
|
623
638
|
}
|
|
624
|
-
else if (!
|
|
639
|
+
else if (!linkedBaseKeysAcrossTestCases.has(baseKey)) {
|
|
625
640
|
missingBaseWhenFamilyUncovered.add(baseKey);
|
|
626
641
|
}
|
|
627
642
|
}
|
|
@@ -641,13 +656,21 @@ class ResultDataProvider {
|
|
|
641
656
|
return;
|
|
642
657
|
const normalizedStepRef = String(stepRef || 'Step ?').trim() || 'Step ?';
|
|
643
658
|
if (!mentionedButNotLinkedByStep.has(normalizedStepRef)) {
|
|
644
|
-
mentionedButNotLinkedByStep.set(normalizedStepRef,
|
|
659
|
+
mentionedButNotLinkedByStep.set(normalizedStepRef, {
|
|
660
|
+
baseIds: new Set(),
|
|
661
|
+
specificIds: new Set(),
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
const entry = mentionedButNotLinkedByStep.get(normalizedStepRef);
|
|
665
|
+
if (/-\d+$/.test(normalizedRequirementId)) {
|
|
666
|
+
entry.specificIds.add(normalizedRequirementId);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
entry.baseIds.add(normalizedRequirementId);
|
|
645
670
|
}
|
|
646
|
-
mentionedButNotLinkedByStep.get(normalizedStepRef).add(normalizedRequirementId);
|
|
647
671
|
};
|
|
648
672
|
const sortedMissingSpecificMentionedNoFamily = [...missingSpecificMentionedNoFamily].sort((a, b) => a.localeCompare(b));
|
|
649
673
|
const sortedMissingBaseWhenFamilyUncovered = [...missingBaseWhenFamilyUncovered].sort((a, b) => a.localeCompare(b));
|
|
650
|
-
const sortedMissingFamilyMembers = [...missingFamilyMembers].sort((a, b) => a.localeCompare(b));
|
|
651
674
|
for (const code of sortedMissingSpecificMentionedNoFamily) {
|
|
652
675
|
const stepRef = mentionedCodeFirstStep.get(code) || 'Step ?';
|
|
653
676
|
appendMentionedButNotLinked(code, stepRef);
|
|
@@ -656,11 +679,6 @@ class ResultDataProvider {
|
|
|
656
679
|
const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
|
|
657
680
|
appendMentionedButNotLinked(baseKey, stepRef);
|
|
658
681
|
}
|
|
659
|
-
for (const code of sortedMissingFamilyMembers) {
|
|
660
|
-
const baseKey = this.toRequirementKey(code);
|
|
661
|
-
const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
|
|
662
|
-
appendMentionedButNotLinked(code, stepRef);
|
|
663
|
-
}
|
|
664
682
|
const sortedExtraLinked = [...new Set(extraLinked)]
|
|
665
683
|
.map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
|
|
666
684
|
.filter((code) => !!code)
|
|
@@ -678,12 +696,20 @@ class ResultDataProvider {
|
|
|
678
696
|
return stepOrderA - stepOrderB;
|
|
679
697
|
return String(a[0]).localeCompare(String(b[0]));
|
|
680
698
|
})
|
|
681
|
-
.map(([stepRef,
|
|
682
|
-
const
|
|
683
|
-
|
|
699
|
+
.map(([stepRef, requirementIdsByType]) => {
|
|
700
|
+
const specificCodes = [...requirementIdsByType.specificIds].sort((a, b) => this.compareMewpRequirementCodes(a, b));
|
|
701
|
+
const specificFamilies = new Set(specificCodes.map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
702
|
+
const baseCodes = [...requirementIdsByType.baseIds]
|
|
703
|
+
.filter((baseCode) => !specificFamilies.has(baseCode))
|
|
704
|
+
.sort((a, b) => this.compareMewpRequirementCodes(a, b));
|
|
705
|
+
const displayCodes = [...specificCodes, ...baseCodes];
|
|
706
|
+
if (displayCodes.length === 0)
|
|
707
|
+
return `${stepRef}:`;
|
|
708
|
+
return `${stepRef}: ${displayCodes.join('; ')}`.trim();
|
|
684
709
|
})
|
|
685
|
-
.join('\n')
|
|
686
|
-
|
|
710
|
+
.join('\n')
|
|
711
|
+
.trim();
|
|
712
|
+
const linkedButNotMentioned = this.formatRequirementCodesGroupedByFamily(sortedExtraLinked).trim();
|
|
687
713
|
const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
|
|
688
714
|
if (validationStatus === 'Fail')
|
|
689
715
|
diagnostics.failingRows += 1;
|
|
@@ -691,8 +717,7 @@ class ResultDataProvider {
|
|
|
691
717
|
`testCaseId=${testCaseId} parsedSteps=${executableSteps.length} ` +
|
|
692
718
|
`stepsWithMentions=${mentionEntries.length} customerIdsFound=${mentionedL2Only.size} ` +
|
|
693
719
|
`linkedRequirements=${linkedFullCodes.size} mentionedButNotLinked=${sortedMissingSpecificMentionedNoFamily.length +
|
|
694
|
-
sortedMissingBaseWhenFamilyUncovered.length +
|
|
695
|
-
sortedMissingFamilyMembers.length} ` +
|
|
720
|
+
sortedMissingBaseWhenFamilyUncovered.length} ` +
|
|
696
721
|
`linkedButNotMentioned=${sortedExtraLinked.length} status=${validationStatus} ` +
|
|
697
722
|
`customerIdSample='${[...mentionedL2Only].slice(0, 5).join(', ')}'`);
|
|
698
723
|
rows.push({
|
|
@@ -2145,6 +2170,35 @@ class ResultDataProvider {
|
|
|
2145
2170
|
return `SR${match[1]}-${match[2]}`;
|
|
2146
2171
|
return `SR${match[1]}`;
|
|
2147
2172
|
}
|
|
2173
|
+
compareMewpRequirementCodes(a, b) {
|
|
2174
|
+
const normalizeComparableCode = (value) => {
|
|
2175
|
+
const normalizedCode = this.normalizeMewpRequirementCodeWithSuffix(value);
|
|
2176
|
+
const match = /^SR(\d+)(?:-(\d+))?$/i.exec(normalizedCode);
|
|
2177
|
+
if (!match) {
|
|
2178
|
+
return {
|
|
2179
|
+
base: Number.POSITIVE_INFINITY,
|
|
2180
|
+
hasSuffix: 1,
|
|
2181
|
+
suffix: Number.POSITIVE_INFINITY,
|
|
2182
|
+
raw: String(value || ''),
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
return {
|
|
2186
|
+
base: Number(match[1]),
|
|
2187
|
+
hasSuffix: match[2] ? 1 : 0,
|
|
2188
|
+
suffix: match[2] ? Number(match[2]) : -1,
|
|
2189
|
+
raw: normalizedCode,
|
|
2190
|
+
};
|
|
2191
|
+
};
|
|
2192
|
+
const left = normalizeComparableCode(a);
|
|
2193
|
+
const right = normalizeComparableCode(b);
|
|
2194
|
+
if (left.base !== right.base)
|
|
2195
|
+
return left.base - right.base;
|
|
2196
|
+
if (left.hasSuffix !== right.hasSuffix)
|
|
2197
|
+
return left.hasSuffix - right.hasSuffix;
|
|
2198
|
+
if (left.suffix !== right.suffix)
|
|
2199
|
+
return left.suffix - right.suffix;
|
|
2200
|
+
return left.raw.localeCompare(right.raw);
|
|
2201
|
+
}
|
|
2148
2202
|
formatRequirementCodesGroupedByFamily(codes) {
|
|
2149
2203
|
const byBaseKey = new Map();
|
|
2150
2204
|
for (const rawCode of codes || []) {
|
|
@@ -2159,12 +2213,13 @@ class ResultDataProvider {
|
|
|
2159
2213
|
if (byBaseKey.size === 0)
|
|
2160
2214
|
return '';
|
|
2161
2215
|
return [...byBaseKey.entries()]
|
|
2162
|
-
.sort((a, b) => a[0]
|
|
2216
|
+
.sort((a, b) => this.compareMewpRequirementCodes(a[0], b[0]))
|
|
2163
2217
|
.map(([baseKey, members]) => {
|
|
2164
|
-
const sortedMembers = [...members].sort((a, b) =>
|
|
2218
|
+
const sortedMembers = [...members].sort((a, b) => this.compareMewpRequirementCodes(a, b));
|
|
2165
2219
|
if (sortedMembers.length <= 1)
|
|
2166
2220
|
return sortedMembers[0];
|
|
2167
|
-
|
|
2221
|
+
// Direction B display is family-level when multiple members exist.
|
|
2222
|
+
return baseKey;
|
|
2168
2223
|
})
|
|
2169
2224
|
.join('\n');
|
|
2170
2225
|
}
|