@elisra-devops/docgen-data-provider 1.88.0 → 1.90.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.
@@ -296,7 +296,7 @@ class ResultDataProvider {
296
296
  };
297
297
  try {
298
298
  const planName = await this.fetchTestPlanName(testPlanId, projectName);
299
- const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, !!(options === null || options === void 0 ? void 0 : options.useRelFallback));
299
+ const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds);
300
300
  const allRequirements = await this.fetchMewpL2Requirements(projectName);
301
301
  if (allRequirements.length === 0) {
302
302
  return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
@@ -475,7 +475,7 @@ class ResultDataProvider {
475
475
  return defaultPayload;
476
476
  }
477
477
  }
478
- async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
478
+ async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest) {
479
479
  var _a, _b, _c;
480
480
  const defaultPayload = {
481
481
  sheetName: `MEWP Internal Validation - Plan ${testPlanId}`,
@@ -484,7 +484,7 @@ class ResultDataProvider {
484
484
  };
485
485
  try {
486
486
  const planName = await this.fetchTestPlanName(testPlanId, projectName);
487
- const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, !!(options === null || options === void 0 ? void 0 : options.useRelFallback));
487
+ const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds);
488
488
  const allRequirements = await this.fetchMewpL2Requirements(projectName);
489
489
  const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
490
490
  const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
@@ -523,35 +523,6 @@ class ResultDataProvider {
523
523
  };
524
524
  for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
525
525
  diagnostics.totalTestCases += 1;
526
- const logList = (items, max = 12) => {
527
- const values = [...items].map((item) => String(item || '').trim()).filter((item) => !!item);
528
- const shown = values.slice(0, max);
529
- const suffix = values.length > max ? ` ...(+${values.length - max} more)` : '';
530
- return shown.join(', ') + suffix;
531
- };
532
- const logByFamily = (items, maxFamilies = 8, maxMembers = 10) => {
533
- const map = new Map();
534
- for (const item of items || []) {
535
- const normalized = this.normalizeMewpRequirementCodeWithSuffix(String(item || ''));
536
- if (!normalized)
537
- continue;
538
- const base = this.toRequirementKey(normalized) || normalized;
539
- if (!map.has(base))
540
- map.set(base, new Set());
541
- map.get(base).add(normalized);
542
- }
543
- const entries = [...map.entries()]
544
- .sort((a, b) => a[0].localeCompare(b[0]))
545
- .slice(0, maxFamilies)
546
- .map(([base, members]) => {
547
- const sortedMembers = [...members].sort((a, b) => a.localeCompare(b));
548
- const shownMembers = sortedMembers.slice(0, maxMembers);
549
- const membersSuffix = sortedMembers.length > maxMembers ? ` ...(+${sortedMembers.length - maxMembers} more)` : '';
550
- return `${base}=[${shownMembers.join(', ')}${membersSuffix}]`;
551
- });
552
- const suffix = map.size > maxFamilies ? ` ...(+${map.size - maxFamilies} families)` : '';
553
- return entries.join(' | ') + suffix;
554
- };
555
526
  const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
556
527
  const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
557
528
  ? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
@@ -592,13 +563,6 @@ class ResultDataProvider {
592
563
  ? new Set([...linkedFullCodesRaw].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code))))
593
564
  : linkedFullCodesRaw;
