@elisra-devops/docgen-data-provider 1.91.0 → 1.93.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.
@@ -23,6 +23,9 @@ import type { MewpCoverageFlatPayload, MewpExternalFilesValidationResponse, Mewp
23
23
  * Instantiate the class with the organization URL and token, and use the provided methods to fetch and process test data.
24
24
  */
25
25
  export default class ResultDataProvider {
26
+ private static readonly MEWP_INTERNAL_VALIDATION_TRACE_TAG;
27
+ private static readonly MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG;
28
+ private static readonly MEWP_INTERNAL_VALIDATION_SUMMARY_TAG;
26
29
  private static readonly MEWP_L2_COVERAGE_COLUMNS;
27
30
  private static readonly INTERNAL_VALIDATION_COLUMNS;
28
31
  orgUrl: string;
@@ -70,7 +73,9 @@ export default class ResultDataProvider {
70
73
  * Rows are one Requirement-TestCase pair; uncovered requirements are emitted with empty test-case columns.
71
74
  */
72
75
  getMewpL2CoverageFlatResults(testPlanId: string, projectName: string, selectedSuiteIds: number[] | undefined, linkedQueryRequest?: any, options?: MewpCoverageRequestOptions): Promise<MewpCoverageFlatPayload>;
73
- getMewpInternalValidationFlatResults(testPlanId: string, projectName: string, selectedSuiteIds: number[] | undefined, linkedQueryRequest?: any): Promise<MewpInternalValidationFlatPayload>;
76
+ getMewpInternalValidationFlatResults(testPlanId: string, projectName: string, selectedSuiteIds: number[] | undefined, linkedQueryRequest?: any, options?: {
77
+ debugMode?: boolean;
78
+ }): Promise<MewpInternalValidationFlatPayload>;
74
79
  validateMewpExternalFiles(options: {
75
80
  externalBugsFile?: MewpExternalFileRef | null;
76
81
  externalL3L4File?: MewpExternalFileRef | null;
@@ -82,6 +87,8 @@ export default class ResultDataProvider {
82
87
  mapAttachmentsUrl(runResults: any[], project: string): any[];
83
88
  private buildMewpCoverageSheetName;
84
89
  private buildInternalValidationSheetName;
90
+ private formatLogValue;
91
+ private buildTaggedLogMessage;
85
92
  private createMewpCoverageRow;
86
93
  private createEmptyMewpCoverageBugCell;
87
94
  private createEmptyMewpCoverageL3L4Cell;
@@ -139,7 +146,6 @@ export default class ResultDataProvider {
139
146
  private normalizeMewpRequirementCode;
140
147
  private normalizeMewpRequirementCodeWithSuffix;
141
148
  private compareMewpRequirementCodes;
142
- private formatStepScopedRequirementGroups;
143
149
  private formatRequirementCodesGroupedByFamily;
144
150
  private toMewpComparableText;
145
151
  private fetchTestPlanName;
@@ -475,7 +475,7 @@ class ResultDataProvider {
475
475
  return defaultPayload;
476
476
  }
477
477
  }
478
- async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest) {
478
+ async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
479
479
  var _a, _b;
480
480
  const defaultPayload = {
481
481
  sheetName: `MEWP Internal Validation - Plan ${testPlanId}`,
@@ -546,8 +546,16 @@ class ResultDataProvider {
546
546
  testCasesWithoutMentionedCustomerIds: 0,
547
547
  failingRows: 0,
548
548
  };
549
+ const traceInternalValidation = (options === null || options === void 0 ? void 0 : options.debugMode) === true;
550
+ if (traceInternalValidation) {
551
+ logger_1.default.info(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
552
+ mode: 'enabled',
553
+ source: 'ui-debug-mode',
554
+ }));
555
+ }
549
556
  for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
550
557
  diagnostics.totalTestCases += 1;
558
+ const traceCurrentTestCase = traceInternalValidation;
551
559
  const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
552
560
  const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
553
561
  ? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
@@ -593,9 +601,24 @@ class ResultDataProvider {
593
601
  mentionedCodesByBase.set(baseKey, new Set());
594
602
  mentionedCodesByBase.get(baseKey).add(code);
595
603
  }
604
+ if (traceCurrentTestCase) {
605
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
606
+ event: 'test-case-start',
607
+ tc: testCaseId,
608
+ parsedSteps: executableSteps.length,
609
+ stepsWithMentions: mentionEntries.length,
610
+ mentionedCodes: [...mentionedL2Only].sort((a, b) => this.compareMewpRequirementCodes(a, b)).join('; ') ||
611
+ '<none>',
612
+ linkedCodesInTestCase: [...linkedFullCodes]
613
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
614
+ .join('; ') || '<none>',
615
+ }));
616
+ }
596
617
  // 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.
618
+ // 1) Base mention ("SR0054") is parent-level and considered covered only when
619
+ // the whole family is covered across scoped test cases:
620
+ // - if family has children, all children must be linked;
621
+ // - if family has no children, the base item itself must be linked.
599
622
  // 2) Child mention ("SR0054-1") is exact-match and checked across scoped test cases.
600
623
  const missingBaseWhenFamilyUncovered = new Set();
601
624
  const missingSpecificMentionedNoFamily = new Set();
@@ -606,32 +629,88 @@ class ResultDataProvider {
606
629
  const mentionedSpecificMembers = mentionedCodesList.filter((code) => /-\d+$/.test(code));
607
630
  if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
608
631
  const familyLinkedCodes = linkedFamilyCodesAcrossTestCases.get(baseKey) || new Set();
609
- // Base mention ("SR0054") is satisfied by any linked member in the same family.
632
+ const normalizedFamilyMembers = [...familyCodes]
633
+ .map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
634
+ .filter((code) => !!code);
635
+ const specificFamilyMembers = normalizedFamilyMembers.filter((code) => /-\d+$/.test(code));
636
+ const requiredFamilyMembers = specificFamilyMembers.length > 0 ? specificFamilyMembers : normalizedFamilyMembers;
637
+ // Base mention ("SR0054") requires full family coverage across selected test cases.
610
638
  if (hasBaseMention) {
611
- if (familyLinkedCodes.size === 0) {
639
+ const missingRequiredFamilyMembers = requiredFamilyMembers.filter((memberCode) => !familyLinkedCodes.has(memberCode));
640
+ const isWholeFamilyCovered = requiredFamilyMembers.every((memberCode) => familyLinkedCodes.has(memberCode));
641
+ if (!isWholeFamilyCovered) {
612
642
  missingBaseWhenFamilyUncovered.add(baseKey);
613
643
  }
644
+ if (traceCurrentTestCase) {
645
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
646
+ event: 'base-family-coverage',
647
+ tc: testCaseId,
648
+ base: baseKey,
649
+ baseMention: true,
650
+ requiredFamily: requiredFamilyMembers
651
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
652
+ .join('; ') || '<none>',
653
+ linkedAcrossScope: [...familyLinkedCodes]
654
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
655
+ .join('; ') || '<none>',
656
+ missingRequired: missingRequiredFamilyMembers
657
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
658
+ .join('; ') || '<none>',
659
+ covered: isWholeFamilyCovered,
660
+ }));
661
+ }
614
662
  }
615
663
  // Specific mention ("SR0054-1") validates as exact-match only across scoped test cases.
616
- for (const code of mentionedSpecificMembers) {
617
- if (!familyLinkedCodes.has(code)) {
618
- missingSpecificMentionedNoFamily.add(code);
619
- }
664
+ const missingSpecificMembers = mentionedSpecificMembers.filter((code) => !familyLinkedCodes.has(code));
665
+ for (const code of missingSpecificMembers) {
666
+ missingSpecificMentionedNoFamily.add(code);
667
+ }
668
+ if (traceCurrentTestCase && mentionedSpecificMembers.length > 0) {
669
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
670
+ event: 'specific-members-check',
671
+ tc: testCaseId,
672
+ base: baseKey,
673
+ specificMentioned: mentionedSpecificMembers
674
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
675
+ .join('; ') || '<none>',
676
+ specificMissing: missingSpecificMembers
677
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
678
+ .join('; ') || '<none>',
679
+ }));
620
680
  }
