@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.
- package/bin/modules/ResultDataProvider.d.ts +14 -1
- package/bin/modules/ResultDataProvider.js +86 -35
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +267 -2
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +108 -73
- package/src/tests/modules/ResultDataProvider.test.ts +332 -2
|
@@ -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 () => {
|