594
565
  const linkedBaseKeys = new Set([...linkedFullCodes].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
595
- const mentionStepSample = mentionEntries
596
- .slice(0, 8)
597
- .map((entry) => `${entry.stepRef}=[${logList(entry.codes, 6)}]`)
598
- .join(' | ');
599
- logger_1.default.debug(`MEWP internal validation trace: testCaseId=${testCaseId} ` +
600
- `mentionSteps=${mentionEntries.length} mentionStepSample='${mentionStepSample}' ` +
601
- `mentionedL2ByFamily='${logByFamily(mentionedL2Only)}' linkedByFamily='${logByFamily(linkedFullCodes)}'`);
602
566
  const mentionedCodesByBase = new Map();
603
567
  for (const code of mentionedL2Only) {
604
568
  const baseKey = this.toRequirementKey(code);
@@ -620,82 +584,47 @@ class ResultDataProvider {
620
584
  const mentionedCodesList = [...mentionedCodes];
621
585
  const hasBaseMention = mentionedCodesList.some((code) => !/-\d+$/.test(code));
622
586
  const mentionedSpecificMembers = mentionedCodesList.filter((code) => /-\d+$/.test(code));
623
- let familyDecision = 'no-action';
624
- let familyTargetCodes = [];
625
- let familyMissingCodes = [];
626
- let familyLinkedCodes = [];
627
- let familyAllCodes = [];
628
587
  if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
629
- familyAllCodes = [...familyCodes].sort((a, b) => a.localeCompare(b));
630
- familyLinkedCodes = familyAllCodes.filter((code) => linkedFullCodes.has(code));
631
588
  // Base mention ("SR0054") validates against child coverage when children exist.
632
589
  // If no child variants exist, fallback to the single standalone requirement code.
633
590
  if (hasBaseMention) {
634
591
  const familyCodesList = [...familyCodes];
635
592
  const childFamilyCodes = familyCodesList.filter((code) => /-\d+$/.test(code));
636
593
  const targetFamilyCodes = childFamilyCodes.length > 0 ? childFamilyCodes : familyCodesList;
637
- familyTargetCodes = [...targetFamilyCodes].sort((a, b) => a.localeCompare(b));
638
594
  const missingInTargetFamily = targetFamilyCodes.filter((code) => !linkedFullCodes.has(code));
639
- familyMissingCodes = [...missingInTargetFamily].sort((a, b) => a.localeCompare(b));
640
595
  if (missingInTargetFamily.length > 0) {
641
596
  const hasAnyLinkedInFamily = familyCodesList.some((code) => linkedFullCodes.has(code));
642
597
  if (!hasAnyLinkedInFamily) {
643
598
  missingBaseWhenFamilyUncovered.add(baseKey);
644
- familyDecision = 'base-mentioned-family-uncovered';
645
599
  }
646
600
  else {
647
601
  for (const code of missingInTargetFamily) {
648
602
  missingFamilyMembers.add(code);
649
603
  }
650
- familyDecision = 'base-mentioned-family-partial-missing-children';
651
604
  }
652
605
  }
653
- else {
654
- familyDecision = 'base-mentioned-family-fully-covered';
655
- }
656
- logger_1.default.debug(`MEWP internal validation family decision: testCaseId=${testCaseId} base=${baseKey} ` +
657
- `mode=baseMention mentioned='${logList(mentionedCodesList)}' familyAll='${logList(familyAllCodes)}' ` +
658
- `target='${logList(familyTargetCodes)}' linked='${logList(familyLinkedCodes)}' ` +
659
- `missing='${logList(familyMissingCodes)}' decision=${familyDecision}`);
660
606
  continue;
661
607
  }
662
608
  // Specific mention ("SR0054-1") validates as exact-match only.
663
- const missingSpecificMembers = [];
664
609
  for (const code of mentionedSpecificMembers) {
665
610
  if (!linkedFullCodes.has(code)) {
666
611
  missingSpecificMentionedNoFamily.add(code);
667
- missingSpecificMembers.push(code);
668
612
  }
669
613
  }
670
- familyDecision =
671
- missingSpecificMembers.length > 0
672
- ? 'specific-mentioned-exact-missing'
673
- : 'specific-mentioned-exact-covered';
674
- logger_1.default.debug(`MEWP internal validation family decision: testCaseId=${testCaseId} base=${baseKey} ` +
675
- `mode=specificMention mentioned='${logList(mentionedCodesList)}' familyAll='${logList(familyAllCodes)}' ` +
676
- `linked='${logList(familyLinkedCodes)}' missingSpecific='${logList(missingSpecificMembers)}' ` +
677
- `decision=${familyDecision}`);
678
614
  continue;
679
615
  }
680
616
  // Fallback path when family data is unavailable for this base key.
681
- const fallbackMissingSpecific = [];
682
- let fallbackBaseMissing = false;
683
617
  for (const code of mentionedCodes) {
684
618
  const hasSpecificSuffix = /-\d+$/.test(code);
685
619
  if (hasSpecificSuffix) {
686
620
  if (!linkedFullCodes.has(code)) {
687
621
  missingSpecificMentionedNoFamily.add(code);
688
- fallbackMissingSpecific.push(code);
689
622
  }
690
623
  }
691
624
  else if (!linkedBaseKeys.has(baseKey)) {
692
625
  missingBaseWhenFamilyUncovered.add(baseKey);
693
- fallbackBaseMissing = true;
694
626
  }
695
627
  }
696
- logger_1.default.debug(`MEWP internal validation family decision: testCaseId=${testCaseId} base=${baseKey} ` +
697
- `mode=noFamilyData mentioned='${logList(mentionedCodesList)}' linkedBasePresent=${linkedBaseKeys.has(baseKey)} ` +
698
- `missingSpecific='${logList(fallbackMissingSpecific)}' missingBase=${fallbackBaseMissing}`);
699
628
  }
700
629
  // Direction B is family-based: if any member of a family is mentioned in Expected Result,
701
630
  // linked members of that same family are not considered "linked but not mentioned".
@@ -753,14 +682,8 @@ class ResultDataProvider {
753
682
  const groupedRequirementList = this.formatRequirementCodesGroupedByFamily(requirementIds);
754
683
  return `${stepRef}: ${groupedRequirementList}`;
755
684
  })