621
681
  continue;
622
682
  }
623
683
  // Fallback path when family data is unavailable for this base key.
684
+ const fallbackMissingSpecific = [];
685
+ let fallbackMissingBase = false;
624
686
  for (const code of mentionedCodes) {
625
687
  const hasSpecificSuffix = /-\d+$/.test(code);
626
688
  if (hasSpecificSuffix) {
627
689
  if (!linkedFullCodesAcrossTestCases.has(code)) {
628
690
  missingSpecificMentionedNoFamily.add(code);
691
+ fallbackMissingSpecific.push(code);
629
692
  }
630
693
  }
631
694
  else if (!linkedBaseKeysAcrossTestCases.has(baseKey)) {
632
695
  missingBaseWhenFamilyUncovered.add(baseKey);
696
+ fallbackMissingBase = true;
633
697
  }
634
698
  }
699
+ if (traceCurrentTestCase) {
700
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
701
+ event: 'fallback-path',
702
+ tc: testCaseId,
703
+ base: baseKey,
704
+ fallbackUsed: true,
705
+ mentioned: mentionedCodesList
706
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
707
+ .join('; ') || '<none>',
708
+ missingSpecific: fallbackMissingSpecific
709
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b))
710
+ .join('; ') || '<none>',
711
+ missingBase: fallbackMissingBase,
712
+ }));
713
+ }
635
714
  }
