@elisra-devops/docgen-data-provider 1.74.0 → 1.76.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 +47 -13
- package/bin/modules/ResultDataProvider.js +1122 -257
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +1293 -113
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/bin/utils/mewpExternalIngestionUtils.d.ts +17 -0
- package/bin/utils/mewpExternalIngestionUtils.js +197 -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 +1325 -353
- package/src/tests/modules/ResultDataProvider.test.ts +1481 -138
- package/src/utils/mewpExternalIngestionUtils.ts +270 -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, linkedQueryRequest) {
|
|
287
|
-
var _a
|
|
290
|
+
async getMewpL2CoverageFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
|
|
291
|
+
var _a;
|
|
288
292
|
const defaultPayload = {
|
|
289
293
|
sheetName: `MEWP L2 Coverage - Plan ${testPlanId}`,
|
|
290
294
|
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
@@ -292,9 +296,17 @@ 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);
|
|
298
310
|
if (requirements.length === 0) {
|
|
299
311
|
return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
|
|
300
312
|
}
|
|
@@ -302,32 +314,23 @@ class ResultDataProvider {
|
|
|
302
314
|
const observedTestCaseIdsByRequirement = new Map();
|
|
303
315
|
const requirementKeys = new Set();
|
|
304
316
|
requirements.forEach((requirement) => {
|
|
305
|
-
const key =
|
|
317
|
+
const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
306
318
|
if (!key)
|
|
307
319
|
return;
|
|
308
320
|
requirementKeys.add(key);
|
|
309
321
|
});
|
|
310
322
|
const parsedDefinitionStepsByTestCase = new Map();
|
|
311
323
|
const testCaseStepsXmlMap = this.buildTestCaseStepsXmlMap(testData);
|
|
312
|
-
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
313
324
|
const runResults = await this.fetchAllResultDataTestReporter(testData, projectName, [], false, false);
|
|
314
325
|
for (const runResult of runResults) {
|
|
315
326
|
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
|
|
327
|
+
const rawActionResults = Array.isArray((_a = runResult === null || runResult === void 0 ? void 0 : runResult.iteration) === null || _a === void 0 ? void 0 : _a.actionResults)
|
|
328
|
+
? runResult.iteration.actionResults.filter((item) => !(item === null || item === void 0 ? void 0 : item.isSharedStepTitle))
|
|
322
329
|
: [];
|
|
330
|
+
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
331
|
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
332
|
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
|
-
}
|
|
333
|
+
this.accumulateRequirementCountsFromActionResults(actionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
331
334
|
continue;
|
|
332
335
|
}
|
|
333
336
|
// Do not force "not run" from definition steps when a run exists:
|
|
@@ -345,13 +348,17 @@ class ResultDataProvider {
|
|
|
345
348
|
parsedDefinitionStepsByTestCase.set(testCaseId, parsed);
|
|
346
349
|
}
|
|
347
350
|
const definitionSteps = parsedDefinitionStepsByTestCase.get(testCaseId) || [];
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
351
|
+
const fallbackActionResults = definitionSteps
|
|
352
|
+
.filter((step) => !(step === null || step === void 0 ? void 0 : step.isSharedStepTitle))
|
|
353
|
+
.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) || '')))
|
|
354
|
+
.map((step) => ({
|
|
355
|
+
stepPosition: step === null || step === void 0 ? void 0 : step.stepPosition,
|
|
356
|
+
expected: step === null || step === void 0 ? void 0 : step.expected,
|
|
357
|
+
outcome: 'Unspecified',
|
|
358
|
+
}));
|
|
359
|
+
this.accumulateRequirementCountsFromActionResults(fallbackActionResults, testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
353
360
|
}
|
|
354
|
-
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement,
|
|
361
|
+
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, externalL3L4ByBaseKey, externalBugsByTestCase);
|
|
355
362
|
return {
|
|
356
363
|
sheetName: this.buildMewpCoverageSheetName(planName, testPlanId),
|
|
357
364
|
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
@@ -360,9 +367,210 @@ class ResultDataProvider {
|
|
|
360
367
|
}
|
|
361
368
|
catch (error) {
|
|
362
369
|
logger_1.default.error(`Error during getMewpL2CoverageFlatResults: ${error.message}`);
|
|
370
|
+
if (error instanceof mewpExternalTableUtils_1.MewpExternalFileValidationError) {
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
return defaultPayload;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async getMewpInternalValidationFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest, options) {
|
|
377
|
+
var _a, _b, _c;
|
|
378
|
+
const defaultPayload = {
|
|
379
|
+
sheetName: `MEWP Internal Validation - Plan ${testPlanId}`,
|
|
380
|
+
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
381
|
+
rows: [],
|
|
382
|
+
};
|
|
383
|
+
try {
|
|
384
|
+
const planName = await this.fetchTestPlanName(testPlanId, projectName);
|
|
385
|
+
const testData = await this.fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, !!(options === null || options === void 0 ? void 0 : options.useRelFallback));
|
|
386
|
+
const allRequirements = await this.fetchMewpL2Requirements(projectName);
|
|
387
|
+
const linkedRequirementsByTestCase = await this.buildLinkedRequirementsByTestCase(allRequirements, testData, projectName);
|
|
388
|
+
const scopedRequirementKeys = await this.resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, allRequirements, linkedRequirementsByTestCase);
|
|
389
|
+
const requirementFamilies = this.buildRequirementFamilyMap(allRequirements, (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) ? scopedRequirementKeys : undefined);
|
|
390
|
+
const rows = [];
|
|
391
|
+
const stepsXmlByTestCase = this.buildTestCaseStepsXmlMap(testData);
|
|
392
|
+
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
393
|
+
const allTestCaseIds = new Set();
|
|
394
|
+
for (const suite of testData || []) {
|
|
395
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
396
|
+
for (const testCase of testCasesItems) {
|
|
397
|
+
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);
|
|
398
|
+
if (Number.isFinite(id) && id > 0)
|
|
399
|
+
allTestCaseIds.add(id);
|
|
400
|
+
}
|
|
401
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
402
|
+
for (const testPoint of testPointsItems) {
|
|
403
|
+
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);
|
|
404
|
+
if (Number.isFinite(id) && id > 0)
|
|
405
|
+
allTestCaseIds.add(id);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
const validL2BaseKeys = new Set([...requirementFamilies.keys()]);
|
|
409
|
+
for (const testCaseId of [...allTestCaseIds].sort((a, b) => a - b)) {
|
|
410
|
+
const stepsXml = stepsXmlByTestCase.get(testCaseId) || '';
|
|
411
|
+
const parsedSteps = stepsXml && String(stepsXml).trim() !== ''
|
|
412
|
+
? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
|
|
413
|
+
: [];
|
|
414
|
+
const mentionEntries = this.extractRequirementMentionsFromExpectedSteps(parsedSteps, true);
|
|
415
|
+
const mentionedL2Only = new Set();
|
|
416
|
+
const mentionedCodeFirstStep = new Map();
|
|
417
|
+
const mentionedBaseFirstStep = new Map();
|
|
418
|
+
for (const mentionEntry of mentionEntries) {
|
|
419
|
+
const scopeFilteredCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && mentionEntry.codes.size > 0
|
|
420
|
+
? [...mentionEntry.codes].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code)))
|
|
421
|
+
: [...mentionEntry.codes];
|
|
422
|
+
for (const code of scopeFilteredCodes) {
|
|
423
|
+
const baseKey = this.toRequirementKey(code);
|
|
424
|
+
if (!baseKey)
|
|
425
|
+
continue;
|
|
426
|
+
if (validL2BaseKeys.has(baseKey)) {
|
|
427
|
+
mentionedL2Only.add(code);
|
|
428
|
+
if (!mentionedCodeFirstStep.has(code)) {
|
|
429
|
+
mentionedCodeFirstStep.set(code, mentionEntry.stepRef);
|
|
430
|
+
}
|
|
431
|
+
if (!mentionedBaseFirstStep.has(baseKey)) {
|
|
432
|
+
mentionedBaseFirstStep.set(baseKey, mentionEntry.stepRef);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
const mentionedBaseKeys = new Set([...mentionedL2Only].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
438
|
+
const expectedFamilyCodes = new Set();
|
|
439
|
+
for (const baseKey of mentionedBaseKeys) {
|
|
440
|
+
const familyCodes = requirementFamilies.get(baseKey);
|
|
441
|
+
if (familyCodes === null || familyCodes === void 0 ? void 0 : familyCodes.size) {
|
|
442
|
+
familyCodes.forEach((code) => expectedFamilyCodes.add(code));
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
for (const code of mentionedL2Only) {
|
|
446
|
+
if (this.toRequirementKey(code) === baseKey)
|
|
447
|
+
expectedFamilyCodes.add(code);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const linkedFullCodesRaw = ((_c = linkedRequirementsByTestCase.get(testCaseId)) === null || _c === void 0 ? void 0 : _c.fullCodes) || new Set();
|
|
452
|
+
const linkedFullCodes = (scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && linkedFullCodesRaw.size > 0
|
|
453
|
+
? new Set([...linkedFullCodesRaw].filter((code) => scopedRequirementKeys.has(this.toRequirementKey(code))))
|
|
454
|
+
: linkedFullCodesRaw;
|
|
455
|
+
const linkedBaseKeys = new Set([...linkedFullCodes].map((code) => this.toRequirementKey(code)).filter((code) => !!code));
|
|
456
|
+
const missingMentioned = [...mentionedL2Only].filter((code) => {
|
|
457
|
+
const baseKey = this.toRequirementKey(code);
|
|
458
|
+
if (!baseKey)
|
|
459
|
+
return false;
|
|
460
|
+
const hasSpecificSuffix = /-\d+$/.test(code);
|
|
461
|
+
if (hasSpecificSuffix)
|
|
462
|
+
return !linkedFullCodes.has(code);
|
|
463
|
+
return !linkedBaseKeys.has(baseKey);
|
|
464
|
+
});
|
|
465
|
+
const missingFamily = [...expectedFamilyCodes].filter((code) => !linkedFullCodes.has(code));
|
|
466
|
+
const extraLinked = [...linkedFullCodes].filter((code) => !expectedFamilyCodes.has(code));
|
|
467
|
+
const mentionedButNotLinkedByStep = new Map();
|
|
468
|
+
const appendMentionedButNotLinked = (requirementId, stepRef) => {
|
|
469
|
+
const normalizedRequirementId = this.normalizeMewpRequirementCodeWithSuffix(requirementId);
|
|
470
|
+
if (!normalizedRequirementId)
|
|
471
|
+
return;
|
|
472
|
+
const normalizedStepRef = String(stepRef || 'Step ?').trim() || 'Step ?';
|
|
473
|
+
if (!mentionedButNotLinkedByStep.has(normalizedStepRef)) {
|
|
474
|
+
mentionedButNotLinkedByStep.set(normalizedStepRef, new Set());
|
|
475
|
+
}
|
|
476
|
+
mentionedButNotLinkedByStep.get(normalizedStepRef).add(normalizedRequirementId);
|
|
477
|
+
};
|
|
478
|
+
const sortedMissingMentioned = [...new Set(missingMentioned)].sort((a, b) => a.localeCompare(b));
|
|
479
|
+
const sortedMissingFamily = [...new Set(missingFamily)].sort((a, b) => a.localeCompare(b));
|
|
480
|
+
for (const code of sortedMissingMentioned) {
|
|
481
|
+
const stepRef = mentionedCodeFirstStep.get(code) || 'Step ?';
|
|
482
|
+
appendMentionedButNotLinked(code, stepRef);
|
|
483
|
+
}
|
|
484
|
+
for (const code of sortedMissingFamily) {
|
|
485
|
+
const baseKey = this.toRequirementKey(code);
|
|
486
|
+
const stepRef = mentionedBaseFirstStep.get(baseKey) || 'Step ?';
|
|
487
|
+
appendMentionedButNotLinked(code, stepRef);
|
|
488
|
+
}
|
|
489
|
+
const sortedExtraLinked = [...new Set(extraLinked)]
|
|
490
|
+
.map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
|
|
491
|
+
.filter((code) => !!code)
|
|
492
|
+
.sort((a, b) => a.localeCompare(b));
|
|
493
|
+
const parseStepOrder = (stepRef) => {
|
|
494
|
+
const match = /step\s+(\d+)/i.exec(String(stepRef || ''));
|
|
495
|
+
const parsed = Number((match === null || match === void 0 ? void 0 : match[1]) || Number.POSITIVE_INFINITY);
|
|
496
|
+
return Number.isFinite(parsed) ? parsed : Number.POSITIVE_INFINITY;
|
|
497
|
+
};
|
|
498
|
+
const mentionedButNotLinked = [...mentionedButNotLinkedByStep.entries()]
|
|
499
|
+
.sort((a, b) => {
|
|
500
|
+
const stepOrderA = parseStepOrder(a[0]);
|
|
501
|
+
const stepOrderB = parseStepOrder(b[0]);
|
|
502
|
+
if (stepOrderA !== stepOrderB)
|
|
503
|
+
return stepOrderA - stepOrderB;
|
|
504
|
+
return String(a[0]).localeCompare(String(b[0]));
|
|
505
|
+
})
|
|
506
|
+
.map(([stepRef, requirementIds]) => {
|
|
507
|
+
const requirementList = [...requirementIds].sort((a, b) => a.localeCompare(b));
|
|
508
|
+
return `${stepRef}: ${requirementList.join(', ')}`;
|
|
509
|
+
})
|
|
510
|
+
.join('; ');
|
|
511
|
+
const linkedButNotMentioned = sortedExtraLinked.join('; ');
|
|
512
|
+
const validationStatus = mentionedButNotLinked || linkedButNotMentioned ? 'Fail' : 'Pass';
|
|
513
|
+
rows.push({
|
|
514
|
+
'Test Case ID': testCaseId,
|
|
515
|
+
'Test Case Title': String(testCaseTitleMap.get(testCaseId) || '').trim(),
|
|
516
|
+
'Mentioned but Not Linked': mentionedButNotLinked,
|
|
517
|
+
'Linked but Not Mentioned': linkedButNotMentioned,
|
|
518
|
+
'Validation Status': validationStatus,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
sheetName: this.buildInternalValidationSheetName(planName, testPlanId),
|
|
523
|
+
columnOrder: [...ResultDataProvider.INTERNAL_VALIDATION_COLUMNS],
|
|
524
|
+
rows,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
logger_1.default.error(`Error during getMewpInternalValidationFlatResults: ${error.message}`);
|
|
363
529
|
return defaultPayload;
|
|
364
530
|
}
|
|
365
531
|
}
|
|
532
|
+
async validateMewpExternalFiles(options) {
|
|
533
|
+
const response = { valid: true };
|
|
534
|
+
const validateOne = async (file, tableType) => {
|
|
535
|
+
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();
|
|
536
|
+
if (!sourceName)
|
|
537
|
+
return undefined;
|
|
538
|
+
try {
|
|
539
|
+
const { rows, meta } = await this.mewpExternalTableUtils.loadExternalTableRowsWithMeta(file, tableType);
|
|
540
|
+
return {
|
|
541
|
+
tableType,
|
|
542
|
+
sourceName: meta.sourceName,
|
|
543
|
+
valid: true,
|
|
544
|
+
headerRow: meta.headerRow,
|
|
545
|
+
matchedRequiredColumns: meta.matchedRequiredColumns,
|
|
546
|
+
totalRequiredColumns: meta.totalRequiredColumns,
|
|
547
|
+
missingRequiredColumns: [],
|
|
548
|
+
rowCount: rows.length,
|
|
549
|
+
message: 'File schema is valid',
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
if (error instanceof mewpExternalTableUtils_1.MewpExternalFileValidationError) {
|
|
554
|
+
return Object.assign(Object.assign({}, error.details), { valid: false });
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
tableType,
|
|
558
|
+
sourceName: sourceName || tableType,
|
|
559
|
+
valid: false,
|
|
560
|
+
headerRow: '',
|
|
561
|
+
matchedRequiredColumns: 0,
|
|
562
|
+
totalRequiredColumns: this.mewpExternalTableUtils.getRequiredColumnCount(tableType),
|
|
563
|
+
missingRequiredColumns: [],
|
|
564
|
+
rowCount: 0,
|
|
565
|
+
message: String((error === null || error === void 0 ? void 0 : error.message) || error || 'Unknown validation error'),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
response.bugs = await validateOne(options === null || options === void 0 ? void 0 : options.externalBugsFile, 'bugs');
|
|
570
|
+
response.l3l4 = await validateOne(options === null || options === void 0 ? void 0 : options.externalL3L4File, 'l3l4');
|
|
571
|
+
response.valid = [response.bugs, response.l3l4].filter(Boolean).every((item) => !!(item === null || item === void 0 ? void 0 : item.valid));
|
|
572
|
+
return response;
|
|
573
|
+
}
|
|
366
574
|
/**
|
|
367
575
|
* Mapping each attachment to a proper URL for downloading it
|
|
368
576
|
* @param runResults Array of run results
|
|
@@ -394,49 +602,344 @@ class ResultDataProvider {
|
|
|
394
602
|
const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
|
|
395
603
|
return `MEWP L2 Coverage - ${suffix}`;
|
|
396
604
|
}
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
605
|
+
buildInternalValidationSheetName(planName, testPlanId) {
|
|
606
|
+
const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
|
|
607
|
+
return `MEWP Internal Validation - ${suffix}`;
|
|
608
|
+
}
|
|
609
|
+
createMewpCoverageRow(requirement, runStatus, bug, linkedL3L4) {
|
|
610
|
+
const l2ReqId = this.formatMewpCustomerId(requirement.requirementId);
|
|
611
|
+
const l2ReqTitle = this.toMewpComparableText(requirement.title);
|
|
612
|
+
const l2SubSystem = this.toMewpComparableText(requirement.subSystem);
|
|
402
613
|
return {
|
|
403
|
-
'
|
|
404
|
-
'
|
|
405
|
-
'
|
|
406
|
-
'
|
|
407
|
-
'
|
|
408
|
-
'
|
|
409
|
-
'
|
|
410
|
-
'
|
|
614
|
+
'L2 REQ ID': l2ReqId,
|
|
615
|
+
'L2 REQ Title': l2ReqTitle,
|
|
616
|
+
'L2 SubSystem': l2SubSystem,
|
|
617
|
+
'L2 Run Status': runStatus,
|
|
618
|
+
'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) : '',
|
|
619
|
+
'Bug Title': String((bug === null || bug === void 0 ? void 0 : bug.title) || '').trim(),
|
|
620
|
+
'Bug Responsibility': String((bug === null || bug === void 0 ? void 0 : bug.responsibility) || '').trim(),
|
|
621
|
+
'L3 REQ ID': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l3Id) || '').trim(),
|
|
622
|
+
'L3 REQ Title': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l3Title) || '').trim(),
|
|
623
|
+
'L4 REQ ID': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l4Id) || '').trim(),
|
|
624
|
+
'L4 REQ Title': String((linkedL3L4 === null || linkedL3L4 === void 0 ? void 0 : linkedL3L4.l4Title) || '').trim(),
|
|
411
625
|
};
|
|
412
626
|
}
|
|
413
|
-
|
|
627
|
+
createEmptyMewpCoverageBugCell() {
|
|
628
|
+
return { id: '', title: '', responsibility: '' };
|
|
629
|
+
}
|
|
630
|
+
createEmptyMewpCoverageL3L4Cell() {
|
|
631
|
+
return { l3Id: '', l3Title: '', l4Id: '', l4Title: '' };
|
|
632
|
+
}
|
|
633
|
+
buildMewpCoverageL3L4Rows(links) {
|
|
634
|
+
const sorted = [...(links || [])].sort((a, b) => {
|
|
635
|
+
if (a.level !== b.level)
|
|
636
|
+
return a.level === 'L3' ? -1 : 1;
|
|
637
|
+
return String(a.id || '').localeCompare(String(b.id || ''));
|
|
638
|
+
});
|
|
639
|
+
const rows = [];
|
|
640
|
+
for (const item of sorted) {
|
|
641
|
+
const isL3 = item.level === 'L3';
|
|
642
|
+
rows.push({
|
|
643
|
+
l3Id: isL3 ? String((item === null || item === void 0 ? void 0 : item.id) || '').trim() : '',
|
|
644
|
+
l3Title: isL3 ? String((item === null || item === void 0 ? void 0 : item.title) || '').trim() : '',
|
|
645
|
+
l4Id: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.id) || '').trim(),
|
|
646
|
+
l4Title: isL3 ? '' : String((item === null || item === void 0 ? void 0 : item.title) || '').trim(),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
return rows;
|
|
650
|
+
}
|
|
651
|
+
formatMewpCustomerId(rawValue) {
|
|
652
|
+
const normalized = this.normalizeMewpRequirementCode(this.toMewpComparableText(rawValue));
|
|
653
|
+
if (normalized)
|
|
654
|
+
return normalized;
|
|
655
|
+
const onlyDigits = String(rawValue || '').replace(/\D/g, '');
|
|
656
|
+
if (onlyDigits)
|
|
657
|
+
return `SR${onlyDigits}`;
|
|
658
|
+
return '';
|
|
659
|
+
}
|
|
660
|
+
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase) {
|
|
414
661
|
var _a;
|
|
415
662
|
const rows = [];
|
|
663
|
+
const linkedByRequirement = this.invertBaseRequirementLinks(linkedRequirementsByTestCase);
|
|
416
664
|
for (const requirement of requirements) {
|
|
417
|
-
const key = this.toRequirementKey(requirement.requirementId);
|
|
665
|
+
const key = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || this.toRequirementKey(requirement.requirementId) || '').trim();
|
|
418
666
|
const linkedTestCaseIds = ((requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []).filter((id) => Number.isFinite(id) && Number(id) > 0);
|
|
667
|
+
const linkedByTestCase = key ? Array.from(linkedByRequirement.get(key) || []) : [];
|
|
419
668
|
const observedTestCaseIds = key
|
|
420
669
|
? Array.from(observedTestCaseIdsByRequirement.get(key) || [])
|
|
421
670
|
: [];
|
|
422
|
-
const testCaseIds = Array.from(new Set([...linkedTestCaseIds, ...observedTestCaseIds])).sort((a, b) => a - b);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
notRun: 0,
|
|
428
|
-
}));
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
671
|
+
const testCaseIds = Array.from(new Set([...linkedTestCaseIds, ...linkedByTestCase, ...observedTestCaseIds])).sort((a, b) => a - b);
|
|
672
|
+
let totalPassed = 0;
|
|
673
|
+
let totalFailed = 0;
|
|
674
|
+
let totalNotRun = 0;
|
|
675
|
+
const aggregatedBugs = new Map();
|
|
431
676
|
for (const testCaseId of testCaseIds) {
|
|
432
677
|
const summary = key
|
|
433
678
|
? ((_a = requirementIndex.get(key)) === null || _a === void 0 ? void 0 : _a.get(testCaseId)) || { passed: 0, failed: 0, notRun: 0 }
|
|
434
679
|
: { passed: 0, failed: 0, notRun: 0 };
|
|
435
|
-
|
|
680
|
+
totalPassed += summary.passed;
|
|
681
|
+
totalFailed += summary.failed;
|
|
682
|
+
totalNotRun += summary.notRun;
|
|
683
|
+
if (summary.failed > 0) {
|
|
684
|
+
const externalBugs = externalBugsByTestCase.get(testCaseId) || [];
|
|
685
|
+
for (const bug of externalBugs) {
|
|
686
|
+
const bugBaseKey = String((bug === null || bug === void 0 ? void 0 : bug.requirementBaseKey) || '').trim();
|
|
687
|
+
if (bugBaseKey && bugBaseKey !== key)
|
|
688
|
+
continue;
|
|
689
|
+
const bugId = Number((bug === null || bug === void 0 ? void 0 : bug.id) || 0);
|
|
690
|
+
if (!Number.isFinite(bugId) || bugId <= 0)
|
|
691
|
+
continue;
|
|
692
|
+
aggregatedBugs.set(bugId, Object.assign(Object.assign({}, bug), { responsibility: this.resolveCoverageBugResponsibility(String((bug === null || bug === void 0 ? void 0 : bug.responsibility) || ''), requirement) }));
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const runStatus = this.resolveMewpL2RunStatus({
|
|
697
|
+
passed: totalPassed,
|
|
698
|
+
failed: totalFailed,
|
|
699
|
+
notRun: totalNotRun,
|
|
700
|
+
hasAnyTestCase: testCaseIds.length > 0,
|
|
701
|
+
});
|
|
702
|
+
const bugsForRows = runStatus === 'Fail'
|
|
703
|
+
? Array.from(aggregatedBugs.values()).sort((a, b) => a.id - b.id)
|
|
704
|
+
: [];
|
|
705
|
+
const l3l4ForRows = [...(l3l4ByBaseKey.get(key) || [])];
|
|
706
|
+
const bugRows = bugsForRows.length > 0
|
|
707
|
+
? bugsForRows
|
|
708
|
+
: [];
|
|
709
|
+
const l3l4Rows = this.buildMewpCoverageL3L4Rows(l3l4ForRows);
|
|
710
|
+
if (bugRows.length === 0 && l3l4Rows.length === 0) {
|
|
711
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), this.createEmptyMewpCoverageL3L4Cell()));
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
for (const bug of bugRows) {
|
|
715
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, bug, this.createEmptyMewpCoverageL3L4Cell()));
|
|
716
|
+
}
|
|
717
|
+
for (const linkedL3L4 of l3l4Rows) {
|
|
718
|
+
rows.push(this.createMewpCoverageRow(requirement, runStatus, this.createEmptyMewpCoverageBugCell(), linkedL3L4));
|
|
436
719
|
}
|
|
437
720
|
}
|
|
438
721
|
return rows;
|
|
439
722
|
}
|
|
723
|
+
resolveCoverageBugResponsibility(rawResponsibility, requirement) {
|
|
724
|
+
const direct = String(rawResponsibility || '').trim();
|
|
725
|
+
if (direct && direct.toLowerCase() !== 'unknown')
|
|
726
|
+
return direct;
|
|
727
|
+
const requirementResponsibility = String((requirement === null || requirement === void 0 ? void 0 : requirement.responsibility) || '')
|
|
728
|
+
.trim()
|
|
729
|
+
.toUpperCase();
|
|
730
|
+
if (requirementResponsibility === 'ESUK')
|
|
731
|
+
return 'ESUK';
|
|
732
|
+
if (requirementResponsibility === 'IL' || requirementResponsibility === 'ELISRA')
|
|
733
|
+
return 'Elisra';
|
|
734
|
+
return direct || 'Unknown';
|
|
735
|
+
}
|
|
736
|
+
resolveMewpL2RunStatus(input) {
|
|
737
|
+
if (((input === null || input === void 0 ? void 0 : input.failed) || 0) > 0)
|
|
738
|
+
return 'Fail';
|
|
739
|
+
if (((input === null || input === void 0 ? void 0 : input.notRun) || 0) > 0)
|
|
740
|
+
return 'Not Run';
|
|
741
|
+
if (((input === null || input === void 0 ? void 0 : input.passed) || 0) > 0)
|
|
742
|
+
return 'Pass';
|
|
743
|
+
return (input === null || input === void 0 ? void 0 : input.hasAnyTestCase) ? 'Not Run' : 'Not Run';
|
|
744
|
+
}
|
|
745
|
+
async fetchMewpScopedTestData(testPlanId, projectName, selectedSuiteIds, useRelFallback) {
|
|
746
|
+
if (!useRelFallback) {
|
|
747
|
+
const suites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
|
|
748
|
+
return this.fetchTestData(suites, projectName, testPlanId, false);
|
|
749
|
+
}
|
|
750
|
+
const selectedSuites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
|
|
751
|
+
const selectedRel = this.resolveMaxRelNumberFromSuites(selectedSuites);
|
|
752
|
+
if (selectedRel <= 0) {
|
|
753
|
+
return this.fetchTestData(selectedSuites, projectName, testPlanId, false);
|
|
754
|
+
}
|
|
755
|
+
const allSuites = await this.fetchTestSuites(testPlanId, projectName, undefined, true);
|
|
756
|
+
const relScopedSuites = allSuites.filter((suite) => {
|
|
757
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
758
|
+
return rel > 0 && rel <= selectedRel;
|
|
759
|
+
});
|
|
760
|
+
const suitesForFetch = relScopedSuites.length > 0 ? relScopedSuites : selectedSuites;
|
|
761
|
+
const rawTestData = await this.fetchTestData(suitesForFetch, projectName, testPlanId, false);
|
|
762
|
+
return this.reduceToLatestRelRunPerTestCase(rawTestData);
|
|
763
|
+
}
|
|
764
|
+
extractRelNumberFromSuite(suite) {
|
|
765
|
+
const candidates = [
|
|
766
|
+
suite === null || suite === void 0 ? void 0 : suite.suiteName,
|
|
767
|
+
suite === null || suite === void 0 ? void 0 : suite.parentSuiteName,
|
|
768
|
+
suite === null || suite === void 0 ? void 0 : suite.suitePath,
|
|
769
|
+
suite === null || suite === void 0 ? void 0 : suite.testGroupName,
|
|
770
|
+
];
|
|
771
|
+
const pattern = /(?:^|[^a-z0-9])rel\s*([0-9]+)/i;
|
|
772
|
+
for (const item of candidates) {
|
|
773
|
+
const match = pattern.exec(String(item || ''));
|
|
774
|
+
if (!match)
|
|
775
|
+
continue;
|
|
776
|
+
const parsed = Number(match[1]);
|
|
777
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
778
|
+
return parsed;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
return 0;
|
|
782
|
+
}
|
|
783
|
+
resolveMaxRelNumberFromSuites(suites) {
|
|
784
|
+
let maxRel = 0;
|
|
785
|
+
for (const suite of suites || []) {
|
|
786
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
787
|
+
if (rel > maxRel)
|
|
788
|
+
maxRel = rel;
|
|
789
|
+
}
|
|
790
|
+
return maxRel;
|
|
791
|
+
}
|
|
792
|
+
reduceToLatestRelRunPerTestCase(testData) {
|
|
793
|
+
var _a, _b;
|
|
794
|
+
const candidatesByTestCase = new Map();
|
|
795
|
+
const testCaseDefinitionById = new Map();
|
|
796
|
+
for (const suite of testData || []) {
|
|
797
|
+
const rel = this.extractRelNumberFromSuite(suite);
|
|
798
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
799
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
800
|
+
for (const testCase of testCasesItems) {
|
|
801
|
+
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);
|
|
802
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
803
|
+
continue;
|
|
804
|
+
if (!testCaseDefinitionById.has(testCaseId)) {
|
|
805
|
+
testCaseDefinitionById.set(testCaseId, testCase);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
for (const point of testPointsItems) {
|
|
809
|
+
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);
|
|
810
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
811
|
+
continue;
|
|
812
|
+
const runId = Number((point === null || point === void 0 ? void 0 : point.lastRunId) || 0);
|
|
813
|
+
const resultId = Number((point === null || point === void 0 ? void 0 : point.lastResultId) || 0);
|
|
814
|
+
const hasRun = runId > 0 && resultId > 0;
|
|
815
|
+
if (!candidatesByTestCase.has(testCaseId)) {
|
|
816
|
+
candidatesByTestCase.set(testCaseId, []);
|
|
817
|
+
}
|
|
818
|
+
candidatesByTestCase.get(testCaseId).push({
|
|
819
|
+
point,
|
|
820
|
+
rel,
|
|
821
|
+
runId,
|
|
822
|
+
resultId,
|
|
823
|
+
hasRun,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const selectedPoints = [];
|
|
828
|
+
const selectedTestCaseIds = new Set();
|
|
829
|
+
for (const [testCaseId, candidates] of candidatesByTestCase.entries()) {
|
|
830
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
831
|
+
if (a.hasRun !== b.hasRun)
|
|
832
|
+
return a.hasRun ? -1 : 1;
|
|
833
|
+
if (a.rel !== b.rel)
|
|
834
|
+
return b.rel - a.rel;
|
|
835
|
+
if (a.runId !== b.runId)
|
|
836
|
+
return b.runId - a.runId;
|
|
837
|
+
return b.resultId - a.resultId;
|
|
838
|
+
});
|
|
839
|
+
const chosen = sorted[0];
|
|
840
|
+
if (!(chosen === null || chosen === void 0 ? void 0 : chosen.point))
|
|
841
|
+
continue;
|
|
842
|
+
selectedPoints.push(chosen.point);
|
|
843
|
+
selectedTestCaseIds.add(testCaseId);
|
|
844
|
+
}
|
|
845
|
+
const selectedTestCases = [];
|
|
846
|
+
for (const testCaseId of selectedTestCaseIds) {
|
|
847
|
+
const definition = testCaseDefinitionById.get(testCaseId);
|
|
848
|
+
if (definition) {
|
|
849
|
+
selectedTestCases.push(definition);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return [
|
|
853
|
+
{
|
|
854
|
+
testSuiteId: 'MEWP_REL_SCOPED',
|
|
855
|
+
suiteId: 'MEWP_REL_SCOPED',
|
|
856
|
+
suiteName: 'MEWP Rel Scoped',
|
|
857
|
+
parentSuiteId: '',
|
|
858
|
+
parentSuiteName: '',
|
|
859
|
+
suitePath: 'MEWP Rel Scoped',
|
|
860
|
+
testGroupName: 'MEWP Rel Scoped',
|
|
861
|
+
testPointsItems: selectedPoints,
|
|
862
|
+
testCasesItems: selectedTestCases,
|
|
863
|
+
},
|
|
864
|
+
];
|
|
865
|
+
}
|
|
866
|
+
async loadExternalBugsByTestCase(externalBugsFile) {
|
|
867
|
+
return this.mewpExternalIngestionUtils.loadExternalBugsByTestCase(externalBugsFile, {
|
|
868
|
+
toComparableText: (value) => this.toMewpComparableText(value),
|
|
869
|
+
toRequirementKey: (value) => this.toRequirementKey(value),
|
|
870
|
+
resolveBugResponsibility: (fields) => this.resolveBugResponsibility(fields),
|
|
871
|
+
isExternalStateInScope: (value, itemType) => this.isExternalStateInScope(value, itemType),
|
|
872
|
+
isExcludedL3L4BySapWbs: (value) => this.isExcludedL3L4BySapWbs(value),
|
|
873
|
+
resolveRequirementSapWbsByBaseKey: () => '',
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
async loadExternalL3L4ByBaseKey(externalL3L4File, requirementSapWbsByBaseKey = new Map()) {
|
|
877
|
+
return this.mewpExternalIngestionUtils.loadExternalL3L4ByBaseKey(externalL3L4File, {
|
|
878
|
+
toComparableText: (value) => this.toMewpComparableText(value),
|
|
879
|
+
toRequirementKey: (value) => this.toRequirementKey(value),
|
|
880
|
+
resolveBugResponsibility: (fields) => this.resolveBugResponsibility(fields),
|
|
881
|
+
isExternalStateInScope: (value, itemType) => this.isExternalStateInScope(value, itemType),
|
|
882
|
+
isExcludedL3L4BySapWbs: (value) => this.isExcludedL3L4BySapWbs(value),
|
|
883
|
+
resolveRequirementSapWbsByBaseKey: (baseKey) => String(requirementSapWbsByBaseKey.get(baseKey) || ''),
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
buildRequirementSapWbsByBaseKey(requirements) {
|
|
887
|
+
const out = new Map();
|
|
888
|
+
for (const requirement of requirements || []) {
|
|
889
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
890
|
+
if (!baseKey)
|
|
891
|
+
continue;
|
|
892
|
+
const normalized = this.resolveMewpResponsibility(this.toMewpComparableText(requirement === null || requirement === void 0 ? void 0 : requirement.responsibility));
|
|
893
|
+
if (!normalized)
|
|
894
|
+
continue;
|
|
895
|
+
const existing = out.get(baseKey) || '';
|
|
896
|
+
// Keep ESUK as dominant if conflicting values are ever present across family items.
|
|
897
|
+
if (existing === 'ESUK')
|
|
898
|
+
continue;
|
|
899
|
+
if (normalized === 'ESUK' || !existing) {
|
|
900
|
+
out.set(baseKey, normalized);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return out;
|
|
904
|
+
}
|
|
905
|
+
isExternalStateInScope(value, itemType) {
|
|
906
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
907
|
+
if (!normalized)
|
|
908
|
+
return true;
|
|
909
|
+
// TFS/ADO processes usually don't expose a literal "Open" state.
|
|
910
|
+
// Keep non-terminal states, exclude terminal states.
|
|
911
|
+
const terminalStates = new Set([
|
|
912
|
+
'resolved',
|
|
913
|
+
'closed',
|
|
914
|
+
'done',
|
|
915
|
+
'completed',
|
|
916
|
+
'complete',
|
|
917
|
+
'removed',
|
|
918
|
+
'rejected',
|
|
919
|
+
'cancelled',
|
|
920
|
+
'canceled',
|
|
921
|
+
'obsolete',
|
|
922
|
+
]);
|
|
923
|
+
if (terminalStates.has(normalized))
|
|
924
|
+
return false;
|
|
925
|
+
// Bug-specific terminal variants often used in custom processes.
|
|
926
|
+
if (itemType === 'bug') {
|
|
927
|
+
if (normalized === 'fixed')
|
|
928
|
+
return false;
|
|
929
|
+
}
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
932
|
+
invertBaseRequirementLinks(linkedRequirementsByTestCase) {
|
|
933
|
+
const out = new Map();
|
|
934
|
+
for (const [testCaseId, links] of linkedRequirementsByTestCase.entries()) {
|
|
935
|
+
for (const baseKey of (links === null || links === void 0 ? void 0 : links.baseKeys) || []) {
|
|
936
|
+
if (!out.has(baseKey))
|
|
937
|
+
out.set(baseKey, new Set());
|
|
938
|
+
out.get(baseKey).add(testCaseId);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return out;
|
|
942
|
+
}
|
|
440
943
|
buildMewpTestCaseTitleMap(testData) {
|
|
441
944
|
var _a, _b, _c, _d, _e;
|
|
442
945
|
const map = new Map();
|
|
@@ -536,49 +1039,181 @@ class ResultDataProvider {
|
|
|
536
1039
|
return 'failed';
|
|
537
1040
|
return 'notRun';
|
|
538
1041
|
}
|
|
539
|
-
|
|
1042
|
+
accumulateRequirementCountsFromActionResults(actionResults, testCaseId, requirementKeys, counters, observedTestCaseIdsByRequirement) {
|
|
540
1043
|
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
541
1044
|
return;
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
1045
|
+
const sortedResults = Array.isArray(actionResults) ? actionResults : [];
|
|
1046
|
+
let previousRequirementStepIndex = -1;
|
|
1047
|
+
for (let i = 0; i < sortedResults.length; i++) {
|
|
1048
|
+
const actionResult = sortedResults[i];
|
|
1049
|
+
if (actionResult === null || actionResult === void 0 ? void 0 : actionResult.isSharedStepTitle)
|
|
545
1050
|
continue;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
1051
|
+
const requirementCodes = this.extractRequirementCodesFromText((actionResult === null || actionResult === void 0 ? void 0 : actionResult.expected) || '');
|
|
1052
|
+
if (requirementCodes.size === 0)
|
|
1053
|
+
continue;
|
|
1054
|
+
const startIndex = previousRequirementStepIndex + 1;
|
|
1055
|
+
const status = this.resolveRequirementStatusForWindow(sortedResults, startIndex, i);
|
|
1056
|
+
previousRequirementStepIndex = i;
|
|
1057
|
+
for (const code of requirementCodes) {
|
|
1058
|
+
if (requirementKeys.size > 0 && !requirementKeys.has(code))
|
|
1059
|
+
continue;
|
|
1060
|
+
if (!counters.has(code)) {
|
|
1061
|
+
counters.set(code, new Map());
|
|
1062
|
+
}
|
|
1063
|
+
const perTestCaseCounters = counters.get(code);
|
|
1064
|
+
if (!perTestCaseCounters.has(testCaseId)) {
|
|
1065
|
+
perTestCaseCounters.set(testCaseId, { passed: 0, failed: 0, notRun: 0 });
|
|
1066
|
+
}
|
|
1067
|
+
if (!observedTestCaseIdsByRequirement.has(code)) {
|
|
1068
|
+
observedTestCaseIdsByRequirement.set(code, new Set());
|
|
1069
|
+
}
|
|
1070
|
+
observedTestCaseIdsByRequirement.get(code).add(testCaseId);
|
|
1071
|
+
const counter = perTestCaseCounters.get(testCaseId);
|
|
1072
|
+
if (status === 'passed')
|
|
1073
|
+
counter.passed += 1;
|
|
1074
|
+
else if (status === 'failed')
|
|
1075
|
+
counter.failed += 1;
|
|
1076
|
+
else
|
|
1077
|
+
counter.notRun += 1;
|
|
555
1078
|
}
|
|
556
|
-
observedTestCaseIdsByRequirement.get(code).add(testCaseId);
|
|
557
|
-
const counter = perTestCaseCounters.get(testCaseId);
|
|
558
|
-
if (status === 'passed')
|
|
559
|
-
counter.passed += 1;
|
|
560
|
-
else if (status === 'failed')
|
|
561
|
-
counter.failed += 1;
|
|
562
|
-
else
|
|
563
|
-
counter.notRun += 1;
|
|
564
1079
|
}
|
|
565
1080
|
}
|
|
1081
|
+
resolveRequirementStatusForWindow(actionResults, startIndex, endIndex) {
|
|
1082
|
+
var _a;
|
|
1083
|
+
let hasNotRun = false;
|
|
1084
|
+
for (let index = startIndex; index <= endIndex; index++) {
|
|
1085
|
+
const status = this.classifyRequirementStepOutcome((_a = actionResults[index]) === null || _a === void 0 ? void 0 : _a.outcome);
|
|
1086
|
+
if (status === 'failed')
|
|
1087
|
+
return 'failed';
|
|
1088
|
+
if (status === 'notRun')
|
|
1089
|
+
hasNotRun = true;
|
|
1090
|
+
}
|
|
1091
|
+
return hasNotRun ? 'notRun' : 'passed';
|
|
1092
|
+
}
|
|
566
1093
|
extractRequirementCodesFromText(text) {
|
|
1094
|
+
return this.extractRequirementCodesFromExpectedText(text, false);
|
|
1095
|
+
}
|
|
1096
|
+
extractRequirementMentionsFromExpectedSteps(steps, includeSuffix) {
|
|
1097
|
+
const out = [];
|
|
1098
|
+
const allSteps = Array.isArray(steps) ? steps : [];
|
|
1099
|
+
for (let index = 0; index < allSteps.length; index += 1) {
|
|
1100
|
+
const step = allSteps[index];
|
|
1101
|
+
if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
|
|
1102
|
+
continue;
|
|
1103
|
+
const codes = this.extractRequirementCodesFromExpectedText((step === null || step === void 0 ? void 0 : step.expected) || '', includeSuffix);
|
|
1104
|
+
if (codes.size === 0)
|
|
1105
|
+
continue;
|
|
1106
|
+
out.push({
|
|
1107
|
+
stepRef: this.resolveValidationStepReference(step, index),
|
|
1108
|
+
codes,
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
return out;
|
|
1112
|
+
}
|
|
1113
|
+
extractRequirementCodesFromExpectedSteps(steps, includeSuffix) {
|
|
1114
|
+
const out = new Set();
|
|
1115
|
+
for (const step of Array.isArray(steps) ? steps : []) {
|
|
1116
|
+
if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
|
|
1117
|
+
continue;
|
|
1118
|
+
const codes = this.extractRequirementCodesFromExpectedText((step === null || step === void 0 ? void 0 : step.expected) || '', includeSuffix);
|
|
1119
|
+
codes.forEach((code) => out.add(code));
|
|
1120
|
+
}
|
|
1121
|
+
return out;
|
|
1122
|
+
}
|
|
1123
|
+
extractRequirementCodesFromExpectedText(text, includeSuffix) {
|
|
567
1124
|
const out = new Set();
|
|
568
1125
|
const source = this.normalizeRequirementStepText(text);
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
1126
|
+
if (!source)
|
|
1127
|
+
return out;
|
|
1128
|
+
const tokens = source
|
|
1129
|
+
.split(';')
|
|
1130
|
+
.map((token) => String(token || '').trim())
|
|
1131
|
+
.filter((token) => token !== '');
|
|
1132
|
+
for (const token of tokens) {
|
|
1133
|
+
const candidates = this.extractRequirementCandidatesFromToken(token);
|
|
1134
|
+
for (const candidate of candidates) {
|
|
1135
|
+
const expandedTokens = this.expandRequirementTokenByComma(candidate);
|
|
1136
|
+
for (const expandedToken of expandedTokens) {
|
|
1137
|
+
if (!expandedToken || /vvrm/i.test(expandedToken))
|
|
1138
|
+
continue;
|
|
1139
|
+
const normalized = this.normalizeRequirementCodeToken(expandedToken, includeSuffix);
|
|
1140
|
+
if (normalized) {
|
|
1141
|
+
out.add(normalized);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
578
1144
|
}
|
|
579
1145
|
}
|
|
580
1146
|
return out;
|
|
581
1147
|
}
|
|
1148
|
+
extractRequirementCandidatesFromToken(token) {
|
|
1149
|
+
const source = String(token || '');
|
|
1150
|
+
if (!source)
|
|
1151
|
+
return [];
|
|
1152
|
+
const out = new Set();
|
|
1153
|
+
const collectCandidates = (input, rejectTailPattern) => {
|
|
1154
|
+
for (const match of input.matchAll(/SR\d{4,}(?:-\d+(?:,\d+)*)?/gi)) {
|
|
1155
|
+
const matchedValue = String((match === null || match === void 0 ? void 0 : match[0]) || '')
|
|
1156
|
+
.trim()
|
|
1157
|
+
.toUpperCase();
|
|
1158
|
+
if (!matchedValue)
|
|
1159
|
+
continue;
|
|
1160
|
+
const endIndex = Number((match === null || match === void 0 ? void 0 : match.index) || 0) + matchedValue.length;
|
|
1161
|
+
const tail = String(input.slice(endIndex) || '');
|
|
1162
|
+
if (rejectTailPattern.test(tail))
|
|
1163
|
+
continue;
|
|
1164
|
+
out.add(matchedValue);
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
// Normal scan keeps punctuation context (" SR0817-V3.2 " -> reject via tail).
|
|
1168
|
+
collectCandidates(source, /^\s*(?:V\d|VVRM|-V\d)/i);
|
|
1169
|
+
// Compact scan preserves legacy support for spaced SR letters/digits
|
|
1170
|
+
// such as "S R 0 0 0 1" and HTML-fragmented tokens.
|
|
1171
|
+
const compactSource = source.replace(/\s+/g, '');
|
|
1172
|
+
if (compactSource && compactSource !== source) {
|
|
1173
|
+
collectCandidates(compactSource, /^(?:V\d|VVRM|-V\d)/i);
|
|
1174
|
+
}
|
|
1175
|
+
return [...out];
|
|
1176
|
+
}
|
|
1177
|
+
expandRequirementTokenByComma(token) {
|
|
1178
|
+
const compact = String(token || '').trim().toUpperCase();
|
|
1179
|
+
if (!compact)
|
|
1180
|
+
return [];
|
|
1181
|
+
const suffixBatchMatch = /^SR(\d{4,})-(\d+(?:,\d+)+)$/.exec(compact);
|
|
1182
|
+
if (suffixBatchMatch) {
|
|
1183
|
+
const base = String(suffixBatchMatch[1] || '').trim();
|
|
1184
|
+
const suffixes = String(suffixBatchMatch[2] || '')
|
|
1185
|
+
.split(',')
|
|
1186
|
+
.map((item) => String(item || '').trim())
|
|
1187
|
+
.filter((item) => /^\d+$/.test(item));
|
|
1188
|
+
return suffixes.map((suffix) => `SR${base}-${suffix}`);
|
|
1189
|
+
}
|
|
1190
|
+
return compact
|
|
1191
|
+
.split(',')
|
|
1192
|
+
.map((part) => String(part || '').trim())
|
|
1193
|
+
.filter((part) => !!part);
|
|
1194
|
+
}
|
|
1195
|
+
normalizeRequirementCodeToken(token, includeSuffix) {
|
|
1196
|
+
const compact = String(token || '')
|
|
1197
|
+
.trim()
|
|
1198
|
+
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
1199
|
+
.toUpperCase();
|
|
1200
|
+
if (!compact)
|
|
1201
|
+
return '';
|
|
1202
|
+
const pattern = includeSuffix ? /^SR(\d{4,})(?:-(\d+))?$/ : /^SR(\d{4,})(?:-\d+)?$/;
|
|
1203
|
+
const match = pattern.exec(compact);
|
|
1204
|
+
if (!match)
|
|
1205
|
+
return '';
|
|
1206
|
+
const baseDigits = String(match[1] || '').trim();
|
|
1207
|
+
if (!baseDigits)
|
|
1208
|
+
return '';
|
|
1209
|
+
if (includeSuffix && match[2]) {
|
|
1210
|
+
const suffixDigits = String(match[2] || '').trim();
|
|
1211
|
+
if (!suffixDigits)
|
|
1212
|
+
return '';
|
|
1213
|
+
return `SR${baseDigits}-${suffixDigits}`;
|
|
1214
|
+
}
|
|
1215
|
+
return `SR${baseDigits}`;
|
|
1216
|
+
}
|
|
582
1217
|
normalizeRequirementStepText(text) {
|
|
583
1218
|
const raw = String(text || '');
|
|
584
1219
|
if (!raw)
|
|
@@ -594,21 +1229,19 @@ class ResultDataProvider {
|
|
|
594
1229
|
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
595
1230
|
.replace(/\s+/g, ' ');
|
|
596
1231
|
}
|
|
1232
|
+
resolveValidationStepReference(step, index) {
|
|
1233
|
+
const fromPosition = String((step === null || step === void 0 ? void 0 : step.stepPosition) || '').trim();
|
|
1234
|
+
if (fromPosition)
|
|
1235
|
+
return `Step ${fromPosition}`;
|
|
1236
|
+
const fromId = String((step === null || step === void 0 ? void 0 : step.stepId) || '').trim();
|
|
1237
|
+
if (fromId)
|
|
1238
|
+
return `Step ${fromId}`;
|
|
1239
|
+
return `Step ${index + 1}`;
|
|
1240
|
+
}
|
|
597
1241
|
toRequirementKey(requirementId) {
|
|
598
|
-
|
|
599
|
-
if (!normalized)
|
|
600
|
-
return '';
|
|
601
|
-
const digits = Number.parseInt(normalized.replace(/^SR/i, ''), 10);
|
|
602
|
-
if (!Number.isFinite(digits))
|
|
603
|
-
return '';
|
|
604
|
-
return `SR${digits}`;
|
|
1242
|
+
return this.normalizeMewpRequirementCode(requirementId);
|
|
605
1243
|
}
|
|
606
|
-
async fetchMewpL2Requirements(projectName
|
|
607
|
-
var _a;
|
|
608
|
-
const queryHref = this.extractMewpQueryHref(linkedQueryRequest);
|
|
609
|
-
if (queryHref) {
|
|
610
|
-
return this.fetchMewpL2RequirementsFromQuery(projectName, queryHref);
|
|
611
|
-
}
|
|
1244
|
+
async fetchMewpL2Requirements(projectName) {
|
|
612
1245
|
const workItemTypeNames = await this.fetchMewpRequirementTypeNames(projectName);
|
|
613
1246
|
if (workItemTypeNames.length === 0) {
|
|
614
1247
|
return [];
|
|
@@ -616,184 +1249,315 @@ class ResultDataProvider {
|
|
|
616
1249
|
const quotedTypeNames = workItemTypeNames
|
|
617
1250
|
.map((name) => `'${String(name).replace(/'/g, "''")}'`)
|
|
618
1251
|
.join(', ');
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
AND [System.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
.
|
|
1252
|
+
const queryRequirementIds = async (l2AreaPath) => {
|
|
1253
|
+
var _a;
|
|
1254
|
+
const escapedAreaPath = l2AreaPath ? String(l2AreaPath).replace(/'/g, "''") : '';
|
|
1255
|
+
const areaFilter = escapedAreaPath ? `\n AND [System.AreaPath] UNDER '${escapedAreaPath}'` : '';
|
|
1256
|
+
const wiql = `SELECT [System.Id]
|
|
1257
|
+
FROM WorkItems
|
|
1258
|
+
WHERE [System.TeamProject] = @project
|
|
1259
|
+
AND [System.WorkItemType] IN (${quotedTypeNames})${areaFilter}
|
|
1260
|
+
ORDER BY [System.Id]`;
|
|
1261
|
+
const wiqlUrl = `${this.orgUrl}${projectName}/_apis/wit/wiql?api-version=7.1-preview.2`;
|
|
1262
|
+
const wiqlResponse = await tfs_1.TFSServices.postRequest(wiqlUrl, this.token, 'Post', { query: wiql }, null);
|
|
1263
|
+
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 : [];
|
|
1264
|
+
return workItemRefs
|
|
1265
|
+
.map((item) => Number(item === null || item === void 0 ? void 0 : item.id))
|
|
1266
|
+
.filter((id) => Number.isFinite(id));
|
|
1267
|
+
};
|
|
1268
|
+
const defaultL2AreaPath = `${String(projectName || '').trim()}\\Customer Requirements\\Level 2`;
|
|
1269
|
+
let requirementIds = [];
|
|
1270
|
+
try {
|
|
1271
|
+
requirementIds = await queryRequirementIds(defaultL2AreaPath);
|
|
1272
|
+
}
|
|
1273
|
+
catch (error) {
|
|
1274
|
+
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}`);
|
|
1275
|
+
}
|
|
1276
|
+
if (requirementIds.length === 0) {
|
|
1277
|
+
requirementIds = await queryRequirementIds(null);
|
|
1278
|
+
}
|
|
630
1279
|
if (requirementIds.length === 0) {
|
|
631
1280
|
return [];
|
|
632
1281
|
}
|
|
633
1282
|
const workItems = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
|
|
634
1283
|
const requirements = workItems.map((wi) => {
|
|
635
1284
|
const fields = (wi === null || wi === void 0 ? void 0 : wi.fields) || {};
|
|
1285
|
+
const requirementId = this.extractMewpRequirementIdentifier(fields, Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0));
|
|
1286
|
+
const areaPath = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
636
1287
|
return {
|
|
637
1288
|
workItemId: Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0),
|
|
638
|
-
requirementId
|
|
639
|
-
|
|
1289
|
+
requirementId,
|
|
1290
|
+
baseKey: this.toRequirementKey(requirementId),
|
|
1291
|
+
title: this.toMewpComparableText((fields === null || fields === void 0 ? void 0 : fields['System.Title']) || (wi === null || wi === void 0 ? void 0 : wi.title)),
|
|
1292
|
+
subSystem: this.deriveMewpSubSystem(fields),
|
|
640
1293
|
responsibility: this.deriveMewpResponsibility(fields),
|
|
641
1294
|
linkedTestCaseIds: this.extractLinkedTestCaseIdsFromRequirement((wi === null || wi === void 0 ? void 0 : wi.relations) || []),
|
|
1295
|
+
relatedWorkItemIds: this.extractLinkedWorkItemIdsFromRelations((wi === null || wi === void 0 ? void 0 : wi.relations) || []),
|
|
1296
|
+
areaPath,
|
|
642
1297
|
};
|
|
643
1298
|
});
|
|
644
|
-
return requirements
|
|
1299
|
+
return requirements
|
|
1300
|
+
.filter((item) => {
|
|
1301
|
+
if (!item.baseKey)
|
|
1302
|
+
return false;
|
|
1303
|
+
if (!item.areaPath)
|
|
1304
|
+
return true;
|
|
1305
|
+
return this.isMewpL2AreaPath(item.areaPath);
|
|
1306
|
+
})
|
|
1307
|
+
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
645
1308
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const mode = String((linkedQueryRequest === null || linkedQueryRequest === void 0 ? void 0 : linkedQueryRequest.linkedQueryMode) || '')
|
|
1309
|
+
isMewpL2AreaPath(areaPath) {
|
|
1310
|
+
const normalized = String(areaPath || '')
|
|
649
1311
|
.trim()
|
|
650
|
-
.toLowerCase()
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
1312
|
+
.toLowerCase()
|
|
1313
|
+
.replace(/\//g, '\\');
|
|
1314
|
+
if (!normalized)
|
|
1315
|
+
return false;
|
|
1316
|
+
return normalized.includes('\\customer requirements\\level 2');
|
|
654
1317
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if ((
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
if (sourceIsRequirement && targetIsTestCase) {
|
|
700
|
-
linkRequirementToTestCase(sourceItem, targetItem);
|
|
701
|
-
}
|
|
702
|
-
else if (sourceIsTestCase && targetIsRequirement) {
|
|
703
|
-
linkRequirementToTestCase(targetItem, sourceItem);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
1318
|
+
collapseMewpRequirementFamilies(requirements, scopedRequirementKeys) {
|
|
1319
|
+
const families = new Map();
|
|
1320
|
+
const calcScore = (item) => {
|
|
1321
|
+
const requirementId = String((item === null || item === void 0 ? void 0 : item.requirementId) || '').trim();
|
|
1322
|
+
const areaPath = String((item === null || item === void 0 ? void 0 : item.areaPath) || '')
|
|
1323
|
+
.trim()
|
|
1324
|
+
.toLowerCase();
|
|
1325
|
+
let score = 0;
|
|
1326
|
+
if (/^SR\d+$/i.test(requirementId))
|
|
1327
|
+
score += 6;
|
|
1328
|
+
if (areaPath.includes('\\customer requirements\\level 2'))
|
|
1329
|
+
score += 3;
|
|
1330
|
+
if (!areaPath.includes('\\mop'))
|
|
1331
|
+
score += 2;
|
|
1332
|
+
if (String((item === null || item === void 0 ? void 0 : item.title) || '').trim())
|
|
1333
|
+
score += 1;
|
|
1334
|
+
if (String((item === null || item === void 0 ? void 0 : item.subSystem) || '').trim())
|
|
1335
|
+
score += 1;
|
|
1336
|
+
if (String((item === null || item === void 0 ? void 0 : item.responsibility) || '').trim())
|
|
1337
|
+
score += 1;
|
|
1338
|
+
return score;
|
|
1339
|
+
};
|
|
1340
|
+
for (const requirement of requirements || []) {
|
|
1341
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1342
|
+
if (!baseKey)
|
|
1343
|
+
continue;
|
|
1344
|
+
if ((scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && !scopedRequirementKeys.has(baseKey))
|
|
1345
|
+
continue;
|
|
1346
|
+
if (!families.has(baseKey)) {
|
|
1347
|
+
families.set(baseKey, {
|
|
1348
|
+
representative: requirement,
|
|
1349
|
+
score: calcScore(requirement),
|
|
1350
|
+
linkedTestCaseIds: new Set(),
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
const family = families.get(baseKey);
|
|
1354
|
+
const score = calcScore(requirement);
|
|
1355
|
+
if (score > family.score) {
|
|
1356
|
+
family.representative = requirement;
|
|
1357
|
+
family.score = score;
|
|
1358
|
+
}
|
|
1359
|
+
for (const testCaseId of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
|
|
1360
|
+
if (Number.isFinite(testCaseId) && Number(testCaseId) > 0) {
|
|
1361
|
+
family.linkedTestCaseIds.add(Number(testCaseId));
|
|
706
1362
|
}
|
|
707
1363
|
}
|
|
708
|
-
await this.hydrateMewpRequirementsFromWorkItems(projectName, requirementsById);
|
|
709
|
-
return [...requirementsById.values()]
|
|
710
|
-
.map((requirement) => ({
|
|
711
|
-
workItemId: requirement.workItemId,
|
|
712
|
-
requirementId: requirement.requirementId,
|
|
713
|
-
title: requirement.title,
|
|
714
|
-
responsibility: requirement.responsibility,
|
|
715
|
-
linkedTestCaseIds: [...requirement.linkedTestCaseIds].sort((a, b) => a - b),
|
|
716
|
-
}))
|
|
717
|
-
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
718
1364
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1365
|
+
return [...families.entries()]
|
|
1366
|
+
.map(([baseKey, family]) => {
|
|
1367
|
+
var _a, _b, _c, _d;
|
|
1368
|
+
return ({
|
|
1369
|
+
requirementId: String(((_a = family === null || family === void 0 ? void 0 : family.representative) === null || _a === void 0 ? void 0 : _a.requirementId) || baseKey),
|
|
1370
|
+
baseKey,
|
|
1371
|
+
title: String(((_b = family === null || family === void 0 ? void 0 : family.representative) === null || _b === void 0 ? void 0 : _b.title) || ''),
|
|
1372
|
+
subSystem: String(((_c = family === null || family === void 0 ? void 0 : family.representative) === null || _c === void 0 ? void 0 : _c.subSystem) || ''),
|
|
1373
|
+
responsibility: String(((_d = family === null || family === void 0 ? void 0 : family.representative) === null || _d === void 0 ? void 0 : _d.responsibility) || ''),
|
|
1374
|
+
linkedTestCaseIds: [...family.linkedTestCaseIds].sort((a, b) => a - b),
|
|
1375
|
+
});
|
|
1376
|
+
})
|
|
1377
|
+
.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
1378
|
+
}
|
|
1379
|
+
buildRequirementFamilyMap(requirements, scopedRequirementKeys) {
|
|
1380
|
+
const familyMap = new Map();
|
|
1381
|
+
for (const requirement of requirements || []) {
|
|
1382
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1383
|
+
if (!baseKey)
|
|
1384
|
+
continue;
|
|
1385
|
+
if ((scopedRequirementKeys === null || scopedRequirementKeys === void 0 ? void 0 : scopedRequirementKeys.size) && !scopedRequirementKeys.has(baseKey))
|
|
1386
|
+
continue;
|
|
1387
|
+
const fullCode = this.normalizeMewpRequirementCodeWithSuffix((requirement === null || requirement === void 0 ? void 0 : requirement.requirementId) || '');
|
|
1388
|
+
if (!fullCode)
|
|
1389
|
+
continue;
|
|
1390
|
+
if (!familyMap.has(baseKey))
|
|
1391
|
+
familyMap.set(baseKey, new Set());
|
|
1392
|
+
familyMap.get(baseKey).add(fullCode);
|
|
722
1393
|
}
|
|
1394
|
+
return familyMap;
|
|
723
1395
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
responsibility: '',
|
|
737
|
-
linkedTestCaseIds: new Set(),
|
|
1396
|
+
async buildLinkedRequirementsByTestCase(requirements, testData, projectName) {
|
|
1397
|
+
var _a, _b, _c;
|
|
1398
|
+
const map = new Map();
|
|
1399
|
+
const ensure = (testCaseId) => {
|
|
1400
|
+
if (!map.has(testCaseId)) {
|
|
1401
|
+
map.set(testCaseId, {
|
|
1402
|
+
baseKeys: new Set(),
|
|
1403
|
+
fullCodes: new Set(),
|
|
1404
|
+
bugIds: new Set(),
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
return map.get(testCaseId);
|
|
738
1408
|
};
|
|
739
|
-
const
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1409
|
+
const requirementById = new Map();
|
|
1410
|
+
for (const requirement of requirements || []) {
|
|
1411
|
+
const workItemId = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
|
|
1412
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1413
|
+
const fullCode = this.normalizeMewpRequirementCodeWithSuffix((requirement === null || requirement === void 0 ? void 0 : requirement.requirementId) || '');
|
|
1414
|
+
if (workItemId > 0 && baseKey && fullCode) {
|
|
1415
|
+
requirementById.set(workItemId, { baseKey, fullCode });
|
|
1416
|
+
}
|
|
1417
|
+
for (const testCaseIdRaw of (requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []) {
|
|
1418
|
+
const testCaseId = Number(testCaseIdRaw);
|
|
1419
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0 || !baseKey || !fullCode)
|
|
1420
|
+
continue;
|
|
1421
|
+
const entry = ensure(testCaseId);
|
|
1422
|
+
entry.baseKeys.add(baseKey);
|
|
1423
|
+
entry.fullCodes.add(fullCode);
|
|
1424
|
+
}
|
|
745
1425
|
}
|
|
746
|
-
|
|
747
|
-
|
|
1426
|
+
const testCaseIds = new Set();
|
|
1427
|
+
for (const suite of testData || []) {
|
|
1428
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
1429
|
+
for (const testCase of testCasesItems) {
|
|
1430
|
+
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);
|
|
1431
|
+
if (Number.isFinite(id) && id > 0)
|
|
1432
|
+
testCaseIds.add(id);
|
|
1433
|
+
}
|
|
1434
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
1435
|
+
for (const testPoint of testPointsItems) {
|
|
1436
|
+
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);
|
|
1437
|
+
if (Number.isFinite(id) && id > 0)
|
|
1438
|
+
testCaseIds.add(id);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
const relatedIdsByTestCase = new Map();
|
|
1442
|
+
const allRelatedIds = new Set();
|
|
1443
|
+
if (testCaseIds.size > 0) {
|
|
1444
|
+
const testCaseWorkItems = await this.fetchWorkItemsByIds(projectName, [...testCaseIds], true);
|
|
1445
|
+
for (const workItem of testCaseWorkItems || []) {
|
|
1446
|
+
const testCaseId = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1447
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
1448
|
+
continue;
|
|
1449
|
+
const relations = Array.isArray(workItem === null || workItem === void 0 ? void 0 : workItem.relations) ? workItem.relations : [];
|
|
1450
|
+
if (!relatedIdsByTestCase.has(testCaseId))
|
|
1451
|
+
relatedIdsByTestCase.set(testCaseId, new Set());
|
|
1452
|
+
for (const relation of relations) {
|
|
1453
|
+
const linkedWorkItemId = this.extractLinkedWorkItemIdFromRelation(relation);
|
|
1454
|
+
if (!linkedWorkItemId)
|
|
1455
|
+
continue;
|
|
1456
|
+
relatedIdsByTestCase.get(testCaseId).add(linkedWorkItemId);
|
|
1457
|
+
allRelatedIds.add(linkedWorkItemId);
|
|
1458
|
+
if (this.isTestCaseToRequirementRelation(relation) && requirementById.has(linkedWorkItemId)) {
|
|
1459
|
+
const linkedRequirement = requirementById.get(linkedWorkItemId);
|
|
1460
|
+
const entry = ensure(testCaseId);
|
|
1461
|
+
entry.baseKeys.add(linkedRequirement.baseKey);
|
|
1462
|
+
entry.fullCodes.add(linkedRequirement.fullCode);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
748
1466
|
}
|
|
749
|
-
|
|
1467
|
+
if (allRelatedIds.size > 0) {
|
|
1468
|
+
const relatedWorkItems = await this.fetchWorkItemsByIds(projectName, [...allRelatedIds], false);
|
|
1469
|
+
const typeById = new Map();
|
|
1470
|
+
for (const workItem of relatedWorkItems || []) {
|
|
1471
|
+
const id = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1472
|
+
if (!Number.isFinite(id) || id <= 0)
|
|
1473
|
+
continue;
|
|
1474
|
+
const type = String(((_c = workItem === null || workItem === void 0 ? void 0 : workItem.fields) === null || _c === void 0 ? void 0 : _c['System.WorkItemType']) || '')
|
|
1475
|
+
.trim()
|
|
1476
|
+
.toLowerCase();
|
|
1477
|
+
typeById.set(id, type);
|
|
1478
|
+
}
|
|
1479
|
+
for (const [testCaseId, ids] of relatedIdsByTestCase.entries()) {
|
|
1480
|
+
const entry = ensure(testCaseId);
|
|
1481
|
+
for (const linkedId of ids) {
|
|
1482
|
+
const linkedType = typeById.get(linkedId) || '';
|
|
1483
|
+
if (linkedType === 'bug') {
|
|
1484
|
+
entry.bugIds.add(linkedId);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return map;
|
|
750
1490
|
}
|
|
751
|
-
async
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
current.requirementId = requirementId || current.requirementId || String(workItemId);
|
|
769
|
-
if (title) {
|
|
770
|
-
current.title = title;
|
|
1491
|
+
async resolveMewpRequirementScopeKeysFromQuery(linkedQueryRequest, requirements, linkedRequirementsByTestCase) {
|
|
1492
|
+
var _a, _b, _c, _d, _e;
|
|
1493
|
+
const mode = String((linkedQueryRequest === null || linkedQueryRequest === void 0 ? void 0 : linkedQueryRequest.linkedQueryMode) || '')
|
|
1494
|
+
.trim()
|
|
1495
|
+
.toLowerCase();
|
|
1496
|
+
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();
|
|
1497
|
+
if (mode !== 'query' || !wiqlHref)
|
|
1498
|
+
return undefined;
|
|
1499
|
+
try {
|
|
1500
|
+
const queryResult = await tfs_1.TFSServices.getItemContent(wiqlHref, this.token);
|
|
1501
|
+
const queryIds = new Set();
|
|
1502
|
+
if (Array.isArray(queryResult === null || queryResult === void 0 ? void 0 : queryResult.workItems)) {
|
|
1503
|
+
for (const workItem of queryResult.workItems) {
|
|
1504
|
+
const id = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
1505
|
+
if (Number.isFinite(id) && id > 0)
|
|
1506
|
+
queryIds.add(id);
|
|
1507
|
+
}
|
|
771
1508
|
}
|
|
772
|
-
if (
|
|
773
|
-
|
|
1509
|
+
if (Array.isArray(queryResult === null || queryResult === void 0 ? void 0 : queryResult.workItemRelations)) {
|
|
1510
|
+
for (const relation of queryResult.workItemRelations) {
|
|
1511
|
+
const sourceId = Number(((_c = relation === null || relation === void 0 ? void 0 : relation.source) === null || _c === void 0 ? void 0 : _c.id) || 0);
|
|
1512
|
+
const targetId = Number(((_d = relation === null || relation === void 0 ? void 0 : relation.target) === null || _d === void 0 ? void 0 : _d.id) || 0);
|
|
1513
|
+
if (Number.isFinite(sourceId) && sourceId > 0)
|
|
1514
|
+
queryIds.add(sourceId);
|
|
1515
|
+
if (Number.isFinite(targetId) && targetId > 0)
|
|
1516
|
+
queryIds.add(targetId);
|
|
1517
|
+
}
|
|
774
1518
|
}
|
|
775
|
-
|
|
1519
|
+
if (queryIds.size === 0)
|
|
1520
|
+
return undefined;
|
|
1521
|
+
const reqIdToBaseKey = new Map();
|
|
1522
|
+
for (const requirement of requirements || []) {
|
|
1523
|
+
const id = Number((requirement === null || requirement === void 0 ? void 0 : requirement.workItemId) || 0);
|
|
1524
|
+
const baseKey = String((requirement === null || requirement === void 0 ? void 0 : requirement.baseKey) || '').trim();
|
|
1525
|
+
if (id > 0 && baseKey)
|
|
1526
|
+
reqIdToBaseKey.set(id, baseKey);
|
|
1527
|
+
}
|
|
1528
|
+
const scopedKeys = new Set();
|
|
1529
|
+
for (const queryId of queryIds) {
|
|
1530
|
+
if (reqIdToBaseKey.has(queryId)) {
|
|
1531
|
+
scopedKeys.add(reqIdToBaseKey.get(queryId));
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
const linked = linkedRequirementsByTestCase.get(queryId);
|
|
1535
|
+
if (!((_e = linked === null || linked === void 0 ? void 0 : linked.baseKeys) === null || _e === void 0 ? void 0 : _e.size))
|
|
1536
|
+
continue;
|
|
1537
|
+
linked.baseKeys.forEach((baseKey) => scopedKeys.add(baseKey));
|
|
1538
|
+
}
|
|
1539
|
+
return scopedKeys.size > 0 ? scopedKeys : undefined;
|
|
1540
|
+
}
|
|
1541
|
+
catch (error) {
|
|
1542
|
+
logger_1.default.warn(`Could not resolve MEWP query scope: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
|
|
1543
|
+
return undefined;
|
|
776
1544
|
}
|
|
777
1545
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
return this.toMewpComparableText((_a = workItem === null || workItem === void 0 ? void 0 : workItem.fields) === null || _a === void 0 ? void 0 : _a['System.WorkItemType']);
|
|
781
|
-
}
|
|
782
|
-
isMewpRequirementType(workItemType, requirementTypeSet) {
|
|
783
|
-
const normalized = String(workItemType || '')
|
|
1546
|
+
isTestCaseToRequirementRelation(relation) {
|
|
1547
|
+
const rel = String((relation === null || relation === void 0 ? void 0 : relation.rel) || '')
|
|
784
1548
|
.trim()
|
|
785
1549
|
.toLowerCase();
|
|
786
|
-
if (!
|
|
1550
|
+
if (!rel)
|
|
787
1551
|
return false;
|
|
788
|
-
|
|
789
|
-
return true;
|
|
790
|
-
return normalized.includes('requirement') || normalized === 'epic';
|
|
1552
|
+
return rel.includes('testedby-reverse') || (rel.includes('tests') && rel.includes('reverse'));
|
|
791
1553
|
}
|
|
792
|
-
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1554
|
+
extractLinkedWorkItemIdFromRelation(relation) {
|
|
1555
|
+
const url = String((relation === null || relation === void 0 ? void 0 : relation.url) || '');
|
|
1556
|
+
const match = /\/workItems\/(\d+)/i.exec(url);
|
|
1557
|
+
if (!match)
|
|
1558
|
+
return 0;
|
|
1559
|
+
const parsed = Number(match[1]);
|
|
1560
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
797
1561
|
}
|
|
798
1562
|
async fetchMewpRequirementTypeNames(projectName) {
|
|
799
1563
|
try {
|
|
@@ -849,6 +1613,19 @@ ORDER BY [System.Id]`;
|
|
|
849
1613
|
}
|
|
850
1614
|
return [...out].sort((a, b) => a - b);
|
|
851
1615
|
}
|
|
1616
|
+
extractLinkedWorkItemIdsFromRelations(relations) {
|
|
1617
|
+
const out = new Set();
|
|
1618
|
+
for (const relation of Array.isArray(relations) ? relations : []) {
|
|
1619
|
+
const url = String((relation === null || relation === void 0 ? void 0 : relation.url) || '');
|
|
1620
|
+
const match = /\/workItems\/(\d+)/i.exec(url);
|
|
1621
|
+
if (!match)
|
|
1622
|
+
continue;
|
|
1623
|
+
const id = Number(match[1]);
|
|
1624
|
+
if (Number.isFinite(id) && id > 0)
|
|
1625
|
+
out.add(id);
|
|
1626
|
+
}
|
|
1627
|
+
return [...out].sort((a, b) => a - b);
|
|
1628
|
+
}
|
|
852
1629
|
extractMewpRequirementIdentifier(fields, fallbackWorkItemId) {
|
|
853
1630
|
const entries = Object.entries(fields || {});
|
|
854
1631
|
// First pass: only trusted identifier-like fields.
|
|
@@ -868,7 +1645,7 @@ ORDER BY [System.Id]`;
|
|
|
868
1645
|
const valueAsString = this.toMewpComparableText(value);
|
|
869
1646
|
if (!valueAsString)
|
|
870
1647
|
continue;
|
|
871
|
-
const normalized = this.
|
|
1648
|
+
const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
872
1649
|
if (normalized)
|
|
873
1650
|
return normalized;
|
|
874
1651
|
}
|
|
@@ -881,18 +1658,30 @@ ORDER BY [System.Id]`;
|
|
|
881
1658
|
const valueAsString = this.toMewpComparableText(value);
|
|
882
1659
|
if (!valueAsString)
|
|
883
1660
|
continue;
|
|
884
|
-
const normalized = this.
|
|
1661
|
+
const normalized = this.normalizeMewpRequirementCodeWithSuffix(valueAsString);
|
|
885
1662
|
if (normalized)
|
|
886
1663
|
return normalized;
|
|
887
1664
|
}
|
|
888
1665
|
// Optional fallback from title only (avoid scanning all fields and accidental SR matches).
|
|
889
1666
|
const title = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
890
|
-
const titleCode = this.
|
|
1667
|
+
const titleCode = this.normalizeMewpRequirementCodeWithSuffix(title);
|
|
891
1668
|
if (titleCode)
|
|
892
1669
|
return titleCode;
|
|
893
|
-
return
|
|
1670
|
+
return fallbackWorkItemId ? `SR${fallbackWorkItemId}` : '';
|
|
894
1671
|
}
|
|
895
1672
|
deriveMewpResponsibility(fields) {
|
|
1673
|
+
const explicitSapWbs = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['Custom.SAPWBS']);
|
|
1674
|
+
const fromExplicitSapWbs = this.resolveMewpResponsibility(explicitSapWbs);
|
|
1675
|
+
if (fromExplicitSapWbs)
|
|
1676
|
+
return fromExplicitSapWbs;
|
|
1677
|
+
if (explicitSapWbs)
|
|
1678
|
+
return explicitSapWbs;
|
|
1679
|
+
const explicitSapWbsByLabel = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['SAPWBS']);
|
|
1680
|
+
const fromExplicitLabel = this.resolveMewpResponsibility(explicitSapWbsByLabel);
|
|
1681
|
+
if (fromExplicitLabel)
|
|
1682
|
+
return fromExplicitLabel;
|
|
1683
|
+
if (explicitSapWbsByLabel)
|
|
1684
|
+
return explicitSapWbsByLabel;
|
|
896
1685
|
const areaPath = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
897
1686
|
const fromAreaPath = this.resolveMewpResponsibility(areaPath);
|
|
898
1687
|
if (fromAreaPath)
|
|
@@ -908,18 +1697,69 @@ ORDER BY [System.Id]`;
|
|
|
908
1697
|
}
|
|
909
1698
|
return '';
|
|
910
1699
|
}
|
|
1700
|
+
deriveMewpSubSystem(fields) {
|
|
1701
|
+
const directCandidates = [
|
|
1702
|
+
fields === null || fields === void 0 ? void 0 : fields['Custom.SubSystem'],
|
|
1703
|
+
fields === null || fields === void 0 ? void 0 : fields['Custom.Subsystem'],
|
|
1704
|
+
fields === null || fields === void 0 ? void 0 : fields['SubSystem'],
|
|
1705
|
+
fields === null || fields === void 0 ? void 0 : fields['Subsystem'],
|
|
1706
|
+
fields === null || fields === void 0 ? void 0 : fields['subSystem'],
|
|
1707
|
+
];
|
|
1708
|
+
for (const candidate of directCandidates) {
|
|
1709
|
+
const value = this.toMewpComparableText(candidate);
|
|
1710
|
+
if (value)
|
|
1711
|
+
return value;
|
|
1712
|
+
}
|
|
1713
|
+
const keyHints = ['subsystem', 'sub system', 'sub_system'];
|
|
1714
|
+
for (const [key, value] of Object.entries(fields || {})) {
|
|
1715
|
+
const normalizedKey = String(key || '').toLowerCase();
|
|
1716
|
+
if (!keyHints.some((hint) => normalizedKey.includes(hint)))
|
|
1717
|
+
continue;
|
|
1718
|
+
const resolved = this.toMewpComparableText(value);
|
|
1719
|
+
if (resolved)
|
|
1720
|
+
return resolved;
|
|
1721
|
+
}
|
|
1722
|
+
return '';
|
|
1723
|
+
}
|
|
1724
|
+
resolveBugResponsibility(fields) {
|
|
1725
|
+
const sapWbsRaw = this.toMewpComparableText((fields === null || fields === void 0 ? void 0 : fields['Custom.SAPWBS']) || (fields === null || fields === void 0 ? void 0 : fields['SAPWBS']));
|
|
1726
|
+
const fromSapWbs = this.resolveMewpResponsibility(sapWbsRaw);
|
|
1727
|
+
if (fromSapWbs === 'ESUK')
|
|
1728
|
+
return 'ESUK';
|
|
1729
|
+
if (fromSapWbs === 'IL')
|
|
1730
|
+
return 'Elisra';
|
|
1731
|
+
const areaPathRaw = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
1732
|
+
const fromAreaPath = this.resolveMewpResponsibility(areaPathRaw);
|
|
1733
|
+
if (fromAreaPath === 'ESUK')
|
|
1734
|
+
return 'ESUK';
|
|
1735
|
+
if (fromAreaPath === 'IL')
|
|
1736
|
+
return 'Elisra';
|
|
1737
|
+
return 'Unknown';
|
|
1738
|
+
}
|
|
911
1739
|
resolveMewpResponsibility(value) {
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
.toLowerCase();
|
|
915
|
-
if (!text)
|
|
1740
|
+
const raw = String(value || '').trim();
|
|
1741
|
+
if (!raw)
|
|
916
1742
|
return '';
|
|
917
|
-
|
|
1743
|
+
const rawUpper = raw.toUpperCase();
|
|
1744
|
+
if (rawUpper === 'ESUK')
|
|
1745
|
+
return 'ESUK';
|
|
1746
|
+
if (rawUpper === 'IL')
|
|
1747
|
+
return 'IL';
|
|
1748
|
+
const normalizedPath = raw
|
|
1749
|
+
.toLowerCase()
|
|
1750
|
+
.replace(/\//g, '\\')
|
|
1751
|
+
.replace(/\\+/g, '\\')
|
|
1752
|
+
.trim();
|
|
1753
|
+
if (normalizedPath.endsWith('\\atp\\esuk') || normalizedPath === 'atp\\esuk')
|
|
918
1754
|
return 'ESUK';
|
|
919
|
-
if (
|
|
1755
|
+
if (normalizedPath.endsWith('\\atp') || normalizedPath === 'atp')
|
|
920
1756
|
return 'IL';
|
|
921
1757
|
return '';
|
|
922
1758
|
}
|
|
1759
|
+
isExcludedL3L4BySapWbs(value) {
|
|
1760
|
+
const responsibility = this.resolveMewpResponsibility(this.toMewpComparableText(value));
|
|
1761
|
+
return responsibility === 'ESUK';
|
|
1762
|
+
}
|
|
923
1763
|
normalizeMewpRequirementCode(value) {
|
|
924
1764
|
const text = String(value || '').trim();
|
|
925
1765
|
if (!text)
|
|
@@ -929,6 +1769,18 @@ ORDER BY [System.Id]`;
|
|
|
929
1769
|
return '';
|
|
930
1770
|
return `SR${match[1]}`;
|
|
931
1771
|
}
|
|
1772
|
+
normalizeMewpRequirementCodeWithSuffix(value) {
|
|
1773
|
+
const text = String(value || '').trim();
|
|
1774
|
+
if (!text)
|
|
1775
|
+
return '';
|
|
1776
|
+
const compact = text.replace(/\s+/g, '');
|
|
1777
|
+
const match = /^SR(\d+)(?:-(\d+))?$/i.exec(compact);
|
|
1778
|
+
if (!match)
|
|
1779
|
+
return '';
|
|
1780
|
+
if (match[2])
|
|
1781
|
+
return `SR${match[1]}-${match[2]}`;
|
|
1782
|
+
return `SR${match[1]}`;
|
|
1783
|
+
}
|
|
932
1784
|
toMewpComparableText(value) {
|
|
933
1785
|
if (value === null || value === undefined)
|
|
934
1786
|
return '';
|
|
@@ -945,6 +1797,9 @@ ORDER BY [System.Id]`;
|
|
|
945
1797
|
const uniqueName = value.uniqueName;
|
|
946
1798
|
if (uniqueName)
|
|
947
1799
|
return String(uniqueName).trim();
|
|
1800
|
+
const objectValue = value.value;
|
|
1801
|
+
if (objectValue !== undefined && objectValue !== null)
|
|
1802
|
+
return String(objectValue).trim();
|
|
948
1803
|
}
|
|
949
1804
|
return String(value).trim();
|
|
950
1805
|
}
|
|
@@ -2782,14 +3637,24 @@ ORDER BY [System.Id]`;
|
|
|
2782
3637
|
}
|
|
2783
3638
|
}
|
|
2784
3639
|
ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
|
|
2785
|
-
'
|
|
2786
|
-
'
|
|
2787
|
-
'
|
|
2788
|
-
'
|
|
2789
|
-
'
|
|
2790
|
-
'
|
|
2791
|
-
'
|
|
2792
|
-
'
|
|
3640
|
+
'L2 REQ ID',
|
|
3641
|
+
'L2 REQ Title',
|
|
3642
|
+
'L2 SubSystem',
|
|
3643
|
+
'L2 Run Status',
|
|
3644
|
+
'Bug ID',
|
|
3645
|
+
'Bug Title',
|
|
3646
|
+
'Bug Responsibility',
|
|
3647
|
+
'L3 REQ ID',
|
|
3648
|
+
'L3 REQ Title',
|
|
3649
|
+
'L4 REQ ID',
|
|
3650
|
+
'L4 REQ Title',
|
|
3651
|
+
];
|
|
3652
|
+
ResultDataProvider.INTERNAL_VALIDATION_COLUMNS = [
|
|
3653
|
+
'Test Case ID',
|
|
3654
|
+
'Test Case Title',
|
|
3655
|
+
'Mentioned but Not Linked',
|
|
3656
|
+
'Linked but Not Mentioned',
|
|
3657
|
+
'Validation Status',
|
|
2793
3658
|
];
|
|
2794
3659
|
exports.default = ResultDataProvider;
|
|
2795
3660
|
//# sourceMappingURL=ResultDataProvider.js.map
|