756
- .join('; ');
685
+ .join('\n');
757
686
  const linkedButNotMentioned = this.formatRequirementCodesGroupedByFamily(sortedExtraLinked);
758
- const rawMentionedByStepForLog = [...mentionedButNotLinkedByStep.entries()]
759
- .map(([stepRef, requirementIds]) => `${stepRef}=[${logList(requirementIds, 8)}]`)
760
- .join(' | ');
761
- logger_1.default.debug(`MEWP internal validation grouped diagnostics: testCaseId=${testCaseId} ` +
762
- `rawMentionedByStep='${rawMentionedByStepForLog}' groupedMentioned='${mentionedButNotLinked}' ` +
763
- `rawLinkedOnlyByFamily='${logByFamily(sortedExtraLinked)}' groupedLinkedOnly='${linkedButNotMentioned}'`);
764
687
  const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
765
688
  if (validationStatus === 'Fail')
766
689
  diagnostics.failingRows += 1;
@@ -1117,126 +1040,9 @@ class ResultDataProvider {
1117
1040
  return 'Pass';
1118
1041
  return (input === null || input === void 0 ? void 0 : input.hasAnyTestCase) ? 'Not Run' : 'Not Run';
1119
1042
  }
1120
- async fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, useRelFallback) {
1121
- if (!useRelFallback) {
1122
- const suites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
1123
- return this.fetchTestData(suites, projectName, testPlanId, false);
1124
- }
1125
- const selectedSuites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
1126
- const selectedRel = this.resolveMaxRelNumberFromSuites(selectedSuites);
1127
- if (selectedRel <= 0) {
1128
- return this.fetchTestData(selectedSuites, projectName, testPlanId, false);
1129
- }
1130
- const allSuites = await this.fetchTestSuites(testPlanId, projectName, undefined, true);
1131
- const relScopedSuites = allSuites.filter((suite) => {
1132
- const rel = this.extractRelNumberFromSuite(suite);
1133
- return rel > 0 && rel <= selectedRel;
1134
- });
1135
- const suitesForFetch = relScopedSuites.length > 0 ? relScopedSuites : selectedSuites;
1136
- const rawTestData = await this.fetchTestData(suitesForFetch, projectName, testPlanId, false);
1137
- return this.reduceToLatestRelRunPerTestCase(rawTestData);
1138
- }
1139
- extractRelNumberFromSuite(suite) {
1140
- const candidates = [
1141
- suite === null || suite === void 0 ? void 0 : suite.suiteName,
1142
- suite === null || suite === void 0 ? void 0 : suite.parentSuiteName,
1143
- suite === null || suite === void 0 ? void 0 : suite.suitePath,
1144
- suite === null || suite === void 0 ? void 0 : suite.testGroupName,
1145
- ];
1146
- const pattern = /(?:^|[^a-z0-9])rel\s*([0-9]+)/i;
1147
- for (const item of candidates) {
1148
- const match = pattern.exec(String(item || ''));
1149
- if (!match)
1150
- continue;
1151
- const parsed = Number(match[1]);
1152
- if (Number.isFinite(parsed) && parsed > 0) {
1153
- return parsed;
1154
- }
1155
- }
1156
- return 0;
1157
- }
1158
- resolveMaxRelNumberFromSuites(suites) {
1159
- let maxRel = 0;
1160
- for (const suite of suites || []) {
1161
- const rel = this.extractRelNumberFromSuite(suite);
1162
- if (rel > maxRel)
1163
- maxRel = rel;
1164
- }
1165
- return maxRel;
1166
- }
1167
- reduceToLatestRelRunPerTestCase(testData) {
1168
- var _a, _b;
1169
- const candidatesByTestCase = new Map();
1170
- const testCaseDefinitionById = new Map();
1171
- for (const suite of testData || []) {
1172
- const rel = this.extractRelNumberFromSuite(suite);
1173
- const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
1174
- const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
1175
- for (const testCase of testCasesItems) {
1176
- const testCaseId = Number(((_a = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _a === void 0 ? void 0 : _a.id) || (testCase === null || testCase === void 0 ? void 0 : testCase.testCaseId) || (testCase === null || testCase === void 0 ? void 0 : testCase.id) || 0);
1177
- if (!Number.isFinite(testCaseId) || testCaseId <= 0)
1178
- continue;
1179
- if (!testCaseDefinitionById.has(testCaseId)) {
1180
- testCaseDefinitionById.set(testCaseId, testCase);
1181
- }
1182
- }
1183
- for (const point of testPointsItems) {
1184
- const testCaseId = Number((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_b = point === null || point === void 0 ? void 0 : point.testCase) === null || _b === void 0 ? void 0 : _b.id) || 0);
1185
- if (!Number.isFinite(testCaseId) || testCaseId <= 0)
1186
- continue;
1187
- const runId = Number((point === null || point === void 0 ? void 0 : point.lastRunId) || 0);
1188
- const resultId = Number((point === null || point === void 0 ? void 0 : point.lastResultId) || 0);
1189
- const hasRun = runId > 0 && resultId > 0;
1190
- if (!candidatesByTestCase.has(testCaseId)) {
1191
- candidatesByTestCase.set(testCaseId, []);
1192
- }
1193
- candidatesByTestCase.get(testCaseId).push({
1194
- point,
1195
- rel,
1196
- runId,
1197
- resultId,
1198
- hasRun,
1199
- });
1200
- }
1201
- }
1202
- const selectedPoints = [];
1203
- const selectedTestCaseIds = new Set();
1204
- for (const [testCaseId, candidates] of candidatesByTestCase.entries()) {
1205
- const sorted = [...candidates].sort((a, b) => {
1206
- if (a.hasRun !== b.hasRun)
1207
- return a.hasRun ? -1 : 1;
1208
- if (a.rel !== b.rel)
1209
- return b.rel - a.rel;
1210
- if (a.runId !== b.runId)
1211
- return b.runId - a.runId;
1212
- return b.resultId - a.resultId;
1213
- });
1214
- const chosen = sorted[0];
1215
- if (!(chosen === null || chosen === void 0 ? void 0 : chosen.point))
1216
- continue;
1217
- selectedPoints.push(chosen.point);
1218
- selectedTestCaseIds.add(testCaseId);
1219
- }
1220
- const selectedTestCases = [];
1221
- for (const testCaseId of selectedTestCaseIds) {
1222
- const definition = testCaseDefinitionById.get(testCaseId);
1223
- if (definition) {
1224
- selectedTestCases.push(definition);
1225
- }
1226
- }
1227
- return [
1228
- {
1229
- testSuiteId: 'MEWP_REL_SCOPED',
1230
- suiteId: 'MEWP_REL_SCOPED',
1231
- suiteName: 'MEWP Rel Scoped',
1232
- parentSuiteId: '',
1233
- parentSuiteName: '',
1234
- suitePath: 'MEWP Rel Scoped',
1235
- testGroupName: 'MEWP Rel Scoped',
1236
- testPointsItems: selectedPoints,
1237
- testCasesItems: selectedTestCases,
1238
- },
1239
- ];
1043
+ async fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds) {
1044
+ const suites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
1045
+ return this.fetchTestData(suites, projectName, testPlanId, false);
1240
1046
  }