636
715
  // Direction B is family-based: if any member of a family is mentioned in Expected Result,
637
716
  // linked members of that same family are not considered "linked but not mentioned".
@@ -648,9 +727,18 @@ class ResultDataProvider {
648
727
  return;
649
728
  const normalizedStepRef = String(stepRef || 'Step ?').trim() || 'Step ?';
650
729
  if (!mentionedButNotLinkedByStep.has(normalizedStepRef)) {
651
- mentionedButNotLinkedByStep.set(normalizedStepRef, new Set());
730
+ mentionedButNotLinkedByStep.set(normalizedStepRef, {
731
+ baseIds: new Set(),
732
+ specificIds: new Set(),
733
+ });
734
+ }
735
+ const entry = mentionedButNotLinkedByStep.get(normalizedStepRef);
736
+ if (/-\d+$/.test(normalizedRequirementId)) {
737
+ entry.specificIds.add(normalizedRequirementId);
738
+ }
739
+ else {
740
+ entry.baseIds.add(normalizedRequirementId);
652
741
  }
653
- mentionedButNotLinkedByStep.get(normalizedStepRef).add(normalizedRequirementId);
654
742
  };
655
743
  const sortedMissingSpecificMentionedNoFamily = [...missingSpecificMentionedNoFamily].sort((a, b) => a.localeCompare(b));
656
744
  const sortedMissingBaseWhenFamilyUncovered = [...missingBaseWhenFamilyUncovered].sort((a, b) => a.localeCompare(b));
@@ -662,6 +750,14 @@ class ResultDataProvider {
662
750
  const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
663
751
  appendMentionedButNotLinked(baseKey, stepRef);
664
752
  }
753
+ if (traceCurrentTestCase) {
754
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG, {
755
+ event: 'direction-a-summary',
756
+ tc: testCaseId,
757
+ missingSpecific: sortedMissingSpecificMentionedNoFamily.join('; ') || '<none>',
758
+ missingBase: sortedMissingBaseWhenFamilyUncovered.join('; ') || '<none>',
759
+ }));
760
+ }
665
761
  const sortedExtraLinked = [...new Set(extraLinked)]
666
762
  .map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
667
763
  .filter((code) => !!code)
@@ -679,8 +775,16 @@ class ResultDataProvider {
679
775
  return stepOrderA - stepOrderB;
680
776
  return String(a[0]).localeCompare(String(b[0]));
681
777
  })
