@elisra-devops/docgen-data-provider 1.99.0 → 1.101.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.
@@ -4098,6 +4098,90 @@ describe('ResultDataProvider', () => {
4098
4098
  expect(result).toBe('NotApplicable');
4099
4099
  });
4100
4100
  });
4101
+ describe('runless key/resolution edge cases', () => {
4102
+ it('should prioritize run key over point/revision in buildIterationLookupKey', () => {
4103
+ const key = resultDataProvider.buildIterationLookupKey({
4104
+ testCaseId: 217916,
4105
+ lastRunId: 101,
4106
+ lastResultId: 202,
4107
+ testPointId: 303,
4108
+ testCaseRevision: 18,
4109
+ });
4110
+ expect(key).toBe('101-202-217916');
4111
+ });
4112
+ it('should treat blank run identifiers as missing and use point/revision fallback keys', () => {
4113
+ const pointKey = resultDataProvider.buildIterationLookupKey({
4114
+ testCaseId: 217916,
4115
+ lastRunId: ' ',
4116
+ lastResultId: '',
4117
+ testPointId: 303,
4118
+ testCaseRevision: 18,
4119
+ });
4120
+ const revKey = resultDataProvider.buildIterationLookupKey({
4121
+ testCaseId: 217916,
4122
+ lastRunId: ' ',
4123
+ lastResultId: '',
4124
+ testCaseRevision: 18,
4125
+ });
4126
+ expect(pointKey).toBe('point-303-217916');
4127
+ expect(revKey).toBe('rev-217916-18');
4128
+ });
4129
+ it('should deduplicate candidate keys when primary key is already case-only', () => {
4130
+ const keys = resultDataProvider.buildIterationLookupCandidates({
4131
+ testCaseId: 217916,
4132
+ });
4133
+ expect(keys).toEqual(['217916']);
4134
+ });
4135
+ it('should resolve point revision from suite test case when test case revision is missing', () => {
4136
+ const revision = resultDataProvider.resolvePointRevision({ workItem: { workItemFields: [] } }, {
4137
+ suiteTestCase: {
4138
+ workItem: {
4139
+ workItemFields: [{ key: 'System.Rev', value: 23 }],
4140
+ },
4141
+ },
4142
+ });
4143
+ expect(revision).toBe(23);
4144
+ });
4145
+ it('should short-circuit runless resolution when asOf snapshot already has steps', async () => {
4146
+ const asOfSnapshot = { rev: 18, fields: { 'Microsoft.VSTS.TCM.Steps': '<steps><step id="1"/></steps>' } };
4147
+ const asOfSpy = jest.spyOn(resultDataProvider, 'fetchWorkItemByAsOf').mockResolvedValueOnce(asOfSnapshot);
4148
+ const revSpy = jest.spyOn(resultDataProvider, 'fetchWorkItemByRevision').mockResolvedValueOnce(null);
4149
+ const latestSpy = jest.spyOn(resultDataProvider, 'fetchWorkItemLatest').mockResolvedValueOnce(null);
4150
+ const res = await resultDataProvider.resolveRunlessTestCaseData(mockProjectName, 217916, 18, '2025-05-20T00:47:30.610Z', null, true);
4151
+ expect(res).toBe(asOfSnapshot);
4152
+ expect(asOfSpy).toHaveBeenCalledTimes(1);
4153
+ expect(revSpy).not.toHaveBeenCalled();
4154
+ expect(latestSpy).not.toHaveBeenCalled();
4155
+ });
4156
+ it('should keep earliest snapshot when no source has steps', async () => {
4157
+ const asOfSnapshot = { rev: 1, fields: { 'Microsoft.VSTS.TCM.Steps': '' } };
4158
+ jest
4159
+ .spyOn(resultDataProvider, 'fetchWorkItemByAsOf')
4160
+ .mockResolvedValueOnce(asOfSnapshot);
4161
+ jest
4162
+ .spyOn(resultDataProvider, 'fetchWorkItemByRevision')
4163
+ .mockResolvedValueOnce({ rev: 2, fields: { 'Microsoft.VSTS.TCM.Steps': ' ' } });
4164
+ const latestSnapshot = { rev: 3, fields: { 'Microsoft.VSTS.TCM.Steps': '' } };
4165
+ jest.spyOn(resultDataProvider, 'fetchWorkItemLatest').mockResolvedValueOnce(latestSnapshot);
4166
+ const res = await resultDataProvider.resolveRunlessTestCaseData(mockProjectName, 217916, 18, '2025-05-20T00:47:30.610Z', null, true);
4167
+ expect(res).toBe(asOfSnapshot);
4168
+ });
4169
+ it('should return latest snapshot when only latest has steps', async () => {
4170
+ jest
4171
+ .spyOn(resultDataProvider, 'fetchWorkItemByAsOf')
4172
+ .mockResolvedValueOnce({ rev: 1, fields: { 'Microsoft.VSTS.TCM.Steps': '' } });
4173
+ jest
4174
+ .spyOn(resultDataProvider, 'fetchWorkItemByRevision')
4175
+ .mockResolvedValueOnce({ rev: 2, fields: { 'Microsoft.VSTS.TCM.Steps': '' } });
4176
+ const latestSnapshot = {
4177
+ rev: 3,
4178
+ fields: { 'Microsoft.VSTS.TCM.Steps': '<steps><step id="1"/></steps>' },
4179
+ };
4180
+ jest.spyOn(resultDataProvider, 'fetchWorkItemLatest').mockResolvedValueOnce(latestSnapshot);
4181
+ const res = await resultDataProvider.resolveRunlessTestCaseData(mockProjectName, 217916, 18, '2025-05-20T00:47:30.610Z', null, true);
4182
+ expect(res).toBe(latestSnapshot);
4183
+ });
4184
+ });
4101
4185
  describe('createIterationsMap', () => {
4102
4186
  it('should create iterations map from results', () => {
4103
4187
  // Arrange
@@ -4132,7 +4216,7 @@ describe('ResultDataProvider', () => {
4132
4216
  // Assert
4133
4217
  expect(result['1']).toBeDefined();
4134
4218
  });
4135
- it('should map runless test reporter item with iteration by testCaseId key', () => {
4219
+ it('should map runless test reporter item with iteration by testCaseId key when point id is missing', () => {
4136
4220
  const iterations = [
4137
4221
  { testCaseId: 217897, lastRunId: undefined, lastResultId: undefined, iteration: { actionResults: [] } },
4138
4222
  ];
@@ -4140,6 +4224,34 @@ describe('ResultDataProvider', () => {
4140
4224
  expect(result['217897']).toBeDefined();
4141
4225
  expect(result['undefined-undefined-217897']).toBeUndefined();
4142
4226
  });
4227
+ it('should map runless test reporter item with iteration by point-aware key when point id exists', () => {
4228
+ const iterations = [
4229
+ {
4230
+ testCaseId: 217916,
4231
+ testPointId: 1001,
4232
+ lastRunId: undefined,
4233
+ lastResultId: undefined,
4234
+ iteration: { actionResults: [] },
4235
+ },
4236
+ ];
4237
+ const result = resultDataProvider.createIterationsMap(iterations, true, true);
4238
+ expect(result['point-1001-217916']).toBeDefined();
4239
+ expect(result['217916']).toBeUndefined();
4240
+ });
4241
+ it('should map runless test reporter item with iteration by revision key when point id is missing', () => {
4242
+ const iterations = [
4243
+ {
4244
+ testCaseId: 217916,
4245
+ testCaseRevision: 18,
4246
+ lastRunId: undefined,
4247
+ lastResultId: undefined,
4248
+ iteration: { actionResults: [] },
4249
+ },
4250
+ ];
4251
+ const result = resultDataProvider.createIterationsMap(iterations, true, true);
4252
+ expect(result['rev-217916-18']).toBeDefined();
4253
+ expect(result['217916']).toBeUndefined();
4254
+ });
4143
4255
  });
4144
4256
  describe('alignStepsWithIterationsBase', () => {
4145
4257
  it('should return empty array when no iterations', () => {
@@ -4832,13 +4944,14 @@ describe('ResultDataProvider', () => {
4832
4944
  const testData = [
4833
4945
  {
4834
4946
  testGroupName: 'G',
4835
- testPointsItems: [{ testCaseId: 217916, testCaseName: 'TC 217916', testCaseUrl: 'u' }],
4947
+ testPointsItems: [{ testCaseId: 217916, testPointId: 1001, testCaseName: 'TC 217916', testCaseUrl: 'u' }],
4836
4948
  testCasesItems: [],
4837
4949
  },
4838
4950
  ];
4839
4951
  const iterations = [
4840
4952
  {
4841
4953
  testCaseId: 217916,
4954
+ testPointId: 1001,
4842
4955
  lastRunId: undefined,
4843
4956
  lastResultId: undefined,
4844
4957
  iteration: {
@@ -4870,6 +4983,158 @@ describe('ResultDataProvider', () => {
4870
4983
  expect(res).toHaveLength(1);
4871
4984
  expect(res[0]).toEqual(expect.objectContaining({ stepNo: '1', stepStatus: 'Not Run' }));
4872
4985
  });
4986
+ it('should match runless iterations by testPointId when the same testCase appears in multiple suites', () => {
4987
+ const testData = [
4988
+ {
4989
+ testGroupName: 'Suite A',
4990
+ testPointsItems: [{ testCaseId: 217916, testPointId: 2001, testCaseName: 'TC', testCaseUrl: 'u1' }],
4991
+ testCasesItems: [],
4992
+ },
4993
+ {
4994
+ testGroupName: 'Suite B',
4995
+ testPointsItems: [{ testCaseId: 217916, testPointId: 2002, testCaseName: 'TC', testCaseUrl: 'u2' }],
4996
+ testCasesItems: [],
4997
+ },
4998
+ ];
4999
+ const iterations = [
5000
+ {
5001
+ testCaseId: 217916,
5002
+ testPointId: 2001,
5003
+ lastRunId: undefined,
5004
+ lastResultId: undefined,
5005
+ iteration: {
5006
+ actionResults: [
5007
+ {
5008
+ stepIdentifier: '16',
5009
+ stepPosition: '1',
5010
+ action: 'A',
5011
+ expected: 'E',
5012
+ outcome: 'Unspecified',
5013
+ isSharedStepTitle: false,
5014
+ errorMessage: '',
5015
+ },
5016
+ ],
5017
+ },
5018
+ testCaseResult: 'Not Run',
5019
+ comment: '',
5020
+ runBy: { displayName: 'u' },
5021
+ failureType: '',
5022
+ executionDate: '',
5023
+ configurationName: '',
5024
+ relatedRequirements: [],
5025
+ relatedBugs: [],
5026
+ relatedCRs: [],
5027
+ customFields: {},
5028
+ },
5029
+ {
5030
+ testCaseId: 217916,
5031
+ testPointId: 2002,
5032
+ lastRunId: undefined,
5033
+ lastResultId: undefined,
5034
+ iteration: { actionResults: [] },
5035
+ testCaseResult: 'Not Run',
5036
+ comment: '',
5037
+ runBy: { displayName: 'u' },
5038
+ failureType: '',
5039
+ executionDate: '',
5040
+ configurationName: '',
5041
+ relatedRequirements: [],
5042
+ relatedBugs: [],
5043
+ relatedCRs: [],
5044
+ customFields: {},
5045
+ },
5046
+ ];
5047
+ const res = resultDataProvider.alignStepsWithIterationsTestReporter(testData, iterations, ['includeSteps@stepsRunProperties', 'stepRunStatus@stepsRunProperties'], true);
5048
+ expect(res).toHaveLength(2);
5049
+ const suiteA = res.find((row) => (row === null || row === void 0 ? void 0 : row.suiteName) === 'Suite A');
5050
+ const suiteB = res.find((row) => (row === null || row === void 0 ? void 0 : row.suiteName) === 'Suite B');
5051
+ expect(suiteA).toEqual(expect.objectContaining({ stepNo: '1', stepStatus: 'Not Run' }));
5052
+ expect(suiteB).toBeDefined();
5053
+ expect(Object.prototype.hasOwnProperty.call(suiteB, 'stepNo')).toBe(false);
5054
+ });
5055
+ it('should match runless iterations by revision when point id is missing and same testCase appears in multiple suites', () => {
5056
+ const testData = [
5057
+ {
5058
+ testGroupName: 'Suite A',
5059
+ testPointsItems: [{ testCaseId: 217916, testCaseName: 'TC', testCaseUrl: 'u1' }],
5060
+ testCasesItems: [
5061
+ {
5062
+ workItem: {
5063
+ id: 217916,
5064
+ workItemFields: [{ key: 'System.Rev', value: 18 }],
5065
+ },
5066
+ },
5067
+ ],
5068
+ },
5069
+ {
5070
+ testGroupName: 'Suite B',
5071
+ testPointsItems: [{ testCaseId: 217916, testCaseName: 'TC', testCaseUrl: 'u2' }],
5072
+ testCasesItems: [
5073
+ {
5074
+ workItem: {
5075
+ id: 217916,
5076
+ workItemFields: [{ key: 'System.Rev', value: 23 }],
5077
+ },
5078
+ },
5079
+ ],
5080
+ },
5081
+ ];
5082
+ const iterations = [
5083
+ {
5084
+ testCaseId: 217916,
5085
+ testCaseRevision: 18,
5086
+ lastRunId: undefined,
5087
+ lastResultId: undefined,
5088
+ iteration: {
5089
+ actionResults: [
5090
+ {
5091
+ stepIdentifier: '16',
5092
+ stepPosition: '1',
5093
+ action: 'A',
5094
+ expected: 'E',
5095
+ outcome: 'Unspecified',
5096
+ isSharedStepTitle: false,
5097
+ errorMessage: '',
5098
+ },
5099
+ ],
5100
+ },
5101
+ testCaseResult: 'Not Run',
5102
+ comment: '',
5103
+ runBy: { displayName: 'u' },
5104
+ failureType: '',
5105
+ executionDate: '',
5106
+ configurationName: '',
5107
+ relatedRequirements: [],
5108
+ relatedBugs: [],
5109
+ relatedCRs: [],
5110
+ customFields: {},
5111
+ },
5112
+ {
5113
+ testCaseId: 217916,
5114
+ testCaseRevision: 23,
5115
+ lastRunId: undefined,
5116
+ lastResultId: undefined,
5117
+ iteration: { actionResults: [] },
5118
+ testCaseResult: 'Not Run',
5119
+ comment: '',
5120
+ runBy: { displayName: 'u' },
5121
+ failureType: '',
5122
+ executionDate: '',
5123
+ configurationName: '',
5124
+ relatedRequirements: [],
5125
+ relatedBugs: [],
5126
+ relatedCRs: [],
5127
+ customFields: {},
5128
+ },
5129
+ ];
5130
+ const res = resultDataProvider.alignStepsWithIterationsTestReporter(testData, iterations, ['includeSteps@stepsRunProperties', 'stepRunStatus@stepsRunProperties'], true);
5131
+ expect(res).toHaveLength(2);
5132
+ const suiteA = res.find((row) => (row === null || row === void 0 ? void 0 : row.suiteName) === 'Suite A');
5133
+ const suiteB = res.find((row) => (row === null || row === void 0 ? void 0 : row.suiteName) === 'Suite B');
5134
+ expect(suiteA).toEqual(expect.objectContaining({ stepNo: '1', stepStatus: 'Not Run' }));
5135
+ expect(suiteB).toBeDefined();
5136
+ expect(Object.prototype.hasOwnProperty.call(suiteB, 'stepNo')).toBe(false);
5137
+ });
4873
5138
  });
4874
5139
  describe('fetchOpenPcrData', () => {
4875
5140
  it('should populate both trace maps using linked work items', async () => {