@elisra-devops/docgen-data-provider 1.90.0 → 1.91.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.
@@ -138,6 +138,8 @@ export default class ResultDataProvider {
138
138
  private isExcludedL3L4BySapWbs;
139
139
  private normalizeMewpRequirementCode;
140
140
  private normalizeMewpRequirementCodeWithSuffix;
141
+ private compareMewpRequirementCodes;
142
+ private formatStepScopedRequirementGroups;
141
143
  private formatRequirementCodesGroupedByFamily;
142
144
  private toMewpComparableText;
143
145
  private fetchTestPlanName;
@@ -476,7 +476,7 @@ class ResultDataProvider {
476
476
  }
477
477
  }
478
478
  async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest) {
479
- var _a, _b, _c;
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 linkedFullCodesRaw = ((_c = linkedRequirementsByTestCase.get(testCaseId)) === null || _c === void 0 ? void 0 : _c.fullCodes) || new Set();
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,28 @@ class ResultDataProvider {
572
593
  mentionedCodesByBase.set(baseKey, new Set());
573
594
  mentionedCodesByBase.get(baseKey).add(code);
574
595
  }
575
- // Context-based direction A logic:
576
- // 1) If no member of family is linked -> report only base SR (Step X: SR0054).
577
- // 2) If family is partially linked -> report only specific missing members.
578
- // 3) If family fully linked -> report nothing for that family.
596
+ // Direction A logic:
597
+ // 1) Base mention ("SR0054") is parent-level only and considered covered
598
+ // if any member of that family is linked across scoped test cases.
599
+ // 2) Child mention ("SR0054-1") is exact-match and checked across scoped test cases.
579
600
  const missingBaseWhenFamilyUncovered = new Set();
580
601
  const missingSpecificMentionedNoFamily = new Set();
581
- const missingFamilyMembers = new Set();
582
602
  for (const [baseKey, mentionedCodes] of mentionedCodesByBase.entries()) {
583
603
  const familyCodes = requirementFamilies.get(baseKey);
584
604
  const mentionedCodesList = [...mentionedCodes];
585
605
  const hasBaseMention = mentionedCodesList.some((code) => !/-\d+$/.test(code));
586
606
  const mentionedSpecificMembers = mentionedCodesList.filter((code) => /-\d+$/.test(code));
587
607
  if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
588
- // Base mention ("SR0054") validates against child coverage when children exist.
589
- // If no child variants exist, fallback to the single standalone requirement code.
608
+ const familyLinkedCodes = linkedFamilyCodesAcrossTestCases.get(baseKey) || new Set();
609
+ // Base mention ("SR0054") is satisfied by any linked member in the same family.
590
610
  if (hasBaseMention) {
591
- const familyCodesList = [...familyCodes];
592
- const childFamilyCodes = familyCodesList.filter((code) => /-\d+$/.test(code));
593
- const targetFamilyCodes = childFamilyCodes.length > 0 ? childFamilyCodes : familyCodesList;
594
- const missingInTargetFamily = targetFamilyCodes.filter((code) => !linkedFullCodes.has(code));
595
- if (missingInTargetFamily.length > 0) {
596
- const hasAnyLinkedInFamily = familyCodesList.some((code) => linkedFullCodes.has(code));
597
- if (!hasAnyLinkedInFamily) {
598
- missingBaseWhenFamilyUncovered.add(baseKey);
599
- }
600
- else {
601
- for (const code of missingInTargetFamily) {
602
- missingFamilyMembers.add(code);
603
- }
604
- }
611
+ if (familyLinkedCodes.size === 0) {
612
+ missingBaseWhenFamilyUncovered.add(baseKey);
605
613
  }
606
- continue;
607
614
  }
608
- // Specific mention ("SR0054-1") validates as exact-match only.
615
+ // Specific mention ("SR0054-1") validates as exact-match only across scoped test cases.
609
616
  for (const code of mentionedSpecificMembers) {
610
- if (!linkedFullCodes.has(code)) {
617
+ if (!familyLinkedCodes.has(code)) {
611
618
  missingSpecificMentionedNoFamily.add(code);
612
619
  }
613
620
  }
@@ -617,11 +624,11 @@ class ResultDataProvider {
617
624
  for (const code of mentionedCodes) {
618
625
  const hasSpecificSuffix = /-\d+$/.test(code);
619
626
  if (hasSpecificSuffix) {
620
- if (!linkedFullCodes.has(code)) {
627
+ if (!linkedFullCodesAcrossTestCases.has(code)) {
621
628
  missingSpecificMentionedNoFamily.add(code);
622
629
  }
623
630
  }
624
- else if (!linkedBaseKeys.has(baseKey)) {
631
+ else if (!linkedBaseKeysAcrossTestCases.has(baseKey)) {
625
632
  missingBaseWhenFamilyUncovered.add(baseKey);
626
633
  }
627
634
  }
@@ -647,7 +654,6 @@ class ResultDataProvider {
647
654
  };
648
655
  const sortedMissingSpecificMentionedNoFamily = [...missingSpecificMentionedNoFamily].sort((a, b) => a.localeCompare(b));
649
656
  const sortedMissingBaseWhenFamilyUncovered = [...missingBaseWhenFamilyUncovered].sort((a, b) => a.localeCompare(b));
650
- const sortedMissingFamilyMembers = [...missingFamilyMembers].sort((a, b) => a.localeCompare(b));
651
657
  for (const code of sortedMissingSpecificMentionedNoFamily) {
652
658
  const stepRef = mentionedCodeFirstStep.get(code) || 'Step ?';
653
659
  appendMentionedButNotLinked(code, stepRef);
@@ -656,11 +662,6 @@ class ResultDataProvider {
656
662
  const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
657
663
  appendMentionedButNotLinked(baseKey, stepRef);
658
664
  }
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
665
  const sortedExtraLinked = [...new Set(extraLinked)]
665
666
  .map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
666
667
  .filter((code) => !!code)
@@ -679,11 +680,11 @@ class ResultDataProvider {
679
680
  return String(a[0]).localeCompare(String(b[0]));
680
681
  })
681
682
  .map(([stepRef, requirementIds]) => {
682
- const groupedRequirementList = this.formatRequirementCodesGroupedByFamily(requirementIds);
683
- return `${stepRef}: ${groupedRequirementList}`;
683
+ return this.formatStepScopedRequirementGroups(stepRef, requirementIds);
684
684
  })
685
- .join('\n');
686
- const linkedButNotMentioned = this.formatRequirementCodesGroupedByFamily(sortedExtraLinked);
685
+ .join('\n')
686
+ .trim();
687
+ const linkedButNotMentioned = this.formatRequirementCodesGroupedByFamily(sortedExtraLinked).trim();
687
688
  const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
688
689
  if (validationStatus === 'Fail')
689
690
  diagnostics.failingRows += 1;
@@ -691,8 +692,7 @@ class ResultDataProvider {
691
692
  `testCaseId=${testCaseId} parsedSteps=${executableSteps.length} ` +
692
693
  `stepsWithMentions=${mentionEntries.length} customerIdsFound=${mentionedL2Only.size} ` +
693
694
  `linkedRequirements=${linkedFullCodes.size} mentionedButNotLinked=${sortedMissingSpecificMentionedNoFamily.length +
694
- sortedMissingBaseWhenFamilyUncovered.length +
695
- sortedMissingFamilyMembers.length} ` +
695
+ sortedMissingBaseWhenFamilyUncovered.length} ` +
696
696
  `linkedButNotMentioned=${sortedExtraLinked.length} status=${validationStatus} ` +
697
697
  `customerIdSample='${[...mentionedL2Only].slice(0, 5).join(', ')}'`);
698
698
  rows.push({
@@ -2145,6 +2145,47 @@ class ResultDataProvider {
2145
2145
  return `SR${match[1]}-${match[2]}`;
2146
2146
  return `SR${match[1]}`;
2147
2147
  }
2148
+ compareMewpRequirementCodes(a, b) {
2149
+ const normalizeComparableCode = (value) => {
2150
+ const normalizedCode = this.normalizeMewpRequirementCodeWithSuffix(value);
2151
+ const match = /^SR(\d+)(?:-(\d+))?$/i.exec(normalizedCode);
2152
+ if (!match) {
2153
+ return {
2154
+ base: Number.POSITIVE_INFINITY,
2155
+ hasSuffix: 1,
2156
+ suffix: Number.POSITIVE_INFINITY,
2157
+ raw: String(value || ''),
2158
+ };
2159
+ }
2160
+ return {
2161
+ base: Number(match[1]),
2162
+ hasSuffix: match[2] ? 1 : 0,
2163
+ suffix: match[2] ? Number(match[2]) : -1,
2164
+ raw: normalizedCode,
2165
+ };
2166
+ };
2167
+ const left = normalizeComparableCode(a);
2168
+ const right = normalizeComparableCode(b);
2169
+ if (left.base !== right.base)
2170
+ return left.base - right.base;
2171
+ if (left.hasSuffix !== right.hasSuffix)
2172
+ return left.hasSuffix - right.hasSuffix;
2173
+ if (left.suffix !== right.suffix)
2174
+ return left.suffix - right.suffix;
2175
+ return left.raw.localeCompare(right.raw);
2176
+ }
2177
+ formatStepScopedRequirementGroups(stepRef, requirementIds) {
2178
+ const groupedRequirementList = this.formatRequirementCodesGroupedByFamily(requirementIds);
2179
+ if (!groupedRequirementList)
2180
+ return `${stepRef}:`;
2181
+ const groupedLines = groupedRequirementList
2182
+ .split('\n')
2183
+ .map((line) => String(line || '').trim())
2184
+ .filter((line) => line.length > 0);
2185
+ if (groupedLines.length <= 1)
2186
+ return `${stepRef}: ${groupedLines[0] || ''}`.trim();
2187
+ return `${stepRef}:\n${groupedLines.map((line) => `- ${line}`).join('\n')}`.trim();
2188
+ }
2148
2189
  formatRequirementCodesGroupedByFamily(codes) {
2149
2190
  const byBaseKey = new Map();
2150
2191
  for (const rawCode of codes || []) {
@@ -2159,12 +2200,16 @@ class ResultDataProvider {
2159
2200
  if (byBaseKey.size === 0)
2160
2201
  return '';
2161
2202
  return [...byBaseKey.entries()]
2162
- .sort((a, b) => a[0].localeCompare(b[0]))
2203
+ .sort((a, b) => this.compareMewpRequirementCodes(a[0], b[0]))
2163
2204
  .map(([baseKey, members]) => {
2164
- const sortedMembers = [...members].sort((a, b) => a.localeCompare(b));
2205
+ const sortedMembers = [...members].sort((a, b) => this.compareMewpRequirementCodes(a, b));
2165
2206
  if (sortedMembers.length <= 1)
2166
2207
  return sortedMembers[0];
2167
- return `${baseKey}: ${sortedMembers.join(', ')}`;
2208
+ const nonBaseMembers = sortedMembers.filter((member) => member !== baseKey);
2209
+ if (nonBaseMembers.length > 0) {
2210
+ return `${baseKey}: ${nonBaseMembers.join(', ')}`;
2211
+ }
2212
+ return baseKey;
2168
2213
  })
2169
2214
  .join('\n');
2170
2215
  }