@elisra-devops/docgen-data-provider 1.26.6 → 1.28.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/tfs-data.d.ts +6 -0
- package/bin/models/tfs-data.js.map +1 -1
- package/bin/modules/ResultDataProvider.d.ts +6 -23
- package/bin/modules/ResultDataProvider.js +53 -58
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/modules/TestDataProvider.js +3 -2
- package/bin/modules/TestDataProvider.js.map +1 -1
- package/bin/modules/TicketsDataProvider.d.ts +40 -5
- package/bin/modules/TicketsDataProvider.js +67 -19
- package/bin/modules/TicketsDataProvider.js.map +1 -1
- package/bin/modules/test/ResultDataProvider.test.js +62 -73
- package/bin/modules/test/ResultDataProvider.test.js.map +1 -1
- package/bin/modules/test/testDataProvider.test.js +13 -15
- package/bin/modules/test/testDataProvider.test.js.map +1 -1
- package/bin/utils/DataProviderUtils.d.ts +11 -0
- package/bin/utils/DataProviderUtils.js +21 -0
- package/bin/utils/DataProviderUtils.js.map +1 -0
- package/package.json +1 -1
- package/src/models/tfs-data.ts +7 -0
- package/src/modules/ResultDataProvider.ts +70 -71
- package/src/modules/TestDataProvider.ts +10 -5
- package/src/modules/TicketsDataProvider.ts +86 -25
- package/src/modules/test/ResultDataProvider.test.ts +534 -543
- package/src/modules/test/testDataProvider.test.ts +19 -18
- package/src/utils/DataProviderUtils.ts +16 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import DataProviderUtils from '../utils/DataProviderUtils';
|
|
2
2
|
import { TFSServices } from '../helpers/tfs';
|
|
3
|
-
import { TestSteps } from '../models/tfs-data';
|
|
3
|
+
import { OpenPcrRequest, TestSteps } from '../models/tfs-data';
|
|
4
4
|
import logger from '../utils/logger';
|
|
5
|
-
import
|
|
5
|
+
import Utils from '../utils/testStepParserHelper';
|
|
6
6
|
const pLimit = require('p-limit');
|
|
7
7
|
/**
|
|
8
8
|
* Provides methods to fetch, process, and summarize test data from Azure DevOps.
|
|
@@ -30,11 +30,11 @@ export default class ResultDataProvider {
|
|
|
30
30
|
orgUrl: string = '';
|
|
31
31
|
token: string = '';
|
|
32
32
|
private limit = pLimit(10);
|
|
33
|
-
private testStepParserHelper:
|
|
33
|
+
private testStepParserHelper: Utils;
|
|
34
34
|
constructor(orgUrl: string, token: string) {
|
|
35
35
|
this.orgUrl = orgUrl;
|
|
36
36
|
this.token = token;
|
|
37
|
-
this.testStepParserHelper = new
|
|
37
|
+
this.testStepParserHelper = new Utils(orgUrl, token);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
@@ -46,14 +46,20 @@ export default class ResultDataProvider {
|
|
|
46
46
|
selectedSuiteIds?: number[],
|
|
47
47
|
addConfiguration: boolean = false,
|
|
48
48
|
isHierarchyGroupName: boolean = false,
|
|
49
|
-
|
|
49
|
+
openPcrRequest: OpenPcrRequest | null = null,
|
|
50
50
|
includeTestLog: boolean = false,
|
|
51
51
|
stepExecution?: any,
|
|
52
52
|
stepAnalysis?: any,
|
|
53
53
|
includeHardCopyRun: boolean = false
|
|
54
|
-
): Promise<
|
|
54
|
+
): Promise<{
|
|
55
|
+
combinedResults: any[];
|
|
56
|
+
openPcrToTestCaseTraceMap: Map<string, string[]>;
|
|
57
|
+
testCaseToOpenPcrTraceMap: Map<string, string[]>;
|
|
58
|
+
}> {
|
|
55
59
|
const combinedResults: any[] = [];
|
|
56
60
|
try {
|
|
61
|
+
const openPcrToTestCaseTraceMap = new Map<string, string[]>();
|
|
62
|
+
const testCaseToOpenPcrTraceMap = new Map<string, string[]>();
|
|
57
63
|
// Fetch test suites
|
|
58
64
|
const suites = await this.fetchTestSuites(
|
|
59
65
|
testPlanId,
|
|
@@ -131,9 +137,14 @@ export default class ResultDataProvider {
|
|
|
131
137
|
skin: 'detailed-test-result-table',
|
|
132
138
|
});
|
|
133
139
|
|
|
134
|
-
if (
|
|
140
|
+
if (openPcrRequest?.openPcrMode === 'linked') {
|
|
135
141
|
//5. Open PCRs data (only if enabled)
|
|
136
|
-
await this.fetchOpenPcrData(
|
|
142
|
+
await this.fetchOpenPcrData(
|
|
143
|
+
testResultsSummary,
|
|
144
|
+
projectName,
|
|
145
|
+
openPcrToTestCaseTraceMap,
|
|
146
|
+
testCaseToOpenPcrTraceMap
|
|
147
|
+
);
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
//6. Test Log (only if enabled)
|
|
@@ -181,7 +192,7 @@ export default class ResultDataProvider {
|
|
|
181
192
|
});
|
|
182
193
|
}
|
|
183
194
|
|
|
184
|
-
return combinedResults;
|
|
195
|
+
return { combinedResults, openPcrToTestCaseTraceMap, testCaseToOpenPcrTraceMap };
|
|
185
196
|
} catch (error: any) {
|
|
186
197
|
logger.error(`Error during getCombinedResultsSummary: ${error.message}`);
|
|
187
198
|
if (error.response) {
|
|
@@ -456,39 +467,12 @@ export default class ResultDataProvider {
|
|
|
456
467
|
return testCases;
|
|
457
468
|
}
|
|
458
469
|
|
|
459
|
-
/**
|
|
460
|
-
* Fetches result data based on a work item base (WiBase) for a specific test run and result.
|
|
461
|
-
*
|
|
462
|
-
* @param projectName - The name of the project in Azure DevOps.
|
|
463
|
-
* @param runId - The ID of the test run.
|
|
464
|
-
* @param resultId - The ID of the test result.
|
|
465
|
-
* @param options - Optional parameters for customizing the data retrieval.
|
|
466
|
-
* @param options.expandWorkItem - If true, expands all fields of the work item.
|
|
467
|
-
* @param options.selectedFields - An array of field names to filter the work item fields.
|
|
468
|
-
* @param options.processRelatedRequirements - If true, processes related requirements linked to the work item.
|
|
469
|
-
* @param options.includeFullErrorStack - If true, includes the full error stack in the logs when an error occurs.
|
|
470
|
-
* @returns A promise that resolves to an object containing the fetched result data, including:
|
|
471
|
-
* - `stepsResultXml`: The test steps result in XML format.
|
|
472
|
-
* - `analysisAttachments`: Attachments related to the test result analysis.
|
|
473
|
-
* - `testCaseRevision`: The revision number of the test case.
|
|
474
|
-
* - `filteredFields`: The filtered fields from the work item based on the selected fields.
|
|
475
|
-
* - `relatedRequirements`: An array of related requirements with details such as ID, title, customer ID, and URL.
|
|
476
|
-
* - `relatedBugs`: An array of related bugs with details such as ID, title, and URL.
|
|
477
|
-
* If an error occurs, logs the error and returns `null`.
|
|
478
|
-
*
|
|
479
|
-
* @throws Logs an error message if the data retrieval fails.
|
|
480
|
-
*/
|
|
481
470
|
private async fetchResultDataBasedOnWiBase(
|
|
482
471
|
projectName: string,
|
|
483
472
|
runId: string,
|
|
484
473
|
resultId: string,
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
selectedFields?: string[];
|
|
488
|
-
processRelatedRequirements?: boolean;
|
|
489
|
-
processRelatedBugs?: boolean;
|
|
490
|
-
includeFullErrorStack?: boolean;
|
|
491
|
-
} = {}
|
|
474
|
+
isTestReporter: boolean = false,
|
|
475
|
+
selectedFields?: string[]
|
|
492
476
|
): Promise<any> {
|
|
493
477
|
try {
|
|
494
478
|
const url = `${this.orgUrl}${projectName}/_apis/test/runs/${runId}/results/${resultId}?detailsToInclude=Iterations`;
|
|
@@ -498,21 +482,16 @@ export default class ResultDataProvider {
|
|
|
498
482
|
const { value: analysisAttachments } = await TFSServices.getItemContent(attachmentsUrl, this.token);
|
|
499
483
|
|
|
500
484
|
// Build workItem URL with optional expand parameter
|
|
501
|
-
const expandParam =
|
|
485
|
+
const expandParam = isTestReporter ? '?$expand=all' : '';
|
|
502
486
|
const wiUrl = `${this.orgUrl}${projectName}/_apis/wit/workItems/${resultData.testCase.id}/revisions/${resultData.testCaseRevision}${expandParam}`;
|
|
503
487
|
const wiByRevision = await TFSServices.getItemContent(wiUrl, this.token);
|
|
504
488
|
let filteredFields: any = {};
|
|
505
489
|
let relatedRequirements: any[] = [];
|
|
506
490
|
let relatedBugs: any[] = [];
|
|
507
|
-
|
|
508
|
-
// TODO: Add logic for grabbing the relations from cross projects
|
|
509
|
-
|
|
491
|
+
let relatedCRs: any[] = [];
|
|
510
492
|
// Process selected fields if provided
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
(options.processRelatedRequirements || options.processRelatedBugs)
|
|
514
|
-
) {
|
|
515
|
-
const filtered = options.selectedFields
|
|
493
|
+
if (selectedFields?.length && isTestReporter) {
|
|
494
|
+
const filtered = selectedFields
|
|
516
495
|
?.filter((field: string) => field.includes('@testCaseWorkItemField'))
|
|
517
496
|
?.map((field: string) => field.split('@')[0]);
|
|
518
497
|
const selectedFieldSet = new Set(filtered);
|
|
@@ -523,7 +502,7 @@ export default class ResultDataProvider {
|
|
|
523
502
|
if (relations) {
|
|
524
503
|
for (const relation of relations) {
|
|
525
504
|
if (
|
|
526
|
-
relation.rel?.includes('System.LinkTypes
|
|
505
|
+
relation.rel?.includes('System.LinkTypes') ||
|
|
527
506
|
relation.rel?.includes('Microsoft.VSTS.Common.TestedBy')
|
|
528
507
|
) {
|
|
529
508
|
const relatedUrl = relation.url;
|
|
@@ -542,6 +521,11 @@ export default class ResultDataProvider {
|
|
|
542
521
|
const bugTitle = fields['System.Title'];
|
|
543
522
|
const url = _links.html.href;
|
|
544
523
|
relatedBugs.push({ id, bugTitle, url });
|
|
524
|
+
} else if (wi.fields['System.WorkItemType'] === 'Change Request') {
|
|
525
|
+
const { id, fields, _links } = wi;
|
|
526
|
+
const crTitle = fields['System.Title'];
|
|
527
|
+
const url = _links.html.href;
|
|
528
|
+
relatedCRs.push({ id, crTitle, url });
|
|
545
529
|
}
|
|
546
530
|
}
|
|
547
531
|
}
|
|
@@ -564,10 +548,11 @@ export default class ResultDataProvider {
|
|
|
564
548
|
filteredFields,
|
|
565
549
|
relatedRequirements,
|
|
566
550
|
relatedBugs,
|
|
551
|
+
relatedCRs,
|
|
567
552
|
};
|
|
568
553
|
} catch (error: any) {
|
|
569
554
|
logger.error(`Error while fetching run result: ${error.message}`);
|
|
570
|
-
if (
|
|
555
|
+
if (isTestReporter) {
|
|
571
556
|
logger.error(`Error stack: ${error.stack}`);
|
|
572
557
|
}
|
|
573
558
|
return null;
|
|
@@ -1122,21 +1107,32 @@ export default class ResultDataProvider {
|
|
|
1122
1107
|
* Fetching Open PCRs data
|
|
1123
1108
|
*/
|
|
1124
1109
|
|
|
1125
|
-
private async fetchOpenPcrData(
|
|
1110
|
+
private async fetchOpenPcrData(
|
|
1111
|
+
testItems: any[],
|
|
1112
|
+
projectName: string,
|
|
1113
|
+
openPcrToTestCaseTraceMap: Map<string, string[]>,
|
|
1114
|
+
testCaseToOpenPcrTraceMap: Map<string, string[]>
|
|
1115
|
+
) {
|
|
1126
1116
|
const linkedWorkItems = await this.fetchLinkedWi(projectName, testItems);
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
.
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
// Add openPCR to combined results
|
|
1135
|
-
combinedResults.push({
|
|
1136
|
-
contentControl: 'open-pcr-content-control',
|
|
1137
|
-
data: flatOpenPcrsItems,
|
|
1138
|
-
skin: 'open-pcr-table',
|
|
1117
|
+
for (const wi of linkedWorkItems) {
|
|
1118
|
+
const { linkItems, ...restItem } = wi;
|
|
1119
|
+
const stringifiedTestCase = JSON.stringify({
|
|
1120
|
+
id: restItem.testId,
|
|
1121
|
+
title: restItem.testName,
|
|
1122
|
+
testCaseUrl: restItem.testCaseUrl,
|
|
1123
|
+
runStatus: restItem.runStatus,
|
|
1139
1124
|
});
|
|
1125
|
+
for (const linkedItem of linkItems) {
|
|
1126
|
+
const stringifiedPcr = JSON.stringify({
|
|
1127
|
+
pcrId: linkedItem.pcrId,
|
|
1128
|
+
workItemType: linkedItem.workItemType,
|
|
1129
|
+
title: linkedItem.title,
|
|
1130
|
+
severity: linkedItem.severity,
|
|
1131
|
+
pcrUrl: linkedItem.pcrUrl,
|
|
1132
|
+
});
|
|
1133
|
+
DataProviderUtils.addToTraceMap(openPcrToTestCaseTraceMap, stringifiedPcr, stringifiedTestCase);
|
|
1134
|
+
DataProviderUtils.addToTraceMap(testCaseToOpenPcrTraceMap, stringifiedTestCase, stringifiedPcr);
|
|
1135
|
+
}
|
|
1140
1136
|
}
|
|
1141
1137
|
}
|
|
1142
1138
|
|
|
@@ -1346,6 +1342,7 @@ export default class ResultDataProvider {
|
|
|
1346
1342
|
testGroupName: testPoint.testGroupName,
|
|
1347
1343
|
testId: testPoint.testCaseId,
|
|
1348
1344
|
testName: testPoint.testCaseName,
|
|
1345
|
+
testCaseUrl: testPoint.testCaseUrl,
|
|
1349
1346
|
runStatus: !includeHardCopyRun ? this.convertRunStatus(testPoint.outcome) : '',
|
|
1350
1347
|
};
|
|
1351
1348
|
|
|
@@ -1374,13 +1371,7 @@ export default class ResultDataProvider {
|
|
|
1374
1371
|
resultId: string,
|
|
1375
1372
|
selectedFields?: string[]
|
|
1376
1373
|
): Promise<any> {
|
|
1377
|
-
return this.fetchResultDataBasedOnWiBase(projectName, runId, resultId,
|
|
1378
|
-
expandWorkItem: true,
|
|
1379
|
-
selectedFields,
|
|
1380
|
-
processRelatedRequirements: true,
|
|
1381
|
-
processRelatedBugs: true,
|
|
1382
|
-
includeFullErrorStack: true,
|
|
1383
|
-
});
|
|
1374
|
+
return this.fetchResultDataBasedOnWiBase(projectName, runId, resultId, true, selectedFields);
|
|
1384
1375
|
}
|
|
1385
1376
|
|
|
1386
1377
|
/**
|
|
@@ -1459,6 +1450,7 @@ export default class ResultDataProvider {
|
|
|
1459
1450
|
runBy: fetchedTestCase.runBy,
|
|
1460
1451
|
activatedBy: fetchedTestCase.activatedBy,
|
|
1461
1452
|
assignedTo: fetchedTestCase.assignedTo,
|
|
1453
|
+
subSystem: fetchedTestCase.subSystem,
|
|
1462
1454
|
failureType: fetchedTestCase.failureType,
|
|
1463
1455
|
automationStatus: fetchedTestCase.automationStatus,
|
|
1464
1456
|
executionDate: fetchedTestCase.executionDate,
|
|
@@ -1466,6 +1458,7 @@ export default class ResultDataProvider {
|
|
|
1466
1458
|
errorMessage: fetchedTestCase.errorMessage,
|
|
1467
1459
|
relatedRequirements: fetchedTestCase.relatedRequirements,
|
|
1468
1460
|
relatedBugs: fetchedTestCase.relatedBugs,
|
|
1461
|
+
relatedCRs: fetchedTestCase.relatedCRs,
|
|
1469
1462
|
};
|
|
1470
1463
|
|
|
1471
1464
|
// If we have action results, add step-specific properties
|
|
@@ -1518,7 +1511,6 @@ export default class ResultDataProvider {
|
|
|
1518
1511
|
resultData.iterationDetails.length > 0
|
|
1519
1512
|
? resultData.iterationDetails[resultData.iterationDetails.length - 1]
|
|
1520
1513
|
: undefined;
|
|
1521
|
-
|
|
1522
1514
|
const resultDataResponse = {
|
|
1523
1515
|
testCaseName: `${resultData.testCase.name} - ${resultData.testCase.id}`,
|
|
1524
1516
|
testCaseId: resultData.testCase.id,
|
|
@@ -1534,6 +1526,8 @@ export default class ResultDataProvider {
|
|
|
1534
1526
|
failureType: undefined as string | undefined,
|
|
1535
1527
|
comment: undefined as string | undefined,
|
|
1536
1528
|
priority: undefined,
|
|
1529
|
+
assignedTo: resultData.filteredFields['System.AssignedTo'],
|
|
1530
|
+
subSystem: resultData.filteredFields['Custom.SubSystem'],
|
|
1537
1531
|
runBy: undefined as string | undefined,
|
|
1538
1532
|
executionDate: undefined as string | undefined,
|
|
1539
1533
|
testCaseResult: undefined as any | undefined,
|
|
@@ -1541,6 +1535,7 @@ export default class ResultDataProvider {
|
|
|
1541
1535
|
configurationName: undefined as string | undefined,
|
|
1542
1536
|
relatedRequirements: undefined,
|
|
1543
1537
|
relatedBugs: undefined,
|
|
1538
|
+
relatedCRs: undefined,
|
|
1544
1539
|
lastRunResult: undefined as any,
|
|
1545
1540
|
};
|
|
1546
1541
|
|
|
@@ -1582,6 +1577,7 @@ export default class ResultDataProvider {
|
|
|
1582
1577
|
const runBy = lastResultDetails.runBy.displayName;
|
|
1583
1578
|
resultDataResponse.runBy = runBy;
|
|
1584
1579
|
break;
|
|
1580
|
+
|
|
1585
1581
|
case 'executionDate':
|
|
1586
1582
|
resultDataResponse.executionDate = lastResultDetails.dateCompleted;
|
|
1587
1583
|
break;
|
|
@@ -1594,6 +1590,9 @@ export default class ResultDataProvider {
|
|
|
1594
1590
|
case 'associatedBug':
|
|
1595
1591
|
resultDataResponse.relatedBugs = resultData.relatedBugs;
|
|
1596
1592
|
break;
|
|
1593
|
+
case 'associatedCR':
|
|
1594
|
+
resultDataResponse.relatedCRs = resultData.relatedCRs;
|
|
1595
|
+
break;
|
|
1597
1596
|
default:
|
|
1598
1597
|
logger.debug(`Field ${field} not handled`);
|
|
1599
1598
|
break;
|
|
@@ -4,20 +4,21 @@ import { TestSteps, createMomRelation, createRequirementRelation } from '../mode
|
|
|
4
4
|
import { TestCase } from '../models/tfs-data';
|
|
5
5
|
import * as xml2js from 'xml2js';
|
|
6
6
|
import logger from '../utils/logger';
|
|
7
|
-
import
|
|
7
|
+
import Utils from '../utils/testStepParserHelper';
|
|
8
|
+
import DataProviderUtils from '../utils/DataProviderUtils';
|
|
8
9
|
const pLimit = require('p-limit');
|
|
9
10
|
|
|
10
11
|
export default class TestDataProvider {
|
|
11
12
|
orgUrl: string = '';
|
|
12
13
|
token: string = '';
|
|
13
|
-
private testStepParserHelper:
|
|
14
|
+
private testStepParserHelper: Utils;
|
|
14
15
|
private cache = new Map<string, any>(); // Cache for API responses
|
|
15
16
|
private limit = pLimit(10);
|
|
16
17
|
|
|
17
18
|
constructor(orgUrl: string, token: string) {
|
|
18
19
|
this.orgUrl = orgUrl;
|
|
19
20
|
this.token = token;
|
|
20
|
-
this.testStepParserHelper = new
|
|
21
|
+
this.testStepParserHelper = new Utils(orgUrl, token);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
private async fetchWithCache(url: string, ttlMs = 60000): Promise<any> {
|
|
@@ -226,10 +227,14 @@ export default class TestDataProvider {
|
|
|
226
227
|
const stringifiedRequirement = JSON.stringify(newRequirementRelation);
|
|
227
228
|
|
|
228
229
|
// Add the test case to the requirement-to-test-case trace map
|
|
229
|
-
|
|
230
|
+
DataProviderUtils.addToTraceMap(
|
|
231
|
+
requirementToTestCaseTraceMap,
|
|
232
|
+
stringifiedRequirement,
|
|
233
|
+
stringifiedTestCase
|
|
234
|
+
);
|
|
230
235
|
|
|
231
236
|
// Add the requirement to the test-case-to-requirements trace map
|
|
232
|
-
|
|
237
|
+
DataProviderUtils.addToTraceMap(
|
|
233
238
|
testCaseToRequirementsTraceMap,
|
|
234
239
|
stringifiedTestCase,
|
|
235
240
|
stringifiedRequirement
|
|
@@ -120,9 +120,11 @@ export default class TicketsDataProvider {
|
|
|
120
120
|
logger.debug(`doctype: ${docType}`);
|
|
121
121
|
switch (docType) {
|
|
122
122
|
case 'STD':
|
|
123
|
-
return await this.
|
|
123
|
+
return await this.fetchLinkedReqTestQueries(queries, false);
|
|
124
124
|
case 'STR':
|
|
125
|
-
|
|
125
|
+
const reqTestTrees = await this.fetchLinkedReqTestQueries(queries, false);
|
|
126
|
+
const openPcrTestTrees = await this.fetchLinkedOpenPcrTestQueries(queries, false);
|
|
127
|
+
return { reqTestTrees, openPcrTestTrees };
|
|
126
128
|
case 'SVD':
|
|
127
129
|
return await this.fetchAnyQueries(queries);
|
|
128
130
|
default:
|
|
@@ -140,13 +142,41 @@ export default class TicketsDataProvider {
|
|
|
140
142
|
* @param onlyTestReq get only test req
|
|
141
143
|
* @returns ReqTestTree and TestReqTree
|
|
142
144
|
*/
|
|
143
|
-
private async
|
|
144
|
-
const { tree1: reqTestTree, tree2: testReqTree } = await this.
|
|
145
|
+
private async fetchLinkedReqTestQueries(queries: any, onlyTestReq: boolean = false) {
|
|
146
|
+
const { tree1: reqTestTree, tree2: testReqTree } = await this.structureFetchedQueries(
|
|
145
147
|
queries,
|
|
146
|
-
onlyTestReq
|
|
148
|
+
onlyTestReq,
|
|
149
|
+
null,
|
|
150
|
+
['Requirement'],
|
|
151
|
+
['Test Case']
|
|
147
152
|
);
|
|
148
153
|
return { reqTestTree, testReqTree };
|
|
149
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Fetches and structures linked queries related to open PCR (Problem Change Request) tests.
|
|
158
|
+
*
|
|
159
|
+
* This method retrieves and organizes the relationships between "Test Case" and
|
|
160
|
+
* other entities such as "Bug" and "Change Request" into two tree structures.
|
|
161
|
+
*
|
|
162
|
+
* @param queries - The input queries to be processed and structured.
|
|
163
|
+
* @param onlySourceSide - A flag indicating whether to process only the source side of the queries.
|
|
164
|
+
* Defaults to `false`.
|
|
165
|
+
* @returns An object containing two tree structures:
|
|
166
|
+
* - `OpenPcrToTestTree`: The tree representing the relationship from Open PCR to Test Case.
|
|
167
|
+
* - `TestToOpenPcrTree`: The tree representing the relationship from Test Case to Open PCR.
|
|
168
|
+
*/
|
|
169
|
+
private async fetchLinkedOpenPcrTestQueries(queries: any, onlySourceSide: boolean = false) {
|
|
170
|
+
const { tree1: OpenPcrToTestTree, tree2: TestToOpenPcrTree } = await this.structureFetchedQueries(
|
|
171
|
+
queries,
|
|
172
|
+
onlySourceSide,
|
|
173
|
+
null,
|
|
174
|
+
['Bug', 'Change Request'],
|
|
175
|
+
['Test Case']
|
|
176
|
+
);
|
|
177
|
+
return { OpenPcrToTestTree, TestToOpenPcrTree };
|
|
178
|
+
}
|
|
179
|
+
|
|
150
180
|
private async fetchAnyQueries(queries: any) {
|
|
151
181
|
const { tree1: systemOverviewQueryTree, tree2: knownBugsQueryTree } = await this.structureAllQueryPath(
|
|
152
182
|
queries
|
|
@@ -756,12 +786,25 @@ export default class TicketsDataProvider {
|
|
|
756
786
|
}
|
|
757
787
|
|
|
758
788
|
/**
|
|
759
|
-
* Recursively structures
|
|
789
|
+
* Recursively structures fetched queries into two hierarchical trees (tree1 and tree2)
|
|
790
|
+
* based on specific conditions. It processes both leaf and non-leaf nodes, fetching
|
|
791
|
+
* children if necessary, and builds the trees by matching source and target conditions.
|
|
792
|
+
*
|
|
793
|
+
* @param rootQuery - The root query object to process. It may contain children or be a leaf node.
|
|
794
|
+
* @param onlyTestReq - A boolean flag indicating whether to exclude requirement-to-test-case queries.
|
|
795
|
+
* @param parentId - The ID of the parent node, used to maintain the hierarchy. Defaults to `null`.
|
|
796
|
+
* @param sources - An array of source strings used to match queries.
|
|
797
|
+
* @param targets - An array of target strings used to match queries.
|
|
798
|
+
* @returns A promise that resolves to an object containing two trees (`tree1` and `tree2`),
|
|
799
|
+
* or `null` for each tree if no valid nodes are found.
|
|
800
|
+
* @throws Logs an error if an exception occurs during processing.
|
|
760
801
|
*/
|
|
761
|
-
private async
|
|
802
|
+
private async structureFetchedQueries(
|
|
762
803
|
rootQuery: any,
|
|
763
804
|
onlyTestReq: boolean,
|
|
764
|
-
parentId: any = null
|
|
805
|
+
parentId: any = null,
|
|
806
|
+
sources: string[],
|
|
807
|
+
targets: string[]
|
|
765
808
|
): Promise<any> {
|
|
766
809
|
try {
|
|
767
810
|
if (!rootQuery.hasChildren) {
|
|
@@ -770,7 +813,7 @@ export default class TicketsDataProvider {
|
|
|
770
813
|
let tree1Node = null;
|
|
771
814
|
let tree2Node = null;
|
|
772
815
|
// Check if the query is a requirement to test case query
|
|
773
|
-
if (!onlyTestReq && this.
|
|
816
|
+
if (!onlyTestReq && this.matchesSourceTargetCondition(wiql, sources, targets)) {
|
|
774
817
|
tree1Node = {
|
|
775
818
|
id: rootQuery.id,
|
|
776
819
|
pId: parentId,
|
|
@@ -781,7 +824,7 @@ export default class TicketsDataProvider {
|
|
|
781
824
|
isValidQuery: true,
|
|
782
825
|
};
|
|
783
826
|
}
|
|
784
|
-
if (this.
|
|
827
|
+
if (this.matchesSourceTargetCondition(wiql, targets, sources)) {
|
|
785
828
|
tree2Node = {
|
|
786
829
|
id: rootQuery.id,
|
|
787
830
|
pId: parentId,
|
|
@@ -802,13 +845,15 @@ export default class TicketsDataProvider {
|
|
|
802
845
|
const queryUrl = `${rootQuery.url}?$depth=2&$expand=all`;
|
|
803
846
|
const currentQuery = await TFSServices.getItemContent(queryUrl, this.token);
|
|
804
847
|
return currentQuery
|
|
805
|
-
? await this.
|
|
848
|
+
? await this.structureFetchedQueries(currentQuery, onlyTestReq, currentQuery.id, sources, targets)
|
|
806
849
|
: { tree1: null, tree2: null };
|
|
807
850
|
}
|
|
808
851
|
|
|
809
852
|
// Process children recursively
|
|
810
853
|
const childResults = await Promise.all(
|
|
811
|
-
rootQuery.children.map((child: any) =>
|
|
854
|
+
rootQuery.children.map((child: any) =>
|
|
855
|
+
this.structureFetchedQueries(child, onlyTestReq, rootQuery.id, sources, targets)
|
|
856
|
+
)
|
|
812
857
|
);
|
|
813
858
|
|
|
814
859
|
// Build tree1
|
|
@@ -848,20 +893,36 @@ export default class TicketsDataProvider {
|
|
|
848
893
|
}
|
|
849
894
|
}
|
|
850
895
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
896
|
+
/**
|
|
897
|
+
* Determines whether the given WIQL (Work Item Query Language) string matches the specified
|
|
898
|
+
* source and target conditions. It checks if the WIQL contains references to the specified
|
|
899
|
+
* source and target work item types.
|
|
900
|
+
*
|
|
901
|
+
* @param wiql - The WIQL string to evaluate.
|
|
902
|
+
* @param source - An array of source work item types to check for in the WIQL.
|
|
903
|
+
* @param target - An array of target work item types to check for in the WIQL.
|
|
904
|
+
* @returns A boolean indicating whether the WIQL includes at least one source work item type
|
|
905
|
+
* and at least one target work item type.
|
|
906
|
+
*/
|
|
907
|
+
private matchesSourceTargetCondition(wiql: string, source: string[], target: string[]): boolean {
|
|
908
|
+
let isSourceIncluded = false;
|
|
909
|
+
let isTargetIncluded = false;
|
|
910
|
+
|
|
911
|
+
for (const src of source) {
|
|
912
|
+
if (wiql.includes(`Source.[System.WorkItemType] = '${src}'`)) {
|
|
913
|
+
isSourceIncluded = true;
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
858
917
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
918
|
+
for (const tgt of target) {
|
|
919
|
+
if (wiql.includes(`Target.[System.WorkItemType] = '${tgt}'`)) {
|
|
920
|
+
isTargetIncluded = true;
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return isSourceIncluded && isTargetIncluded;
|
|
865
926
|
}
|
|
866
927
|
|
|
867
928
|
// check if the query is a bug query
|