@elisra-devops/docgen-data-provider 1.101.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 +32 -0
- package/bin/modules/ResultDataProvider.js +148 -1
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +296 -0
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +162 -1
- package/src/tests/modules/ResultDataProvider.test.ts +332 -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;
|
|
@@ -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);
|
|
@@ -4511,6 +4656,8 @@ class ResultDataProvider {
|
|
|
4511
4656
|
ResultDataProvider.MEWP_INTERNAL_VALIDATION_TRACE_TAG = '[MEWP][InternalValidation][Trace]';
|
|
4512
4657
|
ResultDataProvider.MEWP_INTERNAL_VALIDATION_DIAGNOSTICS_TAG = '[MEWP][InternalValidation][Diagnostics]';
|
|
4513
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;
|
|
4514
4661
|
ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
|
|
4515
4662
|
'L2 REQ ID',
|
|
4516
4663
|
'L2 REQ Title',
|