@elisra-devops/docgen-data-provider 1.93.0 → 1.94.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elisra-devops/docgen-data-provider",
3
- "version": "1.93.0",
3
+ "version": "1.94.0",
4
4
  "description": "A document generator data provider, aimed to retrive data from azure devops",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3049,7 +3049,7 @@ export default class ResultDataProvider {
3049
3049
  // Fetch detailed information for each test point and map to required format
3050
3050
  const detailedPoints = await Promise.all(
3051
3051
  latestPoints.map(async (point: any) => {
3052
- const url = `${point.url}?witFields=Microsoft.VSTS.TCM.Steps&includePointDetails=true`;
3052
+ const url = `${point.url}?witFields=Microsoft.VSTS.TCM.Steps,System.Rev&includePointDetails=true`;
3053
3053
  const detailedPoint = await TFSServices.getItemContent(url, this.token);
3054
3054
  return this.mapTestPointForCrossPlans(detailedPoint, projectName);
3055
3055
  // return this.mapTestPointForCrossPlans(detailedPoint, projectName);
@@ -3142,7 +3142,7 @@ export default class ResultDataProvider {
3142
3142
  testPlanId: string,
3143
3143
  suiteId: string
3144
3144
  ): Promise<any[]> {
3145
- const url = `${this.orgUrl}${projectName}/_apis/testplan/Plans/${testPlanId}/Suites/${suiteId}/TestCase?witFields=Microsoft.VSTS.TCM.Steps`;
3145
+ const url = `${this.orgUrl}${projectName}/_apis/testplan/Plans/${testPlanId}/Suites/${suiteId}/TestCase?witFields=Microsoft.VSTS.TCM.Steps,System.Rev`;
3146
3146
 
3147
3147
  const { value: testCases } = await TFSServices.getItemContent(url, this.token);
3148
3148
 
@@ -3186,8 +3186,28 @@ export default class ResultDataProvider {
3186
3186
  return fields;
3187
3187
  }
3188
3188
 
3189
+ private getFieldValueByName(fields: Record<string, any>, fieldName: string): any {
3190
+ if (!fields || typeof fields !== 'object') return undefined;
3191
+ if (Object.prototype.hasOwnProperty.call(fields, fieldName)) {
3192
+ return fields[fieldName];
3193
+ }
3194
+
3195
+ const lookupName = String(fieldName || '').toLowerCase().trim();
3196
+ if (!lookupName) return undefined;
3197
+ const matchedKey = Object.keys(fields).find(
3198
+ (key) => String(key || '').toLowerCase().trim() === lookupName
3199
+ );
3200
+ return matchedKey ? fields[matchedKey] : undefined;
3201
+ }
3202
+
3189
3203
  private resolveSuiteTestCaseRevision(testCaseItem: any): number {
3204
+ const fieldsFromList = this.extractWorkItemFieldsMap(testCaseItem?.workItem?.workItemFields);
3205
+ const fieldsFromMap = testCaseItem?.workItem?.fields || {};
3206
+ const systemRevFromList = this.getFieldValueByName(fieldsFromList, 'System.Rev');
3207
+ const systemRevFromMap = this.getFieldValueByName(fieldsFromMap, 'System.Rev');
3190
3208
  const revisionCandidates = [
3209
+ systemRevFromList,
3210
+ systemRevFromMap,
3191
3211
  testCaseItem?.workItem?.rev,
3192
3212
  testCaseItem?.workItem?.revision,
3193
3213
  testCaseItem?.workItem?.version,
@@ -679,6 +679,34 @@ describe('ResultDataProvider', () => {
679
679
 
680
680
  // Assert
681
681
  expect(result).toEqual(mockTestCases.value);
682
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
683
+ expect.stringContaining(
684
+ `/_apis/testplan/Plans/${mockTestPlanId}/Suites/${mockSuiteId}/TestCase?witFields=Microsoft.VSTS.TCM.Steps,System.Rev`
685
+ ),
686
+ mockToken
687
+ );
688
+ });
689
+ });
690
+
691
+ describe('resolveSuiteTestCaseRevision', () => {
692
+ it('should resolve System.Rev from workItemFields', () => {
693
+ const revision = (resultDataProvider as any).resolveSuiteTestCaseRevision({
694
+ workItem: {
695
+ workItemFields: [{ key: 'System.Rev', value: '12' }],
696
+ },
697
+ });
698
+
699
+ expect(revision).toBe(12);
700
+ });
701
+
702
+ it('should resolve System.Rev case-insensitively from workItem fields map', () => {
703
+ const revision = (resultDataProvider as any).resolveSuiteTestCaseRevision({
704
+ workItem: {
705
+ fields: { 'system.rev': 14 },
706
+ },
707
+ });
708
+
709
+ expect(revision).toBe(14);
682
710
  });
683
711
  });
684
712
 
@@ -837,7 +865,7 @@ describe('ResultDataProvider', () => {
837
865
  const result = await (resultDataProvider as any).fetchCrossTestPoints(mockProjectName, [1, 2]);
838
866
 
839
867
  expect(TFSServices.getItemContent).toHaveBeenCalledWith(
840
- 'https://example.com/points/2?witFields=Microsoft.VSTS.TCM.Steps&includePointDetails=true',
868
+ 'https://example.com/points/2?witFields=Microsoft.VSTS.TCM.Steps,System.Rev&includePointDetails=true',
841
869
  mockToken
842
870
  );
843
871
  expect(result).toHaveLength(2);
@@ -3874,6 +3902,53 @@ describe('ResultDataProvider', () => {
3874
3902
  expect(res).toEqual(expect.objectContaining({ testCaseRevision: 9 }));
3875
3903
  });
3876
3904
 
3905
+ it('should resolve no-run revision from System.Rev in suite test-case fields', async () => {
3906
+ const point = {
3907
+ testCaseId: '321',
3908
+ testCaseName: 'TC 321',
3909
+ outcome: 'Not Run',
3910
+ suiteTestCase: {
3911
+ workItem: {
3912
+ id: 321,
3913
+ workItemFields: [
3914
+ { key: 'System.Rev', value: '13' },
3915
+ { key: 'Microsoft.VSTS.TCM.Steps', value: '<steps></steps>' },
3916
+ ],
3917
+ },
3918
+ },
3919
+ testSuite: { id: '1', name: 'Suite' },
3920
+ };
3921
+
3922
+ (TFSServices.getItemContent as jest.Mock).mockResolvedValueOnce({
3923
+ id: 321,
3924
+ rev: 13,
3925
+ fields: {
3926
+ 'System.State': 'Design',
3927
+ 'System.CreatedDate': '2024-05-01T00:00:00',
3928
+ 'Microsoft.VSTS.TCM.Priority': 1,
3929
+ 'System.Title': 'Title 321',
3930
+ 'Microsoft.VSTS.TCM.Steps': '<steps></steps>',
3931
+ },
3932
+ relations: [],
3933
+ });
3934
+
3935
+ const res = await (resultDataProvider as any).fetchResultDataBasedOnWiBase(
3936
+ mockProjectName,
3937
+ '0',
3938
+ '0',
3939
+ true,
3940
+ [],
3941
+ false,
3942
+ point
3943
+ );
3944
+
3945
+ expect(TFSServices.getItemContent).toHaveBeenCalledWith(
3946
+ expect.stringContaining('/_apis/wit/workItems/321/revisions/13?$expand=all'),
3947
+ mockToken
3948
+ );
3949
+ expect(res).toEqual(expect.objectContaining({ testCaseRevision: 13 }));
3950
+ });
3951
+
3877
3952
  it('should append linked relations and filter testCaseWorkItemField when isTestReporter=true and isQueryMode=false', async () => {
3878
3953
  (TFSServices.getItemContent as jest.Mock).mockReset();
3879
3954
 
@@ -5045,6 +5120,35 @@ describe('ResultDataProvider', () => {
5045
5120
  expect(fetchStrategy).not.toHaveBeenCalled();
5046
5121
  expect(result).toEqual([]);
5047
5122
  });
5123
+
5124
+ it('should keep points without run/result IDs when test reporter mode is enabled', async () => {
5125
+ const testData = [
5126
+ {
5127
+ testSuiteId: 1,
5128
+ testPointsItems: [{ testCaseId: 10, lastRunId: 101, lastResultId: 201 }, { testCaseId: 11 }],
5129
+ },
5130
+ ];
5131
+ const fetchStrategy = jest
5132
+ .fn()
5133
+ .mockResolvedValueOnce({ testCaseId: 10 })
5134
+ .mockResolvedValueOnce({ testCaseId: 11 });
5135
+
5136
+ const result = await (resultDataProvider as any).fetchAllResultDataBase(
5137
+ testData,
5138
+ mockProjectName,
5139
+ true,
5140
+ fetchStrategy
5141
+ );
5142
+
5143
+ expect(fetchStrategy).toHaveBeenCalledTimes(2);
5144
+ expect(fetchStrategy).toHaveBeenNthCalledWith(
5145
+ 2,
5146
+ mockProjectName,
5147
+ 1,
5148
+ expect.objectContaining({ testCaseId: 11 })
5149
+ );
5150
+ expect(result).toEqual([{ testCaseId: 10 }, { testCaseId: 11 }]);
5151
+ });
5048
5152
  });
5049
5153
 
5050
5154
  describe('fetchResultDataBase', () => {
@@ -5071,6 +5175,26 @@ describe('ResultDataProvider', () => {
5071
5175
  expect(fetchResultMethod).toHaveBeenCalled();
5072
5176
  expect(result).toBeDefined();
5073
5177
  });
5178
+
5179
+ it('should call fetch method with runId/resultId as 0 when point has no run history', async () => {
5180
+ const point = { testCaseId: 15, lastRunId: undefined, lastResultId: undefined };
5181
+ const fetchResultMethod = jest.fn().mockResolvedValue({
5182
+ testCase: { id: 15, name: 'TC 15' },
5183
+ testSuite: { name: 'S' },
5184
+ iterationDetails: [],
5185
+ });
5186
+ const createResponseObject = jest.fn().mockReturnValue({ id: 15 });
5187
+
5188
+ await (resultDataProvider as any).fetchResultDataBase(
5189
+ mockProjectName,
5190
+ 'suite-no-runs',
5191
+ point,
5192
+ fetchResultMethod,
5193
+ createResponseObject
5194
+ );
5195
+
5196
+ expect(fetchResultMethod).toHaveBeenCalledWith(mockProjectName, '0', '0');
5197
+ });
5074
5198
  });
5075
5199
 
5076
5200
  describe('getCombinedResultsSummary', () => {
@@ -5460,6 +5584,86 @@ describe('ResultDataProvider', () => {
5460
5584
  })
5461
5585
  );
5462
5586
  });
5587
+
5588
+ it('should return test-level row with empty step fields when suite has no run history', async () => {
5589
+ jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan 12');
5590
+ jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([
5591
+ {
5592
+ testSuiteId: 300,
5593
+ suiteId: 300,
5594
+ suiteName: 'suite no runs',
5595
+ parentSuiteId: 100,
5596
+ parentSuiteName: 'Rel3',
5597
+ suitePath: 'Root/Rel3/suite no runs',
5598
+ testGroupName: 'suite no runs',
5599
+ },
5600
+ ]);
5601
+
5602
+ jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
5603
+ {
5604
+ testSuiteId: 300,
5605
+ suiteId: 300,
5606
+ suiteName: 'suite no runs',
5607
+ parentSuiteId: 100,
5608
+ parentSuiteName: 'Rel3',
5609
+ suitePath: 'Root/Rel3/suite no runs',
5610
+ testGroupName: 'suite no runs',
5611
+ testPointsItems: [
5612
+ {
5613
+ testCaseId: 55,
5614
+ testCaseName: 'TC 55',
5615
+ outcome: 'Not Run',
5616
+ testPointId: 9001,
5617
+ lastRunId: undefined,
5618
+ lastResultId: undefined,
5619
+ lastResultDetails: undefined,
5620
+ },
5621
+ ],
5622
+ testCasesItems: [
5623
+ {
5624
+ workItem: {
5625
+ id: 55,
5626
+ workItemFields: [{ key: 'System.Rev', value: 4 }],
5627
+ },
5628
+ },
5629
+ ],
5630
+ },
5631
+ ]);
5632
+
5633
+ jest.spyOn(resultDataProvider as any, 'fetchAllResultDataTestReporter').mockResolvedValueOnce([
5634
+ {
5635
+ testCaseId: 55,
5636
+ testCase: { id: 55, name: 'TC 55' },
5637
+ testSuite: { name: 'suite no runs' },
5638
+ executionDate: '',
5639
+ testCaseResult: { resultMessage: 'Not Run', url: '' },
5640
+ customFields: {},
5641
+ runBy: '',
5642
+ iteration: undefined,
5643
+ lastRunId: undefined,
5644
+ lastResultId: undefined,
5645
+ },
5646
+ ]);
5647
+
5648
+ const result = await resultDataProvider.getTestReporterFlatResults(
5649
+ mockTestPlanId,
5650
+ mockProjectName,
5651
+ undefined,
5652
+ [],
5653
+ false
5654
+ );
5655
+
5656
+ expect(result.rows).toHaveLength(1);
5657
+ expect(result.rows[0]).toEqual(
5658
+ expect.objectContaining({
5659
+ testCaseId: 55,
5660
+ testRunId: undefined,
5661
+ testPointId: 9001,
5662
+ stepOutcome: undefined,
5663
+ stepStepIdentifier: '',
5664
+ })
5665
+ );
5666
+ });
5463
5667
  });
5464
5668
 
5465
5669
  describe('getCombinedResultsSummary - appendix branches', () => {