682
- .map(([stepRef, requirementIds]) => {
683
- return this.formatStepScopedRequirementGroups(stepRef, requirementIds);
778
+ .map(([stepRef, requirementIdsByType]) => {
779
+ const specificCodes = [...requirementIdsByType.specificIds].sort((a, b) => this.compareMewpRequirementCodes(a, b));
780
+ const specificFamilies = new Set(specificCodes.map((code) => this.toRequirementKey(code)).filter((code) => !!code));
781
+ const baseCodes = [...requirementIdsByType.baseIds]
782
+ .filter((baseCode) => !specificFamilies.has(baseCode))
783
+ .sort((a, b) => this.compareMewpRequirementCodes(a, b));
784
+ const displayCodes = [...specificCodes, ...baseCodes];
785
+ if (displayCodes.length === 0)
786
+ return `${stepRef}:`;
787
+ return `${stepRef}: ${displayCodes.join('; ')}`.trim();
684
788
  })
685
789
  .join('\n')
686
790
  .trim();
@@ -688,13 +792,18 @@ class ResultDataProvider {
688
792
  const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
689
793
  if (validationStatus === 'Fail')
690
794
  diagnostics.failingRows += 1;
691
- logger_1.default.debug(`MEWP internal validation parse diagnostics: ` +
692
- `testCaseId=${testCaseId} parsedSteps=${executableSteps.length} ` +
693
- `stepsWithMentions=${mentionEntries.length} customerIdsFound=${mentionedL2Only.size} ` +
694
- `linkedRequirements=${linkedFullCodes.size} mentionedButNotLinked=${sortedMissingSpecificMentionedNoFamily.length +
695
- sortedMissingBaseWhenFamilyUncovered.length} ` +
696
- `linkedButNotMentioned=${sortedExtraLinked.length} status=${validationStatus} ` +
697
- `customerIdSample='${[...mentionedL2Only].slice(0, 5).join(', ')}'`);
795
+ logger_1.default.debug(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG, {
796
+ testCaseId,
797
+ parsedSteps: executableSteps.length,
798
+ stepsWithMentions: mentionEntries.length,
799
+ customerIdsFound: mentionedL2Only.size,
800
+ linkedRequirements: linkedFullCodes.size,
801
+ mentionedButNotLinked: sortedMissingSpecificMentionedNoFamily.length +
802
+ sortedMissingBaseWhenFamilyUncovered.length,
803
+ linkedButNotMentioned: sortedExtraLinked.length,
804
+ status: validationStatus,
805
+ customerIdSample: [...mentionedL2Only].slice(0, 5).join(', '),
806
+ }));
698
807
  rows.push({
699
808
  'Test Case ID': testCaseId,
700
809
  'Test Case Title': String(testCaseTitleMap.get(testCaseId) || '').trim(),
@@ -703,11 +812,14 @@ class ResultDataProvider {
703
812
  'Validation Status': validationStatus,
704
813
  });
705
814
  }
706
- logger_1.default.info(`MEWP internal validation summary: testCases=${diagnostics.totalTestCases} ` +
707
- `parsedSteps=${diagnostics.totalParsedSteps} stepsWithMentions=${diagnostics.totalStepsWithMentions} ` +
708
- `totalCustomerIdsFound=${diagnostics.totalMentionedCustomerIds} ` +
709
- `testCasesWithoutCustomerIds=${diagnostics.testCasesWithoutMentionedCustomerIds} ` +
710
- `failingRows=${diagnostics.failingRows}`);
815
+ logger_1.default.info(this.buildTaggedLogMessage(ResultDataProvider.MEWP_INTERNAL_VALIDATION_SUMMARY_TAG, {
816
+ testCases: diagnostics.totalTestCases,
817
+ parsedSteps: diagnostics.totalParsedSteps,
818
+ stepsWithMentions: diagnostics.totalStepsWithMentions,
819
+ totalCustomerIdsFound: diagnostics.totalMentionedCustomerIds,
820
+ testCasesWithoutCustomerIds: diagnostics.testCasesWithoutMentionedCustomerIds,
821
+ failingRows: diagnostics.failingRows,
822
+ }));
711
823
  return {
712
824
  sheetName: this.buildInternalValidationSheetName(planName, testPlanId),
713
825
  columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
@@ -796,6 +908,16 @@ class ResultDataProvider {
796
908
  const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
797
909
  return `MEWP Internal Validation - ${suffix}`;
798
910
  }
911
+ formatLogValue(value) {
912
+ if (value === null || value === undefined)
913
+ return '<none>';
914
+ const asText = String(value).trim();
915
+ return asText !== '' ? asText : '<none>';
916
+ }
917
+ buildTaggedLogMessage(tag, fields) {
918
+ const sections = Object.entries(fields).map(([key, value]) => `${key}=${this.formatLogValue(value)}`);
919
+ return `${tag} ${sections.join(' | ')}`;
920
+ }
799
921
  createMewpCoverageRow(requirement, runStatus, bug, linkedL3L4) {
800
922
  const l2ReqIdNumeric = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
801
923
  const l2ReqId = l2ReqIdNumeric > 0 ? String(l2ReqIdNumeric) : '';
@@ -2174,18 +2296,6 @@ class ResultDataProvider {
2174
2296
  return left.suffix - right.suffix;
2175
2297
  return left.raw.localeCompare(right.raw);
2176
2298
  }
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
- }
2189
2299
  formatRequirementCodesGroupedByFamily(codes) {
2190
2300
  const byBaseKey = new Map();
2191
2301
  for (const rawCode of codes || []) {
@@ -2205,13 +2315,10 @@ class ResultDataProvider {
2205
2315
  const sortedMembers = [...members].sort((a, b) => this.compareMewpRequirementCodes(a, b));
2206
2316
  if (sortedMembers.length <= 1)
2207
2317
  return sortedMembers[0];
2208
- const nonBaseMembers = sortedMembers.filter((member) => member !== baseKey);
2209
- if (nonBaseMembers.length > 0) {
2210
- return `${baseKey}: ${nonBaseMembers.join(', ')}`;
2211
- }
2318
+ // Direction B display is family-level when multiple members exist.
2212
2319
  return baseKey;
2213
2320
  })
2214
- .join('\n');
2321
+ .join('; ');
2215
2322
  }
2216
2323
  toMewpComparableText(value) {
2217
2324
  if (value === null || value === undefined)
@@ -4190,6 +4297,9 @@ class ResultDataProvider {
4190
4297
  return customFields;
4191
4298
  }
4192
4299
  }
4300
+ ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG = '[MEWP][InternalValidation][Trace]';
4301
+ ResultDataProvider.MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG = '[MEWP][InternalValidation][Diagnostics]';
4302
+ ResultDataProvider.MEWP_INTERNAL_VALIDATION_SUMMARY_TAG = '[MEWP][InternalValidation][Summary]';
4193
4303
  ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
4194
4304
  'L2 REQ ID',
4195
4305
  'L2 REQ Title',