@elisra-devops/docgen-data-provider 1.75.0 → 1.77.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/models/mewp-reporting.d.ts +120 -0
- package/bin/models/mewp-reporting.js +3 -0
- package/bin/models/mewp-reporting.js.map +1 -0
- package/bin/modules/ResultDataProvider.d.ts +46 -6
- package/bin/modules/ResultDataProvider.js +1156 -119
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +1285 -33
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/bin/utils/mewpExternalIngestionUtils.d.ts +17 -0
- package/bin/utils/mewpExternalIngestionUtils.js +269 -0
- package/bin/utils/mewpExternalIngestionUtils.js.map +1 -0
- package/bin/utils/mewpExternalTableUtils.d.ts +36 -0
- package/bin/utils/mewpExternalTableUtils.js +320 -0
- package/bin/utils/mewpExternalTableUtils.js.map +1 -0
- package/package.json +10 -1
- package/src/models/mewp-reporting.ts +138 -0
- package/src/modules/ResultDataProvider.ts +1399 -166
- package/src/tests/modules/ResultDataProvider.test.ts +1471 -42
- package/src/utils/mewpExternalIngestionUtils.ts +349 -0
- package/src/utils/mewpExternalTableUtils.ts +461 -0
|
@@ -14,6 +14,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
const DataProviderUtils_1 = require("../utils/DataProviderUtils");
|
|
15
15
|
const tfs_1 = require("../helpers/tfs");
|
|
16
16
|
const logger_1 = require("../utils/logger");
|
|
17
|
+
const mewpExternalIngestionUtils_1 = require("../utils/mewpExternalIngestionUtils");
|
|
18
|
+
const mewpExternalTableUtils_1 = require("../utils/mewpExternalTableUtils");
|
|
17
19
|
const testStepParserHelper_1 = require("../utils/testStepParserHelper");
|
|
18
20
|
const TicketsDataProvider_1 = require("./TicketsDataProvider");
|
|
19
21
|
const pLimit = require('p-limit');
|
|
@@ -69,6 +71,8 @@ class ResultDataProvider {
|
|
|
69
71
|
this.testToAssociatedItemMap = new Map();
|
|
70
72
|
this.querySelectedColumns = [];
|
|
71
73
|
this.workItemDiscussionCache = new Map();
|
|
74
|
+
this.mewpExternalTableUtils = new mewpExternalTableUtils_1.default();
|
|
75
|
+
this.mewpExternalIngestionUtils = new mewpExternalIngestionUtils_1.default(this.mewpExternalTableUtils);
|
|
72
76
|
}
|
|
73
77
|
/**
|
|
74
78
|
* Combines the results of test group result summary, test results summary, and detailed results summary into a single key-value pair array.
|
|
@@ -283,8 +287,8 @@ class ResultDataProvider {
|
|
|
283
287
|
* Builds MEWP L2 requirement coverage rows for audit reporting.
|
|
284
288
|
* Rows are one Requirement-TestCase pair; uncovered requirements are emitted with empty test-case columns.
|
|
285
289
|
*/
|
|
286
|
-
async getMewpL2CoverageFlatResults(testPlanId, projectName, selectedSuiteIds) {
|
|
287
|
-
var _a, _b;
|
|
290
|
+
async getMewpL2CoverageFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
|
|
291
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
288
292
|
const defaultPayload = {
|
|
289
293
|
sheetName: `MEWP L2 Coverage - Plan ${testPlanId}`,
|
|
290
294
|
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
@@ -292,9 +296,40 @@ class ResultDataProvider {
|
|
|
292
296
|
};
|
|
293
297
|
try {
|
|
294
298
|
const planName = await this.fetchTestPlanName(testPlanId, projectName);
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
|
|
299
|
+
const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, !!(options === null || options === void 0 ? void 0 : options.useRelFallback));
|
|
300
|
+
const allRequirements = await this.fetchMewpL2Requirements(projectName);
|
|
301
|
+
if (allRequirements.length === 0) {
|
|
302
|
+
return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
|
|
303
|
+
}
|
|
304
|
+
const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
|
|
305
|
+
const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
|
|
306
|
+
const requirements = this.collapseMewpRequirementFamilies(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
|
|
307
|
+
const requirementSapWbsByBaseKey = this.buildRequirementSapWbsByBaseKey(allRequirements);
|
|
308
|
+
const externalBugsByTestCase = await this.loadExternalBugsByTestCase(options === null || options === void 0 ? void 0 : options.externalBugsFile);
|
|
309
|
+
const externalL3L4ByBaseKey = await this.loadExternalL3L4ByBaseKey(options === null || options === void 0 ? void 0 : options.externalL3L4File, requirementSapWbsByBaseKey);
|
|
310
|
+
const hasExternalBugsFile = !!String(((_a = options === null || options === void 0 ? void 0 : options.externalBugsFile) === null || _a === void 0 ? void 0 : _a.name) ||
|
|
311
|
+
((_b = options === null || options === void 0 ? void 0 : options.externalBugsFile) === null || _b === void 0 ? void 0 : _b.objectName) ||
|
|
312
|
+
((_c = options === null || options === void 0 ? void 0 : options.externalBugsFile) === null || _c === void 0 ? void 0 : _c.text) ||
|
|
313
|
+
((_d = options === null || options === void 0 ? void 0 : options.externalBugsFile) === null || _d === void 0 ? void 0 : _d.url) ||
|
|
314
|
+
'').trim();
|
|
315
|
+
const hasExternalL3L4File = !!String(((_e = options === null || options === void 0 ? void 0 : options.externalL3L4File) === null || _e === void 0 ? void 0 : _e.name) ||
|
|
316
|
+
((_f = options === null || options === void 0 ? void 0 : options.externalL3L4File) === null || _f === void 0 ? void 0 : _f.objectName) ||
|
|
317
|
+
((_g = options === null || options === void 0 ? void 0 : options.externalL3L4File) === null || _g === void 0 ? void 0 : _g.text) ||
|
|
318
|
+
((_h = options === null || options === void 0 ? void 0 : options.externalL3L4File) === null || _h === void 0 ? void 0 : _h.url) ||
|
|
319
|
+
'').trim();
|
|
320
|
+
const externalBugLinksCount = [...externalBugsByTestCase.values()].reduce((sum, items) => sum + (Array.isArray(items) ? items.length : 0), 0);
|
|
321
|
+
const externalL3L4LinksCount = [...externalL3L4ByBaseKey.values()].reduce((sum, items) => sum + (Array.isArray(items) ? items.length : 0), 0);
|
|
322
|
+
logger_1.default.info(`MEWP coverage external ingestion summary: ` +
|
|
323
|
+
`bugsFileProvided=${hasExternalBugsFile} bugsTestCases=${externalBugsByTestCase.size} bugsLinks=${externalBugLinksCount}; ` +
|
|
324
|
+
`l3l4FileProvided=${hasExternalL3L4File} l3l4BaseKeys=${externalL3L4ByBaseKey.size} l3l4Links=${externalL3L4LinksCount}`);
|
|
325
|
+
if (hasExternalBugsFile && externalBugLinksCount === 0) {
|
|
326
|
+
logger_1.default.warn(`MEWP coverage: external bugs file was provided but produced 0 links. ` +
|
|
327
|
+
`Check SR/test-case/state values in ingestion logs.`);
|
|
328
|
+
}
|
|
329
|
+
if (hasExternalL3L4File && externalL3L4LinksCount === 0) {
|
|
330
|
+
logger_1.default.warn(`MEWP coverage: external L3/L4 file was provided but produced 0 links. ` +
|
|
331
|
+
`Check SR/AREA34/state/SAPWBS filters in ingestion logs.`);
|
|
332
|
+
}
|
|
298
333
|
if (requirements.length === 0) {
|
|
299
334
|
return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
|
|
300
335
|
}
|
|
@@ -302,32 +337,23 @@ class ResultDataProvider {
|
|
|
302
337
|
const observedTestCaseIdsByRequirement = new Map();
|
|
303
338
|
const requirementKeys = new Set();
|
|
304
339
|
requirements.forEach((requirement) => {
|
|
305
|
-
const key =
|
|
340
|
+
const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
306
341
|
if (!key)
|
|
307
342
|
return;
|
|
308
343
|
requirementKeys.add(key);
|
|
309
344
|
});
|
|
310
345
|
const parsedDefinitionStepsByTestCase = new Map();
|
|
311
346
|
const testCaseStepsXmlMap = this.buildTestCaseStepsXmlMap(testData);
|
|
312
|
-
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
313
347
|
const runResults = await this.fetchAllResultDataTestReporter(testData, projectName, [], false, false);
|
|
314
348
|
for (const runResult of runResults) {
|
|
315
349
|
const testCaseId = this.extractMewpTestCaseId(runResult);
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
testCaseTitleMap.set(testCaseId, runTestCaseTitle);
|
|
319
|
-
}
|
|
320
|
-
const actionResults = Array.isArray((_b = runResult === null || runResult === void 0 ? void 0 : runResult.iteration) === null || _b === void 0 ? void 0 : _b.actionResults)
|
|
321
|
-
? runResult.iteration.actionResults
|
|
350
|
+
const rawActionResults = Array.isArray((_j = runResult === null || runResult === void 0 ? void 0 : runResult.iteration) === null || _j === void 0 ? void 0 : _j.actionResults)
|
|
351
|
+
? runResult.iteration.actionResults.filter((item) => !(item === null || item === void 0 ? void 0 : item.isSharedStepTitle))
|
|
322
352
|
: [];
|
|
353
|
+
const actionResults = rawActionResults.sort((a, b) => this.compareActionResults(String((a === null || a === void 0 ? void 0 : a.stepPosition) || (a === null || a === void 0 ? void 0 : a.stepIdentifier) || ''), String((b === null || b === void 0 ? void 0 : b.stepPosition) || (b === null || b === void 0 ? void 0 : b.stepIdentifier) || '')));
|
|
323
354
|
const hasExecutedRun = Number((runResult === null || runResult === void 0 ? void 0 : runResult.lastRunId) || 0) > 0 && Number((runResult === null || runResult === void 0 ? void 0 : runResult.lastResultId) || 0) > 0;
|
|
324
355
|
if (actionResults.length > 0) {
|
|
325
|
-
|
|
326
|
-
if (actionResult === null || actionResult === void 0 ? void 0 : actionResult.isSharedStepTitle)
|
|
327
|
-
continue;
|
|
328
|
-
const stepStatus = this.classifyRequirementStepOutcome(actionResult === null || actionResult === void 0 ? void 0 : actionResult.outcome);
|
|
329
|
-
this.accumulateRequirementCountsFromStepText(`${String((actionResult === null || actionResult === void 0 ? void 0 : actionResult.action) || '')} ${String((actionResult === null || actionResult === void 0 ? void 0 : actionResult.expected) || '')}`, stepStatus, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
330
|
-
}
|
|
356
|
+
this.accumulateRequirementCountsFromActionResults(actionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
331
357
|
continue;
|
|
332
358
|
}
|
|
333
359
|
// Do not force "not run" from definition steps when a run exists:
|
|
@@ -345,13 +371,34 @@ class ResultDataProvider {
|
|
|
345
371
|
parsedDefinitionStepsByTestCase.set(testCaseId, parsed);
|
|
346
372
|
}
|
|
347
373
|
const definitionSteps = parsedDefinitionStepsByTestCase.get(testCaseId) || [];
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
374
|
+
const fallbackActionResults = definitionSteps
|
|
375
|
+
.filter((step) => !(step === null || step === void 0 ? void 0 : step.isSharedStepTitle))
|
|
376
|
+
.sort((a, b) => this.compareActionResults(String((a === null || a === void 0 ? void 0 : a.stepPosition) || ''), String((b === null || b === void 0 ? void 0 : b.stepPosition) || '')))
|
|
377
|
+
.map((step) => ({
|
|
378
|
+
stepPosition: step === null || step === void 0 ? void 0 : step.stepPosition,
|
|
379
|
+
expected: step === null || step === void 0 ? void 0 : step.expected,
|
|
380
|
+
outcome: 'Unspecified',
|
|
381
|
+
}));
|
|
382
|
+
this.accumulateRequirementCountsFromActionResults(fallbackActionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
383
|
+
}
|
|
384
|
+
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase);
|
|
385
|
+
const coverageRowStats = rows.reduce((acc, row) => {
|
|
386
|
+
const hasBug = Number((row === null || row === void 0 ? void 0 : row['Bug ID']) || 0) > 0;
|
|
387
|
+
const hasL3 = String((row === null || row === void 0 ? void 0 : row['L3 REQ ID']) || '').trim() !== '';
|
|
388
|
+
const hasL4 = String((row === null || row === void 0 ? void 0 : row['L4 REQ ID']) || '').trim() !== '';
|
|
389
|
+
if (hasBug)
|
|
390
|
+
acc.bugRows += 1;
|
|
391
|
+
if (hasL3)
|
|
392
|
+
acc.l3Rows += 1;
|
|
393
|
+
if (hasL4)
|
|
394
|
+
acc.l4Rows += 1;
|
|
395
|
+
if (!hasBug && !hasL3 && !hasL4)
|
|
396
|
+
acc.baseOnlyRows += 1;
|
|
397
|
+
return acc;
|
|
398
|
+
}, { bugRows: 0, l3Rows: 0, l4Rows: 0, baseOnlyRows: 0 });
|
|
399
|
+
logger_1.default.info(`MEWP coverage output summary: requirements=${requirements.length} rows=${rows.length} ` +
|
|
400
|
+
`bugRows=${coverageRowStats.bugRows} l3Rows=${coverageRowStats.l3Rows} ` +
|
|
401
|
+
`l4Rows=${coverageRowStats.l4Rows} baseOnlyRows=${coverageRowStats.baseOnlyRows}`);
|
|
355
402
|
return {
|
|
356
403
|
sheetName: this.buildMewpCoverageSheetName(planName, testPlanId),
|
|
357
404
|
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
@@ -360,9 +407,210 @@ class ResultDataProvider {
|
|
|
360
407
|
}
|
|
361
408
|
catch (error) {
|
|
362
409
|
logger_1.default.error(`Error during getMewpL2CoverageFlatResults: ${error.message}`);
|
|
410
|
+
if (error instanceof mewpExternalTableUtils_1.MewpExternalFileValidationError) {
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
return defaultPayload;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
|
|
417
|
+
var _a, _b, _c;
|
|
418
|
+
const defaultPayload = {
|
|
419
|
+
sheetName: `MEWP Internal Validation - Plan ${testPlanId}`,
|
|
420
|
+
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
421
|
+
rows: [],
|
|
422
|
+
};
|
|
423
|
+
try {
|
|
424
|
+
const planName = await this.fetchTestPlanName(testPlanId, projectName);
|
|
425
|
+
const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, !!(options === null || options === void 0 ? void 0 : options.useRelFallback));
|
|
426
|
+
const allRequirements = await this.fetchMewpL2Requirements(projectName);
|
|
427
|
+
const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
|
|
428
|
+
const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
|
|
429
|
+
const requirementFamilies = this.buildRequirementFamilyMap(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
|
|
430
|
+
const rows = [];
|
|
431
|
+
const stepsXmlByTestCase = this.buildTestCaseStepsXmlMap(testData);
|
|
432
|
+
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
433
|
+
const allTestCaseIds = new Set();
|
|
434
|
+
for (const suite of testData || []) {
|
|
435
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
436
|
+
for (const testCase of testCasesItems) {
|
|
437
|
+
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) || 0);
|
|
438
|
+
if (Number.isFinite(id) && id > 0)
|
|
439
|
+
allTestCaseIds.add(id);
|
|
440
|
+
}
|
|
441
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
442
|
+
for (const testPoint of testPointsItems) {
|
|
443
|
+
const id = Number((testPoint === null || testPoint === void 0 ? void 0 : testPoint.testCaseId) || ((_b = testPoint === null || testPoint === void 0 ? void 0 : testPoint.testCase) === null || _b === void 0 ? void 0 : _b.id) || 0);
|
|
444
|
+
if (Number.isFinite(id) && id > 0)
|
|
445
|
+
allTestCaseIds.add(id);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const validL2BaseKeys = new Set([...requirementFamilies.keys()]);
|
|
449
|
+
for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
|
|
450
|
+
const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
|
|
451
|
+
const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
|
|
452
|
+
? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
|
|
453
|
+
: [];
|
|
454
|
+
const mentionEntries = this.extractRequirementMentionsFromExpectedSteps(parsedSteps, true);
|
|
455
|
+
const mentionedL2Only = new Set();
|
|
456
|
+
const mentionedCodeFirstStep = new Map();
|
|
457
|
+
const mentionedBaseFirstStep = new Map();
|
|
458
|
+
for (const mentionEntry of mentionEntries) {
|
|
459
|
+
const scopeFilteredCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && mentionEntry.codes.size > 0
|
|
460
|
+
? [...mentionEntry.codes].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code)))
|
|
461
|
+
: [...mentionEntry.codes];
|
|
462
|
+
for (const code of scopeFilteredCodes) {
|
|
463
|
+
const baseKey = this.toRequirementKey(code);
|
|
464
|
+
if (!baseKey)
|
|
465
|
+
continue;
|
|
466
|
+
if (validL2BaseKeys.has(baseKey)) {
|
|
467
|
+
mentionedL2Only.add(code);
|
|
468
|
+
if (!mentionedCodeFirstStep.has(code)) {
|
|
469
|
+
mentionedCodeFirstStep.set(code, mentionEntry.stepRef);
|
|
470
|
+
}
|
|
471
|
+
if (!mentionedBaseFirstStep.has(baseKey)) {
|
|
472
|
+
mentionedBaseFirstStep.set(baseKey, mentionEntry.stepRef);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const mentionedBaseKeys = new Set([...mentionedL2Only].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
478
|
+
const expectedFamilyCodes = new Set();
|
|
479
|
+
for (const baseKey of mentionedBaseKeys) {
|
|
480
|
+
const familyCodes = requirementFamilies.get(baseKey);
|
|
481
|
+
if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
|
|
482
|
+
familyCodes.forEach((code) => expectedFamilyCodes.add(code));
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
for (const code of mentionedL2Only) {
|
|
486
|
+
if (this.toRequirementKey(code) === baseKey)
|
|
487
|
+
expectedFamilyCodes.add(code);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const linkedFullCodesRaw = ((_c = linkedRequirementsByTestCase.get(testCaseId)) === null || _c === void 0 ? void 0 : _c.fullCodes) || new Set();
|
|
492
|
+
const linkedFullCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && linkedFullCodesRaw.size > 0
|
|
493
|
+
? new Set([...linkedFullCodesRaw].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code))))
|
|
494
|
+
: linkedFullCodesRaw;
|
|
495
|
+
const linkedBaseKeys = new Set([...linkedFullCodes].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
496
|
+
const missingMentioned = [...mentionedL2Only].filter((code) => {
|
|
497
|
+
const baseKey = this.toRequirementKey(code);
|
|
498
|
+
if (!baseKey)
|
|
499
|
+
return false;
|
|
500
|
+
const hasSpecificSuffix = /-\d+$/.test(code);
|
|
501
|
+
if (hasSpecificSuffix)
|
|
502
|
+
return !linkedFullCodes.has(code);
|
|
503
|
+
return !linkedBaseKeys.has(baseKey);
|
|
504
|
+
});
|
|
505
|
+
const missingFamily = [...expectedFamilyCodes].filter((code) => !linkedFullCodes.has(code));
|
|
506
|
+
const extraLinked = [...linkedFullCodes].filter((code) => !expectedFamilyCodes.has(code));
|
|
507
|
+
const mentionedButNotLinkedByStep = new Map();
|
|
508
|
+
const appendMentionedButNotLinked = (requirementId, stepRef) => {
|
|
509
|
+
const normalizedRequirementId = this.normalizeMewpRequirementCodeWithSuffix(requirementId);
|
|
510
|
+
if (!normalizedRequirementId)
|
|
511
|
+
return;
|
|
512
|
+
const normalizedStepRef = String(stepRef || 'Step ?').trim() || 'Step ?';
|
|
513
|
+
if (!mentionedButNotLinkedByStep.has(normalizedStepRef)) {
|
|
514
|
+
mentionedButNotLinkedByStep.set(normalizedStepRef, new Set());
|
|
515
|
+
}
|
|
516
|
+
mentionedButNotLinkedByStep.get(normalizedStepRef).add(normalizedRequirementId);
|
|
517
|
+
};
|
|
518
|
+
const sortedMissingMentioned = [...new Set(missingMentioned)].sort((a, b) => a.localeCompare(b));
|
|
519
|
+
const sortedMissingFamily = [...new Set(missingFamily)].sort((a, b) => a.localeCompare(b));
|
|
520
|
+
for (const code of sortedMissingMentioned) {
|
|
521
|
+
const stepRef = mentionedCodeFirstStep.get(code) || 'Step ?';
|
|
522
|
+
appendMentionedButNotLinked(code, stepRef);
|
|
523
|
+
}
|
|
524
|
+
for (const code of sortedMissingFamily) {
|
|
525
|
+
const baseKey = this.toRequirementKey(code);
|
|
526
|
+
const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
|
|
527
|
+
appendMentionedButNotLinked(code, stepRef);
|
|
528
|
+
}
|
|
529
|
+
const sortedExtraLinked = [...new Set(extraLinked)]
|
|
530
|
+
.map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
|
|
531
|
+
.filter((code) => !!code)
|
|
532
|
+
.sort((a, b) => a.localeCompare(b));
|
|
533
|
+
const parseStepOrder = (stepRef) => {
|
|
534
|
+
const match = /step\s+(\d+)/i.exec(String(stepRef || ''));
|
|
535
|
+
const parsed = Number((match === null || match === void 0 ? void 0 : match[1]) || Number.POSITIVE_INFINITY);
|
|
536
|
+
return Number.isFinite(parsed) ? parsed : Number.POSITIVE_INFINITY;
|
|
537
|
+
};
|
|
538
|
+
const mentionedButNotLinked = [...mentionedButNotLinkedByStep.entries()]
|
|
539
|
+
.sort((a, b) => {
|
|
540
|
+
const stepOrderA = parseStepOrder(a[0]);
|
|
541
|
+
const stepOrderB = parseStepOrder(b[0]);
|
|
542
|
+
if (stepOrderA !== stepOrderB)
|
|
543
|
+
return stepOrderA - stepOrderB;
|
|
544
|
+
return String(a[0]).localeCompare(String(b[0]));
|
|
545
|
+
})
|
|
546
|
+
.map(([stepRef, requirementIds]) => {
|
|
547
|
+
const requirementList = [...requirementIds].sort((a, b) => a.localeCompare(b));
|
|
548
|
+
return `${stepRef}: ${requirementList.join(', ')}`;
|
|
549
|
+
})
|
|
550
|
+
.join('; ');
|
|
551
|
+
const linkedButNotMentioned = sortedExtraLinked.join('; ');
|
|
552
|
+
const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
|
|
553
|
+
rows.push({
|
|
554
|
+
'Test Case ID': testCaseId,
|
|
555
|
+
'Test Case Title': String(testCaseTitleMap.get(testCaseId) || '').trim(),
|
|
556
|
+
'Mentioned but Not Linked': mentionedButNotLinked,
|
|
557
|
+
'Linked but Not Mentioned': linkedButNotMentioned,
|
|
558
|
+
'Validation Status': validationStatus,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
sheetName: this.buildInternalValidationSheetName(planName, testPlanId),
|
|
563
|
+
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
564
|
+
rows,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
logger_1.default.error(`Error during getMewpInternalValidationFlatResults: ${error.message}`);
|
|
363
569
|
return defaultPayload;
|
|
364
570
|
}
|
|
365
571
|
}
|
|
572
|
+
async validateMewpExternalFiles(options) {
|
|
573
|
+
const response = { valid: true };
|
|
574
|
+
const validateOne = async (file, tableType) => {
|
|
575
|
+
const sourceName = String((file === null || file === void 0 ? void 0 : file.name) || (file === null || file === void 0 ? void 0 : file.objectName) || (file === null || file === void 0 ? void 0 : file.text) || (file === null || file === void 0 ? void 0 : file.url) || '').trim();
|
|
576
|
+
if (!sourceName)
|
|
577
|
+
return undefined;
|
|
578
|
+
try {
|
|
579
|
+
const { rows, meta } = await this.mewpExternalTableUtils.loadExternalTableRowsWithMeta(file, tableType);
|
|
580
|
+
return {
|
|
581
|
+
tableType,
|
|
582
|
+
sourceName: meta.sourceName,
|
|
583
|
+
valid: true,
|
|
584
|
+
headerRow: meta.headerRow,
|
|
585
|
+
matchedRequiredColumns: meta.matchedRequiredColumns,
|
|
586
|
+
totalRequiredColumns: meta.totalRequiredColumns,
|
|
587
|
+
missingRequiredColumns: [],
|
|
588
|
+
rowCount: rows.length,
|
|
589
|
+
message: 'File schema is valid',
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
if (error instanceof mewpExternalTableUtils_1.MewpExternalFileValidationError) {
|
|
594
|
+
return Object.assign(Object.assign({}, error.details), { valid: false });
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
tableType,
|
|
598
|
+
sourceName: sourceName || tableType,
|
|
599
|
+
valid: false,
|
|
600
|
+
headerRow: '',
|
|
601
|
+
matchedRequiredColumns: 0,
|
|
602
|
+
totalRequiredColumns: this.mewpExternalTableUtils.getRequiredColumnCount(tableType),
|
|
603
|
+
missingRequiredColumns: [],
|
|
604
|
+
rowCount: 0,
|
|
605
|
+
message: String((error === null || error === void 0 ? void 0 : error.message) || error || 'Unknown validation error'),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
response.bugs = await validateOne(options === null || options === void 0 ? void 0 : options.externalBugsFile, 'bugs');
|
|
610
|
+
response.l3l4 = await validateOne(options === null || options === void 0 ? void 0 : options.externalL3L4File, 'l3l4');
|
|
611
|
+
response.valid = [response.bugs, response.l3l4].filter(Boolean).every((item) => !!(item === null || item === void 0 ? void 0 : item.valid));
|
|
612
|
+
return response;
|
|
613
|
+
}
|
|
366
614
|
/**
|
|
367
615
|
* Mapping each attachment to a proper URL for downloading it
|
|
368
616
|
* @param runResults Array of run results
|
|
@@ -394,22 +642,52 @@ class ResultDataProvider {
|
|
|
394
642
|
const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
|
|
395
643
|
return `MEWP L2 Coverage - ${suffix}`;
|
|
396
644
|
}
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
645
|
+
buildInternalValidationSheetName(planName, testPlanId) {
|
|
646
|
+
const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
|
|
647
|
+
return `MEWP Internal Validation - ${suffix}`;
|
|
648
|
+
}
|
|
649
|
+
createMewpCoverageRow(requirement, runStatus, bug, linkedL3L4) {
|
|
650
|
+
const l2ReqId = this.formatMewpCustomerId(requirement.requirementId);
|
|
651
|
+
const l2ReqTitle = this.toMewpComparableText(requirement.title);
|
|
652
|
+
const l2SubSystem = this.toMewpComparableText(requirement.subSystem);
|
|
402
653
|
return {
|
|
403
|
-
'
|
|
404
|
-
'
|
|
405
|
-
'
|
|
406
|
-
'
|
|
407
|
-
'
|
|
408
|
-
'
|
|
409
|
-
'
|
|
410
|
-
'
|
|
654
|
+
'L2 REQ ID': l2ReqId,
|
|
655
|
+
'L2 REQ Title': l2ReqTitle,
|
|
656
|
+
'L2 SubSystem': l2SubSystem,
|
|
657
|
+
'L2 Run Status': runStatus,
|
|
658
|
+
'Bug ID': Number.isFinite(Number(bug === null || bug === void 0 ? void 0 : bug.id)) && Number(bug === null || bug === void 0 ? void 0 : bug.id) > 0 ? Number(bug === null || bug === void 0 ? void 0 : bug.id) : '',
|
|
659
|
+
'Bug Title': String((bug === null || bug === void 0 ? void 0 : bug.title) || '').trim(),
|
|
660
|
+
'Bug Responsibility': String((bug === null || bug === void 0 ? void 0 : bug.responsibility) || '').trim(),
|
|
661
|
+
'L3 REQ ID': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l3Id) || '').trim(),
|
|
662
|
+
'L3 REQ Title': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l3Title) || '').trim(),
|
|
663
|
+
'L4 REQ ID': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l4Id) || '').trim(),
|
|
664
|
+
'L4 REQ Title': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l4Title) || '').trim(),
|
|
411
665
|
};
|
|
412
666
|
}
|
|
667
|
+
createEmptyMewpCoverageBugCell() {
|
|
668
|
+
return { id: '', title: '', responsibility: '' };
|
|
669
|
+
}
|
|
670
|
+
createEmptyMewpCoverageL3L4Cell() {
|
|
671
|
+
return { l3Id: '', l3Title: '', l4Id: '', l4Title: '' };
|
|
672
|
+
}
|
|
673
|
+
buildMewpCoverageL3L4Rows(links) {
|
|
674
|
+
const sorted = [...(links || [])].sort((a, b) => {
|
|
675
|
+
if (a.level !== b.level)
|
|
676
|
+
return a.level === 'L3' ? -1 : 1;
|
|
677
|
+
return String(a.id || '').localeCompare(String(b.id || ''));
|
|
678
|
+
});
|
|
679
|
+
const rows = [];
|
|
680
|
+
for (const item of sorted) {
|
|
681
|
+
const isL3 = item.level === 'L3';
|
|
682
|
+
rows.push({
|
|
683
|
+
l3Id: isL3 ? String((item === null || item === void 0 ? void 0 : item.id) || '').trim() : '',
|
|
684
|
+
l3Title: isL3 ? String((item === null || item === void 0 ? void 0 : item.title) || '').trim() : '',
|
|
685
|
+
l4Id: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.id) || '').trim(),
|
|
686
|
+
l4Title: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.title) || '').trim(),
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
return rows;
|
|
690
|
+
}
|
|
413
691
|
formatMewpCustomerId(rawValue) {
|
|
414
692
|
const normalized = this.normalizeMewpRequirementCode(this.toMewpComparableText(rawValue));
|
|
415
693
|
if (normalized)
|
|
@@ -419,33 +697,289 @@ class ResultDataProvider {
|
|
|
419
697
|
return `SR${onlyDigits}`;
|
|
420
698
|
return '';
|
|
421
699
|
}
|
|
422
|
-
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement,
|
|
700
|
+
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase) {
|
|
423
701
|
var _a;
|
|
424
702
|
const rows = [];
|
|
703
|
+
const linkedByRequirement = this.invertBaseRequirementLinks(linkedRequirementsByTestCase);
|
|
425
704
|
for (const requirement of requirements) {
|
|
426
|
-
const key = this.toRequirementKey(requirement.requirementId);
|
|
705
|
+
const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || this.toRequirementKey(requirement.requirementId) || '').trim();
|
|
427
706
|
const linkedTestCaseIds = ((requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []).filter((id) => Number.isFinite(id) && Number(id) > 0);
|
|
707
|
+
const linkedByTestCase = key ? Array.from(linkedByRequirement.get(key) || []) : [];
|
|
428
708
|
const observedTestCaseIds = key
|
|
429
709
|
? Array.from(observedTestCaseIdsByRequirement.get(key) || [])
|
|
430
710
|
: [];
|
|
431
|
-
const testCaseIds = Array.from(new Set([...linkedTestCaseIds, ...observedTestCaseIds])).sort((a, b) => a - b);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
notRun: 0,
|
|
437
|
-
}));
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
711
|
+
const testCaseIds = Array.from(new Set([...linkedTestCaseIds, ...linkedByTestCase, ...observedTestCaseIds])).sort((a, b) => a - b);
|
|
712
|
+
let totalPassed = 0;
|
|
713
|
+
let totalFailed = 0;
|
|
714
|
+
let totalNotRun = 0;
|
|
715
|
+
const aggregatedBugs = new Map();
|
|
440
716
|
for (const testCaseId of testCaseIds) {
|
|
441
717
|
const summary = key
|
|
442
718
|
? ((_a = requirementIndex.get(key)) === null || _a === void 0 ? void 0 : _a.get(testCaseId)) || { passed: 0, failed: 0, notRun: 0 }
|
|
443
719
|
: { passed: 0, failed: 0, notRun: 0 };
|
|
444
|
-
|
|
720
|
+
totalPassed += summary.passed;
|
|
721
|
+
totalFailed += summary.failed;
|
|
722
|
+
totalNotRun += summary.notRun;
|
|
723
|
+
if (summary.failed > 0) {
|
|
724
|
+
const externalBugs = externalBugsByTestCase.get(testCaseId) || [];
|
|
725
|
+
for (const bug of externalBugs) {
|
|
726
|
+
const bugBaseKey = String((bug === null || bug === void 0 ? void 0 : bug.requirementBaseKey) || '').trim();
|
|
727
|
+
if (bugBaseKey && bugBaseKey !== key)
|
|
728
|
+
continue;
|
|
729
|
+
const bugId = Number((bug === null || bug === void 0 ? void 0 : bug.id) || 0);
|
|
730
|
+
if (!Number.isFinite(bugId) || bugId <= 0)
|
|
731
|
+
continue;
|
|
732
|
+
aggregatedBugs.set(bugId, Object.assign(Object.assign({}, bug), { responsibility: this.resolveCoverageBugResponsibility(String((bug === null || bug === void 0 ? void 0 : bug.responsibility) || ''), requirement) }));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const runStatus = this.resolveMewpL2RunStatus({
|
|
737
|
+
passed: totalPassed,
|
|
738
|
+
failed: totalFailed,
|
|
739
|
+
notRun: totalNotRun,
|
|
740
|
+
hasAnyTestCase: testCaseIds.length > 0,
|
|
741
|
+
});
|
|
742
|
+
const bugsForRows = runStatus === 'Fail'
|
|
743
|
+
? Array.from(aggregatedBugs.values()).sort((a, b) => a.id - b.id)
|
|
744
|
+
: [];
|
|
745
|
+
const l3l4ForRows = [...(l3l4ByBaseKey.get(key) || [])];
|
|
746
|
+
const bugRows = bugsForRows.length > 0
|
|
747
|
+
? bugsForRows
|
|
748
|
+
: [];
|
|
749
|
+
const l3l4Rows = this.buildMewpCoverageL3L4Rows(l3l4ForRows);
|
|
750
|
+
if (bugRows.length === 0 && l3l4Rows.length === 0) {
|
|
751
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), this.createEmptyMewpCoverageL3L4Cell()));
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
for (const bug of bugRows) {
|
|
755
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, bug, this.createEmptyMewpCoverageL3L4Cell()));
|
|
756
|
+
}
|
|
757
|
+
for (const linkedL3L4 of l3l4Rows) {
|
|
758
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), linkedL3L4));
|
|
445
759
|
}
|
|
446
760
|
}
|
|
447
761
|
return rows;
|
|
448
762
|
}
|
|
763
|
+
resolveCoverageBugResponsibility(rawResponsibility, requirement) {
|
|
764
|
+
const direct = String(rawResponsibility || '').trim();
|
|
765
|
+
if (direct && direct.toLowerCase() !== 'unknown')
|
|
766
|
+
return direct;
|
|
767
|
+
const requirementResponsibility = String((requirement === null || requirement === void 0 ? void 0 : requirement.responsibility) || '')
|
|
768
|
+
.trim()
|
|
769
|
+
.toUpperCase();
|
|
770
|
+
if (requirementResponsibility === 'ESUK')
|
|
771
|
+
return 'ESUK';
|
|
772
|
+
if (requirementResponsibility === 'IL' || requirementResponsibility === 'ELISRA')
|
|
773
|
+
return 'Elisra';
|
|
774
|
+
return direct || 'Unknown';
|
|
775
|
+
}
|
|
776
|
+
resolveMewpL2RunStatus(input) {
|
|
777
|
+
if (((input === null || input === void 0 ? void 0 : input.failed) || 0) > 0)
|
|
778
|
+
return 'Fail';
|
|
779
|
+
if (((input === null || input === void 0 ? void 0 : input.notRun) || 0) > 0)
|
|
780
|
+
return 'Not Run';
|
|
781
|
+
if (((input === null || input === void 0 ? void 0 : input.passed) || 0) > 0)
|
|
782
|
+
return 'Pass';
|
|
783
|
+
return (input === null || input === void 0 ? void 0 : input.hasAnyTestCase) ? 'Not Run' : 'Not Run';
|
|
784
|
+
}
|
|
785
|
+
async fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, useRelFallback) {
|
|
786
|
+
if (!useRelFallback) {
|
|
787
|
+
const suites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
|
|
788
|
+
return this.fetchTestData(suites, projectName, testPlanId, false);
|
|
789
|
+
}
|
|
790
|
+
const selectedSuites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
|
|
791
|
+
const selectedRel = this.resolveMaxRelNumberFromSuites(selectedSuites);
|
|
792
|
+
if (selectedRel <= 0) {
|
|
793
|
+
return this.fetchTestData(selectedSuites, projectName, testPlanId, false);
|
|
794
|
+
}
|
|
795
|
+
const allSuites = await this.fetchTestSuites(testPlanId, projectName, undefined, true);
|
|
796
|
+
const relScopedSuites = allSuites.filter((suite) => {
|
|
797
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
798
|
+
return rel > 0 && rel <= selectedRel;
|
|
799
|
+
});
|
|
800
|
+
const suitesForFetch = relScopedSuites.length > 0 ? relScopedSuites : selectedSuites;
|
|
801
|
+
const rawTestData = await this.fetchTestData(suitesForFetch, projectName, testPlanId, false);
|
|
802
|
+
return this.reduceToLatestRelRunPerTestCase(rawTestData);
|
|
803
|
+
}
|
|
804
|
+
extractRelNumberFromSuite(suite) {
|
|
805
|
+
const candidates = [
|
|
806
|
+
suite === null || suite === void 0 ? void 0 : suite.suiteName,
|
|
807
|
+
suite === null || suite === void 0 ? void 0 : suite.parentSuiteName,
|
|
808
|
+
suite === null || suite === void 0 ? void 0 : suite.suitePath,
|
|
809
|
+
suite === null || suite === void 0 ? void 0 : suite.testGroupName,
|
|
810
|
+
];
|
|
811
|
+
const pattern = /(?:^|[^a-z0-9])rel\s*([0-9]+)/i;
|
|
812
|
+
for (const item of candidates) {
|
|
813
|
+
const match = pattern.exec(String(item || ''));
|
|
814
|
+
if (!match)
|
|
815
|
+
continue;
|
|
816
|
+
const parsed = Number(match[1]);
|
|
817
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
818
|
+
return parsed;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return 0;
|
|
822
|
+
}
|
|
823
|
+
resolveMaxRelNumberFromSuites(suites) {
|
|
824
|
+
let maxRel = 0;
|
|
825
|
+
for (const suite of suites || []) {
|
|
826
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
827
|
+
if (rel > maxRel)
|
|
828
|
+
maxRel = rel;
|
|
829
|
+
}
|
|
830
|
+
return maxRel;
|
|
831
|
+
}
|
|
832
|
+
reduceToLatestRelRunPerTestCase(testData) {
|
|
833
|
+
var _a, _b;
|
|
834
|
+
const candidatesByTestCase = new Map();
|
|
835
|
+
const testCaseDefinitionById = new Map();
|
|
836
|
+
for (const suite of testData || []) {
|
|
837
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
838
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
839
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
840
|
+
for (const testCase of testCasesItems) {
|
|
841
|
+
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);
|
|
842
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
843
|
+
continue;
|
|
844
|
+
if (!testCaseDefinitionById.has(testCaseId)) {
|
|
845
|
+
testCaseDefinitionById.set(testCaseId, testCase);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
for (const point of testPointsItems) {
|
|
849
|
+
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);
|
|
850
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
851
|
+
continue;
|
|
852
|
+
const runId = Number((point === null || point === void 0 ? void 0 : point.lastRunId) || 0);
|
|
853
|
+
const resultId = Number((point === null || point === void 0 ? void 0 : point.lastResultId) || 0);
|
|
854
|
+
const hasRun = runId > 0 && resultId > 0;
|
|
855
|
+
if (!candidatesByTestCase.has(testCaseId)) {
|
|
856
|
+
candidatesByTestCase.set(testCaseId, []);
|
|
857
|
+
}
|
|
858
|
+
candidatesByTestCase.get(testCaseId).push({
|
|
859
|
+
point,
|
|
860
|
+
rel,
|
|
861
|
+
runId,
|
|
862
|
+
resultId,
|
|
863
|
+
hasRun,
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const selectedPoints = [];
|
|
868
|
+
const selectedTestCaseIds = new Set();
|
|
869
|
+
for (const [testCaseId, candidates] of candidatesByTestCase.entries()) {
|
|
870
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
871
|
+
if (a.hasRun !== b.hasRun)
|
|
872
|
+
return a.hasRun ? -1 : 1;
|
|
873
|
+
if (a.rel !== b.rel)
|
|
874
|
+
return b.rel - a.rel;
|
|
875
|
+
if (a.runId !== b.runId)
|
|
876
|
+
return b.runId - a.runId;
|
|
877
|
+
return b.resultId - a.resultId;
|
|
878
|
+
});
|
|
879
|
+
const chosen = sorted[0];
|
|
880
|
+
if (!(chosen === null || chosen === void 0 ? void 0 : chosen.point))
|
|
881
|
+
continue;
|
|
882
|
+
selectedPoints.push(chosen.point);
|
|
883
|
+
selectedTestCaseIds.add(testCaseId);
|
|
884
|
+
}
|
|
885
|
+
const selectedTestCases = [];
|
|
886
|
+
for (const testCaseId of selectedTestCaseIds) {
|
|
887
|
+
const definition = testCaseDefinitionById.get(testCaseId);
|
|
888
|
+
if (definition) {
|
|
889
|
+
selectedTestCases.push(definition);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return [
|
|
893
|
+
{
|
|
894
|
+
testSuiteId: 'MEWP_REL_SCOPED',
|
|
895
|
+
suiteId: 'MEWP_REL_SCOPED',
|
|
896
|
+
suiteName: 'MEWP Rel Scoped',
|
|
897
|
+
parentSuiteId: '',
|
|
898
|
+
parentSuiteName: '',
|
|
899
|
+
suitePath: 'MEWP Rel Scoped',
|
|
900
|
+
testGroupName: 'MEWP Rel Scoped',
|
|
901
|
+
testPointsItems: selectedPoints,
|
|
902
|
+
testCasesItems: selectedTestCases,
|
|
903
|
+
},
|
|
904
|
+
];
|
|
905
|
+
}
|
|
906
|
+
async loadExternalBugsByTestCase(externalBugsFile) {
|
|
907
|
+
return this.mewpExternalIngestionUtils.loadExternalBugsByTestCase(externalBugsFile, {
|
|
908
|
+
toComparableText: (value) => this.toMewpComparableText(value),
|
|
909
|
+
toRequirementKey: (value) => this.toRequirementKey(value),
|
|
910
|
+
resolveBugResponsibility: (fields) => this.resolveBugResponsibility(fields),
|
|
911
|
+
isExternalStateInScope: (value, itemType) => this.isExternalStateInScope(value, itemType),
|
|
912
|
+
isExcludedL3L4BySapWbs: (value) => this.isExcludedL3L4BySapWbs(value),
|
|
913
|
+
resolveRequirementSapWbsByBaseKey: () => '',
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
async loadExternalL3L4ByBaseKey(externalL3L4File, requirementSapWbsByBaseKey = new Map()) {
|
|
917
|
+
return this.mewpExternalIngestionUtils.loadExternalL3L4ByBaseKey(externalL3L4File, {
|
|
918
|
+
toComparableText: (value) => this.toMewpComparableText(value),
|
|
919
|
+
toRequirementKey: (value) => this.toRequirementKey(value),
|
|
920
|
+
resolveBugResponsibility: (fields) => this.resolveBugResponsibility(fields),
|
|
921
|
+
isExternalStateInScope: (value, itemType) => this.isExternalStateInScope(value, itemType),
|
|
922
|
+
isExcludedL3L4BySapWbs: (value) => this.isExcludedL3L4BySapWbs(value),
|
|
923
|
+
resolveRequirementSapWbsByBaseKey: (baseKey) => String(requirementSapWbsByBaseKey.get(baseKey) || ''),
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
buildRequirementSapWbsByBaseKey(requirements) {
|
|
927
|
+
const out = new Map();
|
|
928
|
+
for (const requirement of requirements || []) {
|
|
929
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
930
|
+
if (!baseKey)
|
|
931
|
+
continue;
|
|
932
|
+
const normalized = this.resolveMewpResponsibility(this.toMewpComparableText(requirement === null || requirement === void 0 ? void 0 : requirement.responsibility));
|
|
933
|
+
if (!normalized)
|
|
934
|
+
continue;
|
|
935
|
+
const existing = out.get(baseKey) || '';
|
|
936
|
+
// Keep ESUK as dominant if conflicting values are ever present across family items.
|
|
937
|
+
if (existing === 'ESUK')
|
|
938
|
+
continue;
|
|
939
|
+
if (normalized === 'ESUK' || !existing) {
|
|
940
|
+
out.set(baseKey, normalized);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return out;
|
|
944
|
+
}
|
|
945
|
+
isExternalStateInScope(value, itemType) {
|
|
946
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
947
|
+
if (!normalized)
|
|
948
|
+
return true;
|
|
949
|
+
// TFS/ADO processes usually don't expose a literal "Open" state.
|
|
950
|
+
// Keep non-terminal states, exclude terminal states.
|
|
951
|
+
const terminalStates = new Set([
|
|
952
|
+
'resolved',
|
|
953
|
+
'closed',
|
|
954
|
+
'done',
|
|
955
|
+
'completed',
|
|
956
|
+
'complete',
|
|
957
|
+
'removed',
|
|
958
|
+
'rejected',
|
|
959
|
+
'cancelled',
|
|
960
|
+
'canceled',
|
|
961
|
+
'obsolete',
|
|
962
|
+
]);
|
|
963
|
+
if (terminalStates.has(normalized))
|
|
964
|
+
return false;
|
|
965
|
+
// Bug-specific terminal variants often used in custom processes.
|
|
966
|
+
if (itemType === 'bug') {
|
|
967
|
+
if (normalized === 'fixed')
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
invertBaseRequirementLinks(linkedRequirementsByTestCase) {
|
|
973
|
+
const out = new Map();
|
|
974
|
+
for (const [testCaseId, links] of linkedRequirementsByTestCase.entries()) {
|
|
975
|
+
for (const baseKey of (links === null || links === void 0 ? void 0 : links.baseKeys) || []) {
|
|
976
|
+
if (!out.has(baseKey))
|
|
977
|
+
out.set(baseKey, new Set());
|
|
978
|
+
out.get(baseKey).add(testCaseId);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return out;
|
|
982
|
+
}
|
|
449
983
|
buildMewpTestCaseTitleMap(testData) {
|
|
450
984
|
var _a, _b, _c, _d, _e;
|
|
451
985
|
const map = new Map();
|
|
@@ -545,49 +1079,181 @@ class ResultDataProvider {
|
|
|
545
1079
|
return 'failed';
|
|
546
1080
|
return 'notRun';
|
|
547
1081
|
}
|
|
548
|
-
|
|
1082
|
+
accumulateRequirementCountsFromActionResults(actionResults, testCaseId, requirementKeys, counters, observedTestCaseIdsByRequirement) {
|
|
549
1083
|
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
550
1084
|
return;
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
1085
|
+
const sortedResults = Array.isArray(actionResults) ? actionResults : [];
|
|
1086
|
+
let previousRequirementStepIndex = -1;
|
|
1087
|
+
for (let i = 0; i < sortedResults.length; i++) {
|
|
1088
|
+
const actionResult = sortedResults[i];
|
|
1089
|
+
if (actionResult === null || actionResult === void 0 ? void 0 : actionResult.isSharedStepTitle)
|
|
554
1090
|
continue;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
1091
|
+
const requirementCodes = this.extractRequirementCodesFromText((actionResult === null || actionResult === void 0 ? void 0 : actionResult.expected) || '');
|
|
1092
|
+
if (requirementCodes.size === 0)
|
|
1093
|
+
continue;
|
|
1094
|
+
const startIndex = previousRequirementStepIndex + 1;
|
|
1095
|
+
const status = this.resolveRequirementStatusForWindow(sortedResults, startIndex, i);
|
|
1096
|
+
previousRequirementStepIndex = i;
|
|
1097
|
+
for (const code of requirementCodes) {
|
|
1098
|
+
if (requirementKeys.size > 0 && !requirementKeys.has(code))
|
|
1099
|
+
continue;
|
|
1100
|
+
if (!counters.has(code)) {
|
|
1101
|
+
counters.set(code, new Map());
|
|
1102
|
+
}
|
|
1103
|
+
const perTestCaseCounters = counters.get(code);
|
|
1104
|
+
if (!perTestCaseCounters.has(testCaseId)) {
|
|
1105
|
+
perTestCaseCounters.set(testCaseId, { passed: 0, failed: 0, notRun: 0 });
|
|
1106
|
+
}
|
|
1107
|
+
if (!observedTestCaseIdsByRequirement.has(code)) {
|
|
1108
|
+
observedTestCaseIdsByRequirement.set(code, new Set());
|
|
1109
|
+
}
|
|
1110
|
+
observedTestCaseIdsByRequirement.get(code).add(testCaseId);
|
|
1111
|
+
const counter = perTestCaseCounters.get(testCaseId);
|
|
1112
|
+
if (status === 'passed')
|
|
1113
|
+
counter.passed += 1;
|
|
1114
|
+
else if (status === 'failed')
|
|
1115
|
+
counter.failed += 1;
|
|
1116
|
+
else
|
|
1117
|
+
counter.notRun += 1;
|
|
564
1118
|
}
|
|
565
|
-
observedTestCaseIdsByRequirement.get(code).add(testCaseId);
|
|
566
|
-
const counter = perTestCaseCounters.get(testCaseId);
|
|
567
|
-
if (status === 'passed')
|
|
568
|
-
counter.passed += 1;
|
|
569
|
-
else if (status === 'failed')
|
|
570
|
-
counter.failed += 1;
|
|
571
|
-
else
|
|
572
|
-
counter.notRun += 1;
|
|
573
1119
|
}
|
|
574
1120
|
}
|
|
1121
|
+
resolveRequirementStatusForWindow(actionResults, startIndex, endIndex) {
|
|
1122
|
+
var _a;
|
|
1123
|
+
let hasNotRun = false;
|
|
1124
|
+
for (let index = startIndex; index <= endIndex; index++) {
|
|
1125
|
+
const status = this.classifyRequirementStepOutcome((_a = actionResults[index]) === null || _a === void 0 ? void 0 : _a.outcome);
|
|
1126
|
+
if (status === 'failed')
|
|
1127
|
+
return 'failed';
|
|
1128
|
+
if (status === 'notRun')
|
|
1129
|
+
hasNotRun = true;
|
|
1130
|
+
}
|
|
1131
|
+
return hasNotRun ? 'notRun' : 'passed';
|
|
1132
|
+
}
|
|
575
1133
|
extractRequirementCodesFromText(text) {
|
|
1134
|
+
return this.extractRequirementCodesFromExpectedText(text, false);
|
|
1135
|
+
}
|
|
1136
|
+
extractRequirementMentionsFromExpectedSteps(steps, includeSuffix) {
|
|
1137
|
+
const out = [];
|
|
1138
|
+
const allSteps = Array.isArray(steps) ? steps : [];
|
|
1139
|
+
for (let index = 0; index < allSteps.length; index += 1) {
|
|
1140
|
+
const step = allSteps[index];
|
|
1141
|
+
if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
|
|
1142
|
+
continue;
|
|
1143
|
+
const codes = this.extractRequirementCodesFromExpectedText((step === null || step === void 0 ? void 0 : step.expected) || '', includeSuffix);
|
|
1144
|
+
if (codes.size === 0)
|
|
1145
|
+
continue;
|
|
1146
|
+
out.push({
|
|
1147
|
+
stepRef: this.resolveValidationStepReference(step, index),
|
|
1148
|
+
codes,
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
return out;
|
|
1152
|
+
}
|
|
1153
|
+
extractRequirementCodesFromExpectedSteps(steps, includeSuffix) {
|
|
1154
|
+
const out = new Set();
|
|
1155
|
+
for (const step of Array.isArray(steps) ? steps : []) {
|
|
1156
|
+
if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
|
|
1157
|
+
continue;
|
|
1158
|
+
const codes = this.extractRequirementCodesFromExpectedText((step === null || step === void 0 ? void 0 : step.expected) || '', includeSuffix);
|
|
1159
|
+
codes.forEach((code) => out.add(code));
|
|
1160
|
+
}
|
|
1161
|
+
return out;
|
|
1162
|
+
}
|
|
1163
|
+
extractRequirementCodesFromExpectedText(text, includeSuffix) {
|
|
576
1164
|
const out = new Set();
|
|
577
1165
|
const source = this.normalizeRequirementStepText(text);
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1166
|
+
if (!source)
|
|
1167
|
+
return out;
|
|
1168
|
+
const tokens = source
|
|
1169
|
+
.split(';')
|
|
1170
|
+
.map((token) => String(token || '').trim())
|
|
1171
|
+
.filter((token) => token !== '');
|
|
1172
|
+
for (const token of tokens) {
|
|
1173
|
+
const candidates = this.extractRequirementCandidatesFromToken(token);
|
|
1174
|
+
for (const candidate of candidates) {
|
|
1175
|
+
const expandedTokens = this.expandRequirementTokenByComma(candidate);
|
|
1176
|
+
for (const expandedToken of expandedTokens) {
|
|
1177
|
+
if (!expandedToken || /vvrm/i.test(expandedToken))
|
|
1178
|
+
continue;
|
|
1179
|
+
const normalized = this.normalizeRequirementCodeToken(expandedToken, includeSuffix);
|
|
1180
|
+
if (normalized) {
|
|
1181
|
+
out.add(normalized);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
587
1184
|
}
|
|
588
1185
|
}
|
|
589
1186
|
return out;
|
|
590
1187
|
}
|
|
1188
|
+
extractRequirementCandidatesFromToken(token) {
|
|
1189
|
+
const source = String(token || '');
|
|
1190
|
+
if (!source)
|
|
1191
|
+
return [];
|
|
1192
|
+
const out = new Set();
|
|
1193
|
+
const collectCandidates = (input, rejectTailPattern) => {
|
|
1194
|
+
for (const match of input.matchAll(/SR\d{4,}(?:-\d+(?:,\d+)*)?/gi)) {
|
|
1195
|
+
const matchedValue = String((match === null || match === void 0 ? void 0 : match[0]) || '')
|
|
1196
|
+
.trim()
|
|
1197
|
+
.toUpperCase();
|
|
1198
|
+
if (!matchedValue)
|
|
1199
|
+
continue;
|
|
1200
|
+
const endIndex = Number((match === null || match === void 0 ? void 0 : match.index) || 0) + matchedValue.length;
|
|
1201
|
+
const tail = String(input.slice(endIndex) || '');
|
|
1202
|
+
if (rejectTailPattern.test(tail))
|
|
1203
|
+
continue;
|
|
1204
|
+
out.add(matchedValue);
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
// Normal scan keeps punctuation context (" SR0817-V3.2 " -> reject via tail).
|
|
1208
|
+
collectCandidates(source, /^\s*(?:V\d|VVRM|-V\d)/i);
|
|
1209
|
+
// Compact scan preserves legacy support for spaced SR letters/digits
|
|
1210
|
+
// such as "S R 0 0 0 1" and HTML-fragmented tokens.
|
|
1211
|
+
const compactSource = source.replace(/\s+/g, '');
|
|
1212
|
+
if (compactSource && compactSource !== source) {
|
|
1213
|
+
collectCandidates(compactSource, /^(?:V\d|VVRM|-V\d)/i);
|
|
1214
|
+
}
|
|
1215
|
+
return [...out];
|
|
1216
|
+
}
|
|
1217
|
+
expandRequirementTokenByComma(token) {
|
|
1218
|
+
const compact = String(token || '').trim().toUpperCase();
|
|
1219
|
+
if (!compact)
|
|
1220
|
+
return [];
|
|
1221
|
+
const suffixBatchMatch = /^SR(\d{4,})-(\d+(?:,\d+)+)$/.exec(compact);
|
|
1222
|
+
if (suffixBatchMatch) {
|
|
1223
|
+
const base = String(suffixBatchMatch[1] || '').trim();
|
|
1224
|
+
const suffixes = String(suffixBatchMatch[2] || '')
|
|
1225
|
+
.split(',')
|
|
1226
|
+
.map((item) => String(item || '').trim())
|
|
1227
|
+
.filter((item) => /^\d+$/.test(item));
|
|
1228
|
+
return suffixes.map((suffix) => `SR${base}-${suffix}`);
|
|
1229
|
+
}
|
|
1230
|
+
return compact
|
|
1231
|
+
.split(',')
|
|
1232
|
+
.map((part) => String(part || '').trim())
|
|
1233
|
+
.filter((part) => !!part);
|
|
1234
|
+
}
|
|
1235
|
+
normalizeRequirementCodeToken(token, includeSuffix) {
|
|
1236
|
+
const compact = String(token || '')
|
|
1237
|
+
.trim()
|
|
1238
|
+
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
1239
|
+
.toUpperCase();
|
|
1240
|
+
if (!compact)
|
|
1241
|
+
return '';
|
|
1242
|
+
const pattern = includeSuffix ? /^SR(\d{4,})(?:-(\d+))?$/ : /^SR(\d{4,})(?:-\d+)?$/;
|
|
1243
|
+
const match = pattern.exec(compact);
|
|
1244
|
+
if (!match)
|
|
1245
|
+
return '';
|
|
1246
|
+
const baseDigits = String(match[1] || '').trim();
|
|
1247
|
+
if (!baseDigits)
|
|
1248
|
+
return '';
|
|
1249
|
+
if (includeSuffix && match[2]) {
|
|
1250
|
+
const suffixDigits = String(match[2] || '').trim();
|
|
1251
|
+
if (!suffixDigits)
|
|
1252
|
+
return '';
|
|
1253
|
+
return `SR${baseDigits}-${suffixDigits}`;
|
|
1254
|
+
}
|
|
1255
|
+
return `SR${baseDigits}`;
|
|
1256
|
+
}
|
|
591
1257
|
normalizeRequirementStepText(text) {
|
|
592
1258
|
const raw = String(text || '');
|
|
593
1259
|
if (!raw)
|
|
@@ -603,17 +1269,19 @@ class ResultDataProvider {
|
|
|
603
1269
|
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
604
1270
|
.replace(/\s+/g, ' ');
|
|
605
1271
|
}
|
|
1272
|
+
resolveValidationStepReference(step, index) {
|
|
1273
|
+
const fromPosition = String((step === null || step === void 0 ? void 0 : step.stepPosition) || '').trim();
|
|
1274
|
+
if (fromPosition)
|
|
1275
|
+
return `Step ${fromPosition}`;
|
|
1276
|
+
const fromId = String((step === null || step === void 0 ? void 0 : step.stepId) || '').trim();
|
|
1277
|
+
if (fromId)
|
|
1278
|
+
return `Step ${fromId}`;
|
|
1279
|
+
return `Step ${index + 1}`;
|
|
1280
|
+
}
|
|
606
1281
|
toRequirementKey(requirementId) {
|
|
607
|
-
|
|
608
|
-
if (!normalized)
|
|
609
|
-
return '';
|
|
610
|
-
const digits = Number.parseInt(normalized.replace(/^SR/i, ''), 10);
|
|
611
|
-
if (!Number.isFinite(digits))
|
|
612
|
-
return '';
|
|
613
|
-
return `SR${digits}`;
|
|
1282
|
+
return this.normalizeMewpRequirementCode(requirementId);
|
|
614
1283
|
}
|
|
615
1284
|
async fetchMewpL2Requirements(projectName) {
|
|
616
|
-
var _a;
|
|
617
1285
|
const workItemTypeNames = await this.fetchMewpRequirementTypeNames(projectName);
|
|
618
1286
|
if (workItemTypeNames.length === 0) {
|
|
619
1287
|
return [];
|
|
@@ -621,32 +1289,315 @@ class ResultDataProvider {
|
|
|
621
1289
|
const quotedTypeNames = workItemTypeNames
|
|
622
1290
|
.map((name) => `'${String(name).replace(/'/g, "''")}'`)
|
|
623
1291
|
.join(', ');
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
AND [System.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
.
|
|
1292
|
+
const queryRequirementIds = async (l2AreaPath) => {
|
|
1293
|
+
var _a;
|
|
1294
|
+
const escapedAreaPath = l2AreaPath ? String(l2AreaPath).replace(/'/g, "''") : '';
|
|
1295
|
+
const areaFilter = escapedAreaPath ? `\n AND [System.AreaPath] UNDER '${escapedAreaPath}'` : '';
|
|
1296
|
+
const wiql = `SELECT [System.Id]
|
|
1297
|
+
FROM WorkItems
|
|
1298
|
+
WHERE [System.TeamProject] = @project
|
|
1299
|
+
AND [System.WorkItemType] IN (${quotedTypeNames})${areaFilter}
|
|
1300
|
+
ORDER BY [System.Id]`;
|
|
1301
|
+
const wiqlUrl = `${this.orgUrl}${projectName}/_apis/wit/wiql?api-version=7.1-preview.2`;
|
|
1302
|
+
const wiqlResponse = await tfs_1.TFSServices.postRequest(wiqlUrl, this.token, 'Post', { query: wiql }, null);
|
|
1303
|
+
const workItemRefs = Array.isArray((_a = wiqlResponse === null || wiqlResponse === void 0 ? void 0 : wiqlResponse.data) === null || _a === void 0 ? void 0 : _a.workItems) ? wiqlResponse.data.workItems : [];
|
|
1304
|
+
return workItemRefs
|
|
1305
|
+
.map((item) => Number(item === null || item === void 0 ? void 0 : item.id))
|
|
1306
|
+
.filter((id) => Number.isFinite(id));
|
|
1307
|
+
};
|
|
1308
|
+
const defaultL2AreaPath = `${String(projectName || '').trim()}\\Customer Requirements\\Level 2`;
|
|
1309
|
+
let requirementIds = [];
|
|
1310
|
+
try {
|
|
1311
|
+
requirementIds = await queryRequirementIds(defaultL2AreaPath);
|
|
1312
|
+
}
|
|
1313
|
+
catch (error) {
|
|
1314
|
+
logger_1.default.warn(`Could not apply MEWP L2 WIQL area-path optimization. Falling back to full requirement scope: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
|
|
1315
|
+
}
|
|
1316
|
+
if (requirementIds.length === 0) {
|
|
1317
|
+
requirementIds = await queryRequirementIds(null);
|
|
1318
|
+
}
|
|
635
1319
|
if (requirementIds.length === 0) {
|
|
636
1320
|
return [];
|
|
637
1321
|
}
|
|
638
1322
|
const workItems = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
|
|
639
1323
|
const requirements = workItems.map((wi) => {
|
|
640
1324
|
const fields = (wi === null || wi === void 0 ? void 0 : wi.fields) || {};
|
|
1325
|
+
const requirementId = this.extractMewpRequirementIdentifier(fields, Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0));
|
|
1326
|
+
const areaPath = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
641
1327
|
return {
|
|
642
1328
|
workItemId: Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0),
|
|
643
|
-
requirementId
|
|
1329
|
+
requirementId,
|
|
1330
|
+
baseKey: this.toRequirementKey(requirementId),
|
|
644
1331
|
title: this.toMewpComparableText((fields === null || fields === void 0 ? void 0 : fields['System.Title']) || (wi === null || wi === void 0 ? void 0 : wi.title)),
|
|
1332
|
+
subSystem: this.deriveMewpSubSystem(fields),
|
|
645
1333
|
responsibility: this.deriveMewpResponsibility(fields),
|
|
646
1334
|
linkedTestCaseIds: this.extractLinkedTestCaseIdsFromRequirement((wi === null || wi === void 0 ? void 0 : wi.relations) || []),
|
|
1335
|
+
relatedWorkItemIds: this.extractLinkedWorkItemIdsFromRelations((wi === null || wi === void 0 ? void 0 : wi.relations) || []),
|
|
1336
|
+
areaPath,
|
|
647
1337
|
};
|
|
648
1338
|
});
|
|
649
|
-
return requirements
|
|
1339
|
+
return requirements
|
|
1340
|
+
.filter((item) => {
|
|
1341
|
+
if (!item.baseKey)
|
|
1342
|
+
return false;
|
|
1343
|
+
if (!item.areaPath)
|
|
1344
|
+
return true;
|
|
1345
|
+
return this.isMewpL2AreaPath(item.areaPath);
|
|
1346
|
+
})
|
|
1347
|
+
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
1348
|
+
}
|
|
1349
|
+
isMewpL2AreaPath(areaPath) {
|
|
1350
|
+
const normalized = String(areaPath || '')
|
|
1351
|
+
.trim()
|
|
1352
|
+
.toLowerCase()
|
|
1353
|
+
.replace(/\//g, '\\');
|
|
1354
|
+
if (!normalized)
|
|
1355
|
+
return false;
|
|
1356
|
+
return normalized.includes('\\customer requirements\\level 2');
|
|
1357
|
+
}
|
|
1358
|
+
collapseMewpRequirementFamilies(requirements, scopedRequirementKeys) {
|
|
1359
|
+
const families = new Map();
|
|
1360
|
+
const calcScore = (item) => {
|
|
1361
|
+
const requirementId = String((item === null || item === void 0 ? void 0 : item.requirementId) || '').trim();
|
|
1362
|
+
const areaPath = String((item === null || item === void 0 ? void 0 : item.areaPath) || '')
|
|
1363
|
+
.trim()
|
|
1364
|
+
.toLowerCase();
|
|
1365
|
+
let score = 0;
|
|
1366
|
+
if (/^SR\d+$/i.test(requirementId))
|
|
1367
|
+
score += 6;
|
|
1368
|
+
if (areaPath.includes('\\customer requirements\\level 2'))
|
|
1369
|
+
score += 3;
|
|
1370
|
+
if (!areaPath.includes('\\mop'))
|
|
1371
|
+
score += 2;
|
|
1372
|
+
if (String((item === null || item === void 0 ? void 0 : item.title) || '').trim())
|
|
1373
|
+
score += 1;
|
|
1374
|
+
if (String((item === null || item === void 0 ? void 0 : item.subSystem) || '').trim())
|
|
1375
|
+
score += 1;
|
|
1376
|
+
if (String((item === null || item === void 0 ? void 0 : item.responsibility) || '').trim())
|
|
1377
|
+
score += 1;
|
|
1378
|
+
return score;
|
|
1379
|
+
};
|
|
1380
|
+
for (const requirement of requirements || []) {
|
|
1381
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1382
|
+
if (!baseKey)
|
|
1383
|
+
continue;
|
|
1384
|
+
if ((scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && !scopedRequirementKeys.has(baseKey))
|
|
1385
|
+
continue;
|
|
1386
|
+
if (!families.has(baseKey)) {
|
|
1387
|
+
families.set(baseKey, {
|
|
1388
|
+
representative: requirement,
|
|
1389
|
+
score: calcScore(requirement),
|
|
1390
|
+
linkedTestCaseIds: new Set(),
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
const family = families.get(baseKey);
|
|
1394
|
+
const score = calcScore(requirement);
|
|
1395
|
+
if (score > family.score) {
|
|
1396
|
+
family.representative = requirement;
|
|
1397
|
+
family.score = score;
|
|
1398
|
+
}
|
|
1399
|
+
for (const testCaseId of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
|
|
1400
|
+
if (Number.isFinite(testCaseId) && Number(testCaseId) > 0) {
|
|
1401
|
+
family.linkedTestCaseIds.add(Number(testCaseId));
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return [...families.entries()]
|
|
1406
|
+
.map(([baseKey, family]) => {
|
|
1407
|
+
var _a, _b, _c, _d;
|
|
1408
|
+
return ({
|
|
1409
|
+
requirementId: String(((_a = family === null || family === void 0 ? void 0 : family.representative) === null || _a === void 0 ? void 0 : _a.requirementId) || baseKey),
|
|
1410
|
+
baseKey,
|
|
1411
|
+
title: String(((_b = family === null || family === void 0 ? void 0 : family.representative) === null || _b === void 0 ? void 0 : _b.title) || ''),
|
|
1412
|
+
subSystem: String(((_c = family === null || family === void 0 ? void 0 : family.representative) === null || _c === void 0 ? void 0 : _c.subSystem) || ''),
|
|
1413
|
+
responsibility: String(((_d = family === null || family === void 0 ? void 0 : family.representative) === null || _d === void 0 ? void 0 : _d.responsibility) || ''),
|
|
1414
|
+
linkedTestCaseIds: [...family.linkedTestCaseIds].sort((a, b) => a - b),
|
|
1415
|
+
});
|
|
1416
|
+
})
|
|
1417
|
+
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
1418
|
+
}
|
|
1419
|
+
buildRequirementFamilyMap(requirements, scopedRequirementKeys) {
|
|
1420
|
+
const familyMap = new Map();
|
|
1421
|
+
for (const requirement of requirements || []) {
|
|
1422
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1423
|
+
if (!baseKey)
|
|
1424
|
+
continue;
|
|
1425
|
+
if ((scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && !scopedRequirementKeys.has(baseKey))
|
|
1426
|
+
continue;
|
|
1427
|
+
const fullCode = this.normalizeMewpRequirementCodeWithSuffix((requirement === null || requirement === void 0 ? void 0 : requirement.requirementId) || '');
|
|
1428
|
+
if (!fullCode)
|
|
1429
|
+
continue;
|
|
1430
|
+
if (!familyMap.has(baseKey))
|
|
1431
|
+
familyMap.set(baseKey, new Set());
|
|
1432
|
+
familyMap.get(baseKey).add(fullCode);
|
|
1433
|
+
}
|
|
1434
|
+
return familyMap;
|
|
1435
|
+
}
|
|
1436
|
+
async buildLinkedRequirementsByTestCase(requirements, testData, projectName) {
|
|
1437
|
+
var _a, _b, _c;
|
|
1438
|
+
const map = new Map();
|
|
1439
|
+
const ensure = (testCaseId) => {
|
|
1440
|
+
if (!map.has(testCaseId)) {
|
|
1441
|
+
map.set(testCaseId, {
|
|
1442
|
+
baseKeys: new Set(),
|
|
1443
|
+
fullCodes: new Set(),
|
|
1444
|
+
bugIds: new Set(),
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
return map.get(testCaseId);
|
|
1448
|
+
};
|
|
1449
|
+
const requirementById = new Map();
|
|
1450
|
+
for (const requirement of requirements || []) {
|
|
1451
|
+
const workItemId = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
|
|
1452
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1453
|
+
const fullCode = this.normalizeMewpRequirementCodeWithSuffix((requirement === null || requirement === void 0 ? void 0 : requirement.requirementId) || '');
|
|
1454
|
+
if (workItemId > 0 && baseKey && fullCode) {
|
|
1455
|
+
requirementById.set(workItemId, { baseKey, fullCode });
|
|
1456
|
+
}
|
|
1457
|
+
for (const testCaseIdRaw of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
|
|
1458
|
+
const testCaseId = Number(testCaseIdRaw);
|
|
1459
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0 || !baseKey || !fullCode)
|
|
1460
|
+
continue;
|
|
1461
|
+
const entry = ensure(testCaseId);
|
|
1462
|
+
entry.baseKeys.add(baseKey);
|
|
1463
|
+
entry.fullCodes.add(fullCode);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const testCaseIds = new Set();
|
|
1467
|
+
for (const suite of testData || []) {
|
|
1468
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
1469
|
+
for (const testCase of testCasesItems) {
|
|
1470
|
+
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) || 0);
|
|
1471
|
+
if (Number.isFinite(id) && id > 0)
|
|
1472
|
+
testCaseIds.add(id);
|
|
1473
|
+
}
|
|
1474
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
1475
|
+
for (const testPoint of testPointsItems) {
|
|
1476
|
+
const id = Number((testPoint === null || testPoint === void 0 ? void 0 : testPoint.testCaseId) || ((_b = testPoint === null || testPoint === void 0 ? void 0 : testPoint.testCase) === null || _b === void 0 ? void 0 : _b.id) || 0);
|
|
1477
|
+
if (Number.isFinite(id) && id > 0)
|
|
1478
|
+
testCaseIds.add(id);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
const relatedIdsByTestCase = new Map();
|
|
1482
|
+
const allRelatedIds = new Set();
|
|
1483
|
+
if (testCaseIds.size > 0) {
|
|
1484
|
+
const testCaseWorkItems = await this.fetchWorkItemsByIds(projectName, [...testCaseIds], true);
|
|
1485
|
+
for (const workItem of testCaseWorkItems || []) {
|
|
1486
|
+
const testCaseId = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1487
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
1488
|
+
continue;
|
|
1489
|
+
const relations = Array.isArray(workItem === null || workItem === void 0 ? void 0 : workItem.relations) ? workItem.relations : [];
|
|
1490
|
+
if (!relatedIdsByTestCase.has(testCaseId))
|
|
1491
|
+
relatedIdsByTestCase.set(testCaseId, new Set());
|
|
1492
|
+
for (const relation of relations) {
|
|
1493
|
+
const linkedWorkItemId = this.extractLinkedWorkItemIdFromRelation(relation);
|
|
1494
|
+
if (!linkedWorkItemId)
|
|
1495
|
+
continue;
|
|
1496
|
+
relatedIdsByTestCase.get(testCaseId).add(linkedWorkItemId);
|
|
1497
|
+
allRelatedIds.add(linkedWorkItemId);
|
|
1498
|
+
if (this.isTestCaseToRequirementRelation(relation) && requirementById.has(linkedWorkItemId)) {
|
|
1499
|
+
const linkedRequirement = requirementById.get(linkedWorkItemId);
|
|
1500
|
+
const entry = ensure(testCaseId);
|
|
1501
|
+
entry.baseKeys.add(linkedRequirement.baseKey);
|
|
1502
|
+
entry.fullCodes.add(linkedRequirement.fullCode);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
if (allRelatedIds.size > 0) {
|
|
1508
|
+
const relatedWorkItems = await this.fetchWorkItemsByIds(projectName, [...allRelatedIds], false);
|
|
1509
|
+
const typeById = new Map();
|
|
1510
|
+
for (const workItem of relatedWorkItems || []) {
|
|
1511
|
+
const id = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1512
|
+
if (!Number.isFinite(id) || id <= 0)
|
|
1513
|
+
continue;
|
|
1514
|
+
const type = String(((_c = workItem === null || workItem === void 0 ? void 0 : workItem.fields) === null || _c === void 0 ? void 0 : _c['System.WorkItemType']) || '')
|
|
1515
|
+
.trim()
|
|
1516
|
+
.toLowerCase();
|
|
1517
|
+
typeById.set(id, type);
|
|
1518
|
+
}
|
|
1519
|
+
for (const [testCaseId, ids] of relatedIdsByTestCase.entries()) {
|
|
1520
|
+
const entry = ensure(testCaseId);
|
|
1521
|
+
for (const linkedId of ids) {
|
|
1522
|
+
const linkedType = typeById.get(linkedId) || '';
|
|
1523
|
+
if (linkedType === 'bug') {
|
|
1524
|
+
entry.bugIds.add(linkedId);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
return map;
|
|
1530
|
+
}
|
|
1531
|
+
async resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, requirements, linkedRequirementsByTestCase) {
|
|
1532
|
+
var _a, _b, _c, _d, _e;
|
|
1533
|
+
const mode = String((linkedQueryRequest === null || linkedQueryRequest === void 0 ? void 0 : linkedQueryRequest.linkedQueryMode) || '')
|
|
1534
|
+
.trim()
|
|
1535
|
+
.toLowerCase();
|
|
1536
|
+
const wiqlHref = String(((_b = (_a = linkedQueryRequest === null || linkedQueryRequest === void 0 ? void 0 : linkedQueryRequest.testAssociatedQuery) === null || _a === void 0 ? void 0 : _a.wiql) === null || _b === void 0 ? void 0 : _b.href) || '').trim();
|
|
1537
|
+
if (mode !== 'query' || !wiqlHref)
|
|
1538
|
+
return undefined;
|
|
1539
|
+
try {
|
|
1540
|
+
const queryResult = await tfs_1.TFSServices.getItemContent(wiqlHref, this.token);
|
|
1541
|
+
const queryIds = new Set();
|
|
1542
|
+
if (Array.isArray(queryResult === null || queryResult === void 0 ? void 0 : queryResult.workItems)) {
|
|
1543
|
+
for (const workItem of queryResult.workItems) {
|
|
1544
|
+
const id = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1545
|
+
if (Number.isFinite(id) && id > 0)
|
|
1546
|
+
queryIds.add(id);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
if (Array.isArray(queryResult === null || queryResult === void 0 ? void 0 : queryResult.workItemRelations)) {
|
|
1550
|
+
for (const relation of queryResult.workItemRelations) {
|
|
1551
|
+
const sourceId = Number(((_c = relation === null || relation === void 0 ? void 0 : relation.source) === null || _c === void 0 ? void 0 : _c.id) || 0);
|
|
1552
|
+
const targetId = Number(((_d = relation === null || relation === void 0 ? void 0 : relation.target) === null || _d === void 0 ? void 0 : _d.id) || 0);
|
|
1553
|
+
if (Number.isFinite(sourceId) && sourceId > 0)
|
|
1554
|
+
queryIds.add(sourceId);
|
|
1555
|
+
if (Number.isFinite(targetId) && targetId > 0)
|
|
1556
|
+
queryIds.add(targetId);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
if (queryIds.size === 0)
|
|
1560
|
+
return undefined;
|
|
1561
|
+
const reqIdToBaseKey = new Map();
|
|
1562
|
+
for (const requirement of requirements || []) {
|
|
1563
|
+
const id = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
|
|
1564
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1565
|
+
if (id > 0 && baseKey)
|
|
1566
|
+
reqIdToBaseKey.set(id, baseKey);
|
|
1567
|
+
}
|
|
1568
|
+
const scopedKeys = new Set();
|
|
1569
|
+
for (const queryId of queryIds) {
|
|
1570
|
+
if (reqIdToBaseKey.has(queryId)) {
|
|
1571
|
+
scopedKeys.add(reqIdToBaseKey.get(queryId));
|
|
1572
|
+
continue;
|
|
1573
|
+
}
|
|
1574
|
+
const linked = linkedRequirementsByTestCase.get(queryId);
|
|
1575
|
+
if (!((_e = linked === null || linked === void 0 ? void 0 : linked.baseKeys) === null || _e === void 0 ? void 0 : _e.size))
|
|
1576
|
+
continue;
|
|
1577
|
+
linked.baseKeys.forEach((baseKey) => scopedKeys.add(baseKey));
|
|
1578
|
+
}
|
|
1579
|
+
return scopedKeys.size > 0 ? scopedKeys : undefined;
|
|
1580
|
+
}
|
|
1581
|
+
catch (error) {
|
|
1582
|
+
logger_1.default.warn(`Could not resolve MEWP query scope: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
|
|
1583
|
+
return undefined;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
isTestCaseToRequirementRelation(relation) {
|
|
1587
|
+
const rel = String((relation === null || relation === void 0 ? void 0 : relation.rel) || '')
|
|
1588
|
+
.trim()
|
|
1589
|
+
.toLowerCase();
|
|
1590
|
+
if (!rel)
|
|
1591
|
+
return false;
|
|
1592
|
+
return rel.includes('testedby-reverse') || (rel.includes('tests') && rel.includes('reverse'));
|
|
1593
|
+
}
|
|
1594
|
+
extractLinkedWorkItemIdFromRelation(relation) {
|
|
1595
|
+
const url = String((relation === null || relation === void 0 ? void 0 : relation.url) || '');
|
|
1596
|
+
const match = /\/workItems\/(\d+)/i.exec(url);
|
|
1597
|
+
if (!match)
|
|
1598
|
+
return 0;
|
|
1599
|
+
const parsed = Number(match[1]);
|
|
1600
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
650
1601
|
}
|
|
651
1602
|
async fetchMewpRequirementTypeNames(projectName) {
|
|
652
1603
|
try {
|
|
@@ -702,6 +1653,19 @@ ORDER BY [System.Id]`;
|
|
|
702
1653
|
}
|
|
703
1654
|
return [...out].sort((a, b) => a - b);
|
|
704
1655
|
}
|
|
1656
|
+
extractLinkedWorkItemIdsFromRelations(relations) {
|
|
1657
|
+
const out = new Set();
|
|
1658
|
+
for (const relation of Array.isArray(relations) ? relations : []) {
|
|
1659
|
+
const url = String((relation === null || relation === void 0 ? void 0 : relation.url) || '');
|
|
1660
|
+
const match = /\/workItems\/(\d+)/i.exec(url);
|
|
1661
|
+
if (!match)
|
|
1662
|
+
continue;
|
|
1663
|
+
const id = Number(match[1]);
|
|
1664
|
+
if (Number.isFinite(id) && id > 0)
|
|
1665
|
+
out.add(id);
|
|
1666
|
+
}
|
|
1667
|
+
return [...out].sort((a, b) => a - b);
|
|
1668
|
+
}
|
|
705
1669
|
extractMewpRequirementIdentifier(fields, fallbackWorkItemId) {
|
|
706
1670
|
const entries = Object.entries(fields || {});
|
|
707
1671
|
// First pass: only trusted identifier-like fields.
|
|
@@ -721,7 +1685,7 @@ ORDER BY [System.Id]`;
|
|
|
721
1685
|
const valueAsString = this.toMewpComparableText(value);
|
|
722
1686
|
if (!valueAsString)
|
|
723
1687
|
continue;
|
|
724
|
-
const normalized = this.
|
|
1688
|
+
const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
725
1689
|
if (normalized)
|
|
726
1690
|
return normalized;
|
|
727
1691
|
}
|
|
@@ -734,13 +1698,13 @@ ORDER BY [System.Id]`;
|
|
|
734
1698
|
const valueAsString = this.toMewpComparableText(value);
|
|
735
1699
|
if (!valueAsString)
|
|
736
1700
|
continue;
|
|
737
|
-
const normalized = this.
|
|
1701
|
+
const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
738
1702
|
if (normalized)
|
|
739
1703
|
return normalized;
|
|
740
1704
|
}
|
|
741
1705
|
// Optional fallback from title only (avoid scanning all fields and accidental SR matches).
|
|
742
1706
|
const title = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
743
|
-
const titleCode = this.
|
|
1707
|
+
const titleCode = this.normalizeMewpRequirementCodeWithSuffix(title);
|
|
744
1708
|
if (titleCode)
|
|
745
1709
|
return titleCode;
|
|
746
1710
|
return fallbackWorkItemId ? `SR${fallbackWorkItemId}` : '';
|
|
@@ -773,18 +1737,69 @@ ORDER BY [System.Id]`;
|
|
|
773
1737
|
}
|
|
774
1738
|
return '';
|
|
775
1739
|
}
|
|
1740
|
+
deriveMewpSubSystem(fields) {
|
|
1741
|
+
const directCandidates = [
|
|
1742
|
+
fields === null || fields === void 0 ? void 0 : fields['Custom.SubSystem'],
|
|
1743
|
+
fields === null || fields === void 0 ? void 0 : fields['Custom.Subsystem'],
|
|
1744
|
+
fields === null || fields === void 0 ? void 0 : fields['SubSystem'],
|
|
1745
|
+
fields === null || fields === void 0 ? void 0 : fields['Subsystem'],
|
|
1746
|
+
fields === null || fields === void 0 ? void 0 : fields['subSystem'],
|
|
1747
|
+
];
|
|
1748
|
+
for (const candidate of directCandidates) {
|
|
1749
|
+
const value = this.toMewpComparableText(candidate);
|
|
1750
|
+
if (value)
|
|
1751
|
+
return value;
|
|
1752
|
+
}
|
|
1753
|
+
const keyHints = ['subsystem', 'sub system', 'sub_system'];
|
|
1754
|
+
for (const [key, value] of Object.entries(fields || {})) {
|
|
1755
|
+
const normalizedKey = String(key || '').toLowerCase();
|
|
1756
|
+
if (!keyHints.some((hint) => normalizedKey.includes(hint)))
|
|
1757
|
+
continue;
|
|
1758
|
+
const resolved = this.toMewpComparableText(value);
|
|
1759
|
+
if (resolved)
|
|
1760
|
+
return resolved;
|
|
1761
|
+
}
|
|
1762
|
+
return '';
|
|
1763
|
+
}
|
|
1764
|
+
resolveBugResponsibility(fields) {
|
|
1765
|
+
const sapWbsRaw = this.toMewpComparableText((fields === null || fields === void 0 ? void 0 : fields['Custom.SAPWBS']) || (fields === null || fields === void 0 ? void 0 : fields['SAPWBS']));
|
|
1766
|
+
const fromSapWbs = this.resolveMewpResponsibility(sapWbsRaw);
|
|
1767
|
+
if (fromSapWbs === 'ESUK')
|
|
1768
|
+
return 'ESUK';
|
|
1769
|
+
if (fromSapWbs === 'IL')
|
|
1770
|
+
return 'Elisra';
|
|
1771
|
+
const areaPathRaw = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
1772
|
+
const fromAreaPath = this.resolveMewpResponsibility(areaPathRaw);
|
|
1773
|
+
if (fromAreaPath === 'ESUK')
|
|
1774
|
+
return 'ESUK';
|
|
1775
|
+
if (fromAreaPath === 'IL')
|
|
1776
|
+
return 'Elisra';
|
|
1777
|
+
return 'Unknown';
|
|
1778
|
+
}
|
|
776
1779
|
resolveMewpResponsibility(value) {
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
.toLowerCase();
|
|
780
|
-
if (!text)
|
|
1780
|
+
const raw = String(value || '').trim();
|
|
1781
|
+
if (!raw)
|
|
781
1782
|
return '';
|
|
782
|
-
|
|
1783
|
+
const rawUpper = raw.toUpperCase();
|
|
1784
|
+
if (rawUpper === 'ESUK')
|
|
1785
|
+
return 'ESUK';
|
|
1786
|
+
if (rawUpper === 'IL')
|
|
1787
|
+
return 'IL';
|
|
1788
|
+
const normalizedPath = raw
|
|
1789
|
+
.toLowerCase()
|
|
1790
|
+
.replace(/\//g, '\\')
|
|
1791
|
+
.replace(/\\+/g, '\\')
|
|
1792
|
+
.trim();
|
|
1793
|
+
if (normalizedPath.endsWith('\\atp\\esuk') || normalizedPath === 'atp\\esuk')
|
|
783
1794
|
return 'ESUK';
|
|
784
|
-
if (
|
|
1795
|
+
if (normalizedPath.endsWith('\\atp') || normalizedPath === 'atp')
|
|
785
1796
|
return 'IL';
|
|
786
1797
|
return '';
|
|
787
1798
|
}
|
|
1799
|
+
isExcludedL3L4BySapWbs(value) {
|
|
1800
|
+
const responsibility = this.resolveMewpResponsibility(this.toMewpComparableText(value));
|
|
1801
|
+
return responsibility === 'ESUK';
|
|
1802
|
+
}
|
|
788
1803
|
normalizeMewpRequirementCode(value) {
|
|
789
1804
|
const text = String(value || '').trim();
|
|
790
1805
|
if (!text)
|
|
@@ -794,6 +1809,18 @@ ORDER BY [System.Id]`;
|
|
|
794
1809
|
return '';
|
|
795
1810
|
return `SR${match[1]}`;
|
|
796
1811
|
}
|
|
1812
|
+
normalizeMewpRequirementCodeWithSuffix(value) {
|
|
1813
|
+
const text = String(value || '').trim();
|
|
1814
|
+
if (!text)
|
|
1815
|
+
return '';
|
|
1816
|
+
const compact = text.replace(/\s+/g, '');
|
|
1817
|
+
const match = /^SR(\d+)(?:-(\d+))?$/i.exec(compact);
|
|
1818
|
+
if (!match)
|
|
1819
|
+
return '';
|
|
1820
|
+
if (match[2])
|
|
1821
|
+
return `SR${match[1]}-${match[2]}`;
|
|
1822
|
+
return `SR${match[1]}`;
|
|
1823
|
+
}
|
|
797
1824
|
toMewpComparableText(value) {
|
|
798
1825
|
if (value === null || value === undefined)
|
|
799
1826
|
return '';
|
|
@@ -2650,14 +3677,24 @@ ORDER BY [System.Id]`;
|
|
|
2650
3677
|
}
|
|
2651
3678
|
}
|
|
2652
3679
|
ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
|
|
2653
|
-
'
|
|
2654
|
-
'
|
|
2655
|
-
'
|
|
2656
|
-
'
|
|
2657
|
-
'
|
|
2658
|
-
'
|
|
2659
|
-
'
|
|
2660
|
-
'
|
|
3680
|
+
'L2 REQ ID',
|
|
3681
|
+
'L2 REQ Title',
|
|
3682
|
+
'L2 SubSystem',
|
|
3683
|
+
'L2 Run Status',
|
|
3684
|
+
'Bug ID',
|
|
3685
|
+
'Bug Title',
|
|
3686
|
+
'Bug Responsibility',
|
|
3687
|
+
'L3 REQ ID',
|
|
3688
|
+
'L3 REQ Title',
|
|
3689
|
+
'L4 REQ ID',
|
|
3690
|
+
'L4 REQ Title',
|
|
3691
|
+
];
|
|
3692
|
+
ResultDataProvider.INTERNAL_VALIDATION_COLUMNS = [
|
|
3693
|
+
'Test Case ID',
|
|
3694
|
+
'Test Case Title',
|
|
3695
|
+
'Mentioned but Not Linked',
|
|
3696
|
+
'Linked but Not Mentioned',
|
|
3697
|
+
'Validation Status',
|
|
2661
3698
|
];
|
|
2662
3699
|
exports.default = ResultDataProvider;
|
|
2663
3700
|
//# sourceMappingURL=ResultDataProvider.js.map
|