@elisra-devops/docgen-data-provider 1.29.2 → 1.30.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 +19 -2
- package/bin/modules/ResultDataProvider.js +172 -92
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/modules/TicketsDataProvider.d.ts +14 -0
- package/bin/modules/TicketsDataProvider.js +46 -4
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +229 -89
- package/src/modules/TicketsDataProvider.ts +59 -4
|
@@ -3,6 +3,7 @@ import { TFSServices } from '../helpers/tfs';
|
|
|
3
3
|
import { OpenPcrRequest, TestSteps } from '../models/tfs-data';
|
|
4
4
|
import logger from '../utils/logger';
|
|
5
5
|
import Utils from '../utils/testStepParserHelper';
|
|
6
|
+
import TicketsDataProvider from './TicketsDataProvider';
|
|
6
7
|
const pLimit = require('p-limit');
|
|
7
8
|
/**
|
|
8
9
|
* Provides methods to fetch, process, and summarize test data from Azure DevOps.
|
|
@@ -31,10 +32,12 @@ export default class ResultDataProvider {
|
|
|
31
32
|
token: string = '';
|
|
32
33
|
private limit = pLimit(10);
|
|
33
34
|
private testStepParserHelper: Utils;
|
|
35
|
+
private testToAssociatedItemMap: Map<number, Set<any>>;
|
|
34
36
|
constructor(orgUrl: string, token: string) {
|
|
35
37
|
this.orgUrl = orgUrl;
|
|
36
38
|
this.token = token;
|
|
37
39
|
this.testStepParserHelper = new Utils(orgUrl, token);
|
|
40
|
+
this.testToAssociatedItemMap = new Map<number, Set<any>>();
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/**
|
|
@@ -222,7 +225,9 @@ export default class ResultDataProvider {
|
|
|
222
225
|
selectedFields: string[],
|
|
223
226
|
allowCrossTestPlan: boolean,
|
|
224
227
|
enableRunTestCaseFilter: boolean,
|
|
225
|
-
enableRunStepStatusFilter: boolean
|
|
228
|
+
enableRunStepStatusFilter: boolean,
|
|
229
|
+
linkedQueryRequest: any,
|
|
230
|
+
errorFilterMode: string = 'none'
|
|
226
231
|
) {
|
|
227
232
|
const fetchedTestResults: any[] = [];
|
|
228
233
|
logger.debug(
|
|
@@ -230,6 +235,7 @@ export default class ResultDataProvider {
|
|
|
230
235
|
);
|
|
231
236
|
logger.debug(`Selected suite IDs: ${selectedSuiteIds}`);
|
|
232
237
|
try {
|
|
238
|
+
const ticketsDataProvider = new TicketsDataProvider(this.orgUrl, this.token);
|
|
233
239
|
logger.debug(`Fetching Plan info for test plan ID: ${testPlanId}, project name: ${projectName}`);
|
|
234
240
|
const plan = await this.fetchTestPlanName(testPlanId, projectName);
|
|
235
241
|
logger.debug(`Fetching Test suites for test plan ID: ${testPlanId}, project name: ${projectName}`);
|
|
@@ -237,7 +243,22 @@ export default class ResultDataProvider {
|
|
|
237
243
|
logger.debug(`Fetching test data for test plan ID: ${testPlanId}, project name: ${projectName}`);
|
|
238
244
|
const testData = await this.fetchTestData(suites, projectName, testPlanId, allowCrossTestPlan);
|
|
239
245
|
logger.debug(`Fetching Run results for test data, project name: ${projectName}`);
|
|
240
|
-
const
|
|
246
|
+
const isQueryMode = linkedQueryRequest.linkedQueryMode === 'query';
|
|
247
|
+
if (isQueryMode) {
|
|
248
|
+
// Fetch associated items
|
|
249
|
+
await ticketsDataProvider.GetQueryResultsFromWiql(
|
|
250
|
+
linkedQueryRequest.testAssociatedQuery.wiql.href,
|
|
251
|
+
true,
|
|
252
|
+
this.testToAssociatedItemMap
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const runResults = await this.fetchAllResultDataTestReporter(
|
|
257
|
+
testData,
|
|
258
|
+
projectName,
|
|
259
|
+
selectedFields,
|
|
260
|
+
isQueryMode
|
|
261
|
+
);
|
|
241
262
|
logger.debug(`Aligning steps with iterations for test reporter results`);
|
|
242
263
|
const testReporterData = this.alignStepsWithIterationsTestReporter(
|
|
243
264
|
testData,
|
|
@@ -247,6 +268,31 @@ export default class ResultDataProvider {
|
|
|
247
268
|
);
|
|
248
269
|
// Apply filters sequentially based on enabled flags
|
|
249
270
|
let filteredResults = testReporterData;
|
|
271
|
+
//TODO: think of a way to filter out the fields that are not selected from this part of the code and not from the fetchAllResultDataTestReporter function
|
|
272
|
+
if (errorFilterMode !== 'none') {
|
|
273
|
+
const onlyFailedTestCaseCondition = (result: any) =>
|
|
274
|
+
result && result.testCase?.result?.resultMessage?.includes('Failed');
|
|
275
|
+
const onlyFailedTestStepsCondition = (result: any) => result && result.stepStatus === 'Failed';
|
|
276
|
+
|
|
277
|
+
switch (errorFilterMode) {
|
|
278
|
+
case 'onlyTestCaseResult':
|
|
279
|
+
logger.debug(`Filtering test reporter results for only test case result`);
|
|
280
|
+
filteredResults = filteredResults.filter(onlyFailedTestCaseCondition);
|
|
281
|
+
break;
|
|
282
|
+
case 'onlyTestStepsResult':
|
|
283
|
+
logger.debug(`Filtering test reporter results for only test steps result`);
|
|
284
|
+
filteredResults = filteredResults.filter(onlyFailedTestStepsCondition);
|
|
285
|
+
break;
|
|
286
|
+
case 'both':
|
|
287
|
+
logger.debug(`Filtering test reporter results for both test case and test steps result`);
|
|
288
|
+
filteredResults = filteredResults
|
|
289
|
+
?.filter(onlyFailedTestCaseCondition)
|
|
290
|
+
?.filter(onlyFailedTestStepsCondition);
|
|
291
|
+
break;
|
|
292
|
+
default:
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
250
296
|
|
|
251
297
|
// filter: Test step run status
|
|
252
298
|
if (enableRunStepStatusFilter) {
|
|
@@ -575,12 +621,27 @@ export default class ResultDataProvider {
|
|
|
575
621
|
return testCases;
|
|
576
622
|
}
|
|
577
623
|
|
|
624
|
+
/**
|
|
625
|
+
* Fetches result data based on the Work Item Test Reporter.
|
|
626
|
+
*
|
|
627
|
+
* This method retrieves detailed result data for a specific test run and result ID,
|
|
628
|
+
* including related work items, selected fields, and additional processing options.
|
|
629
|
+
*
|
|
630
|
+
* @param projectName - The name of the project containing the test run.
|
|
631
|
+
* @param runId - The unique identifier of the test run.
|
|
632
|
+
* @param resultId - The unique identifier of the test result.
|
|
633
|
+
* @param isTestReporter - (Optional) A flag indicating whether the result data is being fetched for the Test Reporter.
|
|
634
|
+
* @param selectedFields - (Optional) An array of field names to include in the result data.
|
|
635
|
+
* @param isQueryMode - (Optional) A flag indicating whether the result data is being fetched in query mode.
|
|
636
|
+
* @returns A promise that resolves to the fetched result data.
|
|
637
|
+
*/
|
|
578
638
|
private async fetchResultDataBasedOnWiBase(
|
|
579
639
|
projectName: string,
|
|
580
640
|
runId: string,
|
|
581
641
|
resultId: string,
|
|
582
642
|
isTestReporter: boolean = false,
|
|
583
|
-
selectedFields?: string[]
|
|
643
|
+
selectedFields?: string[],
|
|
644
|
+
isQueryMode?: boolean
|
|
584
645
|
): Promise<any> {
|
|
585
646
|
try {
|
|
586
647
|
if (runId === '0' || resultId === '0') {
|
|
@@ -602,54 +663,34 @@ export default class ResultDataProvider {
|
|
|
602
663
|
let relatedBugs: any[] = [];
|
|
603
664
|
let relatedCRs: any[] = [];
|
|
604
665
|
// Process selected fields if provided
|
|
605
|
-
if (
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
666
|
+
if (isTestReporter) {
|
|
667
|
+
// Process related requirements if needed
|
|
668
|
+
if (isQueryMode) {
|
|
669
|
+
this.appendQueryRelations(resultData, relatedRequirements, relatedBugs, relatedCRs);
|
|
670
|
+
} else {
|
|
671
|
+
const filteredLinkedFields = selectedFields
|
|
672
|
+
?.filter((field: string) => field.includes('@linked'))
|
|
673
|
+
?.map((field: string) => field.split('@')[0]);
|
|
674
|
+
const selectedLinkedFieldSet = new Set(filteredLinkedFields);
|
|
613
675
|
const { relations } = wiByRevision;
|
|
614
676
|
if (relations) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (wi.fields['System.WorkItemType'] === 'Requirement') {
|
|
624
|
-
const { id, fields, _links } = wi;
|
|
625
|
-
const requirementTitle = fields['System.Title'];
|
|
626
|
-
const customerFieldKey = Object.keys(fields).find((key) =>
|
|
627
|
-
key.toLowerCase().includes('customer')
|
|
628
|
-
);
|
|
629
|
-
const customerId = customerFieldKey ? fields[customerFieldKey] : undefined;
|
|
630
|
-
const url = _links.html.href;
|
|
631
|
-
relatedRequirements.push({ id, requirementTitle, customerId, url });
|
|
632
|
-
} else if (wi.fields['System.WorkItemType'] === 'Bug') {
|
|
633
|
-
const { id, fields, _links } = wi;
|
|
634
|
-
const bugTitle = fields['System.Title'];
|
|
635
|
-
const url = _links.html.href;
|
|
636
|
-
relatedBugs.push({ id, bugTitle, url });
|
|
637
|
-
} else if (wi.fields['System.WorkItemType'] === 'Change Request') {
|
|
638
|
-
const { id, fields, _links } = wi;
|
|
639
|
-
const crTitle = fields['System.Title'];
|
|
640
|
-
const url = _links.html.href;
|
|
641
|
-
relatedCRs.push({ id, crTitle, url });
|
|
642
|
-
}
|
|
643
|
-
} catch (err: any) {
|
|
644
|
-
logger.error(
|
|
645
|
-
`Could not append related work item to test case ${wiByRevision.id}: ${err.message}`
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
}
|
|
677
|
+
await this.appendLinkedRelations(
|
|
678
|
+
relations,
|
|
679
|
+
relatedRequirements,
|
|
680
|
+
relatedBugs,
|
|
681
|
+
relatedCRs,
|
|
682
|
+
wiByRevision,
|
|
683
|
+
selectedLinkedFieldSet
|
|
684
|
+
);
|
|
650
685
|
}
|
|
651
|
-
|
|
652
|
-
|
|
686
|
+
selectedLinkedFieldSet.clear();
|
|
687
|
+
}
|
|
688
|
+
const filteredTestCaseFields = selectedFields
|
|
689
|
+
?.filter((field: string) => field.includes('@testCaseWorkItemField'))
|
|
690
|
+
?.map((field: string) => field.split('@')[0]);
|
|
691
|
+
const selectedFieldSet = new Set(filteredTestCaseFields);
|
|
692
|
+
// Filter fields based on selected field set
|
|
693
|
+
if (selectedFieldSet.size !== 0) {
|
|
653
694
|
filteredFields = Object.keys(wiByRevision.fields)
|
|
654
695
|
.filter((key) => selectedFieldSet.has(key))
|
|
655
696
|
.reduce((obj: any, key) => {
|
|
@@ -657,6 +698,7 @@ export default class ResultDataProvider {
|
|
|
657
698
|
return obj;
|
|
658
699
|
}, {});
|
|
659
700
|
}
|
|
701
|
+
selectedFieldSet.clear();
|
|
660
702
|
}
|
|
661
703
|
return {
|
|
662
704
|
...resultData,
|
|
@@ -681,6 +723,95 @@ export default class ResultDataProvider {
|
|
|
681
723
|
return result && result.stepStatus === 'Not Run';
|
|
682
724
|
};
|
|
683
725
|
|
|
726
|
+
private appendQueryRelations(
|
|
727
|
+
resultData: any,
|
|
728
|
+
relatedRequirements: any[],
|
|
729
|
+
relatedBugs: any[],
|
|
730
|
+
relatedCRs: any[]
|
|
731
|
+
) {
|
|
732
|
+
if (this.testToAssociatedItemMap.size !== 0) {
|
|
733
|
+
const relatedItemSet = this.testToAssociatedItemMap.get(Number(resultData.testCase.id));
|
|
734
|
+
if (relatedItemSet) {
|
|
735
|
+
for (const relatedItem of relatedItemSet) {
|
|
736
|
+
const { id, fields, _links } = relatedItem;
|
|
737
|
+
let url = '';
|
|
738
|
+
switch (fields['System.WorkItemType']) {
|
|
739
|
+
case 'Requirement':
|
|
740
|
+
const requirementTitle = fields['System.Title'];
|
|
741
|
+
url = _links.html.href;
|
|
742
|
+
relatedRequirements.push({ id, requirementTitle, url });
|
|
743
|
+
break;
|
|
744
|
+
case 'Bug':
|
|
745
|
+
const bugTitle = fields['System.Title'];
|
|
746
|
+
url = _links.html.href;
|
|
747
|
+
relatedBugs.push({ id, bugTitle, url });
|
|
748
|
+
break;
|
|
749
|
+
case 'Change Request':
|
|
750
|
+
const crTitle = fields['System.Title'];
|
|
751
|
+
url = _links.html.href;
|
|
752
|
+
relatedCRs.push({ id, crTitle, url });
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
private async appendLinkedRelations(
|
|
761
|
+
relations: any,
|
|
762
|
+
relatedRequirements: any[],
|
|
763
|
+
relatedBugs: any[],
|
|
764
|
+
relatedCRs: any[],
|
|
765
|
+
wiByRevision: any,
|
|
766
|
+
selectedLinkedFieldSet: Set<string>
|
|
767
|
+
) {
|
|
768
|
+
for (const relation of relations) {
|
|
769
|
+
if (
|
|
770
|
+
relation.rel?.includes('System.LinkTypes') ||
|
|
771
|
+
relation.rel?.includes('Microsoft.VSTS.Common.TestedBy')
|
|
772
|
+
) {
|
|
773
|
+
const relatedUrl = relation.url;
|
|
774
|
+
try {
|
|
775
|
+
const wi = await TFSServices.getItemContent(relatedUrl, this.token);
|
|
776
|
+
if (wi.fields['System.State'] === 'Closed') {
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (
|
|
780
|
+
selectedLinkedFieldSet.has('associatedRequirement') &&
|
|
781
|
+
wi.fields['System.WorkItemType'] === 'Requirement'
|
|
782
|
+
) {
|
|
783
|
+
const { id, fields, _links } = wi;
|
|
784
|
+
const requirementTitle = fields['System.Title'];
|
|
785
|
+
const customerFieldKey = Object.keys(fields).find((key) =>
|
|
786
|
+
key.toLowerCase().includes('customer')
|
|
787
|
+
);
|
|
788
|
+
const customerId = customerFieldKey ? fields[customerFieldKey] : undefined;
|
|
789
|
+
const url = _links.html.href;
|
|
790
|
+
relatedRequirements.push({ id, requirementTitle, customerId, url });
|
|
791
|
+
} else if (
|
|
792
|
+
selectedLinkedFieldSet.has('associatedBug') &&
|
|
793
|
+
wi.fields['System.WorkItemType'] === 'Bug'
|
|
794
|
+
) {
|
|
795
|
+
const { id, fields, _links } = wi;
|
|
796
|
+
const bugTitle = fields['System.Title'];
|
|
797
|
+
const url = _links.html.href;
|
|
798
|
+
relatedBugs.push({ id, bugTitle, url });
|
|
799
|
+
} else if (
|
|
800
|
+
selectedLinkedFieldSet.has('associatedCR') &&
|
|
801
|
+
wi.fields['System.WorkItemType'] === 'Change Request'
|
|
802
|
+
) {
|
|
803
|
+
const { id, fields, _links } = wi;
|
|
804
|
+
const crTitle = fields['System.Title'];
|
|
805
|
+
const url = _links.html.href;
|
|
806
|
+
relatedCRs.push({ id, crTitle, url });
|
|
807
|
+
}
|
|
808
|
+
} catch (err: any) {
|
|
809
|
+
logger.error(`Could not append related work item to test case ${wiByRevision.id}: ${err.message}`);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
684
815
|
/**
|
|
685
816
|
* Fetches result data based on the specified work item (WI) details.
|
|
686
817
|
*
|
|
@@ -1536,9 +1667,10 @@ export default class ResultDataProvider {
|
|
|
1536
1667
|
projectName: string,
|
|
1537
1668
|
runId: string,
|
|
1538
1669
|
resultId: string,
|
|
1539
|
-
selectedFields?: string[]
|
|
1670
|
+
selectedFields?: string[],
|
|
1671
|
+
isQueryMode?: boolean
|
|
1540
1672
|
): Promise<any> {
|
|
1541
|
-
return this.fetchResultDataBasedOnWiBase(projectName, runId, resultId, true, selectedFields);
|
|
1673
|
+
return this.fetchResultDataBasedOnWiBase(projectName, runId, resultId, true, selectedFields, isQueryMode);
|
|
1542
1674
|
}
|
|
1543
1675
|
|
|
1544
1676
|
/**
|
|
@@ -1556,14 +1688,15 @@ export default class ResultDataProvider {
|
|
|
1556
1688
|
private async fetchAllResultDataTestReporter(
|
|
1557
1689
|
testData: any[],
|
|
1558
1690
|
projectName: string,
|
|
1559
|
-
selectedFields?: string[]
|
|
1691
|
+
selectedFields?: string[],
|
|
1692
|
+
isQueryMode?: boolean
|
|
1560
1693
|
): Promise<any[]> {
|
|
1561
1694
|
return this.fetchAllResultDataBase(
|
|
1562
1695
|
testData,
|
|
1563
1696
|
projectName,
|
|
1564
|
-
(projectName, testSuiteId, point, selectedFields) =>
|
|
1565
|
-
this.fetchResultDataForTestReporter(projectName, testSuiteId, point, selectedFields),
|
|
1566
|
-
[selectedFields]
|
|
1697
|
+
(projectName, testSuiteId, point, selectedFields, isQueryMode) =>
|
|
1698
|
+
this.fetchResultDataForTestReporter(projectName, testSuiteId, point, selectedFields, isQueryMode),
|
|
1699
|
+
[selectedFields, isQueryMode]
|
|
1567
1700
|
);
|
|
1568
1701
|
}
|
|
1569
1702
|
|
|
@@ -1573,7 +1706,7 @@ export default class ResultDataProvider {
|
|
|
1573
1706
|
*
|
|
1574
1707
|
* @param testData - An array of test data objects to be processed.
|
|
1575
1708
|
* @param iterations - An array of iteration objects to align with the test data.
|
|
1576
|
-
* @param selectedFields - An array of selected fields to determine which properties to include in the
|
|
1709
|
+
* @param selectedFields - An array of selected fields to determine which properties to include in the response.
|
|
1577
1710
|
* @returns An array of structured result objects containing aligned test steps and iterations.
|
|
1578
1711
|
*
|
|
1579
1712
|
* The method uses a base alignment function and provides custom logic for:
|
|
@@ -1604,35 +1737,38 @@ export default class ResultDataProvider {
|
|
|
1604
1737
|
actionResult,
|
|
1605
1738
|
filteredFields = new Set(),
|
|
1606
1739
|
}) => {
|
|
1607
|
-
const baseObj = {
|
|
1740
|
+
const baseObj: any = {
|
|
1608
1741
|
suiteName: testItem.testGroupName,
|
|
1609
1742
|
testCase: {
|
|
1610
1743
|
id: point.testCaseId,
|
|
1611
1744
|
title: point.testCaseName,
|
|
1612
1745
|
url: point.testCaseUrl,
|
|
1613
1746
|
result: fetchedTestCase.testCaseResult,
|
|
1614
|
-
comment: fetchedTestCase.
|
|
1747
|
+
comment: fetchedTestCase.comment,
|
|
1615
1748
|
},
|
|
1616
1749
|
priority: fetchedTestCase.priority,
|
|
1617
1750
|
runBy: fetchedTestCase.runBy,
|
|
1618
1751
|
activatedBy: fetchedTestCase.activatedBy,
|
|
1619
|
-
assignedTo: fetchedTestCase.assignedTo,
|
|
1620
|
-
subSystem: fetchedTestCase.subSystem,
|
|
1621
1752
|
failureType: fetchedTestCase.failureType,
|
|
1622
|
-
automationStatus: fetchedTestCase.automationStatus,
|
|
1623
1753
|
executionDate: fetchedTestCase.executionDate,
|
|
1624
1754
|
configurationName: fetchedTestCase.configurationName,
|
|
1625
1755
|
errorMessage: fetchedTestCase.errorMessage,
|
|
1626
1756
|
relatedRequirements: fetchedTestCase.relatedRequirements,
|
|
1627
1757
|
relatedBugs: fetchedTestCase.relatedBugs,
|
|
1628
1758
|
relatedCRs: fetchedTestCase.relatedCRs,
|
|
1759
|
+
...fetchedTestCase.customFields,
|
|
1629
1760
|
};
|
|
1630
1761
|
|
|
1631
1762
|
// If we have action results, add step-specific properties
|
|
1632
1763
|
if (actionResult) {
|
|
1633
1764
|
return {
|
|
1634
1765
|
...baseObj,
|
|
1635
|
-
stepNo:
|
|
1766
|
+
stepNo:
|
|
1767
|
+
filteredFields.has('includeSteps') ||
|
|
1768
|
+
filteredFields.has('stepRunStatus') ||
|
|
1769
|
+
filteredFields.has('testStepComment')
|
|
1770
|
+
? actionResult.stepPosition
|
|
1771
|
+
: undefined,
|
|
1636
1772
|
stepAction: filteredFields.has('includeSteps') ? actionResult.action : undefined,
|
|
1637
1773
|
stepExpected: filteredFields.has('includeSteps') ? actionResult.expected : undefined,
|
|
1638
1774
|
stepStatus: filteredFields.has('stepRunStatus')
|
|
@@ -1663,14 +1799,15 @@ export default class ResultDataProvider {
|
|
|
1663
1799
|
projectName: string,
|
|
1664
1800
|
testSuiteId: string,
|
|
1665
1801
|
point: any,
|
|
1666
|
-
selectedFields?: string[]
|
|
1802
|
+
selectedFields?: string[],
|
|
1803
|
+
isQueryMode?: boolean
|
|
1667
1804
|
) {
|
|
1668
1805
|
return this.fetchResultDataBase(
|
|
1669
1806
|
projectName,
|
|
1670
1807
|
testSuiteId,
|
|
1671
1808
|
point,
|
|
1672
|
-
(project, runId, resultId, fields) =>
|
|
1673
|
-
this.fetchResultDataBasedOnWiTestReporter(project, runId, resultId, fields),
|
|
1809
|
+
(project, runId, resultId, fields, isQueryMode) =>
|
|
1810
|
+
this.fetchResultDataBasedOnWiTestReporter(project, runId, resultId, fields, isQueryMode),
|
|
1674
1811
|
(resultData, testSuiteId, point, selectedFields) => {
|
|
1675
1812
|
const { lastRunId, lastResultId, configurationName, lastResultDetails } = point;
|
|
1676
1813
|
try {
|
|
@@ -1678,7 +1815,7 @@ export default class ResultDataProvider {
|
|
|
1678
1815
|
resultData.iterationDetails.length > 0
|
|
1679
1816
|
? resultData.iterationDetails[resultData.iterationDetails.length - 1]
|
|
1680
1817
|
: undefined;
|
|
1681
|
-
const resultDataResponse = {
|
|
1818
|
+
const resultDataResponse: any = {
|
|
1682
1819
|
testCaseName: `${resultData.testCase.name} - ${resultData.testCase.id}`,
|
|
1683
1820
|
testCaseId: resultData.testCase.id,
|
|
1684
1821
|
testSuiteName: `${resultData.testSuite.name}`,
|
|
@@ -1688,26 +1825,41 @@ export default class ResultDataProvider {
|
|
|
1688
1825
|
iteration,
|
|
1689
1826
|
testCaseRevision: resultData.testCaseRevision,
|
|
1690
1827
|
resolution: resultData.resolutionState,
|
|
1691
|
-
automationStatus: resultData.filteredFields['Microsoft.VSTS.TCM.AutomationStatus'] || undefined,
|
|
1692
|
-
analysisAttachments: resultData.analysisAttachments,
|
|
1693
1828
|
failureType: undefined as string | undefined,
|
|
1694
|
-
comment: undefined as string | undefined,
|
|
1695
1829
|
priority: undefined,
|
|
1696
|
-
assignedTo: resultData.filteredFields['System.AssignedTo'],
|
|
1697
|
-
subSystem: resultData.filteredFields['Custom.SubSystem'],
|
|
1698
1830
|
runBy: undefined as string | undefined,
|
|
1699
1831
|
executionDate: undefined as string | undefined,
|
|
1700
1832
|
testCaseResult: undefined as any | undefined,
|
|
1833
|
+
comment: undefined as string | undefined,
|
|
1701
1834
|
errorMessage: undefined as string | undefined,
|
|
1702
1835
|
configurationName: undefined as string | undefined,
|
|
1703
|
-
relatedRequirements: undefined,
|
|
1704
|
-
relatedBugs: undefined,
|
|
1705
|
-
relatedCRs: undefined,
|
|
1836
|
+
relatedRequirements: resultData.relatedRequirements || undefined,
|
|
1837
|
+
relatedBugs: resultData.relatedBugs || undefined,
|
|
1838
|
+
relatedCRs: resultData.relatedCRs || undefined,
|
|
1706
1839
|
lastRunResult: undefined as any,
|
|
1840
|
+
customFields: {}, // Create an object to store custom fields
|
|
1707
1841
|
};
|
|
1708
1842
|
|
|
1843
|
+
// Process all custom fields from resultData.filteredFields
|
|
1844
|
+
if (resultData.filteredFields) {
|
|
1845
|
+
for (const [fieldName, fieldValue] of Object.entries(resultData.filteredFields)) {
|
|
1846
|
+
if (fieldValue !== undefined && fieldValue !== null) {
|
|
1847
|
+
// Convert Microsoft.VSTS.TCM.AutomationStatus to automationStatus
|
|
1848
|
+
// or System.AssignedTo to assignedTo
|
|
1849
|
+
const nameParts = fieldName.split('.');
|
|
1850
|
+
let propertyName = nameParts[nameParts.length - 1];
|
|
1851
|
+
|
|
1852
|
+
// Convert to camelCase (first letter lowercase)
|
|
1853
|
+
propertyName = propertyName.charAt(0).toLowerCase() + propertyName.slice(1);
|
|
1854
|
+
|
|
1855
|
+
// Add to customFields object
|
|
1856
|
+
resultDataResponse.customFields[propertyName] = fieldValue;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1709
1861
|
const filteredFields = selectedFields
|
|
1710
|
-
?.filter((field: string) => field.includes('@runResultField')
|
|
1862
|
+
?.filter((field: string) => field.includes('@runResultField'))
|
|
1711
1863
|
?.map((field: string) => field.split('@')[0]);
|
|
1712
1864
|
|
|
1713
1865
|
if (filteredFields && filteredFields.length > 0) {
|
|
@@ -1732,34 +1884,22 @@ export default class ResultDataProvider {
|
|
|
1732
1884
|
url: `${this.orgUrl}${projectName}/_testManagement/runs?runId=${lastRunId}&_a=resultSummary&resultId=${lastResultId}`,
|
|
1733
1885
|
};
|
|
1734
1886
|
}
|
|
1735
|
-
|
|
1887
|
+
case 'testCaseComment':
|
|
1888
|
+
resultDataResponse.comment = iteration?.comment;
|
|
1736
1889
|
break;
|
|
1737
1890
|
case 'failureType':
|
|
1738
1891
|
resultDataResponse.failureType = resultData.failureType;
|
|
1739
1892
|
break;
|
|
1740
|
-
case 'testCaseComment':
|
|
1741
|
-
resultDataResponse.comment = resultData.comment || undefined;
|
|
1742
|
-
break;
|
|
1743
1893
|
case 'runBy':
|
|
1744
1894
|
const runBy = lastResultDetails.runBy.displayName;
|
|
1745
1895
|
resultDataResponse.runBy = runBy;
|
|
1746
1896
|
break;
|
|
1747
|
-
|
|
1748
1897
|
case 'executionDate':
|
|
1749
1898
|
resultDataResponse.executionDate = lastResultDetails.dateCompleted;
|
|
1750
1899
|
break;
|
|
1751
1900
|
case 'configurationName':
|
|
1752
1901
|
resultDataResponse.configurationName = configurationName;
|
|
1753
1902
|
break;
|
|
1754
|
-
case 'associatedRequirement':
|
|
1755
|
-
resultDataResponse.relatedRequirements = resultData.relatedRequirements;
|
|
1756
|
-
break;
|
|
1757
|
-
case 'associatedBug':
|
|
1758
|
-
resultDataResponse.relatedBugs = resultData.relatedBugs;
|
|
1759
|
-
break;
|
|
1760
|
-
case 'associatedCR':
|
|
1761
|
-
resultDataResponse.relatedCRs = resultData.relatedCRs;
|
|
1762
|
-
break;
|
|
1763
1903
|
default:
|
|
1764
1904
|
logger.debug(`Field ${field} not handled`);
|
|
1765
1905
|
break;
|
|
@@ -1773,7 +1913,7 @@ export default class ResultDataProvider {
|
|
|
1773
1913
|
return null;
|
|
1774
1914
|
}
|
|
1775
1915
|
},
|
|
1776
|
-
[selectedFields]
|
|
1916
|
+
[selectedFields, isQueryMode]
|
|
1777
1917
|
);
|
|
1778
1918
|
}
|
|
1779
1919
|
}
|
|
@@ -118,14 +118,17 @@ export default class TicketsDataProvider {
|
|
|
118
118
|
else url = `${this.orgUrl}${project}/_apis/wit/queries/${path}?$depth=2&$expand=all`;
|
|
119
119
|
let queries: any = await TFSServices.getItemContent(url, this.token);
|
|
120
120
|
logger.debug(`doctype: ${docType}`);
|
|
121
|
-
switch (docType) {
|
|
122
|
-
case '
|
|
121
|
+
switch (docType?.toLowerCase()) {
|
|
122
|
+
case 'std':
|
|
123
123
|
return await this.fetchLinkedReqTestQueries(queries, false);
|
|
124
|
-
case '
|
|
124
|
+
case 'str':
|
|
125
125
|
const reqTestTrees = await this.fetchLinkedReqTestQueries(queries, false);
|
|
126
126
|
const openPcrTestTrees = await this.fetchLinkedOpenPcrTestQueries(queries, false);
|
|
127
127
|
return { reqTestTrees, openPcrTestTrees };
|
|
128
|
-
case '
|
|
128
|
+
case 'test-reporter':
|
|
129
|
+
const testAssociatedTree = await this.fetchTestReporterQueries(queries);
|
|
130
|
+
return { testAssociatedTree };
|
|
131
|
+
case 'svd':
|
|
129
132
|
return await this.fetchAnyQueries(queries);
|
|
130
133
|
default:
|
|
131
134
|
break;
|
|
@@ -136,6 +139,42 @@ export default class TicketsDataProvider {
|
|
|
136
139
|
}
|
|
137
140
|
}
|
|
138
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Fetches the fields of a specific work item type.
|
|
144
|
+
*
|
|
145
|
+
* @param project - The project name.
|
|
146
|
+
* @param itemType - The work item type.
|
|
147
|
+
* @returns An array of objects containing the field name and reference name.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
async GetFieldsByType(project: string, itemType: string) {
|
|
151
|
+
try {
|
|
152
|
+
let url = `${this.orgUrl}${project}/_apis/wit/workitemtypes/${itemType}/fields`;
|
|
153
|
+
const { value: fields } = await TFSServices.getItemContent(url, this.token);
|
|
154
|
+
return (
|
|
155
|
+
fields
|
|
156
|
+
// filter out the fields that are not relevant for the user
|
|
157
|
+
.filter(
|
|
158
|
+
(field: any) =>
|
|
159
|
+
field.name !== 'ID' &&
|
|
160
|
+
field.name !== 'Title' &&
|
|
161
|
+
field.name !== 'Description' &&
|
|
162
|
+
field.name !== 'Work Item Type' &&
|
|
163
|
+
field.name !== 'Steps'
|
|
164
|
+
)
|
|
165
|
+
.map((field: any) => {
|
|
166
|
+
return {
|
|
167
|
+
text: `${field.name} (${itemType})`,
|
|
168
|
+
key: field.referenceName,
|
|
169
|
+
};
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
} catch (err: any) {
|
|
173
|
+
logger.error(`Error occurred during fetching fields by type: ${err.message}`);
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
139
178
|
/**
|
|
140
179
|
* fetches linked queries
|
|
141
180
|
* @param queries fetched queries
|
|
@@ -177,6 +216,22 @@ export default class TicketsDataProvider {
|
|
|
177
216
|
return { OpenPcrToTestTree, TestToOpenPcrTree };
|
|
178
217
|
}
|
|
179
218
|
|
|
219
|
+
/**
|
|
220
|
+
* fetches test reporter queries
|
|
221
|
+
* @param queries fetched queries
|
|
222
|
+
* @returns
|
|
223
|
+
*/
|
|
224
|
+
private async fetchTestReporterQueries(queries: any) {
|
|
225
|
+
const { tree1: tree1, tree2: testAssociatedTree } = await this.structureFetchedQueries(
|
|
226
|
+
queries,
|
|
227
|
+
true,
|
|
228
|
+
null,
|
|
229
|
+
['Requirement', 'Bug', 'Change Request'],
|
|
230
|
+
['Test Case']
|
|
231
|
+
);
|
|
232
|
+
return { testAssociatedTree };
|
|
233
|
+
}
|
|
234
|
+
|
|
180
235
|
private async fetchAnyQueries(queries: any) {
|
|
181
236
|
const { tree1: systemOverviewQueryTree, tree2: knownBugsQueryTree } = await this.structureAllQueryPath(
|
|
182
237
|
queries
|