1241
1047
  async loadExternalBugsByTestCase(externalBugsFile) {
1242
1048
  return this.mewpExternalIngestionUtils.loadExternalBugsByTestCase(externalBugsFile, {
@@ -1551,16 +1357,6 @@ class ResultDataProvider {
1551
1357
  }
1552
1358
  return out;
1553
1359
  }
1554
- extractRequirementCodesFromExpectedSteps(steps, includeSuffix) {
1555
- const out = new Set();
1556
- for (const step of Array.isArray(steps) ? steps : []) {
1557
- if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
1558
- continue;
1559
- const codes = this.extractRequirementCodesFromExpectedText((step === null || step === void 0 ? void 0 : step.expected) || '', includeSuffix);
1560
- codes.forEach((code) => out.add(code));
1561
- }
1562
- return out;
1563
- }
1564
1360
  extractRequirementCodesFromExpectedText(text, includeSuffix) {
1565
1361
  const out = new Set();
1566
1362
  const source = this.normalizeRequirementStepText(text);
@@ -2370,7 +2166,7 @@ class ResultDataProvider {
2370
2166
  return sortedMembers[0];
2371
2167
  return `${baseKey}: ${sortedMembers.join(', ')}`;
2372
2168
  })
2373
- .join('; ');
2169
+ .join('\n');
2374
2170
  }
2375
2171
  toMewpComparableText(value) {
2376
2172
  if (value === null || value === undefined)
@@ -2653,6 +2449,120 @@ class ResultDataProvider {
2653
2449
  const { value: testCases } = await tfs_1.TFSServices.getItemContent(url, this.token);
2654
2450
  return testCases;
2655
2451
  }
2452
+ attachSuiteTestCaseContextToPoints(testCasesItems, testPointsItems) {
2453
+ var _a;
2454
+ const points = Array.isArray(testPointsItems) ? testPointsItems : [];
2455
+ const testCases = Array.isArray(testCasesItems) ? testCasesItems : [];
2456
+ if (points.length === 0 || testCases.length === 0)
2457
+ return points;
2458
+ const byTestCaseId = new Map();
2459
+ for (const testCaseItem of testCases) {
2460
+ const testCaseId = Number(((_a = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _a === void 0 ? void 0 : _a.id) || (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.testCaseId) || (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.id) || 0);
2461
+ if (!Number.isFinite(testCaseId) || testCaseId <= 0 || byTestCaseId.has(testCaseId))
2462
+ continue;
2463
+ byTestCaseId.set(testCaseId, testCaseItem);
2464
+ }
2465
+ return points.map((point) => {
2466
+ var _a;
2467
+ const testCaseId = Number((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_a = point === null || point === void 0 ? void 0 : point.testCase) === null || _a === void 0 ? void 0 : _a.id) || 0);
2468
+ if (!Number.isFinite(testCaseId) || testCaseId <= 0)
2469
+ return point;
2470
+ const suiteTestCase = byTestCaseId.get(testCaseId);
2471
+ if (!suiteTestCase)
2472
+ return point;
2473
+ return Object.assign(Object.assign({}, point), { suiteTestCase });
2474
+ });
2475
+ }
2476
+ extractWorkItemFieldsMap(workItemFields) {
2477
+ const fields = {};
2478
+ if (!Array.isArray(workItemFields))
2479
+ return fields;
2480
+ for (const field of workItemFields) {
2481
+ const keyCandidates = [field === null || field === void 0 ? void 0 : field.key, field === null || field === void 0 ? void 0 : field.name, field === null || field === void 0 ? void 0 : field.referenceName]
2482
+ .map((item) => String(item || '').trim())
2483
+ .filter((item) => !!item);
2484
+ for (const key of keyCandidates) {
2485
+ fields[key] = field === null || field === void 0 ? void 0 : field.value;
2486
+ }
2487
+ }
2488
+ return fields;
2489
+ }
2490
+ resolveSuiteTestCaseRevision(testCaseItem) {
2491
+ var _a, _b, _c, _d, _e;
2492
+ const revisionCandidates = [
2493
+ (_a = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _a === void 0 ? void 0 : _a.rev,
2494
+ (_b = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _b === void 0 ? void 0 : _b.revision,
2495
+ (_c = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _c === void 0 ? void 0 : _c.version,
2496
+ (_d = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _d === void 0 ? void 0 : _d.workItemRevision,
2497
+ (_e = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _e === void 0 ? void 0 : _e.workItemVersion,
2498
+ testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.revision,
2499
+ testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItemRevision,
2500
+ testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItemVersion,
2501
+ ];
2502
+ for (const candidate of revisionCandidates) {
2503
+ const parsed = Number(candidate || 0);
2504
+ if (Number.isFinite(parsed) && parsed > 0)
2505
+ return parsed;
2506
+ }
2507
+ return 0;
2508
+ }
2509
+ buildWorkItemSnapshotFromSuiteTestCase(testCaseItem, fallbackTestCaseId, fallbackTestCaseName = '') {
2510
+ var _a;
2511
+ if (!testCaseItem)
2512
+ return null;
2513
+ const testCaseId = Number(((_a = testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) === null || _a === void 0 ? void 0 : _a.id) || (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.testCaseId) || (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.id) || fallbackTestCaseId || 0);
2514
+ if (!Number.isFinite(testCaseId) || testCaseId <= 0)
2515
+ return null;
2516
+ const workItem = (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.workItem) || {};
2517
+ const stepsXml = this.extractStepsXmlFromTestCaseItem(testCaseItem);
2518
+ const fieldsFromList = this.extractWorkItemFieldsMap(workItem === null || workItem === void 0 ? void 0 : workItem.workItemFields);
2519
+ const fieldsFromMap = (workItem === null || workItem === void 0 ? void 0 : workItem.fields) || {};
2520
+ const fields = Object.assign(Object.assign({}, fieldsFromList), fieldsFromMap);
2521
+ if (!fields['System.Title']) {
2522
+ const title = String((testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.testCaseName) || (workItem === null || workItem === void 0 ? void 0 : workItem.name) || (testCaseItem === null || testCaseItem === void 0 ? void 0 : testCaseItem.name) || fallbackTestCaseName || '').trim();
2523
+ if (title) {
2524
+ fields['System.Title'] = title;
2525
+ }
2526
+ }
2527
+ if (stepsXml && !fields['Microsoft.VSTS.TCM.Steps']) {
2528
+ fields['Microsoft.VSTS.TCM.Steps'] = stepsXml;
2529
+ }
2530
+ return {
2531
+ id: testCaseId,
2532
+ rev: this.resolveSuiteTestCaseRevision(testCaseItem) || 1,
2533
+ fields,
2534
+ relations: Array.isArray(workItem === null || workItem === void 0 ? void 0 : workItem.relations) ? workItem.relations : [],
2535
+ };
2536
+ }
2537
+ async fetchWorkItemByRevision(projectName, workItemId, revision, expandAll = false) {
2538
+ const id = Number(workItemId || 0);
2539
+ const rev = Number(revision || 0);
2540
+ if (!Number.isFinite(id) || id <= 0 || !Number.isFinite(rev) || rev <= 0)
2541
+ return null;
2542
+ const expandParam = expandAll ? '?$expand=all' : '';
2543
+ const url = `${this.orgUrl}${projectName}/_apis/wit/workItems/${id}/revisions/${rev}${expandParam}`;
2544
+ try {
2545
+ return await tfs_1.TFSServices.getItemContent(url, this.token);
2546
+ }
2547
+ catch (error) {
2548
+ logger_1.default.warn(`Failed to fetch work item ${id} by revision ${rev}: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
2549
+ return null;
2550
+ }
2551
+ }
2552
+ async fetchWorkItemLatest(projectName, workItemId, expandAll = false) {
2553
+ const id = Number(workItemId || 0);
2554
+ if (!Number.isFinite(id) || id <= 0)
2555
+ return null;
2556
+ const expandParam = expandAll ? '?$expand=all' : '';
2557
+ const url = `${this.orgUrl}${projectName}/_apis/wit/workItems/${id}${expandParam}`;
2558
+ try {
2559
+ return await tfs_1.TFSServices.getItemContent(url, this.token);
2560
+ }
2561
+ catch (error) {
2562
+ logger_1.default.warn(`Failed to fetch latest work item ${id}: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
2563
+ return null;
2564
+ }
2565
+ }
2656
2566
  /**
2657
2567
  * Fetches result data based on the Work Item Test Reporter.
2658
2568
  *
@@ -2668,7 +2578,7 @@ class ResultDataProvider {
2668
2578
  * @returns A promise that resolves to the fetched result data.
2669
2579
  */
2670
2580
  async fetchResultDataBasedOnWiBase(projectName, runId, resultId, isTestReporter = false, selectedFields, isQueryMode, point, includeAllHistory = false) {
2671
- var _a, _b, _c, _d, _e, _f, _g;
2581
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2672
2582
  try {
2673
2583
  let filteredFields = {};
2674
2584
  let relatedRequirements = [];
@@ -2679,16 +2589,29 @@ class ResultDataProvider {
2679
2589
  logger_1.default.warn(`Invalid run result ${runId} or result ${resultId}`);
2680
2590
  return null;
2681
2591
  }
2682
- const url = `${this.orgUrl}${projectName}/_apis/wit/workItems/${point.testCaseId}?$expand=all`;
2683
- const testCaseData = await tfs_1.TFSServices.getItemContent(url, this.token);
2592
+ const suiteTestCaseItem = point === null || point === void 0 ? void 0 : point.suiteTestCase;
2593
+ const testCaseId = Number((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_a = suiteTestCaseItem === null || suiteTestCaseItem === void 0 ? void 0 : suiteTestCaseItem.workItem) === null || _a === void 0 ? void 0 : _a.id) || (suiteTestCaseItem === null || suiteTestCaseItem === void 0 ? void 0 : suiteTestCaseItem.testCaseId) || 0);
2594
+ const suiteTestCaseRevision = this.resolveSuiteTestCaseRevision(suiteTestCaseItem);
2595
+ const fallbackSnapshot = this.buildWorkItemSnapshotFromSuiteTestCase(suiteTestCaseItem, testCaseId, String((point === null || point === void 0 ? void 0 : point.testCaseName) || ''));
2596
+ let testCaseData = await this.fetchWorkItemByRevision(projectName, testCaseId, suiteTestCaseRevision, isTestReporter);
2597
+ if (!testCaseData) {
2598
+ testCaseData = fallbackSnapshot;
2599
+ }
2600
+ if (!testCaseData) {
2601
+ testCaseData = await this.fetchWorkItemLatest(projectName, testCaseId, isTestReporter);
2602
+ }
2603
+ if (!testCaseData) {
2604
+ logger_1.default.warn(`Could not resolve test case ${point.testCaseId} for runless point fallback.`);
2605
+ return null;
2606
+ }
2684
2607
  const newResultData = {
2685
2608
  id: 0,
2686
2609
  outcome: point.outcome,
2687
- revision: (testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.rev) || 1,
2688
- testCase: { id: point.testCaseId, name: point.testCaseName },
2689
- state: ((_a = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _a === void 0 ? void 0 : _a['System.State']) || 'Active',
2690
- priority: ((_b = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _b === void 0 ? void 0 : _b['Microsoft.VSTS.TCM.Priority']) || 0,
2691
- createdDate: ((_c = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _c === void 0 ? void 0 : _c['System.CreatedDate']) || '0001-01-01T00:00:00',
2610
+ revision: Number((testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.rev) || suiteTestCaseRevision || 1),
2611
+ testCase: { id: String(testCaseId), name: point.testCaseName },
2612
+ state: ((_b = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _b === void 0 ? void 0 : _b['System.State']) || 'Active',
2613
+ priority: ((_c = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _c === void 0 ? void 0 : _c['Microsoft.VSTS.TCM.Priority']) || 0,
2614
+ createdDate: ((_d = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _d === void 0 ? void 0 : _d['System.CreatedDate']) || '0001-01-01T00:00:00',
2692
2615
  testSuite: point.testSuite,
2693
2616
  failureType: 'None',
2694
2617
  };
@@ -2696,7 +2619,7 @@ class ResultDataProvider {
2696
2619
  this.appendQueryRelations(point.testCaseId, relatedRequirements, relatedBugs, relatedCRs);
2697
2620
  }
2698
2621
  else {
2699
- const filteredLinkedFields = (_d = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@linked'))) === null || _d === void 0 ? void 0 : _d.map((field) => field.split('@')[0]);
2622
+ const filteredLinkedFields = (_e = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@linked'))) === null || _e === void 0 ? void 0 : _e.map((field) => field.split('@')[0]);
2700
2623
  const selectedLinkedFieldSet = new Set(filteredLinkedFields);
2701
2624
  const { relations } = testCaseData;
2702
2625
  if (relations) {
@@ -2704,7 +2627,7 @@ class ResultDataProvider {
2704
2627
  }
2705
2628
  selectedLinkedFieldSet.clear();
2706
2629
  }
2707
- const filteredTestCaseFields = (_e = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@testCaseWorkItemField'))) === null || _e === void 0 ? void 0 : _e.map((field) => field.split('@')[0]);
2630
+ const filteredTestCaseFields = (_f = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@testCaseWorkItemField'))) === null || _f === void 0 ? void 0 : _f.map((field) => field.split('@')[0]);
2708
2631
  const selectedFieldSet = new Set(filteredTestCaseFields);
2709
2632
  // Filter fields based on selected field set
2710
2633
  if (selectedFieldSet.size !== 0) {
@@ -2718,7 +2641,7 @@ class ResultDataProvider {
2718
2641
  }
2719
2642
  }
2720
2643
  selectedFieldSet.clear();
2721
- return Object.assign(Object.assign({}, newResultData), { stepsResultXml: testCaseData.fields['Microsoft.VSTS.TCM.Steps'] || undefined, testCaseRevision: testCaseData.rev, filteredFields,
2644
+ return Object.assign(Object.assign({}, newResultData), { stepsResultXml: ((_g = testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.fields) === null || _g === void 0 ? void 0 : _g['Microsoft.VSTS.TCM.Steps']) || undefined, testCaseRevision: Number((testCaseData === null || testCaseData === void 0 ? void 0 : testCaseData.rev) || suiteTestCaseRevision || 1), filteredFields,
2722
2645
  relatedRequirements,
2723
2646
  relatedBugs,
2724
2647
  relatedCRs });
@@ -2738,7 +2661,7 @@ class ResultDataProvider {
2738
2661
  this.appendQueryRelations(resultData.testCase.id, relatedRequirements, relatedBugs, relatedCRs);
2739
2662
  }
2740
2663
  else {
2741
- const filteredLinkedFields = (_f = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@linked'))) === null || _f === void 0 ? void 0 : _f.map((field) => field.split('@')[0]);
2664
+ const filteredLinkedFields = (_h = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@linked'))) === null || _h === void 0 ? void 0 : _h.map((field) => field.split('@')[0]);
2742
2665
  const selectedLinkedFieldSet = new Set(filteredLinkedFields);
2743
2666
  const { relations } = wiByRevision;
2744
2667
  if (relations) {
@@ -2746,7 +2669,7 @@ class ResultDataProvider {
2746
2669
  }
2747
2670
  selectedLinkedFieldSet.clear();
2748
2671
  }
2749
- const filteredTestCaseFields = (_g = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@testCaseWorkItemField'))) === null || _g === void 0 ? void 0 : _g.map((field) => field.split('@')[0]);
2672
+ const filteredTestCaseFields = (_j = selectedFields === null || selectedFields === void 0 ? void 0 : selectedFields.filter((field) => field.includes('@testCaseWorkItemField'))) === null || _j === void 0 ? void 0 : _j.map((field) => field.split('@')[0]);
2750
2673
  const selectedFieldSet = new Set(filteredTestCaseFields);
2751
2674
  // Filter fields based on selected field set
2752
2675
  if (selectedFieldSet.size !== 0) {
@@ -2839,11 +2762,6 @@ class ResultDataProvider {
2839
2762
  return (String((_b = (_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.DOCGEN_VERBOSE_HISTORY_DEBUG) !== null && _b !== void 0 ? _b : '').toLowerCase() === 'true' ||
2840
2763
  String((_d = (_c = process === null || process === void 0 ? void 0 : process.env) === null || _c === void 0 ? void 0 : _c.DOCGEN_VERBOSE_HISTORY_DEBUG) !== null && _d !== void 0 ? _d : '') === '1');
2841
2764
  }
2842
- isRunResultDebugEnabled() {
2843
- var _a, _b, _c, _d;
2844
- return (String((_b = (_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.DOCGEN_DEBUG_RUNRESULT) !== null && _b !== void 0 ? _b : '').toLowerCase() === 'true' ||
2845
- String((_d = (_c = process === null || process === void 0 ? void 0 : process.env) === null || _c === void 0 ? void 0 : _c.DOCGEN_DEBUG_RUNRESULT) !== null && _d !== void 0 ? _d : '') === '1');
2846
- }
2847
2765
  extractCommentText(comment) {
2848
2766
  var _a, _b, _c, _d;
2849
2767
  const rendered = comment === null || comment === void 0 ? void 0 : comment.renderedText;
@@ -3447,9 +3365,10 @@ class ResultDataProvider {
3447
3365
  try {
3448
3366
  const testCasesItems = await this.fetchTestCasesBySuiteId(projectName, testPlanId, suite.testSuiteId);
3449
3367
  const testCaseIds = testCasesItems.map((testCase) => testCase.workItem.id);
3450
- const testPointsItems = !fetchCrossPlans
3368
+ const rawTestPointsItems = !fetchCrossPlans
3451
3369
  ? await this.fetchTestPoints(projectName, testPlanId, suite.testSuiteId)
3452
3370
  : await this.fetchCrossTestPoints(projectName, testCaseIds);
3371
+ const testPointsItems = this.attachSuiteTestCaseContextToPoints(testCasesItems, rawTestPointsItems);
3453
3372
  return Object.assign(Object.assign({}, suite), { testPointsItems, testCasesItems });
3454
3373
  }
3455
3374
  catch (error) {
@@ -4088,6 +4007,7 @@ class ResultDataProvider {
4088
4007
  const iteration = ((_a = resultData.iterationDetails) === null || _a === void 0 ? void 0 : _a.length) > 0
4089
4008
  ? resultData.iterationDetails[((_b = resultData.iterationDetails) === null || _b === void 0 ? void 0 : _b.length) - 1]
4090
4009
  : undefined;
4010
+ const testOutcome = this.getTestOutcome(resultData);
4091
4011
  if (!(resultData === null || resultData === void 0 ? void 0 : resultData.testCase) || !(resultData === null || resultData === void 0 ? void 0 : resultData.testSuite)) {
4092
4012
  logger_1.default.debug(`[RunResult] Missing testCase/testSuite for point testCaseId=${String((_c = point === null || point === void 0 ? void 0 : point.testCaseId) !== null && _c !== void 0 ? _c : 'unknown')} (lastRunId=${String(lastRunId !== null && lastRunId !== void 0 ? lastRunId : '')}, lastResultId=${String(lastResultId !== null && lastResultId !== void 0 ? lastResultId : '')}). hasTestCase=${Boolean(resultData === null || resultData === void 0 ? void 0 : resultData.testCase)} hasTestSuite=${Boolean(resultData === null || resultData === void 0 ? void 0 : resultData.testSuite)}`);
4093
4013
  }
@@ -4126,16 +4046,15 @@ class ResultDataProvider {
4126
4046
  resultDataResponse.priority = resultData.priority;
4127
4047
  break;
4128
4048
  case 'testCaseResult':
4129
- const outcome = this.getTestOutcome(resultData);
4130
4049
  if (lastRunId === undefined || lastResultId === undefined) {
4131
4050
  resultDataResponse.testCaseResult = {
4132
- resultMessage: `${this.convertRunStatus(outcome)}`,
4051
+ resultMessage: `${this.convertRunStatus(testOutcome)}`,
4133
4052
  url: '',
4134
4053
  };
4135
4054
  }
4136
4055
  else {
4137
4056
  resultDataResponse.testCaseResult = {
4138
- resultMessage: `${this.convertRunStatus(outcome)} in Run ${lastRunId}`,
4057
+ resultMessage: `${this.convertRunStatus(testOutcome)} in Run ${lastRunId}`,
4139
4058
  url: `${this.orgUrl}${projectName}/_testManagement/runs?runId=${lastRunId}&_a=resultSummary&resultId=${lastResultId}`,
4140
4059
  };
4141
4060
  }