@elisra-devops/docgen-data-provider 1.75.0 → 1.76.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/mewp-reporting.d.ts +120 -0
- package/bin/models/mewp-reporting.js +3 -0
- package/bin/models/mewp-reporting.js.map +1 -0
- package/bin/modules/ResultDataProvider.d.ts +46 -6
- package/bin/modules/ResultDataProvider.js +1115 -118
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +1285 -33
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/bin/utils/mewpExternalIngestionUtils.d.ts +17 -0
- package/bin/utils/mewpExternalIngestionUtils.js +197 -0
- package/bin/utils/mewpExternalIngestionUtils.js.map +1 -0
- package/bin/utils/mewpExternalTableUtils.d.ts +36 -0
- package/bin/utils/mewpExternalTableUtils.js +320 -0
- package/bin/utils/mewpExternalTableUtils.js.map +1 -0
- package/package.json +10 -1
- package/src/models/mewp-reporting.ts +138 -0
- package/src/modules/ResultDataProvider.ts +1342 -166
- package/src/tests/modules/ResultDataProvider.test.ts +1471 -42
- package/src/utils/mewpExternalIngestionUtils.ts +270 -0
- package/src/utils/mewpExternalTableUtils.ts +461 -0
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tfs_1 = require("../../helpers/tfs");
|
|
4
4
|
const ResultDataProvider_1 = require("../../modules/ResultDataProvider");
|
|
5
5
|
const logger_1 = require("../../utils/logger");
|
|
6
|
+
const axios_1 = require("axios");
|
|
7
|
+
const XLSX = require("xlsx");
|
|
6
8
|
// Mock dependencies
|
|
7
9
|
jest.mock('../../helpers/tfs');
|
|
8
10
|
jest.mock('../../utils/logger');
|
|
@@ -22,6 +24,12 @@ describe('ResultDataProvider', () => {
|
|
|
22
24
|
const mockToken = 'mock-token';
|
|
23
25
|
const mockProjectName = 'test-project';
|
|
24
26
|
const mockTestPlanId = '12345';
|
|
27
|
+
const buildWorkbookBuffer = (rows) => {
|
|
28
|
+
const worksheet = XLSX.utils.aoa_to_sheet(rows);
|
|
29
|
+
const workbook = XLSX.utils.book_new();
|
|
30
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
31
|
+
return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' });
|
|
32
|
+
};
|
|
25
33
|
beforeEach(() => {
|
|
26
34
|
jest.clearAllMocks();
|
|
27
35
|
resultDataProvider = new ResultDataProvider_1.default(mockOrgUrl, mockToken);
|
|
@@ -880,6 +888,8 @@ describe('ResultDataProvider', () => {
|
|
|
880
888
|
{
|
|
881
889
|
workItemId: 5001,
|
|
882
890
|
requirementId: 'SR1001',
|
|
891
|
+
baseKey: 'SR1001',
|
|
892
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
883
893
|
title: 'Covered requirement',
|
|
884
894
|
responsibility: 'ESUK',
|
|
885
895
|
linkedTestCaseIds: [101],
|
|
@@ -887,6 +897,8 @@ describe('ResultDataProvider', () => {
|
|
|
887
897
|
{
|
|
888
898
|
workItemId: 5002,
|
|
889
899
|
requirementId: 'SR1002',
|
|
900
|
+
baseKey: 'SR1002',
|
|
901
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
890
902
|
title: 'Referenced from non-linked step text',
|
|
891
903
|
responsibility: 'IL',
|
|
892
904
|
linkedTestCaseIds: [],
|
|
@@ -894,6 +906,8 @@ describe('ResultDataProvider', () => {
|
|
|
894
906
|
{
|
|
895
907
|
workItemId: 5003,
|
|
896
908
|
requirementId: 'SR1003',
|
|
909
|
+
baseKey: 'SR1003',
|
|
910
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
897
911
|
title: 'Not covered by any test case',
|
|
898
912
|
responsibility: 'IL',
|
|
899
913
|
linkedTestCaseIds: [],
|
|
@@ -907,11 +921,11 @@ describe('ResultDataProvider', () => {
|
|
|
907
921
|
actionResults: [
|
|
908
922
|
{
|
|
909
923
|
action: 'Validate <b>S</b><b>R</b> 1 0 0 1 happy path',
|
|
910
|
-
expected: '',
|
|
924
|
+
expected: '<b>S</b><b>R</b> 1 0 0 1',
|
|
911
925
|
outcome: 'Passed',
|
|
912
926
|
},
|
|
913
|
-
{ action: 'Validate SR1001 failed flow', expected: '
|
|
914
|
-
{ action: '', expected: '
|
|
927
|
+
{ action: 'Validate SR1001 failed flow', expected: 'SR1001', outcome: 'Failed' },
|
|
928
|
+
{ action: '', expected: 'S R 1 0 0 1', outcome: 'Unspecified' },
|
|
915
929
|
],
|
|
916
930
|
},
|
|
917
931
|
},
|
|
@@ -928,48 +942,59 @@ describe('ResultDataProvider', () => {
|
|
|
928
942
|
stepId: '1',
|
|
929
943
|
stepPosition: '1',
|
|
930
944
|
action: 'Definition contains SR1002',
|
|
931
|
-
expected: '',
|
|
945
|
+
expected: 'SR1002',
|
|
932
946
|
isSharedStepTitle: false,
|
|
933
947
|
},
|
|
934
948
|
]);
|
|
935
949
|
const result = await resultDataProvider.getMewpL2CoverageFlatResults('123', mockProjectName, [1]);
|
|
936
950
|
expect(result).toEqual(expect.objectContaining({
|
|
937
951
|
sheetName: expect.stringContaining('MEWP L2 Coverage'),
|
|
938
|
-
columnOrder: expect.arrayContaining(['
|
|
952
|
+
columnOrder: expect.arrayContaining(['L2 REQ ID', 'L2 REQ Title', 'L2 Run Status']),
|
|
939
953
|
}));
|
|
940
|
-
const covered = result.rows.find((row) => row['
|
|
941
|
-
const inferredByStepText = result.rows.find((row) => row['
|
|
942
|
-
const uncovered = result.rows.find((row) => row['
|
|
943
|
-
(row['Test case id'] === '' || row['Test case id'] === undefined || row['Test case id'] === null));
|
|
954
|
+
const covered = result.rows.find((row) => row['L2 REQ ID'] === 'SR1001');
|
|
955
|
+
const inferredByStepText = result.rows.find((row) => row['L2 REQ ID'] === 'SR1002');
|
|
956
|
+
const uncovered = result.rows.find((row) => row['L2 REQ ID'] === 'SR1003');
|
|
944
957
|
expect(covered).toEqual(expect.objectContaining({
|
|
945
|
-
'
|
|
946
|
-
'
|
|
947
|
-
'
|
|
948
|
-
'
|
|
949
|
-
'
|
|
950
|
-
'
|
|
958
|
+
'L2 REQ Title': 'Covered requirement',
|
|
959
|
+
'L2 SubSystem': '',
|
|
960
|
+
'L2 Run Status': 'Fail',
|
|
961
|
+
'Bug ID': '',
|
|
962
|
+
'L3 REQ ID': '',
|
|
963
|
+
'L4 REQ ID': '',
|
|
951
964
|
}));
|
|
952
965
|
expect(inferredByStepText).toEqual(expect.objectContaining({
|
|
953
|
-
'
|
|
954
|
-
'
|
|
955
|
-
'
|
|
956
|
-
'Number of passed steps': 0,
|
|
957
|
-
'Number of failed steps': 0,
|
|
958
|
-
'Number of not run tests': 1,
|
|
966
|
+
'L2 REQ Title': 'Referenced from non-linked step text',
|
|
967
|
+
'L2 SubSystem': '',
|
|
968
|
+
'L2 Run Status': 'Not Run',
|
|
959
969
|
}));
|
|
960
970
|
expect(uncovered).toEqual(expect.objectContaining({
|
|
961
|
-
'
|
|
962
|
-
'
|
|
963
|
-
'
|
|
964
|
-
'Number of passed steps': 0,
|
|
965
|
-
'Number of failed steps': 0,
|
|
966
|
-
'Number of not run tests': 0,
|
|
971
|
+
'L2 REQ Title': 'Not covered by any test case',
|
|
972
|
+
'L2 SubSystem': '',
|
|
973
|
+
'L2 Run Status': 'Not Run',
|
|
967
974
|
}));
|
|
968
975
|
});
|
|
969
976
|
it('should extract SR ids from HTML/spacing and return unique ids per step text', () => {
|
|
970
|
-
const text = '
|
|
977
|
+
const text = '<b>S</b><b>R</b> 0 0 0 1; SR0002; S R 0 0 0 3; SR0002; <b>SR</b>0004';
|
|
971
978
|
const codes = resultDataProvider.extractRequirementCodesFromText(text);
|
|
972
|
-
expect([...codes].sort()).toEqual(['
|
|
979
|
+
expect([...codes].sort()).toEqual(['SR0001', 'SR0002', 'SR0003', 'SR0004']);
|
|
980
|
+
});
|
|
981
|
+
it('should keep only clean SR tokens and ignore noisy version/VVRM fragments', () => {
|
|
982
|
+
const extract = (text) => [...resultDataProvider.extractRequirementCodesFromText(text)].sort();
|
|
983
|
+
expect(extract('SR12413; SR24513; SR25135 VVRM2425')).toEqual(['SR12413', 'SR24513']);
|
|
984
|
+
expect(extract('SR12413; SR12412; SR12413-V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
985
|
+
expect(extract('SR12413; SR12412; SR12413 V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
986
|
+
expect(extract('SR12413, SR12412, SR12413-V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
987
|
+
const extractWithSuffix = (text) => [
|
|
988
|
+
...resultDataProvider.extractRequirementCodesFromExpectedText(text, true),
|
|
989
|
+
].sort();
|
|
990
|
+
expect(extractWithSuffix('SR0095-2,3; SR0100-1,2,3,4')).toEqual([
|
|
991
|
+
'SR0095-2',
|
|
992
|
+
'SR0095-3',
|
|
993
|
+
'SR0100-1',
|
|
994
|
+
'SR0100-2',
|
|
995
|
+
'SR0100-3',
|
|
996
|
+
'SR0100-4',
|
|
997
|
+
]);
|
|
973
998
|
});
|
|
974
999
|
it('should not backfill definition steps as not-run when a real run exists but has no action results', async () => {
|
|
975
1000
|
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
@@ -991,6 +1016,8 @@ describe('ResultDataProvider', () => {
|
|
|
991
1016
|
{
|
|
992
1017
|
workItemId: 7001,
|
|
993
1018
|
requirementId: 'SR2001',
|
|
1019
|
+
baseKey: 'SR2001',
|
|
1020
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
994
1021
|
title: 'Has run but no actions',
|
|
995
1022
|
responsibility: 'ESUK',
|
|
996
1023
|
linkedTestCaseIds: [101],
|
|
@@ -1018,12 +1045,10 @@ describe('ResultDataProvider', () => {
|
|
|
1018
1045
|
},
|
|
1019
1046
|
]);
|
|
1020
1047
|
const result = await resultDataProvider.getMewpL2CoverageFlatResults('123', mockProjectName, [1]);
|
|
1021
|
-
const row = result.rows.find((item) => item['
|
|
1048
|
+
const row = result.rows.find((item) => item['L2 REQ ID'] === 'SR2001');
|
|
1022
1049
|
expect(parseSpy).not.toHaveBeenCalled();
|
|
1023
1050
|
expect(row).toEqual(expect.objectContaining({
|
|
1024
|
-
'
|
|
1025
|
-
'Number of failed steps': 0,
|
|
1026
|
-
'Number of not run tests': 0,
|
|
1051
|
+
'L2 Run Status': 'Not Run',
|
|
1027
1052
|
}));
|
|
1028
1053
|
});
|
|
1029
1054
|
it('should not infer requirement id from unrelated SR text in non-identifier fields', () => {
|
|
@@ -1041,6 +1066,1233 @@ describe('ResultDataProvider', () => {
|
|
|
1041
1066
|
});
|
|
1042
1067
|
expect(responsibility).toBe('IL');
|
|
1043
1068
|
});
|
|
1069
|
+
it('should derive responsibility from AreaPath suffix ATP/ATP\\\\ESUK when SAPWBS is empty', () => {
|
|
1070
|
+
const esuk = resultDataProvider.deriveMewpResponsibility({
|
|
1071
|
+
'Custom.SAPWBS': '',
|
|
1072
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK',
|
|
1073
|
+
});
|
|
1074
|
+
const il = resultDataProvider.deriveMewpResponsibility({
|
|
1075
|
+
'Custom.SAPWBS': '',
|
|
1076
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP',
|
|
1077
|
+
});
|
|
1078
|
+
expect(esuk).toBe('ESUK');
|
|
1079
|
+
expect(il).toBe('IL');
|
|
1080
|
+
});
|
|
1081
|
+
it('should emit additive rows for bugs and L3/L4 links without bug duplication', () => {
|
|
1082
|
+
const requirements = [
|
|
1083
|
+
{
|
|
1084
|
+
requirementId: 'SR5303',
|
|
1085
|
+
baseKey: 'SR5303',
|
|
1086
|
+
title: 'Req 5303',
|
|
1087
|
+
subSystem: 'Power',
|
|
1088
|
+
responsibility: 'ESUK',
|
|
1089
|
+
linkedTestCaseIds: [101],
|
|
1090
|
+
},
|
|
1091
|
+
];
|
|
1092
|
+
const requirementIndex = new Map([
|
|
1093
|
+
[
|
|
1094
|
+
'SR5303',
|
|
1095
|
+
new Map([
|
|
1096
|
+
[
|
|
1097
|
+
101,
|
|
1098
|
+
{
|
|
1099
|
+
passed: 0,
|
|
1100
|
+
failed: 1,
|
|
1101
|
+
notRun: 0,
|
|
1102
|
+
},
|
|
1103
|
+
],
|
|
1104
|
+
]),
|
|
1105
|
+
],
|
|
1106
|
+
]);
|
|
1107
|
+
const observedTestCaseIdsByRequirement = new Map([
|
|
1108
|
+
['SR5303', new Set([101])],
|
|
1109
|
+
]);
|
|
1110
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1111
|
+
[
|
|
1112
|
+
101,
|
|
1113
|
+
{
|
|
1114
|
+
baseKeys: new Set(['SR5303']),
|
|
1115
|
+
fullCodes: new Set(['SR5303']),
|
|
1116
|
+
bugIds: new Set([10003, 20003]),
|
|
1117
|
+
},
|
|
1118
|
+
],
|
|
1119
|
+
]);
|
|
1120
|
+
const externalBugsByTestCase = new Map([
|
|
1121
|
+
[
|
|
1122
|
+
101,
|
|
1123
|
+
[
|
|
1124
|
+
{ id: 10003, title: 'Bug 10003', responsibility: 'Elisra', requirementBaseKey: 'SR5303' },
|
|
1125
|
+
{ id: 20003, title: 'Bug 20003', responsibility: 'ESUK', requirementBaseKey: 'SR5303' },
|
|
1126
|
+
],
|
|
1127
|
+
],
|
|
1128
|
+
]);
|
|
1129
|
+
const l3l4ByBaseKey = new Map([
|
|
1130
|
+
[
|
|
1131
|
+
'SR5303',
|
|
1132
|
+
[
|
|
1133
|
+
{ id: '9003', title: 'L3 9003', level: 'L3' },
|
|
1134
|
+
{ id: '9103', title: 'L4 9103', level: 'L4' },
|
|
1135
|
+
],
|
|
1136
|
+
],
|
|
1137
|
+
]);
|
|
1138
|
+
const rows = resultDataProvider.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, l3l4ByBaseKey, externalBugsByTestCase);
|
|
1139
|
+
expect(rows).toHaveLength(4);
|
|
1140
|
+
expect(rows.map((row) => row['Bug ID'])).toEqual([10003, 20003, '', '']);
|
|
1141
|
+
expect(rows[2]).toEqual(expect.objectContaining({
|
|
1142
|
+
'L3 REQ ID': '9003',
|
|
1143
|
+
'L4 REQ ID': '',
|
|
1144
|
+
}));
|
|
1145
|
+
expect(rows[3]).toEqual(expect.objectContaining({
|
|
1146
|
+
'L3 REQ ID': '',
|
|
1147
|
+
'L4 REQ ID': '9103',
|
|
1148
|
+
}));
|
|
1149
|
+
});
|
|
1150
|
+
it('should not emit bug rows from ADO-linked bug ids when external bugs source is empty', () => {
|
|
1151
|
+
const requirements = [
|
|
1152
|
+
{
|
|
1153
|
+
requirementId: 'SR5304',
|
|
1154
|
+
baseKey: 'SR5304',
|
|
1155
|
+
title: 'Req 5304',
|
|
1156
|
+
subSystem: 'Power',
|
|
1157
|
+
responsibility: 'ESUK',
|
|
1158
|
+
linkedTestCaseIds: [101],
|
|
1159
|
+
},
|
|
1160
|
+
];
|
|
1161
|
+
const requirementIndex = new Map([
|
|
1162
|
+
[
|
|
1163
|
+
'SR5304',
|
|
1164
|
+
new Map([
|
|
1165
|
+
[
|
|
1166
|
+
101,
|
|
1167
|
+
{
|
|
1168
|
+
passed: 0,
|
|
1169
|
+
failed: 1,
|
|
1170
|
+
notRun: 0,
|
|
1171
|
+
},
|
|
1172
|
+
],
|
|
1173
|
+
]),
|
|
1174
|
+
],
|
|
1175
|
+
]);
|
|
1176
|
+
const observedTestCaseIdsByRequirement = new Map([
|
|
1177
|
+
['SR5304', new Set([101])],
|
|
1178
|
+
]);
|
|
1179
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1180
|
+
[
|
|
1181
|
+
101,
|
|
1182
|
+
{
|
|
1183
|
+
baseKeys: new Set(['SR5304']),
|
|
1184
|
+
fullCodes: new Set(['SR5304']),
|
|
1185
|
+
bugIds: new Set([55555]), // must be ignored in MEWP coverage mode
|
|
1186
|
+
},
|
|
1187
|
+
],
|
|
1188
|
+
]);
|
|
1189
|
+
const rows = resultDataProvider.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, new Map(), new Map());
|
|
1190
|
+
expect(rows).toHaveLength(1);
|
|
1191
|
+
expect(rows[0]['L2 Run Status']).toBe('Fail');
|
|
1192
|
+
expect(rows[0]['Bug ID']).toBe('');
|
|
1193
|
+
expect(rows[0]['Bug Title']).toBe('');
|
|
1194
|
+
expect(rows[0]['Bug Responsibility']).toBe('');
|
|
1195
|
+
});
|
|
1196
|
+
it('should fallback bug responsibility from requirement when external bug row has unknown responsibility', () => {
|
|
1197
|
+
const requirements = [
|
|
1198
|
+
{
|
|
1199
|
+
requirementId: 'SR5305',
|
|
1200
|
+
baseKey: 'SR5305',
|
|
1201
|
+
title: 'Req 5305',
|
|
1202
|
+
subSystem: 'Auth',
|
|
1203
|
+
responsibility: 'IL',
|
|
1204
|
+
linkedTestCaseIds: [202],
|
|
1205
|
+
},
|
|
1206
|
+
];
|
|
1207
|
+
const requirementIndex = new Map([
|
|
1208
|
+
[
|
|
1209
|
+
'SR5305',
|
|
1210
|
+
new Map([
|
|
1211
|
+
[
|
|
1212
|
+
202,
|
|
1213
|
+
{
|
|
1214
|
+
passed: 0,
|
|
1215
|
+
failed: 1,
|
|
1216
|
+
notRun: 0,
|
|
1217
|
+
},
|
|
1218
|
+
],
|
|
1219
|
+
]),
|
|
1220
|
+
],
|
|
1221
|
+
]);
|
|
1222
|
+
const observedTestCaseIdsByRequirement = new Map([
|
|
1223
|
+
['SR5305', new Set([202])],
|
|
1224
|
+
]);
|
|
1225
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1226
|
+
[
|
|
1227
|
+
202,
|
|
1228
|
+
{
|
|
1229
|
+
baseKeys: new Set(['SR5305']),
|
|
1230
|
+
fullCodes: new Set(['SR5305']),
|
|
1231
|
+
bugIds: new Set(),
|
|
1232
|
+
},
|
|
1233
|
+
],
|
|
1234
|
+
]);
|
|
1235
|
+
const externalBugsByTestCase = new Map([
|
|
1236
|
+
[
|
|
1237
|
+
202,
|
|
1238
|
+
[
|
|
1239
|
+
{
|
|
1240
|
+
id: 99001,
|
|
1241
|
+
title: 'External bug without SAPWBS',
|
|
1242
|
+
responsibility: 'Unknown',
|
|
1243
|
+
requirementBaseKey: 'SR5305',
|
|
1244
|
+
},
|
|
1245
|
+
],
|
|
1246
|
+
],
|
|
1247
|
+
]);
|
|
1248
|
+
const rows = resultDataProvider.buildMewpCoverageRows(requirements, requirementIndex, observedTestCaseIdsByRequirement, linkedRequirementsByTestCase, new Map(), externalBugsByTestCase);
|
|
1249
|
+
expect(rows).toHaveLength(1);
|
|
1250
|
+
expect(rows[0]['Bug ID']).toBe(99001);
|
|
1251
|
+
expect(rows[0]['Bug Responsibility']).toBe('Elisra');
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
describe('getMewpInternalValidationFlatResults', () => {
|
|
1255
|
+
it('should skip test cases with no in-scope expected requirements and no linked requirements', async () => {
|
|
1256
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1257
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1258
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1259
|
+
{
|
|
1260
|
+
testPointsItems: [
|
|
1261
|
+
{ testCaseId: 101, testCaseName: 'TC 101' },
|
|
1262
|
+
{ testCaseId: 102, testCaseName: 'TC 102' },
|
|
1263
|
+
],
|
|
1264
|
+
testCasesItems: [
|
|
1265
|
+
{
|
|
1266
|
+
workItem: {
|
|
1267
|
+
id: 101,
|
|
1268
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1269
|
+
},
|
|
1270
|
+
},
|
|
1271
|
+
{
|
|
1272
|
+
workItem: {
|
|
1273
|
+
id: 102,
|
|
1274
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
],
|
|
1278
|
+
},
|
|
1279
|
+
]);
|
|
1280
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1281
|
+
{
|
|
1282
|
+
workItemId: 5001,
|
|
1283
|
+
requirementId: 'SR0001',
|
|
1284
|
+
baseKey: 'SR0001',
|
|
1285
|
+
title: 'Req 1',
|
|
1286
|
+
responsibility: 'ESUK',
|
|
1287
|
+
linkedTestCaseIds: [101],
|
|
1288
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1289
|
+
},
|
|
1290
|
+
]);
|
|
1291
|
+
jest.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(new Map([[101, { baseKeys: new Set(['SR0001']), fullCodes: new Set(['SR0001']) }]]));
|
|
1292
|
+
jest
|
|
1293
|
+
.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps')
|
|
1294
|
+
.mockResolvedValueOnce([
|
|
1295
|
+
{
|
|
1296
|
+
stepId: '1',
|
|
1297
|
+
stepPosition: '1',
|
|
1298
|
+
action: '',
|
|
1299
|
+
expected: 'SR0001',
|
|
1300
|
+
isSharedStepTitle: false,
|
|
1301
|
+
},
|
|
1302
|
+
])
|
|
1303
|
+
.mockResolvedValueOnce([
|
|
1304
|
+
{
|
|
1305
|
+
stepId: '1',
|
|
1306
|
+
stepPosition: '1',
|
|
1307
|
+
action: '',
|
|
1308
|
+
expected: '',
|
|
1309
|
+
isSharedStepTitle: false,
|
|
1310
|
+
},
|
|
1311
|
+
]);
|
|
1312
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1313
|
+
expect(result.rows).toHaveLength(2);
|
|
1314
|
+
expect(result.rows).toEqual(expect.arrayContaining([
|
|
1315
|
+
expect.objectContaining({
|
|
1316
|
+
'Test Case ID': 101,
|
|
1317
|
+
'Validation Status': 'Pass',
|
|
1318
|
+
'Mentioned but Not Linked': '',
|
|
1319
|
+
'Linked but Not Mentioned': '',
|
|
1320
|
+
}),
|
|
1321
|
+
expect.objectContaining({
|
|
1322
|
+
'Test Case ID': 102,
|
|
1323
|
+
'Validation Status': 'Pass',
|
|
1324
|
+
'Mentioned but Not Linked': '',
|
|
1325
|
+
'Linked but Not Mentioned': '',
|
|
1326
|
+
}),
|
|
1327
|
+
]));
|
|
1328
|
+
});
|
|
1329
|
+
it('should ignore non-L2 SR mentions and still report only real L2 discrepancies', async () => {
|
|
1330
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1331
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1332
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1333
|
+
{
|
|
1334
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1335
|
+
testCasesItems: [
|
|
1336
|
+
{
|
|
1337
|
+
workItem: {
|
|
1338
|
+
id: 101,
|
|
1339
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1340
|
+
},
|
|
1341
|
+
},
|
|
1342
|
+
],
|
|
1343
|
+
},
|
|
1344
|
+
]);
|
|
1345
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1346
|
+
{
|
|
1347
|
+
workItemId: 5001,
|
|
1348
|
+
requirementId: 'SR0001',
|
|
1349
|
+
baseKey: 'SR0001',
|
|
1350
|
+
title: 'Req 1',
|
|
1351
|
+
responsibility: 'ESUK',
|
|
1352
|
+
linkedTestCaseIds: [101],
|
|
1353
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1354
|
+
},
|
|
1355
|
+
]);
|
|
1356
|
+
jest.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(new Map());
|
|
1357
|
+
jest.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1358
|
+
{
|
|
1359
|
+
stepId: '1',
|
|
1360
|
+
stepPosition: '1',
|
|
1361
|
+
action: '',
|
|
1362
|
+
expected: 'SR0001; SR9999',
|
|
1363
|
+
isSharedStepTitle: false,
|
|
1364
|
+
},
|
|
1365
|
+
]);
|
|
1366
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1367
|
+
expect(result.rows).toHaveLength(1);
|
|
1368
|
+
expect(result.rows[0]).toEqual(expect.objectContaining({
|
|
1369
|
+
'Test Case ID': 101,
|
|
1370
|
+
'Mentioned but Not Linked': 'Step 1: SR0001',
|
|
1371
|
+
'Linked but Not Mentioned': '',
|
|
1372
|
+
'Validation Status': 'Fail',
|
|
1373
|
+
}));
|
|
1374
|
+
});
|
|
1375
|
+
it('should emit Direction A rows with step context for missing sibling links', async () => {
|
|
1376
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1377
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1378
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1379
|
+
{
|
|
1380
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1381
|
+
testCasesItems: [
|
|
1382
|
+
{
|
|
1383
|
+
workItem: {
|
|
1384
|
+
id: 101,
|
|
1385
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
],
|
|
1389
|
+
},
|
|
1390
|
+
]);
|
|
1391
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1392
|
+
{
|
|
1393
|
+
workItemId: 5001,
|
|
1394
|
+
requirementId: 'SR0001',
|
|
1395
|
+
baseKey: 'SR0001',
|
|
1396
|
+
title: 'Req parent',
|
|
1397
|
+
responsibility: 'ESUK',
|
|
1398
|
+
linkedTestCaseIds: [101],
|
|
1399
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
workItemId: 5002,
|
|
1403
|
+
requirementId: 'SR0001-1',
|
|
1404
|
+
baseKey: 'SR0001',
|
|
1405
|
+
title: 'Req child',
|
|
1406
|
+
responsibility: 'ESUK',
|
|
1407
|
+
linkedTestCaseIds: [],
|
|
1408
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1409
|
+
},
|
|
1410
|
+
]);
|
|
1411
|
+
jest.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(new Map([[101, { baseKeys: new Set(['SR0001']), fullCodes: new Set(['SR0001']) }]]));
|
|
1412
|
+
jest.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1413
|
+
{
|
|
1414
|
+
stepId: '2',
|
|
1415
|
+
stepPosition: '2',
|
|
1416
|
+
action: '',
|
|
1417
|
+
expected: 'SR0001; SR0002',
|
|
1418
|
+
isSharedStepTitle: false,
|
|
1419
|
+
},
|
|
1420
|
+
]);
|
|
1421
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1422
|
+
expect(result.rows).toEqual(expect.arrayContaining([
|
|
1423
|
+
expect.objectContaining({
|
|
1424
|
+
'Test Case ID': 101,
|
|
1425
|
+
'Mentioned but Not Linked': expect.stringContaining('Step 2: SR0001-1'),
|
|
1426
|
+
'Validation Status': 'Fail',
|
|
1427
|
+
}),
|
|
1428
|
+
]));
|
|
1429
|
+
});
|
|
1430
|
+
it('should not duplicate Direction A discrepancy when same requirement is repeated in multiple steps', async () => {
|
|
1431
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1432
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1433
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1434
|
+
{
|
|
1435
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1436
|
+
testCasesItems: [
|
|
1437
|
+
{
|
|
1438
|
+
workItem: {
|
|
1439
|
+
id: 101,
|
|
1440
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1441
|
+
},
|
|
1442
|
+
},
|
|
1443
|
+
],
|
|
1444
|
+
},
|
|
1445
|
+
]);
|
|
1446
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1447
|
+
{
|
|
1448
|
+
workItemId: 5001,
|
|
1449
|
+
requirementId: 'SR0001',
|
|
1450
|
+
baseKey: 'SR0001',
|
|
1451
|
+
title: 'Req 1',
|
|
1452
|
+
responsibility: 'ESUK',
|
|
1453
|
+
linkedTestCaseIds: [],
|
|
1454
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1455
|
+
},
|
|
1456
|
+
]);
|
|
1457
|
+
jest
|
|
1458
|
+
.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase')
|
|
1459
|
+
.mockResolvedValueOnce(new Map());
|
|
1460
|
+
jest.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1461
|
+
{
|
|
1462
|
+
stepId: '1',
|
|
1463
|
+
stepPosition: '1',
|
|
1464
|
+
action: '',
|
|
1465
|
+
expected: 'SR0001',
|
|
1466
|
+
isSharedStepTitle: false,
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
stepId: '2',
|
|
1470
|
+
stepPosition: '2',
|
|
1471
|
+
action: '',
|
|
1472
|
+
expected: 'SR0001',
|
|
1473
|
+
isSharedStepTitle: false,
|
|
1474
|
+
},
|
|
1475
|
+
]);
|
|
1476
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1477
|
+
expect(result.rows).toHaveLength(1);
|
|
1478
|
+
expect(result.rows[0]).toEqual(expect.objectContaining({
|
|
1479
|
+
'Test Case ID': 101,
|
|
1480
|
+
'Mentioned but Not Linked': 'Step 1: SR0001',
|
|
1481
|
+
'Linked but Not Mentioned': '',
|
|
1482
|
+
'Validation Status': 'Fail',
|
|
1483
|
+
}));
|
|
1484
|
+
});
|
|
1485
|
+
it('should produce one detailed row per test case with correct bidirectional discrepancies', async () => {
|
|
1486
|
+
const mockDetailedStepsByTestCase = new Map([
|
|
1487
|
+
[
|
|
1488
|
+
201,
|
|
1489
|
+
[
|
|
1490
|
+
{
|
|
1491
|
+
stepId: '1',
|
|
1492
|
+
stepPosition: '1',
|
|
1493
|
+
action: 'Validate parent SR0511 and SR0095 siblings',
|
|
1494
|
+
expected: '<b>sr0511</b>; SR0095-2,3; VVRM-05',
|
|
1495
|
+
isSharedStepTitle: false,
|
|
1496
|
+
},
|
|
1497
|
+
{
|
|
1498
|
+
stepId: '2',
|
|
1499
|
+
stepPosition: '2',
|
|
1500
|
+
action: 'Noisy requirement-like token should be ignored',
|
|
1501
|
+
expected: 'SR0511-V3.24',
|
|
1502
|
+
isSharedStepTitle: false,
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
stepId: '3',
|
|
1506
|
+
stepPosition: '3',
|
|
1507
|
+
action: 'Regression note',
|
|
1508
|
+
expected: 'Verification note only',
|
|
1509
|
+
isSharedStepTitle: false,
|
|
1510
|
+
},
|
|
1511
|
+
],
|
|
1512
|
+
],
|
|
1513
|
+
[
|
|
1514
|
+
202,
|
|
1515
|
+
[
|
|
1516
|
+
{
|
|
1517
|
+
stepId: '1',
|
|
1518
|
+
stepPosition: '1',
|
|
1519
|
+
action: 'Linked SR0200 exists but is not cleanly mentioned in expected result',
|
|
1520
|
+
expected: 'VVRM-22; SR0200 V3.1',
|
|
1521
|
+
isSharedStepTitle: false,
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
stepId: '2',
|
|
1525
|
+
stepPosition: '2',
|
|
1526
|
+
action: 'Execution notes',
|
|
1527
|
+
expected: 'Notes without SR requirement token',
|
|
1528
|
+
isSharedStepTitle: false,
|
|
1529
|
+
},
|
|
1530
|
+
],
|
|
1531
|
+
],
|
|
1532
|
+
[
|
|
1533
|
+
203,
|
|
1534
|
+
[
|
|
1535
|
+
{
|
|
1536
|
+
stepId: '1',
|
|
1537
|
+
stepPosition: '1',
|
|
1538
|
+
action: 'Primary requirement validation for SR0100-1',
|
|
1539
|
+
expected: '<i>SR0100-1</i>',
|
|
1540
|
+
isSharedStepTitle: false,
|
|
1541
|
+
},
|
|
1542
|
+
{
|
|
1543
|
+
stepId: '3',
|
|
1544
|
+
stepPosition: '3',
|
|
1545
|
+
action: 'Repeated mention should not create duplicate mismatch',
|
|
1546
|
+
expected: 'SR0100-1; SR0100-1',
|
|
1547
|
+
isSharedStepTitle: false,
|
|
1548
|
+
},
|
|
1549
|
+
],
|
|
1550
|
+
],
|
|
1551
|
+
]);
|
|
1552
|
+
const mockLinkedRequirementsByTestCase = new Map([
|
|
1553
|
+
[
|
|
1554
|
+
201,
|
|
1555
|
+
{
|
|
1556
|
+
baseKeys: new Set(['SR0511', 'SR0095', 'SR8888']),
|
|
1557
|
+
fullCodes: new Set(['SR0511', 'SR0095-2', 'SR8888']),
|
|
1558
|
+
},
|
|
1559
|
+
],
|
|
1560
|
+
[
|
|
1561
|
+
202,
|
|
1562
|
+
{
|
|
1563
|
+
baseKeys: new Set(['SR0200']),
|
|
1564
|
+
fullCodes: new Set(['SR0200']),
|
|
1565
|
+
},
|
|
1566
|
+
],
|
|
1567
|
+
[
|
|
1568
|
+
203,
|
|
1569
|
+
{
|
|
1570
|
+
baseKeys: new Set(['SR0100']),
|
|
1571
|
+
fullCodes: new Set(['SR0100-1']),
|
|
1572
|
+
},
|
|
1573
|
+
],
|
|
1574
|
+
]);
|
|
1575
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1576
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1577
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1578
|
+
{
|
|
1579
|
+
testPointsItems: [
|
|
1580
|
+
{ testCaseId: 201, testCaseName: 'TC 201 - Mixed discrepancies' },
|
|
1581
|
+
{ testCaseId: 202, testCaseName: 'TC 202 - Link only' },
|
|
1582
|
+
{ testCaseId: 203, testCaseName: 'TC 203 - Fully valid' },
|
|
1583
|
+
],
|
|
1584
|
+
testCasesItems: [
|
|
1585
|
+
{
|
|
1586
|
+
workItem: {
|
|
1587
|
+
id: 201,
|
|
1588
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-201"></steps>' }],
|
|
1589
|
+
},
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
workItem: {
|
|
1593
|
+
id: 202,
|
|
1594
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-202"></steps>' }],
|
|
1595
|
+
},
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
workItem: {
|
|
1599
|
+
id: 203,
|
|
1600
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-203"></steps>' }],
|
|
1601
|
+
},
|
|
1602
|
+
},
|
|
1603
|
+
],
|
|
1604
|
+
},
|
|
1605
|
+
]);
|
|
1606
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1607
|
+
{
|
|
1608
|
+
workItemId: 6001,
|
|
1609
|
+
requirementId: 'SR0511',
|
|
1610
|
+
baseKey: 'SR0511',
|
|
1611
|
+
title: 'Parent 0511',
|
|
1612
|
+
responsibility: 'ESUK',
|
|
1613
|
+
linkedTestCaseIds: [201],
|
|
1614
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1615
|
+
},
|
|
1616
|
+
{
|
|
1617
|
+
workItemId: 6002,
|
|
1618
|
+
requirementId: 'SR0511-1',
|
|
1619
|
+
baseKey: 'SR0511',
|
|
1620
|
+
title: 'Child 0511-1',
|
|
1621
|
+
responsibility: 'ESUK',
|
|
1622
|
+
linkedTestCaseIds: [],
|
|
1623
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1624
|
+
},
|
|
1625
|
+
{
|
|
1626
|
+
workItemId: 6003,
|
|
1627
|
+
requirementId: 'SR0511-2',
|
|
1628
|
+
baseKey: 'SR0511',
|
|
1629
|
+
title: 'Child 0511-2',
|
|
1630
|
+
responsibility: 'ESUK',
|
|
1631
|
+
linkedTestCaseIds: [],
|
|
1632
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
workItemId: 6004,
|
|
1636
|
+
requirementId: 'SR0095-2',
|
|
1637
|
+
baseKey: 'SR0095',
|
|
1638
|
+
title: 'SR0095 child 2',
|
|
1639
|
+
responsibility: 'ESUK',
|
|
1640
|
+
linkedTestCaseIds: [],
|
|
1641
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
workItemId: 6005,
|
|
1645
|
+
requirementId: 'SR0095-3',
|
|
1646
|
+
baseKey: 'SR0095',
|
|
1647
|
+
title: 'SR0095 child 3',
|
|
1648
|
+
responsibility: 'ESUK',
|
|
1649
|
+
linkedTestCaseIds: [],
|
|
1650
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1651
|
+
},
|
|
1652
|
+
{
|
|
1653
|
+
workItemId: 6006,
|
|
1654
|
+
requirementId: 'SR0200',
|
|
1655
|
+
baseKey: 'SR0200',
|
|
1656
|
+
title: 'SR0200 standalone',
|
|
1657
|
+
responsibility: 'ESUK',
|
|
1658
|
+
linkedTestCaseIds: [202],
|
|
1659
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
workItemId: 6007,
|
|
1663
|
+
requirementId: 'SR0100-1',
|
|
1664
|
+
baseKey: 'SR0100',
|
|
1665
|
+
title: 'SR0100 child 1',
|
|
1666
|
+
responsibility: 'ESUK',
|
|
1667
|
+
linkedTestCaseIds: [203],
|
|
1668
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1669
|
+
},
|
|
1670
|
+
]);
|
|
1671
|
+
jest
|
|
1672
|
+
.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase')
|
|
1673
|
+
.mockResolvedValueOnce(mockLinkedRequirementsByTestCase);
|
|
1674
|
+
jest
|
|
1675
|
+
.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps')
|
|
1676
|
+
.mockImplementation(async (...args) => {
|
|
1677
|
+
const stepsXml = String((args === null || args === void 0 ? void 0 : args[0]) || '');
|
|
1678
|
+
const testCaseMatch = /mock-steps-tc-(\d+)/i.exec(stepsXml);
|
|
1679
|
+
const testCaseId = Number((testCaseMatch === null || testCaseMatch === void 0 ? void 0 : testCaseMatch[1]) || 0);
|
|
1680
|
+
return mockDetailedStepsByTestCase.get(testCaseId) || [];
|
|
1681
|
+
});
|
|
1682
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1683
|
+
expect(result.rows).toHaveLength(3);
|
|
1684
|
+
const byTestCase = new Map(result.rows.map((row) => [row['Test Case ID'], row]));
|
|
1685
|
+
expect(new Set(result.rows.map((row) => row['Test Case ID']))).toEqual(new Set([201, 202, 203]));
|
|
1686
|
+
expect(byTestCase.get(201)).toEqual(expect.objectContaining({
|
|
1687
|
+
'Test Case Title': 'TC 201 - Mixed discrepancies',
|
|
1688
|
+
'Mentioned but Not Linked': 'Step 1: SR0095-3, SR0511-1, SR0511-2',
|
|
1689
|
+
'Linked but Not Mentioned': 'SR8888',
|
|
1690
|
+
'Validation Status': 'Fail',
|
|
1691
|
+
}));
|
|
1692
|
+
expect(String(byTestCase.get(201)['Mentioned but Not Linked'] || '')).not.toContain('VVRM');
|
|
1693
|
+
expect(byTestCase.get(202)).toEqual(expect.objectContaining({
|
|
1694
|
+
'Test Case Title': 'TC 202 - Link only',
|
|
1695
|
+
'Mentioned but Not Linked': '',
|
|
1696
|
+
'Linked but Not Mentioned': 'SR0200',
|
|
1697
|
+
'Validation Status': 'Fail',
|
|
1698
|
+
}));
|
|
1699
|
+
expect(byTestCase.get(203)).toEqual(expect.objectContaining({
|
|
1700
|
+
'Test Case Title': 'TC 203 - Fully valid',
|
|
1701
|
+
'Mentioned but Not Linked': '',
|
|
1702
|
+
'Linked but Not Mentioned': '',
|
|
1703
|
+
'Validation Status': 'Pass',
|
|
1704
|
+
}));
|
|
1705
|
+
expect(resultDataProvider.testStepParserHelper.parseTestSteps).toHaveBeenCalledTimes(3);
|
|
1706
|
+
const parseCalls = resultDataProvider.testStepParserHelper.parseTestSteps.mock.calls;
|
|
1707
|
+
const parsedCaseIds = parseCalls
|
|
1708
|
+
.map(([xml]) => {
|
|
1709
|
+
const match = /mock-steps-tc-(\d+)/i.exec(String(xml || ''));
|
|
1710
|
+
return Number((match === null || match === void 0 ? void 0 : match[1]) || 0);
|
|
1711
|
+
})
|
|
1712
|
+
.filter((id) => Number.isFinite(id) && id > 0);
|
|
1713
|
+
expect(new Set(parsedCaseIds)).toEqual(new Set([201, 202, 203]));
|
|
1714
|
+
});
|
|
1715
|
+
it('should parse TC-0042 mixed expected text and keep only valid SR requirement codes', async () => {
|
|
1716
|
+
jest.spyOn(resultDataProvider, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1717
|
+
jest.spyOn(resultDataProvider, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1718
|
+
jest.spyOn(resultDataProvider, 'fetchTestData').mockResolvedValueOnce([
|
|
1719
|
+
{
|
|
1720
|
+
testPointsItems: [{ testCaseId: 42, testCaseName: 'TC-0042' }],
|
|
1721
|
+
testCasesItems: [
|
|
1722
|
+
{
|
|
1723
|
+
workItem: {
|
|
1724
|
+
id: 42,
|
|
1725
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-0042"></steps>' }],
|
|
1726
|
+
},
|
|
1727
|
+
},
|
|
1728
|
+
],
|
|
1729
|
+
},
|
|
1730
|
+
]);
|
|
1731
|
+
jest.spyOn(resultDataProvider, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1732
|
+
{
|
|
1733
|
+
workItemId: 7001,
|
|
1734
|
+
requirementId: 'SR0036',
|
|
1735
|
+
baseKey: 'SR0036',
|
|
1736
|
+
title: 'SR0036',
|
|
1737
|
+
responsibility: 'ESUK',
|
|
1738
|
+
linkedTestCaseIds: [],
|
|
1739
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1740
|
+
},
|
|
1741
|
+
{
|
|
1742
|
+
workItemId: 7002,
|
|
1743
|
+
requirementId: 'SR0215',
|
|
1744
|
+
baseKey: 'SR0215',
|
|
1745
|
+
title: 'SR0215',
|
|
1746
|
+
responsibility: 'ESUK',
|
|
1747
|
+
linkedTestCaseIds: [],
|
|
1748
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
workItemId: 7003,
|
|
1752
|
+
requirementId: 'SR0539',
|
|
1753
|
+
baseKey: 'SR0539',
|
|
1754
|
+
title: 'SR0539',
|
|
1755
|
+
responsibility: 'ESUK',
|
|
1756
|
+
linkedTestCaseIds: [],
|
|
1757
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
workItemId: 7004,
|
|
1761
|
+
requirementId: 'SR0348',
|
|
1762
|
+
baseKey: 'SR0348',
|
|
1763
|
+
title: 'SR0348',
|
|
1764
|
+
responsibility: 'ESUK',
|
|
1765
|
+
linkedTestCaseIds: [],
|
|
1766
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1767
|
+
},
|
|
1768
|
+
{
|
|
1769
|
+
workItemId: 7005,
|
|
1770
|
+
requirementId: 'SR0027',
|
|
1771
|
+
baseKey: 'SR0027',
|
|
1772
|
+
title: 'SR0027',
|
|
1773
|
+
responsibility: 'ESUK',
|
|
1774
|
+
linkedTestCaseIds: [],
|
|
1775
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
workItemId: 7006,
|
|
1779
|
+
requirementId: 'SR0041',
|
|
1780
|
+
baseKey: 'SR0041',
|
|
1781
|
+
title: 'SR0041',
|
|
1782
|
+
responsibility: 'ESUK',
|
|
1783
|
+
linkedTestCaseIds: [],
|
|
1784
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
workItemId: 7007,
|
|
1788
|
+
requirementId: 'SR0550',
|
|
1789
|
+
baseKey: 'SR0550',
|
|
1790
|
+
title: 'SR0550',
|
|
1791
|
+
responsibility: 'ESUK',
|
|
1792
|
+
linkedTestCaseIds: [],
|
|
1793
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1794
|
+
},
|
|
1795
|
+
{
|
|
1796
|
+
workItemId: 7008,
|
|
1797
|
+
requirementId: 'SR0550-2',
|
|
1798
|
+
baseKey: 'SR0550',
|
|
1799
|
+
title: 'SR0550-2',
|
|
1800
|
+
responsibility: 'ESUK',
|
|
1801
|
+
linkedTestCaseIds: [],
|
|
1802
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
workItemId: 7009,
|
|
1806
|
+
requirementId: 'SR0817',
|
|
1807
|
+
baseKey: 'SR0817',
|
|
1808
|
+
title: 'SR0817',
|
|
1809
|
+
responsibility: 'ESUK',
|
|
1810
|
+
linkedTestCaseIds: [],
|
|
1811
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
workItemId: 7010,
|
|
1815
|
+
requirementId: 'SR0818',
|
|
1816
|
+
baseKey: 'SR0818',
|
|
1817
|
+
title: 'SR0818',
|
|
1818
|
+
responsibility: 'ESUK',
|
|
1819
|
+
linkedTestCaseIds: [],
|
|
1820
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
workItemId: 7011,
|
|
1824
|
+
requirementId: 'SR0859',
|
|
1825
|
+
baseKey: 'SR0859',
|
|
1826
|
+
title: 'SR0859',
|
|
1827
|
+
responsibility: 'ESUK',
|
|
1828
|
+
linkedTestCaseIds: [],
|
|
1829
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1830
|
+
},
|
|
1831
|
+
]);
|
|
1832
|
+
jest
|
|
1833
|
+
.spyOn(resultDataProvider, 'buildLinkedRequirementsByTestCase')
|
|
1834
|
+
.mockResolvedValueOnce(new Map([
|
|
1835
|
+
[
|
|
1836
|
+
42,
|
|
1837
|
+
{
|
|
1838
|
+
baseKeys: new Set([
|
|
1839
|
+
'SR0036',
|
|
1840
|
+
'SR0215',
|
|
1841
|
+
'SR0539',
|
|
1842
|
+
'SR0348',
|
|
1843
|
+
'SR0041',
|
|
1844
|
+
'SR0550',
|
|
1845
|
+
'SR0817',
|
|
1846
|
+
'SR0818',
|
|
1847
|
+
'SR0859',
|
|
1848
|
+
]),
|
|
1849
|
+
fullCodes: new Set([
|
|
1850
|
+
'SR0036',
|
|
1851
|
+
'SR0215',
|
|
1852
|
+
'SR0539',
|
|
1853
|
+
'SR0348',
|
|
1854
|
+
'SR0041',
|
|
1855
|
+
'SR0550',
|
|
1856
|
+
'SR0550-2',
|
|
1857
|
+
'SR0817',
|
|
1858
|
+
'SR0818',
|
|
1859
|
+
'SR0859',
|
|
1860
|
+
]),
|
|
1861
|
+
},
|
|
1862
|
+
],
|
|
1863
|
+
]));
|
|
1864
|
+
jest.spyOn(resultDataProvider.testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1865
|
+
{
|
|
1866
|
+
stepId: '1',
|
|
1867
|
+
stepPosition: '1',
|
|
1868
|
+
action: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
|
1869
|
+
expected: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. (SR0036; SR0215; VVRM-1; SR0817-V3.2; SR0818-V3.3; SR0859-V3.4)',
|
|
1870
|
+
isSharedStepTitle: false,
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
stepId: '2',
|
|
1874
|
+
stepPosition: '2',
|
|
1875
|
+
action: 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
|
1876
|
+
expected: 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. (SR0539; SR0348; VVRM-1)',
|
|
1877
|
+
isSharedStepTitle: false,
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
stepId: '3',
|
|
1881
|
+
stepPosition: '3',
|
|
1882
|
+
action: 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
|
|
1883
|
+
expected: 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. (SR0027, SR0036; SR0041; VVRM-2)',
|
|
1884
|
+
isSharedStepTitle: false,
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
stepId: '4',
|
|
1888
|
+
stepPosition: '4',
|
|
1889
|
+
action: 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
|
1890
|
+
expected: 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. (SR0550-2)',
|
|
1891
|
+
isSharedStepTitle: false,
|
|
1892
|
+
},
|
|
1893
|
+
{
|
|
1894
|
+
stepId: '5',
|
|
1895
|
+
stepPosition: '5',
|
|
1896
|
+
action: 'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.',
|
|
1897
|
+
expected: 'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit. (VVRM-1)',
|
|
1898
|
+
isSharedStepTitle: false,
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
stepId: '6',
|
|
1902
|
+
stepPosition: '6',
|
|
1903
|
+
action: 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet consectetur adipisci velit.',
|
|
1904
|
+
expected: 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet consectetur adipisci velit. (SR0041; SR0550; VVRM-3)',
|
|
1905
|
+
isSharedStepTitle: false,
|
|
1906
|
+
},
|
|
1907
|
+
]);
|
|
1908
|
+
const result = await resultDataProvider.getMewpInternalValidationFlatResults('123', mockProjectName, [1]);
|
|
1909
|
+
expect(result.rows).toHaveLength(1);
|
|
1910
|
+
expect(result.rows[0]).toEqual(expect.objectContaining({
|
|
1911
|
+
'Test Case ID': 42,
|
|
1912
|
+
'Test Case Title': 'TC-0042',
|
|
1913
|
+
'Mentioned but Not Linked': 'Step 3: SR0027',
|
|
1914
|
+
'Linked but Not Mentioned': 'SR0817; SR0818; SR0859',
|
|
1915
|
+
'Validation Status': 'Fail',
|
|
1916
|
+
}));
|
|
1917
|
+
expect(String(result.rows[0]['Mentioned but Not Linked'] || '')).not.toContain('VVRM');
|
|
1918
|
+
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).not.toContain('VVRM');
|
|
1919
|
+
});
|
|
1920
|
+
});
|
|
1921
|
+
describe('MEWP rel fallback scoping', () => {
|
|
1922
|
+
it('should fallback to previous Rel run when latest selected Rel has no run for a test case', async () => {
|
|
1923
|
+
const suites = [
|
|
1924
|
+
{ testSuiteId: 10, suiteName: 'Rel10 / Validation' },
|
|
1925
|
+
{ testSuiteId: 11, suiteName: 'Rel11 / Validation' },
|
|
1926
|
+
];
|
|
1927
|
+
const allSuites = [
|
|
1928
|
+
{ testSuiteId: 10, suiteName: 'Rel10 / Validation' },
|
|
1929
|
+
{ testSuiteId: 11, suiteName: 'Rel11 / Validation' },
|
|
1930
|
+
];
|
|
1931
|
+
const rawTestData = [
|
|
1932
|
+
{
|
|
1933
|
+
suiteName: 'Rel10 / Validation',
|
|
1934
|
+
testPointsItems: [
|
|
1935
|
+
{ testCaseId: 501, lastRunId: 100, lastResultId: 200 },
|
|
1936
|
+
{ testCaseId: 502, lastRunId: 300, lastResultId: 400 },
|
|
1937
|
+
],
|
|
1938
|
+
testCasesItems: [{ workItem: { id: 501 } }, { workItem: { id: 502 } }],
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
suiteName: 'Rel11 / Validation',
|
|
1942
|
+
testPointsItems: [
|
|
1943
|
+
{ testCaseId: 501, lastRunId: 0, lastResultId: 0 },
|
|
1944
|
+
{ testCaseId: 502, lastRunId: 500, lastResultId: 600 },
|
|
1945
|
+
],
|
|
1946
|
+
testCasesItems: [{ workItem: { id: 501 } }, { workItem: { id: 502 } }],
|
|
1947
|
+
},
|
|
1948
|
+
];
|
|
1949
|
+
const fetchSuitesSpy = jest
|
|
1950
|
+
.spyOn(resultDataProvider, 'fetchTestSuites')
|
|
1951
|
+
.mockResolvedValueOnce(suites)
|
|
1952
|
+
.mockResolvedValueOnce(allSuites);
|
|
1953
|
+
const fetchDataSpy = jest
|
|
1954
|
+
.spyOn(resultDataProvider, 'fetchTestData')
|
|
1955
|
+
.mockResolvedValueOnce(rawTestData);
|
|
1956
|
+
const scoped = await resultDataProvider.fetchMewpScopedTestData('123', mockProjectName, [11], true);
|
|
1957
|
+
expect(fetchSuitesSpy).toHaveBeenCalledTimes(2);
|
|
1958
|
+
expect(fetchDataSpy).toHaveBeenCalledTimes(1);
|
|
1959
|
+
expect(scoped).toHaveLength(1);
|
|
1960
|
+
const selectedPoints = scoped[0].testPointsItems;
|
|
1961
|
+
const tc501 = selectedPoints.find((item) => item.testCaseId === 501);
|
|
1962
|
+
const tc502 = selectedPoints.find((item) => item.testCaseId === 502);
|
|
1963
|
+
expect(tc501).toEqual(expect.objectContaining({ lastRunId: 100, lastResultId: 200 }));
|
|
1964
|
+
expect(tc502).toEqual(expect.objectContaining({ lastRunId: 500, lastResultId: 600 }));
|
|
1965
|
+
});
|
|
1966
|
+
});
|
|
1967
|
+
describe('MEWP external ingestion validation/parsing', () => {
|
|
1968
|
+
const validBugsSource = {
|
|
1969
|
+
name: 'bugs.xlsx',
|
|
1970
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/bugs/bugs.xlsx',
|
|
1971
|
+
sourceType: 'mewpExternalIngestion',
|
|
1972
|
+
};
|
|
1973
|
+
const validL3L4Source = {
|
|
1974
|
+
name: 'l3l4.xlsx',
|
|
1975
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/l3l4/l3l4.xlsx',
|
|
1976
|
+
sourceType: 'mewpExternalIngestion',
|
|
1977
|
+
};
|
|
1978
|
+
it('should validate external files when required columns exist in A3 header row', async () => {
|
|
1979
|
+
const bugsBuffer = buildWorkbookBuffer([
|
|
1980
|
+
['', '', '', '', ''],
|
|
1981
|
+
['', '', '', '', ''],
|
|
1982
|
+
['Elisra_SortIndex', 'SR', 'TargetWorkItemId', 'Title', 'TargetState'],
|
|
1983
|
+
['101', 'SR0001', '9001', 'Bug one', 'Active'],
|
|
1984
|
+
]);
|
|
1985
|
+
const l3l4Buffer = buildWorkbookBuffer([
|
|
1986
|
+
['', '', '', '', '', '', '', ''],
|
|
1987
|
+
['', '', '', '', '', '', '', ''],
|
|
1988
|
+
[
|
|
1989
|
+
'SR',
|
|
1990
|
+
'AREA 34',
|
|
1991
|
+
'TargetWorkItemId Level 3',
|
|
1992
|
+
'TargetTitleLevel3',
|
|
1993
|
+
'TargetStateLevel 3',
|
|
1994
|
+
'TargetWorkItemIdLevel 4',
|
|
1995
|
+
'TargetTitleLevel4',
|
|
1996
|
+
'TargetStateLevel 4',
|
|
1997
|
+
],
|
|
1998
|
+
['SR0001', 'Level 3', '7001', 'Req L3', 'Active', '', '', ''],
|
|
1999
|
+
]);
|
|
2000
|
+
const axiosSpy = jest
|
|
2001
|
+
.spyOn(axios_1.default, 'get')
|
|
2002
|
+
.mockResolvedValueOnce({ data: bugsBuffer, headers: {} })
|
|
2003
|
+
.mockResolvedValueOnce({ data: l3l4Buffer, headers: {} });
|
|
2004
|
+
const result = await resultDataProvider.validateMewpExternalFiles({
|
|
2005
|
+
externalBugsFile: validBugsSource,
|
|
2006
|
+
externalL3L4File: validL3L4Source,
|
|
2007
|
+
});
|
|
2008
|
+
expect(axiosSpy).toHaveBeenCalledTimes(2);
|
|
2009
|
+
expect(result.valid).toBe(true);
|
|
2010
|
+
expect(result.bugs).toEqual(expect.objectContaining({
|
|
2011
|
+
valid: true,
|
|
2012
|
+
headerRow: 'A3',
|
|
2013
|
+
matchedRequiredColumns: 5,
|
|
2014
|
+
}));
|
|
2015
|
+
expect(result.l3l4).toEqual(expect.objectContaining({
|
|
2016
|
+
valid: true,
|
|
2017
|
+
headerRow: 'A3',
|
|
2018
|
+
matchedRequiredColumns: 8,
|
|
2019
|
+
}));
|
|
2020
|
+
});
|
|
2021
|
+
it('should accept A1 fallback header row for backward compatibility', async () => {
|
|
2022
|
+
const bugsBuffer = buildWorkbookBuffer([
|
|
2023
|
+
['Elisra_SortIndex', 'SR', 'TargetWorkItemId', 'Title', 'TargetState'],
|
|
2024
|
+
['101', 'SR0001', '9001', 'Bug one', 'Active'],
|
|
2025
|
+
]);
|
|
2026
|
+
jest.spyOn(axios_1.default, 'get').mockResolvedValueOnce({ data: bugsBuffer, headers: {} });
|
|
2027
|
+
const result = await resultDataProvider.validateMewpExternalFiles({
|
|
2028
|
+
externalBugsFile: validBugsSource,
|
|
2029
|
+
});
|
|
2030
|
+
expect(result.valid).toBe(true);
|
|
2031
|
+
expect(result.bugs).toEqual(expect.objectContaining({
|
|
2032
|
+
valid: true,
|
|
2033
|
+
headerRow: 'A1',
|
|
2034
|
+
}));
|
|
2035
|
+
});
|
|
2036
|
+
it('should fail validation when required columns are missing', async () => {
|
|
2037
|
+
const invalidBuffer = buildWorkbookBuffer([
|
|
2038
|
+
['', '', ''],
|
|
2039
|
+
['', '', ''],
|
|
2040
|
+
['Elisra_SortIndex', 'TargetWorkItemId', 'TargetState'],
|
|
2041
|
+
['101', '9001', 'Active'],
|
|
2042
|
+
]);
|
|
2043
|
+
jest.spyOn(axios_1.default, 'get').mockResolvedValueOnce({ data: invalidBuffer, headers: {} });
|
|
2044
|
+
const result = await resultDataProvider.validateMewpExternalFiles({
|
|
2045
|
+
externalBugsFile: validBugsSource,
|
|
2046
|
+
});
|
|
2047
|
+
expect(result.valid).toBe(false);
|
|
2048
|
+
expect(result.bugs).toEqual(expect.objectContaining({
|
|
2049
|
+
valid: false,
|
|
2050
|
+
matchedRequiredColumns: 3,
|
|
2051
|
+
missingRequiredColumns: expect.arrayContaining(['SR', 'Title']),
|
|
2052
|
+
}));
|
|
2053
|
+
});
|
|
2054
|
+
it('should reject files from non-dedicated bucket/object path', async () => {
|
|
2055
|
+
var _a;
|
|
2056
|
+
const result = await resultDataProvider.validateMewpExternalFiles({
|
|
2057
|
+
externalBugsFile: {
|
|
2058
|
+
name: 'bugs.xlsx',
|
|
2059
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/other-prefix/bugs.xlsx',
|
|
2060
|
+
sourceType: 'mewpExternalIngestion',
|
|
2061
|
+
},
|
|
2062
|
+
});
|
|
2063
|
+
expect(result.valid).toBe(false);
|
|
2064
|
+
expect(((_a = result.bugs) === null || _a === void 0 ? void 0 : _a.message) || '').toContain('Invalid object path');
|
|
2065
|
+
});
|
|
2066
|
+
it('should filter external bugs by SR/state and dedupe by bug+requirement', async () => {
|
|
2067
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2068
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2069
|
+
{
|
|
2070
|
+
Elisra_SortIndex: '101',
|
|
2071
|
+
SR: 'SR0001',
|
|
2072
|
+
TargetWorkItemId: '9001',
|
|
2073
|
+
Title: 'Bug one',
|
|
2074
|
+
TargetState: 'Active',
|
|
2075
|
+
SAPWBS: 'ESUK',
|
|
2076
|
+
},
|
|
2077
|
+
{
|
|
2078
|
+
Elisra_SortIndex: '101',
|
|
2079
|
+
SR: 'SR0001',
|
|
2080
|
+
TargetWorkItemId: '9001',
|
|
2081
|
+
Title: 'Bug one duplicate',
|
|
2082
|
+
TargetState: 'Active',
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
Elisra_SortIndex: '101',
|
|
2086
|
+
SR: '',
|
|
2087
|
+
TargetWorkItemId: '9002',
|
|
2088
|
+
Title: 'Missing SR',
|
|
2089
|
+
TargetState: 'Active',
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
Elisra_SortIndex: '101',
|
|
2093
|
+
SR: 'SR0002',
|
|
2094
|
+
TargetWorkItemId: '9003',
|
|
2095
|
+
Title: 'Closed bug',
|
|
2096
|
+
TargetState: 'Closed',
|
|
2097
|
+
},
|
|
2098
|
+
]);
|
|
2099
|
+
const map = await resultDataProvider.loadExternalBugsByTestCase(validBugsSource);
|
|
2100
|
+
const bugs = map.get(101) || [];
|
|
2101
|
+
expect(bugs).toHaveLength(1);
|
|
2102
|
+
expect(bugs[0]).toEqual(expect.objectContaining({
|
|
2103
|
+
id: 9001,
|
|
2104
|
+
requirementBaseKey: 'SR0001',
|
|
2105
|
+
responsibility: 'ESUK',
|
|
2106
|
+
}));
|
|
2107
|
+
});
|
|
2108
|
+
it('should require Elisra_SortIndex and ignore rows that only provide WorkItemId', async () => {
|
|
2109
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2110
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2111
|
+
{
|
|
2112
|
+
WorkItemId: '101',
|
|
2113
|
+
SR: 'SR0001',
|
|
2114
|
+
TargetWorkItemId: '9010',
|
|
2115
|
+
Title: 'Bug without Elisra_SortIndex',
|
|
2116
|
+
TargetState: 'Active',
|
|
2117
|
+
},
|
|
2118
|
+
]);
|
|
2119
|
+
const map = await resultDataProvider.loadExternalBugsByTestCase(validBugsSource);
|
|
2120
|
+
expect(map.size).toBe(0);
|
|
2121
|
+
});
|
|
2122
|
+
it('should parse external L3/L4 file using AREA 34 semantics and terminal-state filtering', async () => {
|
|
2123
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2124
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2125
|
+
{
|
|
2126
|
+
SR: 'SR0001',
|
|
2127
|
+
'AREA 34': 'Level 4',
|
|
2128
|
+
'TargetWorkItemId Level 3': '7001',
|
|
2129
|
+
TargetTitleLevel3: 'L4 From Level3 Column',
|
|
2130
|
+
'TargetStateLevel 3': 'Active',
|
|
2131
|
+
},
|
|
2132
|
+
{
|
|
2133
|
+
SR: 'SR0001',
|
|
2134
|
+
'AREA 34': 'Level 3',
|
|
2135
|
+
'TargetWorkItemId Level 3': '7002',
|
|
2136
|
+
TargetTitleLevel3: 'L3 Requirement',
|
|
2137
|
+
'TargetStateLevel 3': 'Active',
|
|
2138
|
+
'TargetWorkItemIdLevel 4': '7003',
|
|
2139
|
+
TargetTitleLevel4: 'L4 Requirement',
|
|
2140
|
+
'TargetStateLevel 4': 'Closed',
|
|
2141
|
+
},
|
|
2142
|
+
]);
|
|
2143
|
+
const map = await resultDataProvider.loadExternalL3L4ByBaseKey(validL3L4Source);
|
|
2144
|
+
expect(map.get('SR0001')).toEqual([
|
|
2145
|
+
{ id: '7002', title: 'L3 Requirement', level: 'L3' },
|
|
2146
|
+
{ id: '7001', title: 'L4 From Level3 Column', level: 'L4' },
|
|
2147
|
+
]);
|
|
2148
|
+
});
|
|
2149
|
+
it('should exclude external open L3/L4 rows when SAPWBS resolves to ESUK', async () => {
|
|
2150
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2151
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2152
|
+
{
|
|
2153
|
+
SR: 'SR0001',
|
|
2154
|
+
'AREA 34': 'Level 3',
|
|
2155
|
+
'TargetWorkItemId Level 3': '7101',
|
|
2156
|
+
TargetTitleLevel3: 'L3 ESUK',
|
|
2157
|
+
'TargetStateLevel 3': 'Active',
|
|
2158
|
+
'TargetSapWbsLevel 3': 'ESUK',
|
|
2159
|
+
'TargetWorkItemIdLevel 4': '7201',
|
|
2160
|
+
TargetTitleLevel4: 'L4 IL',
|
|
2161
|
+
'TargetStateLevel 4': 'Active',
|
|
2162
|
+
'TargetSapWbsLevel 4': 'IL',
|
|
2163
|
+
},
|
|
2164
|
+
{
|
|
2165
|
+
SR: 'SR0001',
|
|
2166
|
+
'AREA 34': 'Level 3',
|
|
2167
|
+
'TargetWorkItemId Level 3': '7102',
|
|
2168
|
+
TargetTitleLevel3: 'L3 IL',
|
|
2169
|
+
'TargetStateLevel 3': 'Active',
|
|
2170
|
+
'TargetSapWbsLevel 3': 'IL',
|
|
2171
|
+
},
|
|
2172
|
+
]);
|
|
2173
|
+
const map = await resultDataProvider.loadExternalL3L4ByBaseKey(validL3L4Source);
|
|
2174
|
+
expect(map.get('SR0001')).toEqual([
|
|
2175
|
+
{ id: '7102', title: 'L3 IL', level: 'L3' },
|
|
2176
|
+
{ id: '7201', title: 'L4 IL', level: 'L4' },
|
|
2177
|
+
]);
|
|
2178
|
+
});
|
|
2179
|
+
it('should fallback L3/L4 SAPWBS exclusion from SR-mapped requirement when row SAPWBS is empty', async () => {
|
|
2180
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2181
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2182
|
+
{
|
|
2183
|
+
SR: 'SR0001',
|
|
2184
|
+
'AREA 34': 'Level 3',
|
|
2185
|
+
'TargetWorkItemId Level 3': '7301',
|
|
2186
|
+
TargetTitleLevel3: 'L3 From ESUK Requirement',
|
|
2187
|
+
'TargetStateLevel 3': 'Active',
|
|
2188
|
+
'TargetSapWbsLevel 3': '',
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
SR: 'SR0002',
|
|
2192
|
+
'AREA 34': 'Level 3',
|
|
2193
|
+
'TargetWorkItemId Level 3': '7302',
|
|
2194
|
+
TargetTitleLevel3: 'L3 From IL Requirement',
|
|
2195
|
+
'TargetStateLevel 3': 'Active',
|
|
2196
|
+
'TargetSapWbsLevel 3': '',
|
|
2197
|
+
},
|
|
2198
|
+
]);
|
|
2199
|
+
const map = await resultDataProvider.loadExternalL3L4ByBaseKey(validL3L4Source, new Map([
|
|
2200
|
+
['SR0001', 'ESUK'],
|
|
2201
|
+
['SR0002', 'IL'],
|
|
2202
|
+
]));
|
|
2203
|
+
expect(map.has('SR0001')).toBe(false);
|
|
2204
|
+
expect(map.get('SR0002')).toEqual([{ id: '7302', title: 'L3 From IL Requirement', level: 'L3' }]);
|
|
2205
|
+
});
|
|
2206
|
+
it('should resolve bug responsibility from AreaPath when SAPWBS is empty', () => {
|
|
2207
|
+
const fromEsukAreaPath = resultDataProvider.resolveBugResponsibility({
|
|
2208
|
+
'Custom.SAPWBS': '',
|
|
2209
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK',
|
|
2210
|
+
});
|
|
2211
|
+
const fromIlAreaPath = resultDataProvider.resolveBugResponsibility({
|
|
2212
|
+
'Custom.SAPWBS': '',
|
|
2213
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP',
|
|
2214
|
+
});
|
|
2215
|
+
const unknown = resultDataProvider.resolveBugResponsibility({
|
|
2216
|
+
'Custom.SAPWBS': '',
|
|
2217
|
+
'System.AreaPath': 'MEWP\\Other\\Area',
|
|
2218
|
+
});
|
|
2219
|
+
expect(fromEsukAreaPath).toBe('ESUK');
|
|
2220
|
+
expect(fromIlAreaPath).toBe('Elisra');
|
|
2221
|
+
expect(unknown).toBe('Unknown');
|
|
2222
|
+
});
|
|
2223
|
+
it('should handle 1000 external bug rows and keep only in-scope parsed items', async () => {
|
|
2224
|
+
const rows = [];
|
|
2225
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2226
|
+
rows.push({
|
|
2227
|
+
Elisra_SortIndex: String(4000 + (i % 20)),
|
|
2228
|
+
SR: `SR${String(6000 + (i % 50)).padStart(4, '0')}`,
|
|
2229
|
+
TargetWorkItemId: String(900000 + i),
|
|
2230
|
+
Title: `Bug ${i}`,
|
|
2231
|
+
TargetState: i % 10 === 0 ? 'Closed' : 'Active',
|
|
2232
|
+
SAPWBS: i % 2 === 0 ? 'ESUK' : 'IL',
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2236
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce(rows);
|
|
2237
|
+
const startedAt = Date.now();
|
|
2238
|
+
const map = await resultDataProvider.loadExternalBugsByTestCase({
|
|
2239
|
+
name: 'bulk-bugs.xlsx',
|
|
2240
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/bugs/bulk-bugs.xlsx',
|
|
2241
|
+
sourceType: 'mewpExternalIngestion',
|
|
2242
|
+
});
|
|
2243
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2244
|
+
const totalParsed = [...map.values()].reduce((sum, items) => sum + ((items === null || items === void 0 ? void 0 : items.length) || 0), 0);
|
|
2245
|
+
expect(totalParsed).toBe(900); // every 10th row is closed and filtered out
|
|
2246
|
+
expect(elapsedMs).toBeLessThan(5000);
|
|
2247
|
+
});
|
|
2248
|
+
it('should handle 1000 external L3/L4 rows and map all active links', async () => {
|
|
2249
|
+
const rows = [];
|
|
2250
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2251
|
+
rows.push({
|
|
2252
|
+
SR: `SR${String(7000 + (i % 25)).padStart(4, '0')}`,
|
|
2253
|
+
'AREA 34': i % 3 === 0 ? 'Level 4' : 'Level 3',
|
|
2254
|
+
'TargetWorkItemId Level 3': String(800000 + i),
|
|
2255
|
+
TargetTitleLevel3: `L3/L4 Title ${i}`,
|
|
2256
|
+
'TargetStateLevel 3': i % 11 === 0 ? 'Resolved' : 'Active',
|
|
2257
|
+
'TargetWorkItemIdLevel 4': String(810000 + i),
|
|
2258
|
+
TargetTitleLevel4: `L4 Title ${i}`,
|
|
2259
|
+
'TargetStateLevel 4': i % 13 === 0 ? 'Closed' : 'Active',
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
const mewpExternalTableUtils = resultDataProvider.mewpExternalTableUtils;
|
|
2263
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce(rows);
|
|
2264
|
+
const startedAt = Date.now();
|
|
2265
|
+
const map = await resultDataProvider.loadExternalL3L4ByBaseKey({
|
|
2266
|
+
name: 'bulk-l3l4.xlsx',
|
|
2267
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/l3l4/bulk-l3l4.xlsx',
|
|
2268
|
+
sourceType: 'mewpExternalIngestion',
|
|
2269
|
+
});
|
|
2270
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2271
|
+
const totalLinks = [...map.values()].reduce((sum, items) => sum + ((items === null || items === void 0 ? void 0 : items.length) || 0), 0);
|
|
2272
|
+
expect(totalLinks).toBeGreaterThan(700);
|
|
2273
|
+
expect(elapsedMs).toBeLessThan(5000);
|
|
2274
|
+
});
|
|
2275
|
+
});
|
|
2276
|
+
describe('MEWP high-volume requirement token parsing', () => {
|
|
2277
|
+
it('should parse 1000 expected-result requirement tokens with noisy fragments', () => {
|
|
2278
|
+
const tokens = [];
|
|
2279
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2280
|
+
const code = `SR${String(10000 + i)}`;
|
|
2281
|
+
tokens.push(code);
|
|
2282
|
+
if (i % 100 === 0)
|
|
2283
|
+
tokens.push(`${code} V3.24`);
|
|
2284
|
+
if (i % 125 === 0)
|
|
2285
|
+
tokens.push(`${code} VVRM2425`);
|
|
2286
|
+
}
|
|
2287
|
+
const sourceText = tokens.join('; ');
|
|
2288
|
+
const startedAt = Date.now();
|
|
2289
|
+
const codes = resultDataProvider.extractRequirementCodesFromText(sourceText);
|
|
2290
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2291
|
+
expect(codes.size).toBe(1000);
|
|
2292
|
+
expect(codes.has('SR10001')).toBe(true);
|
|
2293
|
+
expect(codes.has('SR11000')).toBe(true);
|
|
2294
|
+
expect(elapsedMs).toBeLessThan(3000);
|
|
2295
|
+
});
|
|
1044
2296
|
});
|
|
1045
2297
|
describe('fetchResultDataForTestReporter (runResultField switch)', () => {
|
|
1046
2298
|
it('should populate requested runResultField values including testCaseResult URL branches', async () => {
|