@elisra-devops/docgen-data-provider 1.72.0 → 1.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/modules/ResultDataProvider.d.ts +38 -0
- package/bin/modules/ResultDataProvider.js +688 -7
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +438 -0
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +850 -7
- package/src/tests/modules/ResultDataProvider.test.ts +555 -0
|
@@ -279,6 +279,90 @@ class ResultDataProvider {
|
|
|
279
279
|
return { planId: testPlanId, planName: '', rows: [] };
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Builds MEWP L2 requirement coverage rows for audit reporting.
|
|
284
|
+
* Rows are one Requirement-TestCase pair; uncovered requirements are emitted with empty test-case columns.
|
|
285
|
+
*/
|
|
286
|
+
async getMewpL2CoverageFlatResults(testPlanId, projectName, selectedSuiteIds, linkedQueryRequest) {
|
|
287
|
+
var _a, _b;
|
|
288
|
+
const defaultPayload = {
|
|
289
|
+
sheetName: `MEWP L2 Coverage - Plan ${testPlanId}`,
|
|
290
|
+
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
291
|
+
rows: [],
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
const planName = await this.fetchTestPlanName(testPlanId, projectName);
|
|
295
|
+
const suites = await this.fetchTestSuites(testPlanId, projectName, selectedSuiteIds, true);
|
|
296
|
+
const testData = await this.fetchTestData(suites, projectName, testPlanId, false);
|
|
297
|
+
const requirements = await this.fetchMewpL2Requirements(projectName, linkedQueryRequest);
|
|
298
|
+
if (requirements.length === 0) {
|
|
299
|
+
return Object.assign(Object.assign({}, defaultPayload), { sheetName: this.buildMewpCoverageSheetName(planName, testPlanId) });
|
|
300
|
+
}
|
|
301
|
+
const requirementIndex = new Map();
|
|
302
|
+
const observedTestCaseIdsByRequirement = new Map();
|
|
303
|
+
const requirementKeys = new Set();
|
|
304
|
+
requirements.forEach((requirement) => {
|
|
305
|
+
const key = this.toRequirementKey(requirement.requirementId);
|
|
306
|
+
if (!key)
|
|
307
|
+
return;
|
|
308
|
+
requirementKeys.add(key);
|
|
309
|
+
});
|
|
310
|
+
const parsedDefinitionStepsByTestCase = new Map();
|
|
311
|
+
const testCaseStepsXmlMap = this.buildTestCaseStepsXmlMap(testData);
|
|
312
|
+
const testCaseTitleMap = this.buildMewpTestCaseTitleMap(testData);
|
|
313
|
+
const runResults = await this.fetchAllResultDataTestReporter(testData, projectName, [], false, false);
|
|
314
|
+
for (const runResult of runResults) {
|
|
315
|
+
const testCaseId = this.extractMewpTestCaseId(runResult);
|
|
316
|
+
const runTestCaseTitle = this.toMewpComparableText(((_a = runResult === null || runResult === void 0 ? void 0 : runResult.testCase) === null || _a === void 0 ? void 0 : _a.name) || (runResult === null || runResult === void 0 ? void 0 : runResult.testCaseName) || (runResult === null || runResult === void 0 ? void 0 : runResult.testCaseTitle));
|
|
317
|
+
if (Number.isFinite(testCaseId) && testCaseId > 0 && runTestCaseTitle && !testCaseTitleMap.has(testCaseId)) {
|
|
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
|
|
322
|
+
: [];
|
|
323
|
+
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
|
+
if (actionResults.length > 0) {
|
|
325
|
+
for (const actionResult of actionResults) {
|
|
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
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
// Do not force "not run" from definition steps when a run exists:
|
|
334
|
+
// some runs may have missing/unmapped actionResults.
|
|
335
|
+
if (hasExecutedRun) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (!Number.isFinite(testCaseId))
|
|
339
|
+
continue;
|
|
340
|
+
if (!parsedDefinitionStepsByTestCase.has(testCaseId)) {
|
|
341
|
+
const stepsXml = testCaseStepsXmlMap.get(testCaseId) || '';
|
|
342
|
+
const parsed = stepsXml && String(stepsXml).trim() !== ''
|
|
343
|
+
? await this.testStepParserHelper.parseTestSteps(stepsXml, new Map())
|
|
344
|
+
: [];
|
|
345
|
+
parsedDefinitionStepsByTestCase.set(testCaseId, parsed);
|
|
346
|
+
}
|
|
347
|
+
const definitionSteps = parsedDefinitionStepsByTestCase.get(testCaseId) || [];
|
|
348
|
+
for (const step of definitionSteps) {
|
|
349
|
+
if (step === null || step === void 0 ? void 0 : step.isSharedStepTitle)
|
|
350
|
+
continue;
|
|
351
|
+
this.accumulateRequirementCountsFromStepText(`${String((step === null || step === void 0 ? void 0 : step.action) || '')} ${String((step === null || step === void 0 ? void 0 : step.expected) || '')}`, 'notRun', testCaseId, requirementKeys, requirementIndex, observedTestCaseIdsByRequirement);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const rows = this.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, testCaseTitleMap);
|
|
355
|
+
return {
|
|
356
|
+
sheetName: this.buildMewpCoverageSheetName(planName, testPlanId),
|
|
357
|
+
columnOrder: [...ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS],
|
|
358
|
+
rows,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
logger_1.default.error(`Error during getMewpL2CoverageFlatResults: ${error.message}`);
|
|
363
|
+
return defaultPayload;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
282
366
|
/**
|
|
283
367
|
* Mapping each attachment to a proper URL for downloading it
|
|
284
368
|
* @param runResults Array of run results
|
|
@@ -306,6 +390,564 @@ class ResultDataProvider {
|
|
|
306
390
|
return Object.assign({}, restResult);
|
|
307
391
|
});
|
|
308
392
|
}
|
|
393
|
+
buildMewpCoverageSheetName(planName, testPlanId) {
|
|
394
|
+
const suffix = String(planName || '').trim() || `Plan ${testPlanId}`;
|
|
395
|
+
return `MEWP L2 Coverage - ${suffix}`;
|
|
396
|
+
}
|
|
397
|
+
createMewpCoverageRow(requirement, testCaseId, testCaseTitle, stepSummary) {
|
|
398
|
+
const customerId = String(requirement.requirementId || '').trim();
|
|
399
|
+
const customerTitle = String(requirement.title || '').trim();
|
|
400
|
+
const responsibility = String(requirement.responsibility || '').trim();
|
|
401
|
+
const safeTestCaseId = Number.isFinite(testCaseId) && Number(testCaseId) > 0 ? Number(testCaseId) : '';
|
|
402
|
+
return {
|
|
403
|
+
'Customer ID': customerId,
|
|
404
|
+
'Title (Customer name)': customerTitle,
|
|
405
|
+
'Responsibility - SAPWBS (ESUK/IL)': responsibility,
|
|
406
|
+
'Test case id': safeTestCaseId,
|
|
407
|
+
'Test case title': String(testCaseTitle || '').trim(),
|
|
408
|
+
'Number of passed steps': Number.isFinite(stepSummary === null || stepSummary === void 0 ? void 0 : stepSummary.passed) ? stepSummary.passed : 0,
|
|
409
|
+
'Number of failed steps': Number.isFinite(stepSummary === null || stepSummary === void 0 ? void 0 : stepSummary.failed) ? stepSummary.failed : 0,
|
|
410
|
+
'Number of not run tests': Number.isFinite(stepSummary === null || stepSummary === void 0 ? void 0 : stepSummary.notRun) ? stepSummary.notRun : 0,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, testCaseTitleMap) {
|
|
414
|
+
var _a;
|
|
415
|
+
const rows = [];
|
|
416
|
+
for (const requirement of requirements) {
|
|
417
|
+
const key = this.toRequirementKey(requirement.requirementId);
|
|
418
|
+
const linkedTestCaseIds = ((requirement === null || requirement === void 0 ? void 0 : requirement.linkedTestCaseIds) || []).filter((id) => Number.isFinite(id) && Number(id) > 0);
|
|
419
|
+
const observedTestCaseIds = key
|
|
420
|
+
? Array.from(observedTestCaseIdsByRequirement.get(key) || [])
|
|
421
|
+
: [];
|
|
422
|
+
const testCaseIds = Array.from(new Set([...linkedTestCaseIds, ...observedTestCaseIds])).sort((a, b) => a - b);
|
|
423
|
+
if (testCaseIds.length === 0) {
|
|
424
|
+
rows.push(this.createMewpCoverageRow(requirement, undefined, '', {
|
|
425
|
+
passed: 0,
|
|
426
|
+
failed: 0,
|
|
427
|
+
notRun: 0,
|
|
428
|
+
}));
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
for (const testCaseId of testCaseIds) {
|
|
432
|
+
const summary = key
|
|
433
|
+
? ((_a = requirementIndex.get(key)) === null || _a === void 0 ? void 0 : _a.get(testCaseId)) || { passed: 0, failed: 0, notRun: 0 }
|
|
434
|
+
: { passed: 0, failed: 0, notRun: 0 };
|
|
435
|
+
rows.push(this.createMewpCoverageRow(requirement, testCaseId, String(testCaseTitleMap.get(testCaseId) || ''), summary));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return rows;
|
|
439
|
+
}
|
|
440
|
+
buildMewpTestCaseTitleMap(testData) {
|
|
441
|
+
var _a, _b, _c, _d, _e;
|
|
442
|
+
const map = new Map();
|
|
443
|
+
const readTitleFromWorkItemFields = (workItemFields) => {
|
|
444
|
+
if (!Array.isArray(workItemFields))
|
|
445
|
+
return '';
|
|
446
|
+
for (const field of workItemFields) {
|
|
447
|
+
const keyCandidates = [field === null || field === void 0 ? void 0 : field.key, field === null || field === void 0 ? void 0 : field.name, field === null || field === void 0 ? void 0 : field.referenceName, field === null || field === void 0 ? void 0 : field.id]
|
|
448
|
+
.map((item) => String(item || '').toLowerCase().trim());
|
|
449
|
+
const isTitleField = keyCandidates.includes('system.title') || keyCandidates.includes('title');
|
|
450
|
+
if (!isTitleField)
|
|
451
|
+
continue;
|
|
452
|
+
const value = this.toMewpComparableText(field === null || field === void 0 ? void 0 : field.value);
|
|
453
|
+
if (value)
|
|
454
|
+
return value;
|
|
455
|
+
}
|
|
456
|
+
return '';
|
|
457
|
+
};
|
|
458
|
+
for (const suite of testData || []) {
|
|
459
|
+
const testPointsItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testPointsItems) ? suite.testPointsItems : [];
|
|
460
|
+
for (const point of testPointsItems) {
|
|
461
|
+
const pointTestCaseId = Number((point === null || point === void 0 ? void 0 : point.testCaseId) || ((_a = point === null || point === void 0 ? void 0 : point.testCase) === null || _a === void 0 ? void 0 : _a.id));
|
|
462
|
+
if (!Number.isFinite(pointTestCaseId) || pointTestCaseId <= 0 || map.has(pointTestCaseId))
|
|
463
|
+
continue;
|
|
464
|
+
const pointTitle = this.toMewpComparableText((point === null || point === void 0 ? void 0 : point.testCaseName) || ((_b = point === null || point === void 0 ? void 0 : point.testCase) === null || _b === void 0 ? void 0 : _b.name));
|
|
465
|
+
if (pointTitle)
|
|
466
|
+
map.set(pointTestCaseId, pointTitle);
|
|
467
|
+
}
|
|
468
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
469
|
+
for (const testCase of testCasesItems) {
|
|
470
|
+
const id = Number(((_c = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _c === void 0 ? void 0 : _c.id) || (testCase === null || testCase === void 0 ? void 0 : testCase.testCaseId) || (testCase === null || testCase === void 0 ? void 0 : testCase.id));
|
|
471
|
+
if (!Number.isFinite(id) || id <= 0 || map.has(id))
|
|
472
|
+
continue;
|
|
473
|
+
const fromDirectFields = this.toMewpComparableText((testCase === null || testCase === void 0 ? void 0 : testCase.testCaseName) || (testCase === null || testCase === void 0 ? void 0 : testCase.name) || ((_d = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _d === void 0 ? void 0 : _d.name));
|
|
474
|
+
if (fromDirectFields) {
|
|
475
|
+
map.set(id, fromDirectFields);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
const fromWorkItemField = readTitleFromWorkItemFields((_e = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _e === void 0 ? void 0 : _e.workItemFields);
|
|
479
|
+
if (fromWorkItemField) {
|
|
480
|
+
map.set(id, fromWorkItemField);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return map;
|
|
485
|
+
}
|
|
486
|
+
extractMewpTestCaseId(runResult) {
|
|
487
|
+
var _a;
|
|
488
|
+
const testCaseId = Number((runResult === null || runResult === void 0 ? void 0 : runResult.testCaseId) || ((_a = runResult === null || runResult === void 0 ? void 0 : runResult.testCase) === null || _a === void 0 ? void 0 : _a.id) || 0);
|
|
489
|
+
return Number.isFinite(testCaseId) ? testCaseId : 0;
|
|
490
|
+
}
|
|
491
|
+
buildTestCaseStepsXmlMap(testData) {
|
|
492
|
+
var _a, _b;
|
|
493
|
+
const map = new Map();
|
|
494
|
+
for (const suite of testData || []) {
|
|
495
|
+
const testCasesItems = Array.isArray(suite === null || suite === void 0 ? void 0 : suite.testCasesItems) ? suite.testCasesItems : [];
|
|
496
|
+
for (const testCase of testCasesItems) {
|
|
497
|
+
const id = Number((_a = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _a === void 0 ? void 0 : _a.id);
|
|
498
|
+
if (!Number.isFinite(id))
|
|
499
|
+
continue;
|
|
500
|
+
if (map.has(id))
|
|
501
|
+
continue;
|
|
502
|
+
const fields = (_b = testCase === null || testCase === void 0 ? void 0 : testCase.workItem) === null || _b === void 0 ? void 0 : _b.workItemFields;
|
|
503
|
+
const stepsXml = this.extractStepsXmlFromWorkItemFields(fields);
|
|
504
|
+
if (stepsXml) {
|
|
505
|
+
map.set(id, stepsXml);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return map;
|
|
510
|
+
}
|
|
511
|
+
extractStepsXmlFromWorkItemFields(workItemFields) {
|
|
512
|
+
if (!Array.isArray(workItemFields))
|
|
513
|
+
return '';
|
|
514
|
+
const isStepsKey = (name) => {
|
|
515
|
+
const normalized = String(name || '').toLowerCase().trim();
|
|
516
|
+
return normalized === 'steps' || normalized === 'microsoft.vsts.tcm.steps';
|
|
517
|
+
};
|
|
518
|
+
for (const field of workItemFields) {
|
|
519
|
+
const keyCandidates = [field === null || field === void 0 ? void 0 : field.key, field === null || field === void 0 ? void 0 : field.name, field === null || field === void 0 ? void 0 : field.referenceName, field === null || field === void 0 ? void 0 : field.id];
|
|
520
|
+
const hasStepsKey = keyCandidates.some((candidate) => isStepsKey(String(candidate || '')));
|
|
521
|
+
if (!hasStepsKey)
|
|
522
|
+
continue;
|
|
523
|
+
const value = String((field === null || field === void 0 ? void 0 : field.value) || '').trim();
|
|
524
|
+
if (value)
|
|
525
|
+
return value;
|
|
526
|
+
}
|
|
527
|
+
return '';
|
|
528
|
+
}
|
|
529
|
+
classifyRequirementStepOutcome(outcome) {
|
|
530
|
+
const normalized = String(outcome || '')
|
|
531
|
+
.trim()
|
|
532
|
+
.toLowerCase();
|
|
533
|
+
if (normalized === 'passed')
|
|
534
|
+
return 'passed';
|
|
535
|
+
if (normalized === 'failed')
|
|
536
|
+
return 'failed';
|
|
537
|
+
return 'notRun';
|
|
538
|
+
}
|
|
539
|
+
accumulateRequirementCountsFromStepText(stepText, status, testCaseId, requirementKeys, counters, observedTestCaseIdsByRequirement) {
|
|
540
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
541
|
+
return;
|
|
542
|
+
const codes = this.extractRequirementCodesFromText(stepText);
|
|
543
|
+
for (const code of codes) {
|
|
544
|
+
if (requirementKeys.size > 0 && !requirementKeys.has(code))
|
|
545
|
+
continue;
|
|
546
|
+
if (!counters.has(code)) {
|
|
547
|
+
counters.set(code, new Map());
|
|
548
|
+
}
|
|
549
|
+
const perTestCaseCounters = counters.get(code);
|
|
550
|
+
if (!perTestCaseCounters.has(testCaseId)) {
|
|
551
|
+
perTestCaseCounters.set(testCaseId, { passed: 0, failed: 0, notRun: 0 });
|
|
552
|
+
}
|
|
553
|
+
if (!observedTestCaseIdsByRequirement.has(code)) {
|
|
554
|
+
observedTestCaseIdsByRequirement.set(code, new Set());
|
|
555
|
+
}
|
|
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
|
+
}
|
|
565
|
+
}
|
|
566
|
+
extractRequirementCodesFromText(text) {
|
|
567
|
+
const out = new Set();
|
|
568
|
+
const source = this.normalizeRequirementStepText(text);
|
|
569
|
+
// Supports SR<ID> patterns even when HTML formatting breaks the token,
|
|
570
|
+
// e.g. "S<b>R</b> 0 0 1" or "S R 0 0 1".
|
|
571
|
+
const regex = /S[\s\u00A0]*R(?:[\s\u00A0\-_]*\d){1,12}/gi;
|
|
572
|
+
let match = null;
|
|
573
|
+
while ((match = regex.exec(source)) !== null) {
|
|
574
|
+
const digitsOnly = String(match[0] || '').replace(/\D/g, '');
|
|
575
|
+
const digits = Number.parseInt(digitsOnly, 10);
|
|
576
|
+
if (Number.isFinite(digits)) {
|
|
577
|
+
out.add(`SR${digits}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return out;
|
|
581
|
+
}
|
|
582
|
+
normalizeRequirementStepText(text) {
|
|
583
|
+
const raw = String(text || '');
|
|
584
|
+
if (!raw)
|
|
585
|
+
return '';
|
|
586
|
+
return raw
|
|
587
|
+
.replace(/ | | /gi, ' ')
|
|
588
|
+
.replace(/</gi, '<')
|
|
589
|
+
.replace(/>/gi, '>')
|
|
590
|
+
.replace(/&/gi, '&')
|
|
591
|
+
.replace(/"/gi, '"')
|
|
592
|
+
.replace(/'|'/gi, "'")
|
|
593
|
+
.replace(/<[^>]*>/g, ' ')
|
|
594
|
+
.replace(/[\u200B-\u200D\uFEFF]/g, '')
|
|
595
|
+
.replace(/\s+/g, ' ');
|
|
596
|
+
}
|
|
597
|
+
toRequirementKey(requirementId) {
|
|
598
|
+
const normalized = this.normalizeMewpRequirementCode(requirementId);
|
|
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}`;
|
|
605
|
+
}
|
|
606
|
+
async fetchMewpL2Requirements(projectName, linkedQueryRequest) {
|
|
607
|
+
var _a;
|
|
608
|
+
const queryHref = this.extractMewpQueryHref(linkedQueryRequest);
|
|
609
|
+
if (queryHref) {
|
|
610
|
+
return this.fetchMewpL2RequirementsFromQuery(projectName, queryHref);
|
|
611
|
+
}
|
|
612
|
+
const workItemTypeNames = await this.fetchMewpRequirementTypeNames(projectName);
|
|
613
|
+
if (workItemTypeNames.length === 0) {
|
|
614
|
+
return [];
|
|
615
|
+
}
|
|
616
|
+
const quotedTypeNames = workItemTypeNames
|
|
617
|
+
.map((name) => `'${String(name).replace(/'/g, "''")}'`)
|
|
618
|
+
.join(', ');
|
|
619
|
+
const wiql = `SELECT [System.Id]
|
|
620
|
+
FROM WorkItems
|
|
621
|
+
WHERE [System.TeamProject] = @project
|
|
622
|
+
AND [System.WorkItemType] IN (${quotedTypeNames})
|
|
623
|
+
ORDER BY [System.Id]`;
|
|
624
|
+
const wiqlUrl = `${this.orgUrl}${projectName}/_apis/wit/wiql?api-version=7.1-preview.2`;
|
|
625
|
+
const wiqlResponse = await tfs_1.TFSServices.postRequest(wiqlUrl, this.token, 'Post', { query: wiql }, null);
|
|
626
|
+
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 : [];
|
|
627
|
+
const requirementIds = workItemRefs
|
|
628
|
+
.map((item) => Number(item === null || item === void 0 ? void 0 : item.id))
|
|
629
|
+
.filter((id) => Number.isFinite(id));
|
|
630
|
+
if (requirementIds.length === 0) {
|
|
631
|
+
return [];
|
|
632
|
+
}
|
|
633
|
+
const workItems = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
|
|
634
|
+
const requirements = workItems.map((wi) => {
|
|
635
|
+
const fields = (wi === null || wi === void 0 ? void 0 : wi.fields) || {};
|
|
636
|
+
return {
|
|
637
|
+
workItemId: Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0),
|
|
638
|
+
requirementId: this.extractMewpRequirementIdentifier(fields, Number((wi === null || wi === void 0 ? void 0 : wi.id) || 0)),
|
|
639
|
+
title: String(fields['System.Title'] || ''),
|
|
640
|
+
responsibility: this.deriveMewpResponsibility(fields),
|
|
641
|
+
linkedTestCaseIds: this.extractLinkedTestCaseIdsFromRequirement((wi === null || wi === void 0 ? void 0 : wi.relations) || []),
|
|
642
|
+
};
|
|
643
|
+
});
|
|
644
|
+
return requirements.sort((a, b) => String(a.requirementId).localeCompare(String(b.requirementId)));
|
|
645
|
+
}
|
|
646
|
+
extractMewpQueryHref(linkedQueryRequest) {
|
|
647
|
+
var _a, _b;
|
|
648
|
+
const mode = String((linkedQueryRequest === null || linkedQueryRequest === void 0 ? void 0 : linkedQueryRequest.linkedQueryMode) || '')
|
|
649
|
+
.trim()
|
|
650
|
+
.toLowerCase();
|
|
651
|
+
if (mode !== 'query')
|
|
652
|
+
return '';
|
|
653
|
+
return 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();
|
|
654
|
+
}
|
|
655
|
+
async fetchMewpL2RequirementsFromQuery(projectName, queryHref) {
|
|
656
|
+
try {
|
|
657
|
+
const ticketsDataProvider = new TicketsDataProvider_1.default(this.orgUrl, this.token);
|
|
658
|
+
const queryResult = await ticketsDataProvider.GetQueryResultsFromWiql(queryHref, true, new Map());
|
|
659
|
+
const requirementTypeNames = await this.fetchMewpRequirementTypeNames(projectName);
|
|
660
|
+
const requirementTypeSet = new Set(requirementTypeNames.map((name) => String(name || '').trim().toLowerCase()));
|
|
661
|
+
const requirementsById = new Map();
|
|
662
|
+
const upsertRequirement = (workItem) => {
|
|
663
|
+
this.upsertMewpRequirement(requirementsById, workItem, requirementTypeSet);
|
|
664
|
+
};
|
|
665
|
+
const linkRequirementToTestCase = (requirementWorkItem, testCaseWorkItem) => {
|
|
666
|
+
const requirementId = Number((requirementWorkItem === null || requirementWorkItem === void 0 ? void 0 : requirementWorkItem.id) || 0);
|
|
667
|
+
const testCaseId = Number((testCaseWorkItem === null || testCaseWorkItem === void 0 ? void 0 : testCaseWorkItem.id) || 0);
|
|
668
|
+
if (!Number.isFinite(requirementId) || requirementId <= 0)
|
|
669
|
+
return;
|
|
670
|
+
if (!Number.isFinite(testCaseId) || testCaseId <= 0)
|
|
671
|
+
return;
|
|
672
|
+
upsertRequirement(requirementWorkItem);
|
|
673
|
+
const requirement = requirementsById.get(requirementId);
|
|
674
|
+
if (!requirement)
|
|
675
|
+
return;
|
|
676
|
+
requirement.linkedTestCaseIds.add(testCaseId);
|
|
677
|
+
};
|
|
678
|
+
if (Array.isArray(queryResult === null || queryResult === void 0 ? void 0 : queryResult.fetchedWorkItems)) {
|
|
679
|
+
for (const workItem of queryResult.fetchedWorkItems) {
|
|
680
|
+
upsertRequirement(workItem);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if ((queryResult === null || queryResult === void 0 ? void 0 : queryResult.sourceTargetsMap) && typeof queryResult.sourceTargetsMap.entries === 'function') {
|
|
684
|
+
for (const [sourceItem, targets] of queryResult.sourceTargetsMap.entries()) {
|
|
685
|
+
const sourceType = this.getMewpWorkItemType(sourceItem);
|
|
686
|
+
const sourceIsRequirement = this.isMewpRequirementType(sourceType, requirementTypeSet);
|
|
687
|
+
const sourceIsTestCase = this.isMewpTestCaseType(sourceType);
|
|
688
|
+
if (sourceIsRequirement) {
|
|
689
|
+
upsertRequirement(sourceItem);
|
|
690
|
+
}
|
|
691
|
+
const relatedItems = Array.isArray(targets) ? targets : [];
|
|
692
|
+
for (const targetItem of relatedItems) {
|
|
693
|
+
const targetType = this.getMewpWorkItemType(targetItem);
|
|
694
|
+
const targetIsRequirement = this.isMewpRequirementType(targetType, requirementTypeSet);
|
|
695
|
+
const targetIsTestCase = this.isMewpTestCaseType(targetType);
|
|
696
|
+
if (targetIsRequirement) {
|
|
697
|
+
upsertRequirement(targetItem);
|
|
698
|
+
}
|
|
699
|
+
if (sourceIsRequirement && targetIsTestCase) {
|
|
700
|
+
linkRequirementToTestCase(sourceItem, targetItem);
|
|
701
|
+
}
|
|
702
|
+
else if (sourceIsTestCase && targetIsRequirement) {
|
|
703
|
+
linkRequirementToTestCase(targetItem, sourceItem);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
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
|
+
}
|
|
719
|
+
catch (error) {
|
|
720
|
+
logger_1.default.error(`Could not fetch MEWP requirements from query: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
|
|
721
|
+
return [];
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
upsertMewpRequirement(requirementsById, workItem, requirementTypeSet) {
|
|
725
|
+
const workItemId = Number((workItem === null || workItem === void 0 ? void 0 : workItem.id) || 0);
|
|
726
|
+
if (!Number.isFinite(workItemId) || workItemId <= 0)
|
|
727
|
+
return;
|
|
728
|
+
const fields = (workItem === null || workItem === void 0 ? void 0 : workItem.fields) || {};
|
|
729
|
+
const workItemType = this.getMewpWorkItemType(workItem);
|
|
730
|
+
if (!this.isMewpRequirementType(workItemType, requirementTypeSet))
|
|
731
|
+
return;
|
|
732
|
+
const existing = requirementsById.get(workItemId) || {
|
|
733
|
+
workItemId,
|
|
734
|
+
requirementId: String(workItemId),
|
|
735
|
+
title: '',
|
|
736
|
+
responsibility: '',
|
|
737
|
+
linkedTestCaseIds: new Set(),
|
|
738
|
+
};
|
|
739
|
+
const extractedRequirementId = this.extractMewpRequirementIdentifier(fields, workItemId);
|
|
740
|
+
const extractedTitle = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
741
|
+
const extractedResponsibility = this.deriveMewpResponsibility(fields);
|
|
742
|
+
existing.requirementId = extractedRequirementId || existing.requirementId || String(workItemId);
|
|
743
|
+
if (extractedTitle) {
|
|
744
|
+
existing.title = extractedTitle;
|
|
745
|
+
}
|
|
746
|
+
if (extractedResponsibility) {
|
|
747
|
+
existing.responsibility = extractedResponsibility;
|
|
748
|
+
}
|
|
749
|
+
requirementsById.set(workItemId, existing);
|
|
750
|
+
}
|
|
751
|
+
async hydrateMewpRequirementsFromWorkItems(projectName, requirementsById) {
|
|
752
|
+
const requirementIds = [...requirementsById.keys()];
|
|
753
|
+
if (requirementIds.length === 0)
|
|
754
|
+
return;
|
|
755
|
+
const fetchedRequirements = await this.fetchWorkItemsByIds(projectName, requirementIds, true);
|
|
756
|
+
for (const requirementWorkItem of fetchedRequirements) {
|
|
757
|
+
const workItemId = Number((requirementWorkItem === null || requirementWorkItem === void 0 ? void 0 : requirementWorkItem.id) || 0);
|
|
758
|
+
if (!Number.isFinite(workItemId) || workItemId <= 0)
|
|
759
|
+
continue;
|
|
760
|
+
const current = requirementsById.get(workItemId);
|
|
761
|
+
if (!current)
|
|
762
|
+
continue;
|
|
763
|
+
const fields = (requirementWorkItem === null || requirementWorkItem === void 0 ? void 0 : requirementWorkItem.fields) || {};
|
|
764
|
+
const requirementId = this.extractMewpRequirementIdentifier(fields, workItemId);
|
|
765
|
+
const title = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
766
|
+
const responsibility = this.deriveMewpResponsibility(fields);
|
|
767
|
+
const linkedTestCaseIds = this.extractLinkedTestCaseIdsFromRequirement((requirementWorkItem === null || requirementWorkItem === void 0 ? void 0 : requirementWorkItem.relations) || []);
|
|
768
|
+
current.requirementId = requirementId || current.requirementId || String(workItemId);
|
|
769
|
+
if (title) {
|
|
770
|
+
current.title = title;
|
|
771
|
+
}
|
|
772
|
+
if (responsibility) {
|
|
773
|
+
current.responsibility = responsibility;
|
|
774
|
+
}
|
|
775
|
+
linkedTestCaseIds.forEach((testCaseId) => current.linkedTestCaseIds.add(testCaseId));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
getMewpWorkItemType(workItem) {
|
|
779
|
+
var _a;
|
|
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 || '')
|
|
784
|
+
.trim()
|
|
785
|
+
.toLowerCase();
|
|
786
|
+
if (!normalized)
|
|
787
|
+
return false;
|
|
788
|
+
if (requirementTypeSet.has(normalized))
|
|
789
|
+
return true;
|
|
790
|
+
return normalized.includes('requirement') || normalized === 'epic';
|
|
791
|
+
}
|
|
792
|
+
isMewpTestCaseType(workItemType) {
|
|
793
|
+
const normalized = String(workItemType || '')
|
|
794
|
+
.trim()
|
|
795
|
+
.toLowerCase();
|
|
796
|
+
return normalized === 'test case' || normalized === 'testcase';
|
|
797
|
+
}
|
|
798
|
+
async fetchMewpRequirementTypeNames(projectName) {
|
|
799
|
+
try {
|
|
800
|
+
const url = `${this.orgUrl}${projectName}/_apis/wit/workitemtypes?api-version=7.1-preview.2`;
|
|
801
|
+
const result = await tfs_1.TFSServices.getItemContent(url, this.token);
|
|
802
|
+
const values = Array.isArray(result === null || result === void 0 ? void 0 : result.value) ? result.value : [];
|
|
803
|
+
const matched = values
|
|
804
|
+
.map((item) => String((item === null || item === void 0 ? void 0 : item.name) || ''))
|
|
805
|
+
.filter((name) => /requirement/i.test(name) || /^epic$/i.test(name));
|
|
806
|
+
const unique = Array.from(new Set(matched));
|
|
807
|
+
if (unique.length > 0) {
|
|
808
|
+
return unique;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
logger_1.default.debug(`Could not fetch MEWP work item types, using defaults: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
|
|
813
|
+
}
|
|
814
|
+
return ['Requirement', 'Epic'];
|
|
815
|
+
}
|
|
816
|
+
async fetchWorkItemsByIds(projectName, workItemIds, includeRelations) {
|
|
817
|
+
const ids = [...new Set(workItemIds.filter((id) => Number.isFinite(id)))];
|
|
818
|
+
if (ids.length === 0)
|
|
819
|
+
return [];
|
|
820
|
+
const CHUNK_SIZE = 200;
|
|
821
|
+
const allItems = [];
|
|
822
|
+
for (let i = 0; i < ids.length; i += CHUNK_SIZE) {
|
|
823
|
+
const chunk = ids.slice(i, i + CHUNK_SIZE);
|
|
824
|
+
const idsQuery = chunk.join(',');
|
|
825
|
+
const expandParam = includeRelations ? '&$expand=relations' : '';
|
|
826
|
+
const url = `${this.orgUrl}${projectName}/_apis/wit/workitems?ids=${idsQuery}${expandParam}&api-version=7.1-preview.3`;
|
|
827
|
+
const response = await tfs_1.TFSServices.getItemContent(url, this.token);
|
|
828
|
+
const values = Array.isArray(response === null || response === void 0 ? void 0 : response.value) ? response.value : [];
|
|
829
|
+
allItems.push(...values);
|
|
830
|
+
}
|
|
831
|
+
return allItems;
|
|
832
|
+
}
|
|
833
|
+
extractLinkedTestCaseIdsFromRequirement(relations) {
|
|
834
|
+
const out = new Set();
|
|
835
|
+
for (const relation of Array.isArray(relations) ? relations : []) {
|
|
836
|
+
const rel = String((relation === null || relation === void 0 ? void 0 : relation.rel) || '')
|
|
837
|
+
.trim()
|
|
838
|
+
.toLowerCase();
|
|
839
|
+
const isRequirementToTestLink = rel.includes('testedby') || rel.includes('.tests');
|
|
840
|
+
if (!isRequirementToTestLink)
|
|
841
|
+
continue;
|
|
842
|
+
const url = String((relation === null || relation === void 0 ? void 0 : relation.url) || '');
|
|
843
|
+
const match = /\/workItems\/(\d+)/i.exec(url);
|
|
844
|
+
if (!match)
|
|
845
|
+
continue;
|
|
846
|
+
const id = Number(match[1]);
|
|
847
|
+
if (Number.isFinite(id))
|
|
848
|
+
out.add(id);
|
|
849
|
+
}
|
|
850
|
+
return [...out].sort((a, b) => a - b);
|
|
851
|
+
}
|
|
852
|
+
extractMewpRequirementIdentifier(fields, fallbackWorkItemId) {
|
|
853
|
+
const entries = Object.entries(fields || {});
|
|
854
|
+
// First pass: only trusted identifier-like fields.
|
|
855
|
+
const strictHints = [
|
|
856
|
+
'customerid',
|
|
857
|
+
'customer id',
|
|
858
|
+
'customerrequirementid',
|
|
859
|
+
'requirementid',
|
|
860
|
+
'externalid',
|
|
861
|
+
'srid',
|
|
862
|
+
'sapwbsid',
|
|
863
|
+
];
|
|
864
|
+
for (const [key, value] of entries) {
|
|
865
|
+
const normalizedKey = String(key || '').toLowerCase();
|
|
866
|
+
if (!strictHints.some((hint) => normalizedKey.includes(hint)))
|
|
867
|
+
continue;
|
|
868
|
+
const valueAsString = this.toMewpComparableText(value);
|
|
869
|
+
if (!valueAsString)
|
|
870
|
+
continue;
|
|
871
|
+
const normalized = this.normalizeMewpRequirementCode(valueAsString);
|
|
872
|
+
if (normalized)
|
|
873
|
+
return normalized;
|
|
874
|
+
}
|
|
875
|
+
// Second pass: weaker hints, but still key-based only.
|
|
876
|
+
const looseHints = ['customer', 'requirement', 'external', 'sapwbs', 'sr'];
|
|
877
|
+
for (const [key, value] of entries) {
|
|
878
|
+
const normalizedKey = String(key || '').toLowerCase();
|
|
879
|
+
if (!looseHints.some((hint) => normalizedKey.includes(hint)))
|
|
880
|
+
continue;
|
|
881
|
+
const valueAsString = this.toMewpComparableText(value);
|
|
882
|
+
if (!valueAsString)
|
|
883
|
+
continue;
|
|
884
|
+
const normalized = this.normalizeMewpRequirementCode(valueAsString);
|
|
885
|
+
if (normalized)
|
|
886
|
+
return normalized;
|
|
887
|
+
}
|
|
888
|
+
// Optional fallback from title only (avoid scanning all fields and accidental SR matches).
|
|
889
|
+
const title = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.Title']);
|
|
890
|
+
const titleCode = this.normalizeMewpRequirementCode(title);
|
|
891
|
+
if (titleCode)
|
|
892
|
+
return titleCode;
|
|
893
|
+
return String(fallbackWorkItemId || '');
|
|
894
|
+
}
|
|
895
|
+
deriveMewpResponsibility(fields) {
|
|
896
|
+
const areaPath = this.toMewpComparableText(fields === null || fields === void 0 ? void 0 : fields['System.AreaPath']);
|
|
897
|
+
const fromAreaPath = this.resolveMewpResponsibility(areaPath);
|
|
898
|
+
if (fromAreaPath)
|
|
899
|
+
return fromAreaPath;
|
|
900
|
+
const keyHints = ['sapwbs', 'responsibility', 'owner'];
|
|
901
|
+
for (const [key, value] of Object.entries(fields || {})) {
|
|
902
|
+
const normalizedKey = String(key || '').toLowerCase();
|
|
903
|
+
if (!keyHints.some((hint) => normalizedKey.includes(hint)))
|
|
904
|
+
continue;
|
|
905
|
+
const resolved = this.resolveMewpResponsibility(this.toMewpComparableText(value));
|
|
906
|
+
if (resolved)
|
|
907
|
+
return resolved;
|
|
908
|
+
}
|
|
909
|
+
return '';
|
|
910
|
+
}
|
|
911
|
+
resolveMewpResponsibility(value) {
|
|
912
|
+
const text = String(value || '')
|
|
913
|
+
.trim()
|
|
914
|
+
.toLowerCase();
|
|
915
|
+
if (!text)
|
|
916
|
+
return '';
|
|
917
|
+
if (/(^|[^a-z0-9])esuk([^a-z0-9]|$)/i.test(text))
|
|
918
|
+
return 'ESUK';
|
|
919
|
+
if (/(^|[^a-z0-9])il([^a-z0-9]|$)/i.test(text))
|
|
920
|
+
return 'IL';
|
|
921
|
+
return '';
|
|
922
|
+
}
|
|
923
|
+
normalizeMewpRequirementCode(value) {
|
|
924
|
+
const text = String(value || '').trim();
|
|
925
|
+
if (!text)
|
|
926
|
+
return '';
|
|
927
|
+
const match = /\bSR[\s\-_]*([0-9]+)\b/i.exec(text);
|
|
928
|
+
if (!match)
|
|
929
|
+
return '';
|
|
930
|
+
return `SR${match[1]}`;
|
|
931
|
+
}
|
|
932
|
+
toMewpComparableText(value) {
|
|
933
|
+
if (value === null || value === undefined)
|
|
934
|
+
return '';
|
|
935
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
936
|
+
return String(value).trim();
|
|
937
|
+
}
|
|
938
|
+
if (typeof value === 'object') {
|
|
939
|
+
const displayName = value.displayName;
|
|
940
|
+
if (displayName)
|
|
941
|
+
return String(displayName).trim();
|
|
942
|
+
const name = value.name;
|
|
943
|
+
if (name)
|
|
944
|
+
return String(name).trim();
|
|
945
|
+
const uniqueName = value.uniqueName;
|
|
946
|
+
if (uniqueName)
|
|
947
|
+
return String(uniqueName).trim();
|
|
948
|
+
}
|
|
949
|
+
return String(value).trim();
|
|
950
|
+
}
|
|
309
951
|
async fetchTestPlanName(testPlanId, teamProject) {
|
|
310
952
|
try {
|
|
311
953
|
const url = `${this.orgUrl}${teamProject}/_apis/testplan/Plans/${testPlanId}?api-version=5.1`;
|
|
@@ -1444,11 +2086,19 @@ class ResultDataProvider {
|
|
|
1444
2086
|
try {
|
|
1445
2087
|
const { lastRunId, lastResultId } = point;
|
|
1446
2088
|
const resultData = await fetchResultMethod(projectName, (lastRunId === null || lastRunId === void 0 ? void 0 : lastRunId.toString()) || '0', (lastResultId === null || lastResultId === void 0 ? void 0 : lastResultId.toString()) || '0', ...additionalArgs);
|
|
1447
|
-
|
|
2089
|
+
let iteration = ((_a = resultData.iterationDetails) === null || _a === void 0 ? void 0 : _a.length) > 0
|
|
1448
2090
|
? resultData.iterationDetails[resultData.iterationDetails.length - 1]
|
|
1449
2091
|
: undefined;
|
|
2092
|
+
if (resultData.stepsResultXml && !iteration) {
|
|
2093
|
+
iteration = { actionResults: [] };
|
|
2094
|
+
if (!Array.isArray(resultData.iterationDetails)) {
|
|
2095
|
+
resultData.iterationDetails = [];
|
|
2096
|
+
}
|
|
2097
|
+
resultData.iterationDetails.push(iteration);
|
|
2098
|
+
}
|
|
1450
2099
|
if (resultData.stepsResultXml && iteration) {
|
|
1451
|
-
const
|
|
2100
|
+
const actionResults = Array.isArray(iteration.actionResults) ? iteration.actionResults : [];
|
|
2101
|
+
const actionResultsWithSharedModels = actionResults.filter((result) => result.sharedStepModel);
|
|
1452
2102
|
const sharedStepIdToRevisionLookupMap = new Map();
|
|
1453
2103
|
if ((actionResultsWithSharedModels === null || actionResultsWithSharedModels === void 0 ? void 0 : actionResultsWithSharedModels.length) > 0) {
|
|
1454
2104
|
actionResultsWithSharedModels.forEach((actionResult) => {
|
|
@@ -1462,7 +2112,7 @@ class ResultDataProvider {
|
|
|
1462
2112
|
for (const step of stepsList) {
|
|
1463
2113
|
stepMap.set(step.stepId.toString(), step);
|
|
1464
2114
|
}
|
|
1465
|
-
for (const actionResult of
|
|
2115
|
+
for (const actionResult of actionResults) {
|
|
1466
2116
|
const step = stepMap.get(actionResult.stepIdentifier);
|
|
1467
2117
|
if (step) {
|
|
1468
2118
|
actionResult.stepPosition = step.stepPosition;
|
|
@@ -1471,10 +2121,31 @@ class ResultDataProvider {
|
|
|
1471
2121
|
actionResult.isSharedStepTitle = step.isSharedStepTitle;
|
|
1472
2122
|
}
|
|
1473
2123
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
.
|
|
1477
|
-
|
|
2124
|
+
if (actionResults.length > 0) {
|
|
2125
|
+
// Sort mapped action results by logical step position.
|
|
2126
|
+
iteration.actionResults = actionResults
|
|
2127
|
+
.filter((result) => result.stepPosition)
|
|
2128
|
+
.sort((a, b) => this.compareActionResults(a.stepPosition, b.stepPosition));
|
|
2129
|
+
}
|
|
2130
|
+
else {
|
|
2131
|
+
// Fallback for runs that have no action results: emit test definition steps as Not Run.
|
|
2132
|
+
iteration.actionResults = stepsList
|
|
2133
|
+
.filter((step) => step === null || step === void 0 ? void 0 : step.stepPosition)
|
|
2134
|
+
.map((step) => {
|
|
2135
|
+
var _a, _b, _c;
|
|
2136
|
+
return ({
|
|
2137
|
+
stepIdentifier: String((_b = (_a = step === null || step === void 0 ? void 0 : step.stepId) !== null && _a !== void 0 ? _a : step === null || step === void 0 ? void 0 : step.stepPosition) !== null && _b !== void 0 ? _b : ''),
|
|
2138
|
+
stepPosition: step.stepPosition,
|
|
2139
|
+
action: step.action,
|
|
2140
|
+
expected: step.expected,
|
|
2141
|
+
isSharedStepTitle: step.isSharedStepTitle,
|
|
2142
|
+
outcome: 'Unspecified',
|
|
2143
|
+
errorMessage: '',
|
|
2144
|
+
actionPath: String((_c = step === null || step === void 0 ? void 0 : step.stepPosition) !== null && _c !== void 0 ? _c : ''),
|
|
2145
|
+
});
|
|
2146
|
+
})
|
|
2147
|
+
.sort((a, b) => this.compareActionResults(a.stepPosition, b.stepPosition));
|
|
2148
|
+
}
|
|
1478
2149
|
}
|
|
1479
2150
|
return (resultData === null || resultData === void 0 ? void 0 : resultData.testCase)
|
|
1480
2151
|
? createResponseObject(resultData, testSuiteId, point, ...additionalArgs)
|
|
@@ -2110,5 +2781,15 @@ class ResultDataProvider {
|
|
|
2110
2781
|
return customFields;
|
|
2111
2782
|
}
|
|
2112
2783
|
}
|
|
2784
|
+
ResultDataProvider.MEWP_L2_COVERAGE_COLUMNS = [
|
|
2785
|
+
'Customer ID',
|
|
2786
|
+
'Title (Customer name)',
|
|
2787
|
+
'Responsibility - SAPWBS (ESUK/IL)',
|
|
2788
|
+
'Test case id',
|
|
2789
|
+
'Test case title',
|
|
2790
|
+
'Number of passed steps',
|
|
2791
|
+
'Number of failed steps',
|
|
2792
|
+
'Number of not run tests',
|
|
2793
|
+
];
|
|
2113
2794
|
exports.default = ResultDataProvider;
|
|
2114
2795
|
//# sourceMappingURL=ResultDataProvider.js.map
|