@elisra-devops/docgen-data-provider 1.100.0 → 1.102.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 +40 -1
- package/bin/modules/ResultDataProvider.js +180 -47
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +380 -0
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +206 -88
- package/src/tests/modules/ResultDataProvider.test.ts +457 -0
|
@@ -26,6 +26,8 @@ export default class ResultDataProvider {
|
|
|
26
26
|
private static readonly MEWP_INTERNAL_VALIDATION_TRACE_TAG;
|
|
27
27
|
private static readonly MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG;
|
|
28
28
|
private static readonly MEWP_INTERNAL_VALIDATION_SUMMARY_TAG;
|
|
29
|
+
private static readonly MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_REF;
|
|
30
|
+
private static readonly MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_HEADER_PATTERN;
|
|
29
31
|
private static readonly MEWP_L2_COVERAGE_COLUMNS;
|
|
30
32
|
private static readonly INTERNAL_VALIDATION_COLUMNS;
|
|
31
33
|
orgUrl: string;
|
|
@@ -104,6 +106,15 @@ export default class ResultDataProvider {
|
|
|
104
106
|
private isExternalStateInScope;
|
|
105
107
|
private invertBaseRequirementLinks;
|
|
106
108
|
private buildMewpTestCaseTitleMap;
|
|
109
|
+
/**
|
|
110
|
+
* Builds a lookup of test case id -> test case description using suite payload data.
|
|
111
|
+
*
|
|
112
|
+
* Resolution order:
|
|
113
|
+
* 1) direct description fields on test-case payload (`testCase.description` / `workItem.description`)
|
|
114
|
+
* 2) `System.Description` / `Description` in work-item fields map
|
|
115
|
+
* 3) `System.Description` / `Description` from work-item field list (`workItemFields`)
|
|
116
|
+
*/
|
|
117
|
+
private buildMewpTestCaseDescriptionMap;
|
|
107
118
|
private extractMewpTestCaseId;
|
|
108
119
|
private buildTestCaseStepsXmlMap;
|
|
109
120
|
private extractStepsXmlFromTestCaseItem;
|
|
@@ -115,6 +126,27 @@ export default class ResultDataProvider {
|
|
|
115
126
|
private resolveRequirementStatusForWindow;
|
|
116
127
|
private extractRequirementCodesFromText;
|
|
117
128
|
private extractRequirementMentionsFromExpectedSteps;
|
|
129
|
+
/**
|
|
130
|
+
* Extracts SR requirement mentions from the "assumptions" section inside a test-case description.
|
|
131
|
+
*
|
|
132
|
+
* Notes:
|
|
133
|
+
* - Heading detection is case-insensitive and keyed by "assumptions".
|
|
134
|
+
* - Parsing is scoped to that section only and stops when a likely next section heading is reached
|
|
135
|
+
* after at least one requirement-bearing line was collected.
|
|
136
|
+
* - Returned stepRef is a synthetic marker ("Assumptions") so downstream discrepancy output can
|
|
137
|
+
* clearly attribute source to description-level assumptions.
|
|
138
|
+
*/
|
|
139
|
+
private extractRequirementMentionsFromAssumptionsDescription;
|
|
140
|
+
/**
|
|
141
|
+
* Normalizes raw HTML/plain description content into comparable text lines.
|
|
142
|
+
* This makes section/title heuristics resilient to formatting differences.
|
|
143
|
+
*/
|
|
144
|
+
private normalizeMewpDescriptionLines;
|
|
145
|
+
/**
|
|
146
|
+
* Heuristic detector for description section headings used to stop assumptions parsing.
|
|
147
|
+
* A line is considered a heading when it is short/title-like and does not itself contain SR codes.
|
|
148
|
+
*/
|
|
149
|
+
private isLikelyMewpDescriptionSectionHeading;
|
|
118
150
|
private extractRequirementCodesFromExpectedText;
|
|
119
151
|
private extractRequirementCandidatesFromToken;
|
|
120
152
|
private expandRequirementTokenByComma;
|
|
@@ -227,7 +259,14 @@ export default class ResultDataProvider {
|
|
|
227
259
|
* Returns true when the snapshot includes a non-empty test steps XML payload.
|
|
228
260
|
*/
|
|
229
261
|
private hasStepsInWorkItemSnapshot;
|
|
230
|
-
|
|
262
|
+
/**
|
|
263
|
+
* Resolves the suite-aware revision for a test point.
|
|
264
|
+
*/
|
|
265
|
+
private resolvePointRevision;
|
|
266
|
+
/**
|
|
267
|
+
* Builds ordered keys used to match a point with fetched iteration payloads.
|
|
268
|
+
*/
|
|
269
|
+
private buildIterationLookupCandidates;
|
|
231
270
|
/**
|
|
232
271
|
* Resolves runless test case data using ordered fallbacks:
|
|
233
272
|
* 1) point-based `asOf` snapshot, 2) explicit revision, 3) suite payload snapshot, 4) latest WI.
|
|
@@ -517,6 +517,7 @@ class ResultDataProvider {
|
|
|
517
517
|
const rows = [];
|
|
518
518
|
const stepsXmlByTestCase = this.buildTestCaseStepsXmlMap(testData);
|
|
519
519
|
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
520
|
+
const testCaseDescriptionMap = this.buildMewpTestCaseDescriptionMap(testData);
|
|
520
521
|
const allTestCaseIds = new Set();
|
|
521
522
|
for (const suite of testData || []) {
|
|
522
523
|
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
@@ -562,7 +563,13 @@ class ResultDataProvider {
|
|
|
562
563
|
: [];
|
|
563
564
|
const executableSteps = parsedSteps.filter((step) => !(step === null || step === void 0 ? void 0 : step.isSharedStepTitle));
|
|
564
565
|
diagnostics.totalParsedSteps += executableSteps.length;
|
|
565
|
-
|
|
566
|
+
// Direction A/B mentions can come from two sources:
|
|
567
|
+
// 1) Expected Result in executable steps.
|
|
568
|
+
// 2) "Assumptions" section in the test-case description.
|
|
569
|
+
const stepMentionEntries = this.extractRequirementMentionsFromExpectedSteps(parsedSteps, true);
|
|
570
|
+
const descriptionText = testCaseDescriptionMap.get(testCaseId) || '';
|
|
571
|
+
const assumptionsMentionEntries = this.extractRequirementMentionsFromAssumptionsDescription(descriptionText, true);
|
|
572
|
+
const mentionEntries = [...stepMentionEntries, ...assumptionsMentionEntries];
|
|
566
573
|
diagnostics.totalStepsWithMentions += mentionEntries.length;
|
|
567
574
|
const mentionedL2Only = new Set();
|
|
568
575
|
const mentionedCodeFirstStep = new Map();
|
|
@@ -1289,6 +1296,48 @@ class ResultDataProvider {
|
|
|
1289
1296
|
}
|
|
1290
1297
|
return map;
|
|
1291
1298
|
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Builds a lookup of test case id -> test case description using suite payload data.
|
|
1301
|
+
*
|
|
1302
|
+
* Resolution order:
|
|
1303
|
+
* 1) direct description fields on test-case payload (`testCase.description` / `workItem.description`)
|
|
1304
|
+
* 2) `System.Description` / `Description` in work-item fields map
|
|
1305
|
+
* 3) `System.Description` / `Description` from work-item field list (`workItemFields`)
|
|
1306
|
+
*/
|
|
1307
|
+
buildMewpTestCaseDescriptionMap(testData) {
|
|
1308
|
+
var _a, _b, _c, _d;
|
|
1309
|
+
const map = new Map();
|
|
1310
|
+
const readDescriptionFromFields = (fields) => {
|
|
1311
|
+
var _a;
|
|
1312
|
+
const value = (_a = this.getFieldValueByName(fields, 'System.Description')) !== null && _a !== void 0 ? _a : this.getFieldValueByName(fields, 'Description');
|
|
1313
|
+
return this.toMewpComparableText(value);
|
|
1314
|
+
};
|
|
1315
|
+
for (const suite of testData || []) {
|
|
1316
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
1317
|
+
for (const testCase of testCasesItems) {
|
|
1318
|
+
const id = 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));
|
|
1319
|
+
if (!Number.isFinite(id) || id <= 0 || map.has(id))
|
|
1320
|
+
continue;
|
|
1321
|
+
const fromDirect = this.toMewpComparableText((testCase === null || testCase === void 0 ? void 0 : testCase.description) || ((_b = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _b === void 0 ? void 0 : _b.description));
|
|
1322
|
+
if (fromDirect) {
|
|
1323
|
+
map.set(id, fromDirect);
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
const fieldsFromMap = ((_c = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _c === void 0 ? void 0 : _c.fields) || {};
|
|
1327
|
+
const fromFieldsMap = readDescriptionFromFields(fieldsFromMap);
|
|
1328
|
+
if (fromFieldsMap) {
|
|
1329
|
+
map.set(id, fromFieldsMap);
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
const fieldsFromList = this.extractWorkItemFieldsMap((_d = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _d === void 0 ? void 0 : _d.workItemFields);
|
|
1333
|
+
const fromFieldsList = readDescriptionFromFields(fieldsFromList);
|
|
1334
|
+
if (fromFieldsList) {
|
|
1335
|
+
map.set(id, fromFieldsList);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return map;
|
|
1340
|
+
}
|
|
1292
1341
|
extractMewpTestCaseId(runResult) {
|
|
1293
1342
|
var _a;
|
|
1294
1343
|
const testCaseId = Number((runResult === null || runResult === void 0 ? void 0 : runResult.testCaseId) || ((_a = runResult === null || runResult === void 0 ? void 0 : runResult.testCase) === null || _a === void 0 ? void 0 : _a.id) || 0);
|
|
@@ -1479,6 +1528,102 @@ class ResultDataProvider {
|
|
|
1479
1528
|
}
|
|
1480
1529
|
return out;
|
|
1481
1530
|
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Extracts SR requirement mentions from the "assumptions" section inside a test-case description.
|
|
1533
|
+
*
|
|
1534
|
+
* Notes:
|
|
1535
|
+
* - Heading detection is case-insensitive and keyed by "assumptions".
|
|
1536
|
+
* - Parsing is scoped to that section only and stops when a likely next section heading is reached
|
|
1537
|
+
* after at least one requirement-bearing line was collected.
|
|
1538
|
+
* - Returned stepRef is a synthetic marker ("Assumptions") so downstream discrepancy output can
|
|
1539
|
+
* clearly attribute source to description-level assumptions.
|
|
1540
|
+
*/
|
|
1541
|
+
extractRequirementMentionsFromAssumptionsDescription(description, includeSuffix) {
|
|
1542
|
+
const lines = this.normalizeMewpDescriptionLines(description);
|
|
1543
|
+
if (lines.length === 0)
|
|
1544
|
+
return [];
|
|
1545
|
+
const assumptionsHeaderIndex = lines.findIndex((line) => ResultDataProvider.MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_HEADER_PATTERN.test(line));
|
|
1546
|
+
if (assumptionsHeaderIndex < 0)
|
|
1547
|
+
return [];
|
|
1548
|
+
const assumptionCodes = new Set();
|
|
1549
|
+
let hasCollectedRequirementCodes = false;
|
|
1550
|
+
for (let index = assumptionsHeaderIndex + 1; index < lines.length; index += 1) {
|
|
1551
|
+
const rawLine = String(lines[index] || '').trim();
|
|
1552
|
+
if (!rawLine)
|
|
1553
|
+
continue;
|
|
1554
|
+
const line = rawLine.replace(/^[-*•]+\s*/, '').trim();
|
|
1555
|
+
if (!line)
|
|
1556
|
+
continue;
|
|
1557
|
+
const lineCodes = this.extractRequirementCodesFromExpectedText(line, includeSuffix);
|
|
1558
|
+
if (lineCodes.size > 0) {
|
|
1559
|
+
for (const code of lineCodes)
|
|
1560
|
+
assumptionCodes.add(code);
|
|
1561
|
+
hasCollectedRequirementCodes = true;
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
if (hasCollectedRequirementCodes && this.isLikelyMewpDescriptionSectionHeading(line)) {
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (assumptionCodes.size === 0)
|
|
1569
|
+
return [];
|
|
1570
|
+
return [{ stepRef: ResultDataProvider.MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_REF, codes: assumptionCodes }];
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Normalizes raw HTML/plain description content into comparable text lines.
|
|
1574
|
+
* This makes section/title heuristics resilient to formatting differences.
|
|
1575
|
+
*/
|
|
1576
|
+
normalizeMewpDescriptionLines(description) {
|
|
1577
|
+
const raw = String(description || '');
|
|
1578
|
+
if (!raw)
|
|
1579
|
+
return [];
|
|
1580
|
+
const normalized = raw
|
|
1581
|
+
.replace(/\r\n?/g, '\n')
|
|
1582
|
+
.replace(/<\s*br\s*\/?>/gi, '\n')
|
|
1583
|
+
// Underlined sections are commonly used as inline HTML titles in MEWP test-case descriptions.
|
|
1584
|
+
// Treat underline tags as boundaries so compact forms like <u><b>Title</b></u><p>... preserve section split.
|
|
1585
|
+
.replace(/<\s*u\b[^>]*>/gi, '\n')
|
|
1586
|
+
.replace(/<\/\s*u\s*>/gi, '\n')
|
|
1587
|
+
.replace(/<\s*li\b[^>]*>/gi, '\n- ')
|
|
1588
|
+
.replace(/<\/\s*(p|div|li|ul|ol|tr|td|h[1-6])\s*>/gi, '\n')
|
|
1589
|
+
.replace(/ | | /gi, ' ')
|
|
1590
|
+
.replace(/</gi, '<')
|
|
1591
|
+
.replace(/>/gi, '>')
|
|
1592
|
+
.replace(/&/gi, '&')
|
|
1593
|
+
.replace(/"/gi, '"')
|
|
1594
|
+
.replace(/'|'/gi, "'")
|
|
1595
|
+
.replace(/<[^>]*>/g, ' ')
|
|
1596
|
+
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
1597
|
+
.replace(/[ \t]+/g, ' ');
|
|
1598
|
+
return normalized
|
|
1599
|
+
.split('\n')
|
|
1600
|
+
.map((line) => String(line || '').trim())
|
|
1601
|
+
.filter((line) => !!line);
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Heuristic detector for description section headings used to stop assumptions parsing.
|
|
1605
|
+
* A line is considered a heading when it is short/title-like and does not itself contain SR codes.
|
|
1606
|
+
*/
|
|
1607
|
+
isLikelyMewpDescriptionSectionHeading(line) {
|
|
1608
|
+
const normalized = String(line || '')
|
|
1609
|
+
.replace(/^[-*•]+\s*/, '')
|
|
1610
|
+
.trim();
|
|
1611
|
+
if (!normalized)
|
|
1612
|
+
return false;
|
|
1613
|
+
if (ResultDataProvider.MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_HEADER_PATTERN.test(normalized))
|
|
1614
|
+
return false;
|
|
1615
|
+
if (this.extractRequirementCodesFromExpectedText(normalized, true).size > 0)
|
|
1616
|
+
return false;
|
|
1617
|
+
const compact = normalized.replace(/[.:;,\-–—]+$/, '').trim();
|
|
1618
|
+
if (!compact)
|
|
1619
|
+
return false;
|
|
1620
|
+
const wordCount = compact.split(/\s+/).filter((item) => !!item).length;
|
|
1621
|
+
if (wordCount === 0 || wordCount > 12)
|
|
1622
|
+
return false;
|
|
1623
|
+
if (/[.;!?]/.test(compact))
|
|
1624
|
+
return false;
|
|
1625
|
+
return /^[a-z0-9\s()&/+_'-]+$/i.test(compact);
|
|
1626
|
+
}
|
|
1482
1627
|
extractRequirementCodesFromExpectedText(text, includeSuffix) {
|
|
1483
1628
|
const out = new Set();
|
|
1484
1629
|
const source = this.normalizeRequirementStepText(text);
|
|
@@ -2764,7 +2909,6 @@ class ResultDataProvider {
|
|
|
2764
2909
|
const parsedAsOf = new Date(String(asOfTimestamp || '').trim());
|
|
2765
2910
|
if (!Number.isFinite(id) || id <= 0 || Number.isNaN(parsedAsOf.getTime()))
|
|
2766
2911
|
return null;
|
|
2767
|
-
logger_1.default.debug(`[RunlessResolver] Fetching work item ${id} by asOf (raw="${String(asOfTimestamp || '')}", normalized="${parsedAsOf.toISOString()}", expandAll=${String(expandAll)})`);
|
|
2768
2912
|
const query = [`asOf=${encodeURIComponent(parsedAsOf.toISOString())}`];
|
|
2769
2913
|
if (expandAll) {
|
|
2770
2914
|
query.push(`$expand=all`);
|
|
@@ -2799,15 +2943,34 @@ class ResultDataProvider {
|
|
|
2799
2943
|
const stepsXml = this.extractStepsXmlFromFieldsMap((workItemData === null || workItemData === void 0 ? void 0 : workItemData.fields) || {});
|
|
2800
2944
|
return String(stepsXml || '').trim() !== '';
|
|
2801
2945
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2946
|
+
/**
|
|
2947
|
+
* Resolves the suite-aware revision for a test point.
|
|
2948
|
+
*/
|
|
2949
|
+
resolvePointRevision(testCase, point) {
|
|
2950
|
+
return Number(this.resolveSuiteTestCaseRevision(testCase) ||
|
|
2951
|
+
this.resolveSuiteTestCaseRevision(point === null || point === void 0 ? void 0 : point.suiteTestCase) ||
|
|
2952
|
+
0);
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Builds ordered keys used to match a point with fetched iteration payloads.
|
|
2956
|
+
*/
|
|
2957
|
+
buildIterationLookupCandidates(input) {
|
|
2958
|
+
const candidates = [];
|
|
2959
|
+
const addCandidate = (key) => {
|
|
2960
|
+
if (key && !candidates.includes(key)) {
|
|
2961
|
+
candidates.push(key);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
addCandidate(this.buildIterationLookupKey(input));
|
|
2965
|
+
const revision = Number((input === null || input === void 0 ? void 0 : input.testCaseRevision) || 0);
|
|
2966
|
+
if (Number.isFinite(revision) && revision > 0) {
|
|
2967
|
+
addCandidate(this.buildIterationLookupKey({
|
|
2968
|
+
testCaseId: input === null || input === void 0 ? void 0 : input.testCaseId,
|
|
2969
|
+
testCaseRevision: revision,
|
|
2970
|
+
}));
|
|
2807
2971
|
}
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
logger_1.default.debug(`[RunlessResolver] TC ${testCaseId}: source=${source}, rev=${String((_a = snapshot === null || snapshot === void 0 ? void 0 : snapshot.rev) !== null && _a !== void 0 ? _a : '')}, hasSteps=${String(stepsLength > 0)}, stepsLength=${String(stepsLength)}`);
|
|
2972
|
+
addCandidate(`${Number((input === null || input === void 0 ? void 0 : input.testCaseId) || 0)}`);
|
|
2973
|
+
return candidates;
|
|
2811
2974
|
}
|
|
2812
2975
|
/**
|
|
2813
2976
|
* Resolves runless test case data using ordered fallbacks:
|
|
@@ -2817,31 +2980,24 @@ class ResultDataProvider {
|
|
|
2817
2980
|
let bestSnapshotWithoutSteps = null;
|
|
2818
2981
|
if (pointAsOfTimestamp) {
|
|
2819
2982
|
const asOfSnapshot = await this.fetchWorkItemByAsOf(projectName, testCaseId, pointAsOfTimestamp, expandAll);
|
|
2820
|
-
this.logRunlessSnapshotDecision(testCaseId, 'asOf', asOfSnapshot);
|
|
2821
2983
|
if (asOfSnapshot) {
|
|
2822
2984
|
if (this.hasStepsInWorkItemSnapshot(asOfSnapshot))
|
|
2823
2985
|
return asOfSnapshot;
|
|
2824
2986
|
bestSnapshotWithoutSteps = asOfSnapshot;
|
|
2825
2987
|
}
|
|
2826
2988
|
}
|
|
2827
|
-
else {
|
|
2828
|
-
logger_1.default.debug(`[RunlessResolver] TC ${testCaseId}: asOf timestamp is empty, skipping asOf fetch`);
|
|
2829
|
-
}
|
|
2830
2989
|
const revisionSnapshot = await this.fetchWorkItemByRevision(projectName, testCaseId, suiteTestCaseRevision, expandAll);
|
|
2831
|
-
this.logRunlessSnapshotDecision(testCaseId, `revision:${String(suiteTestCaseRevision)}`, revisionSnapshot);
|
|
2832
2990
|
if (revisionSnapshot) {
|
|
2833
2991
|
if (this.hasStepsInWorkItemSnapshot(revisionSnapshot))
|
|
2834
2992
|
return revisionSnapshot;
|
|
2835
2993
|
bestSnapshotWithoutSteps = bestSnapshotWithoutSteps || revisionSnapshot;
|
|
2836
2994
|
}
|
|
2837
|
-
this.logRunlessSnapshotDecision(testCaseId, 'suiteSnapshot', fallbackSnapshot);
|
|
2838
2995
|
if (fallbackSnapshot) {
|
|
2839
2996
|
if (this.hasStepsInWorkItemSnapshot(fallbackSnapshot))
|
|
2840
2997
|
return fallbackSnapshot;
|
|
2841
2998
|
bestSnapshotWithoutSteps = bestSnapshotWithoutSteps || fallbackSnapshot;
|
|
2842
2999
|
}
|
|
2843
3000
|
const latestSnapshot = await this.fetchWorkItemLatest(projectName, testCaseId, expandAll);
|
|
2844
|
-
this.logRunlessSnapshotDecision(testCaseId, 'latest', latestSnapshot);
|
|
2845
3001
|
if (latestSnapshot) {
|
|
2846
3002
|
if (this.hasStepsInWorkItemSnapshot(latestSnapshot))
|
|
2847
3003
|
return latestSnapshot;
|
|
@@ -2881,7 +3037,6 @@ class ResultDataProvider {
|
|
|
2881
3037
|
const pointAsOfTimestamp = useRunlessAsOf
|
|
2882
3038
|
? String((point === null || point === void 0 ? void 0 : point.pointAsOfTimestamp) || '').trim()
|
|
2883
3039
|
: '';
|
|
2884
|
-
logger_1.default.debug(`[RunlessResolver] Start TC ${String(testCaseId)}: useRunlessAsOf=${String(useRunlessAsOf)}, pointAsOfTimestamp="${pointAsOfTimestamp}", suiteRevision=${String(suiteTestCaseRevision)}, pointOutcome="${String((point === null || point === void 0 ? void 0 : point.outcome) || '')}"`);
|
|
2885
3040
|
const fallbackSnapshot = this.buildWorkItemSnapshotFromSuiteTestCase(suiteTestCaseItem, testCaseId, String((point === null || point === void 0 ? void 0 : point.testCaseName) || ''));
|
|
2886
3041
|
const testCaseData = await this.resolveRunlessTestCaseData(projectName, testCaseId, suiteTestCaseRevision, pointAsOfTimestamp, fallbackSnapshot, isTestReporter);
|
|
2887
3042
|
if (!testCaseData) {
|
|
@@ -3522,9 +3677,6 @@ class ResultDataProvider {
|
|
|
3522
3677
|
};
|
|
3523
3678
|
if (!Number.isFinite(pointTestCaseId) || pointTestCaseId <= 0)
|
|
3524
3679
|
continue;
|
|
3525
|
-
if (!testCaseById.has(pointTestCaseId) && isTestReporter) {
|
|
3526
|
-
logger_1.default.debug(`[RunlessResolver] Missing suite testCase payload for point testCaseId=${String(pointTestCaseId)}; using point fallback for alignment`);
|
|
3527
|
-
}
|
|
3528
3680
|
const testCaseWorkItemFields = Array.isArray((_d = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _d === void 0 ? void 0 : _d.workItemFields)
|
|
3529
3681
|
? testCase.workItem.workItemFields
|
|
3530
3682
|
: [];
|
|
@@ -3534,29 +3686,15 @@ class ResultDataProvider {
|
|
|
3534
3686
|
continue;
|
|
3535
3687
|
}
|
|
3536
3688
|
}
|
|
3537
|
-
const
|
|
3689
|
+
const iterationLookupKeys = this.buildIterationLookupCandidates({
|
|
3538
3690
|
testCaseId: testCase.workItem.id,
|
|
3539
3691
|
lastRunId: point === null || point === void 0 ? void 0 : point.lastRunId,
|
|
3540
3692
|
lastResultId: point === null || point === void 0 ? void 0 : point.lastResultId,
|
|
3541
3693
|
testPointId: point === null || point === void 0 ? void 0 : point.testPointId,
|
|
3542
|
-
testCaseRevision: this.
|
|
3543
|
-
this.resolveSuiteTestCaseRevision(point === null || point === void 0 ? void 0 : point.suiteTestCase),
|
|
3694
|
+
testCaseRevision: this.resolvePointRevision(testCase, point),
|
|
3544
3695
|
});
|
|
3545
|
-
const
|
|
3546
|
-
|
|
3547
|
-
0);
|
|
3548
|
-
const fallbackRevisionKey = Number.isFinite(fallbackRevision) && fallbackRevision > 0
|
|
3549
|
-
? this.buildIterationLookupKey({
|
|
3550
|
-
testCaseId: testCase.workItem.id,
|
|
3551
|
-
testCaseRevision: fallbackRevision,
|
|
3552
|
-
})
|
|
3553
|
-
: '';
|
|
3554
|
-
const fallbackCaseOnlyKey = `${testCase.workItem.id}`;
|
|
3555
|
-
const fetchedTestCase = iterationsMap[iterationKey] ||
|
|
3556
|
-
(fallbackRevisionKey && fallbackRevisionKey !== iterationKey
|
|
3557
|
-
? iterationsMap[fallbackRevisionKey]
|
|
3558
|
-
: undefined) ||
|
|
3559
|
-
(iterationKey !== fallbackCaseOnlyKey ? iterationsMap[fallbackCaseOnlyKey] : undefined) ||
|
|
3696
|
+
const fetchedTestCaseFromMap = iterationLookupKeys.find((key) => iterationsMap[key]) || '';
|
|
3697
|
+
const fetchedTestCase = (fetchedTestCaseFromMap ? iterationsMap[fetchedTestCaseFromMap] : undefined) ||
|
|
3560
3698
|
(includeNotRunTestCases ? testCase : undefined);
|
|
3561
3699
|
// First check if fetchedTestCase exists
|
|
3562
3700
|
if (!fetchedTestCase)
|
|
@@ -3610,7 +3748,6 @@ class ResultDataProvider {
|
|
|
3610
3748
|
detailedResults.push(...resultObjectsToAdd);
|
|
3611
3749
|
}
|
|
3612
3750
|
else {
|
|
3613
|
-
logger_1.default.debug(`[RunlessResolver] No step rows generated for testCaseId=${String((point === null || point === void 0 ? void 0 : point.testCaseId) || '')}; falling back to test-level row`);
|
|
3614
3751
|
detailedResults.push(options.createResultObject({
|
|
3615
3752
|
testItem,
|
|
3616
3753
|
point,
|
|
@@ -3687,9 +3824,6 @@ class ResultDataProvider {
|
|
|
3687
3824
|
testCaseRevision: iterationItem === null || iterationItem === void 0 ? void 0 : iterationItem.testCaseRevision,
|
|
3688
3825
|
});
|
|
3689
3826
|
map[key] = iterationItem;
|
|
3690
|
-
if (isTestReporter && (iterationItem === null || iterationItem === void 0 ? void 0 : iterationItem.iteration)) {
|
|
3691
|
-
logger_1.default.debug(`[RunlessResolver] createIterationsMap: mapped runless testCaseId=${String(iterationItem === null || iterationItem === void 0 ? void 0 : iterationItem.testCaseId)} to key=${key}`);
|
|
3692
|
-
}
|
|
3693
3827
|
}
|
|
3694
3828
|
else if ((iterationItem === null || iterationItem === void 0 ? void 0 : iterationItem.iteration) && !isTestReporter) {
|
|
3695
3829
|
const key = this.buildIterationLookupKey({
|
|
@@ -3819,7 +3953,7 @@ class ResultDataProvider {
|
|
|
3819
3953
|
* Fetches result Data for a specific test point
|
|
3820
3954
|
*/
|
|
3821
3955
|
async fetchResultDataBase(projectName, testSuiteId, point, fetchResultMethod, createResponseObject, additionalArgs = []) {
|
|
3822
|
-
var _a
|
|
3956
|
+
var _a;
|
|
3823
3957
|
try {
|
|
3824
3958
|
const { lastRunId, lastResultId } = point;
|
|
3825
3959
|
const resultData = await fetchResultMethod(projectName, (lastRunId === null || lastRunId === void 0 ? void 0 : lastRunId.toString()) || '0', (lastResultId === null || lastResultId === void 0 ? void 0 : lastResultId.toString()) || '0', ...additionalArgs);
|
|
@@ -3844,7 +3978,6 @@ class ResultDataProvider {
|
|
|
3844
3978
|
});
|
|
3845
3979
|
}
|
|
3846
3980
|
const stepsList = await this.testStepParserHelper.parseTestSteps(resultData.stepsResultXml, sharedStepIdToRevisionLookupMap);
|
|
3847
|
-
logger_1.default.debug(`[RunlessResolver] TC ${String((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_b = resultData === null || resultData === void 0 ? void 0 : resultData.testCase) === null || _b === void 0 ? void 0 : _b.id) || '')}: parseTestSteps xmlLength=${String(String(resultData.stepsResultXml || '').length)}, parsedSteps=${String(stepsList.length)}, actionResultsBeforeMap=${String(actionResults.length)}`);
|
|
3848
3981
|
sharedStepIdToRevisionLookupMap.clear();
|
|
3849
3982
|
const stepMap = new Map();
|
|
3850
3983
|
for (const step of stepsList) {
|
|
@@ -3864,7 +3997,6 @@ class ResultDataProvider {
|
|
|
3864
3997
|
iteration.actionResults = actionResults
|
|
3865
3998
|
.filter((result) => result.stepPosition)
|
|
3866
3999
|
.sort((a, b) => this.compareActionResults(a.stepPosition, b.stepPosition));
|
|
3867
|
-
logger_1.default.debug(`[RunlessResolver] TC ${String((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_c = resultData === null || resultData === void 0 ? void 0 : resultData.testCase) === null || _c === void 0 ? void 0 : _c.id) || '')}: mappedActionResults=${String(((_d = iteration.actionResults) === null || _d === void 0 ? void 0 : _d.length) || 0)} (from existing iteration actionResults)`);
|
|
3868
4000
|
}
|
|
3869
4001
|
else {
|
|
3870
4002
|
// Fallback for runs that have no action results: emit test definition steps as Not Run.
|
|
@@ -3884,7 +4016,6 @@ class ResultDataProvider {
|
|
|
3884
4016
|
});
|
|
3885
4017
|
})
|
|
3886
4018
|
.sort((a, b) => this.compareActionResults(a.stepPosition, b.stepPosition));
|
|
3887
|
-
logger_1.default.debug(`[RunlessResolver] TC ${String((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_e = resultData === null || resultData === void 0 ? void 0 : resultData.testCase) === null || _e === void 0 ? void 0 : _e.id) || '')}: fallbackActionResults=${String(((_f = iteration.actionResults) === null || _f === void 0 ? void 0 : _f.length) || 0)} (from parsed steps)`);
|
|
3888
4019
|
}
|
|
3889
4020
|
}
|
|
3890
4021
|
return (resultData === null || resultData === void 0 ? void 0 : resultData.testCase)
|
|
@@ -4525,6 +4656,8 @@ class ResultDataProvider {
|
|
|
4525
4656
|
ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG = '[MEWP][InternalValidation][Trace]';
|
|
4526
4657
|
ResultDataProvider.MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG = '[MEWP][InternalValidation][Diagnostics]';
|
|
4527
4658
|
ResultDataProvider.MEWP_INTERNAL_VALIDATION_SUMMARY_TAG = '[MEWP][InternalValidation][Summary]';
|
|
4659
|
+
ResultDataProvider.MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_REF = 'Assumptions';
|
|
4660
|
+
ResultDataProvider.MEWP_INTERNAL_VALIDATION_ASSUMPTIONS_HEADER_PATTERN = /assumptions/i;
|
|
4528
4661
|
ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
|
|
4529
4662
|
'L2 REQ ID',
|
|
4530
4663
|
'L2 REQ Title',
|