@elisra-devops/docgen-data-provider 1.74.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 +47 -13
- package/bin/modules/ResultDataProvider.js +1122 -257
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +1293 -113
- 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 +1325 -353
- package/src/tests/modules/ResultDataProvider.test.ts +1481 -138
- package/src/utils/mewpExternalIngestionUtils.ts +270 -0
- package/src/utils/mewpExternalTableUtils.ts +461 -0
|
@@ -2,6 +2,8 @@ import { TFSServices } from '../../helpers/tfs';
|
|
|
2
2
|
import ResultDataProvider from '../../modules/ResultDataProvider';
|
|
3
3
|
import logger from '../../utils/logger';
|
|
4
4
|
import Utils from '../../utils/testStepParserHelper';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import * as XLSX from 'xlsx';
|
|
5
7
|
|
|
6
8
|
// Mock dependencies
|
|
7
9
|
jest.mock('../../helpers/tfs');
|
|
@@ -23,6 +25,12 @@ describe('ResultDataProvider', () => {
|
|
|
23
25
|
const mockToken = 'mock-token';
|
|
24
26
|
const mockProjectName = 'test-project';
|
|
25
27
|
const mockTestPlanId = '12345';
|
|
28
|
+
const buildWorkbookBuffer = (rows: any[][]): Buffer => {
|
|
29
|
+
const worksheet = XLSX.utils.aoa_to_sheet(rows);
|
|
30
|
+
const workbook = XLSX.utils.book_new();
|
|
31
|
+
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
|
32
|
+
return XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' }) as Buffer;
|
|
33
|
+
};
|
|
26
34
|
|
|
27
35
|
beforeEach(() => {
|
|
28
36
|
jest.clearAllMocks();
|
|
@@ -1075,101 +1083,6 @@ describe('ResultDataProvider', () => {
|
|
|
1075
1083
|
});
|
|
1076
1084
|
|
|
1077
1085
|
describe('getMewpL2CoverageFlatResults', () => {
|
|
1078
|
-
it('should support query-mode requirement scope for MEWP coverage', async () => {
|
|
1079
|
-
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1080
|
-
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1081
|
-
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1082
|
-
{
|
|
1083
|
-
testPointsItems: [{ testCaseId: 101, lastRunId: 10, lastResultId: 20, testCaseName: 'TC 101' }],
|
|
1084
|
-
testCasesItems: [
|
|
1085
|
-
{
|
|
1086
|
-
workItem: {
|
|
1087
|
-
id: 101,
|
|
1088
|
-
workItemFields: [{ key: 'System.Title', value: 'TC 101' }],
|
|
1089
|
-
},
|
|
1090
|
-
},
|
|
1091
|
-
],
|
|
1092
|
-
},
|
|
1093
|
-
]);
|
|
1094
|
-
jest.spyOn(resultDataProvider as any, 'fetchMewpRequirementTypeNames').mockResolvedValueOnce([
|
|
1095
|
-
'Requirement',
|
|
1096
|
-
]);
|
|
1097
|
-
jest.spyOn(resultDataProvider as any, 'fetchWorkItemsByIds').mockResolvedValueOnce([
|
|
1098
|
-
{
|
|
1099
|
-
id: 9001,
|
|
1100
|
-
fields: {
|
|
1101
|
-
'System.WorkItemType': 'Requirement',
|
|
1102
|
-
'System.Title': 'Requirement from query',
|
|
1103
|
-
'Custom.CustomerId': 'SR3001',
|
|
1104
|
-
'System.AreaPath': 'MEWP\\IL',
|
|
1105
|
-
},
|
|
1106
|
-
relations: [
|
|
1107
|
-
{
|
|
1108
|
-
rel: 'Microsoft.VSTS.Common.TestedBy-Forward',
|
|
1109
|
-
url: 'https://dev.azure.com/org/_apis/wit/workItems/101',
|
|
1110
|
-
},
|
|
1111
|
-
],
|
|
1112
|
-
},
|
|
1113
|
-
]);
|
|
1114
|
-
jest.spyOn(resultDataProvider as any, 'fetchAllResultDataTestReporter').mockResolvedValueOnce([
|
|
1115
|
-
{
|
|
1116
|
-
testCaseId: 101,
|
|
1117
|
-
testCase: { id: 101, name: 'TC 101' },
|
|
1118
|
-
iteration: {
|
|
1119
|
-
actionResults: [{ action: 'Validate SR3001', expected: '', outcome: 'Passed' }],
|
|
1120
|
-
},
|
|
1121
|
-
},
|
|
1122
|
-
]);
|
|
1123
|
-
|
|
1124
|
-
const TicketsProviderMock: any = require('../../modules/TicketsDataProvider').default;
|
|
1125
|
-
TicketsProviderMock.mockImplementationOnce(() => ({
|
|
1126
|
-
GetQueryResultsFromWiql: jest.fn().mockResolvedValue({
|
|
1127
|
-
fetchedWorkItems: [
|
|
1128
|
-
{
|
|
1129
|
-
id: 9001,
|
|
1130
|
-
fields: {
|
|
1131
|
-
'System.WorkItemType': 'Requirement',
|
|
1132
|
-
'System.Title': 'Requirement from query',
|
|
1133
|
-
'Custom.CustomerId': 'SR3001',
|
|
1134
|
-
'System.AreaPath': 'MEWP\\IL',
|
|
1135
|
-
},
|
|
1136
|
-
},
|
|
1137
|
-
],
|
|
1138
|
-
}),
|
|
1139
|
-
}));
|
|
1140
|
-
|
|
1141
|
-
const result = await (resultDataProvider as any).getMewpL2CoverageFlatResults(
|
|
1142
|
-
'123',
|
|
1143
|
-
mockProjectName,
|
|
1144
|
-
[1],
|
|
1145
|
-
{
|
|
1146
|
-
linkedQueryMode: 'query',
|
|
1147
|
-
testAssociatedQuery: { wiql: { href: 'https://example.com/wiql' } },
|
|
1148
|
-
}
|
|
1149
|
-
);
|
|
1150
|
-
|
|
1151
|
-
const row = result.rows.find((item: any) => item['Customer ID'] === 'SR3001');
|
|
1152
|
-
expect(row).toEqual(
|
|
1153
|
-
expect.objectContaining({
|
|
1154
|
-
'Title (Customer name)': 'Requirement from query',
|
|
1155
|
-
'Responsibility - SAPWBS (ESUK/IL)': 'IL',
|
|
1156
|
-
'Test case id': 101,
|
|
1157
|
-
'Test case title': 'TC 101',
|
|
1158
|
-
'Number of passed steps': 1,
|
|
1159
|
-
'Number of failed steps': 0,
|
|
1160
|
-
'Number of not run tests': 0,
|
|
1161
|
-
})
|
|
1162
|
-
);
|
|
1163
|
-
|
|
1164
|
-
expect(TicketsProviderMock).toHaveBeenCalled();
|
|
1165
|
-
const instance = TicketsProviderMock.mock.results[0].value;
|
|
1166
|
-
expect(instance.GetQueryResultsFromWiql).toHaveBeenCalledWith(
|
|
1167
|
-
'https://example.com/wiql',
|
|
1168
|
-
true,
|
|
1169
|
-
expect.any(Map)
|
|
1170
|
-
);
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
1086
|
it('should map SR ids from steps and output requirement-test-case coverage rows', async () => {
|
|
1174
1087
|
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1175
1088
|
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
@@ -1193,6 +1106,8 @@ describe('ResultDataProvider', () => {
|
|
|
1193
1106
|
{
|
|
1194
1107
|
workItemId: 5001,
|
|
1195
1108
|
requirementId: 'SR1001',
|
|
1109
|
+
baseKey: 'SR1001',
|
|
1110
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1196
1111
|
title: 'Covered requirement',
|
|
1197
1112
|
responsibility: 'ESUK',
|
|
1198
1113
|
linkedTestCaseIds: [101],
|
|
@@ -1200,6 +1115,8 @@ describe('ResultDataProvider', () => {
|
|
|
1200
1115
|
{
|
|
1201
1116
|
workItemId: 5002,
|
|
1202
1117
|
requirementId: 'SR1002',
|
|
1118
|
+
baseKey: 'SR1002',
|
|
1119
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1203
1120
|
title: 'Referenced from non-linked step text',
|
|
1204
1121
|
responsibility: 'IL',
|
|
1205
1122
|
linkedTestCaseIds: [],
|
|
@@ -1207,6 +1124,8 @@ describe('ResultDataProvider', () => {
|
|
|
1207
1124
|
{
|
|
1208
1125
|
workItemId: 5003,
|
|
1209
1126
|
requirementId: 'SR1003',
|
|
1127
|
+
baseKey: 'SR1003',
|
|
1128
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1210
1129
|
title: 'Not covered by any test case',
|
|
1211
1130
|
responsibility: 'IL',
|
|
1212
1131
|
linkedTestCaseIds: [],
|
|
@@ -1220,11 +1139,11 @@ describe('ResultDataProvider', () => {
|
|
|
1220
1139
|
actionResults: [
|
|
1221
1140
|
{
|
|
1222
1141
|
action: 'Validate <b>S</b><b>R</b> 1 0 0 1 happy path',
|
|
1223
|
-
expected: '',
|
|
1142
|
+
expected: '<b>S</b><b>R</b> 1 0 0 1',
|
|
1224
1143
|
outcome: 'Passed',
|
|
1225
1144
|
},
|
|
1226
|
-
{ action: 'Validate SR1001 failed flow', expected: '
|
|
1227
|
-
{ action: '', expected: '
|
|
1145
|
+
{ action: 'Validate SR1001 failed flow', expected: 'SR1001', outcome: 'Failed' },
|
|
1146
|
+
{ action: '', expected: 'S R 1 0 0 1', outcome: 'Unspecified' },
|
|
1228
1147
|
],
|
|
1229
1148
|
},
|
|
1230
1149
|
},
|
|
@@ -1241,7 +1160,7 @@ describe('ResultDataProvider', () => {
|
|
|
1241
1160
|
stepId: '1',
|
|
1242
1161
|
stepPosition: '1',
|
|
1243
1162
|
action: 'Definition contains SR1002',
|
|
1244
|
-
expected: '',
|
|
1163
|
+
expected: 'SR1002',
|
|
1245
1164
|
isSharedStepTitle: false,
|
|
1246
1165
|
},
|
|
1247
1166
|
]);
|
|
@@ -1255,59 +1174,71 @@ describe('ResultDataProvider', () => {
|
|
|
1255
1174
|
expect(result).toEqual(
|
|
1256
1175
|
expect.objectContaining({
|
|
1257
1176
|
sheetName: expect.stringContaining('MEWP L2 Coverage'),
|
|
1258
|
-
columnOrder: expect.arrayContaining(['
|
|
1177
|
+
columnOrder: expect.arrayContaining(['L2 REQ ID', 'L2 REQ Title', 'L2 Run Status']),
|
|
1259
1178
|
})
|
|
1260
1179
|
);
|
|
1261
1180
|
|
|
1262
|
-
const covered = result.rows.find(
|
|
1263
|
-
|
|
1264
|
-
);
|
|
1265
|
-
const inferredByStepText = result.rows.find(
|
|
1266
|
-
(row: any) => row['Customer ID'] === 'SR1002' && row['Test case id'] === 102
|
|
1267
|
-
);
|
|
1268
|
-
const uncovered = result.rows.find(
|
|
1269
|
-
(row: any) =>
|
|
1270
|
-
row['Customer ID'] === 'SR1003' &&
|
|
1271
|
-
(row['Test case id'] === '' || row['Test case id'] === undefined || row['Test case id'] === null)
|
|
1272
|
-
);
|
|
1181
|
+
const covered = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1001');
|
|
1182
|
+
const inferredByStepText = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1002');
|
|
1183
|
+
const uncovered = result.rows.find((row: any) => row['L2 REQ ID'] === 'SR1003');
|
|
1273
1184
|
|
|
1274
1185
|
expect(covered).toEqual(
|
|
1275
1186
|
expect.objectContaining({
|
|
1276
|
-
'
|
|
1277
|
-
'
|
|
1278
|
-
'
|
|
1279
|
-
'
|
|
1280
|
-
'
|
|
1281
|
-
'
|
|
1187
|
+
'L2 REQ Title': 'Covered requirement',
|
|
1188
|
+
'L2 SubSystem': '',
|
|
1189
|
+
'L2 Run Status': 'Fail',
|
|
1190
|
+
'Bug ID': '',
|
|
1191
|
+
'L3 REQ ID': '',
|
|
1192
|
+
'L4 REQ ID': '',
|
|
1282
1193
|
})
|
|
1283
1194
|
);
|
|
1284
1195
|
expect(inferredByStepText).toEqual(
|
|
1285
1196
|
expect.objectContaining({
|
|
1286
|
-
'
|
|
1287
|
-
'
|
|
1288
|
-
'
|
|
1289
|
-
'Number of passed steps': 0,
|
|
1290
|
-
'Number of failed steps': 0,
|
|
1291
|
-
'Number of not run tests': 1,
|
|
1197
|
+
'L2 REQ Title': 'Referenced from non-linked step text',
|
|
1198
|
+
'L2 SubSystem': '',
|
|
1199
|
+
'L2 Run Status': 'Not Run',
|
|
1292
1200
|
})
|
|
1293
1201
|
);
|
|
1294
1202
|
expect(uncovered).toEqual(
|
|
1295
1203
|
expect.objectContaining({
|
|
1296
|
-
'
|
|
1297
|
-
'
|
|
1298
|
-
'
|
|
1299
|
-
'Number of passed steps': 0,
|
|
1300
|
-
'Number of failed steps': 0,
|
|
1301
|
-
'Number of not run tests': 0,
|
|
1204
|
+
'L2 REQ Title': 'Not covered by any test case',
|
|
1205
|
+
'L2 SubSystem': '',
|
|
1206
|
+
'L2 Run Status': 'Not Run',
|
|
1302
1207
|
})
|
|
1303
1208
|
);
|
|
1304
1209
|
});
|
|
1305
1210
|
|
|
1306
1211
|
it('should extract SR ids from HTML/spacing and return unique ids per step text', () => {
|
|
1307
1212
|
const text =
|
|
1308
|
-
'
|
|
1213
|
+
'<b>S</b><b>R</b> 0 0 0 1; SR0002; S R 0 0 0 3; SR0002; <b>SR</b>0004';
|
|
1309
1214
|
const codes = (resultDataProvider as any).extractRequirementCodesFromText(text);
|
|
1310
|
-
expect([...codes].sort()).toEqual(['
|
|
1215
|
+
expect([...codes].sort()).toEqual(['SR0001', 'SR0002', 'SR0003', 'SR0004']);
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
it('should keep only clean SR tokens and ignore noisy version/VVRM fragments', () => {
|
|
1219
|
+
const extract = (text: string) =>
|
|
1220
|
+
[...((resultDataProvider as any).extractRequirementCodesFromText(text) as Set<string>)].sort();
|
|
1221
|
+
|
|
1222
|
+
expect(extract('SR12413; SR24513; SR25135 VVRM2425')).toEqual(['SR12413', 'SR24513']);
|
|
1223
|
+
expect(extract('SR12413; SR12412; SR12413-V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
1224
|
+
expect(extract('SR12413; SR12412; SR12413 V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
1225
|
+
expect(extract('SR12413, SR12412, SR12413-V3.24')).toEqual(['SR12412', 'SR12413']);
|
|
1226
|
+
|
|
1227
|
+
const extractWithSuffix = (text: string) =>
|
|
1228
|
+
[
|
|
1229
|
+
...((resultDataProvider as any).extractRequirementCodesFromExpectedText(
|
|
1230
|
+
text,
|
|
1231
|
+
true
|
|
1232
|
+
) as Set<string>),
|
|
1233
|
+
].sort();
|
|
1234
|
+
expect(extractWithSuffix('SR0095-2,3; SR0100-1,2,3,4')).toEqual([
|
|
1235
|
+
'SR0095-2',
|
|
1236
|
+
'SR0095-3',
|
|
1237
|
+
'SR0100-1',
|
|
1238
|
+
'SR0100-2',
|
|
1239
|
+
'SR0100-3',
|
|
1240
|
+
'SR0100-4',
|
|
1241
|
+
]);
|
|
1311
1242
|
});
|
|
1312
1243
|
|
|
1313
1244
|
it('should not backfill definition steps as not-run when a real run exists but has no action results', async () => {
|
|
@@ -1330,6 +1261,8 @@ describe('ResultDataProvider', () => {
|
|
|
1330
1261
|
{
|
|
1331
1262
|
workItemId: 7001,
|
|
1332
1263
|
requirementId: 'SR2001',
|
|
1264
|
+
baseKey: 'SR2001',
|
|
1265
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1333
1266
|
title: 'Has run but no actions',
|
|
1334
1267
|
responsibility: 'ESUK',
|
|
1335
1268
|
linkedTestCaseIds: [101],
|
|
@@ -1364,15 +1297,11 @@ describe('ResultDataProvider', () => {
|
|
|
1364
1297
|
[1]
|
|
1365
1298
|
);
|
|
1366
1299
|
|
|
1367
|
-
const row = result.rows.find(
|
|
1368
|
-
(item: any) => item['Customer ID'] === 'SR2001' && item['Test case id'] === 101
|
|
1369
|
-
);
|
|
1300
|
+
const row = result.rows.find((item: any) => item['L2 REQ ID'] === 'SR2001');
|
|
1370
1301
|
expect(parseSpy).not.toHaveBeenCalled();
|
|
1371
1302
|
expect(row).toEqual(
|
|
1372
1303
|
expect.objectContaining({
|
|
1373
|
-
'
|
|
1374
|
-
'Number of failed steps': 0,
|
|
1375
|
-
'Number of not run tests': 0,
|
|
1304
|
+
'L2 Run Status': 'Not Run',
|
|
1376
1305
|
})
|
|
1377
1306
|
);
|
|
1378
1307
|
});
|
|
@@ -1387,7 +1316,1421 @@ describe('ResultDataProvider', () => {
|
|
|
1387
1316
|
4321
|
|
1388
1317
|
);
|
|
1389
1318
|
|
|
1390
|
-
expect(requirementId).toBe('
|
|
1319
|
+
expect(requirementId).toBe('SR4321');
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
it('should derive responsibility from Custom.SAPWBS when present', () => {
|
|
1323
|
+
const responsibility = (resultDataProvider as any).deriveMewpResponsibility({
|
|
1324
|
+
'Custom.SAPWBS': 'IL',
|
|
1325
|
+
'System.AreaPath': 'MEWP\\ESUK',
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
expect(responsibility).toBe('IL');
|
|
1329
|
+
});
|
|
1330
|
+
|
|
1331
|
+
it('should derive responsibility from AreaPath suffix ATP/ATP\\\\ESUK when SAPWBS is empty', () => {
|
|
1332
|
+
const esuk = (resultDataProvider as any).deriveMewpResponsibility({
|
|
1333
|
+
'Custom.SAPWBS': '',
|
|
1334
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK',
|
|
1335
|
+
});
|
|
1336
|
+
const il = (resultDataProvider as any).deriveMewpResponsibility({
|
|
1337
|
+
'Custom.SAPWBS': '',
|
|
1338
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP',
|
|
1339
|
+
});
|
|
1340
|
+
|
|
1341
|
+
expect(esuk).toBe('ESUK');
|
|
1342
|
+
expect(il).toBe('IL');
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it('should emit additive rows for bugs and L3/L4 links without bug duplication', () => {
|
|
1346
|
+
const requirements = [
|
|
1347
|
+
{
|
|
1348
|
+
requirementId: 'SR5303',
|
|
1349
|
+
baseKey: 'SR5303',
|
|
1350
|
+
title: 'Req 5303',
|
|
1351
|
+
subSystem: 'Power',
|
|
1352
|
+
responsibility: 'ESUK',
|
|
1353
|
+
linkedTestCaseIds: [101],
|
|
1354
|
+
},
|
|
1355
|
+
];
|
|
1356
|
+
|
|
1357
|
+
const requirementIndex = new Map([
|
|
1358
|
+
[
|
|
1359
|
+
'SR5303',
|
|
1360
|
+
new Map([
|
|
1361
|
+
[
|
|
1362
|
+
101,
|
|
1363
|
+
{
|
|
1364
|
+
passed: 0,
|
|
1365
|
+
failed: 1,
|
|
1366
|
+
notRun: 0,
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
1369
|
+
]),
|
|
1370
|
+
],
|
|
1371
|
+
]);
|
|
1372
|
+
|
|
1373
|
+
const observedTestCaseIdsByRequirement = new Map<string, Set<number>>([
|
|
1374
|
+
['SR5303', new Set([101])],
|
|
1375
|
+
]);
|
|
1376
|
+
|
|
1377
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1378
|
+
[
|
|
1379
|
+
101,
|
|
1380
|
+
{
|
|
1381
|
+
baseKeys: new Set(['SR5303']),
|
|
1382
|
+
fullCodes: new Set(['SR5303']),
|
|
1383
|
+
bugIds: new Set([10003, 20003]),
|
|
1384
|
+
},
|
|
1385
|
+
],
|
|
1386
|
+
]);
|
|
1387
|
+
|
|
1388
|
+
const externalBugsByTestCase = new Map([
|
|
1389
|
+
[
|
|
1390
|
+
101,
|
|
1391
|
+
[
|
|
1392
|
+
{ id: 10003, title: 'Bug 10003', responsibility: 'Elisra', requirementBaseKey: 'SR5303' },
|
|
1393
|
+
{ id: 20003, title: 'Bug 20003', responsibility: 'ESUK', requirementBaseKey: 'SR5303' },
|
|
1394
|
+
],
|
|
1395
|
+
],
|
|
1396
|
+
]);
|
|
1397
|
+
|
|
1398
|
+
const l3l4ByBaseKey = new Map([
|
|
1399
|
+
[
|
|
1400
|
+
'SR5303',
|
|
1401
|
+
[
|
|
1402
|
+
{ id: '9003', title: 'L3 9003', level: 'L3' as const },
|
|
1403
|
+
{ id: '9103', title: 'L4 9103', level: 'L4' as const },
|
|
1404
|
+
],
|
|
1405
|
+
],
|
|
1406
|
+
]);
|
|
1407
|
+
|
|
1408
|
+
const rows = (resultDataProvider as any).buildMewpCoverageRows(
|
|
1409
|
+
requirements,
|
|
1410
|
+
requirementIndex,
|
|
1411
|
+
observedTestCaseIdsByRequirement,
|
|
1412
|
+
linkedRequirementsByTestCase,
|
|
1413
|
+
l3l4ByBaseKey,
|
|
1414
|
+
externalBugsByTestCase
|
|
1415
|
+
);
|
|
1416
|
+
|
|
1417
|
+
expect(rows).toHaveLength(4);
|
|
1418
|
+
expect(rows.map((row: any) => row['Bug ID'])).toEqual([10003, 20003, '', '']);
|
|
1419
|
+
expect(rows[2]).toEqual(
|
|
1420
|
+
expect.objectContaining({
|
|
1421
|
+
'L3 REQ ID': '9003',
|
|
1422
|
+
'L4 REQ ID': '',
|
|
1423
|
+
})
|
|
1424
|
+
);
|
|
1425
|
+
expect(rows[3]).toEqual(
|
|
1426
|
+
expect.objectContaining({
|
|
1427
|
+
'L3 REQ ID': '',
|
|
1428
|
+
'L4 REQ ID': '9103',
|
|
1429
|
+
})
|
|
1430
|
+
);
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
it('should not emit bug rows from ADO-linked bug ids when external bugs source is empty', () => {
|
|
1434
|
+
const requirements = [
|
|
1435
|
+
{
|
|
1436
|
+
requirementId: 'SR5304',
|
|
1437
|
+
baseKey: 'SR5304',
|
|
1438
|
+
title: 'Req 5304',
|
|
1439
|
+
subSystem: 'Power',
|
|
1440
|
+
responsibility: 'ESUK',
|
|
1441
|
+
linkedTestCaseIds: [101],
|
|
1442
|
+
},
|
|
1443
|
+
];
|
|
1444
|
+
const requirementIndex = new Map([
|
|
1445
|
+
[
|
|
1446
|
+
'SR5304',
|
|
1447
|
+
new Map([
|
|
1448
|
+
[
|
|
1449
|
+
101,
|
|
1450
|
+
{
|
|
1451
|
+
passed: 0,
|
|
1452
|
+
failed: 1,
|
|
1453
|
+
notRun: 0,
|
|
1454
|
+
},
|
|
1455
|
+
],
|
|
1456
|
+
]),
|
|
1457
|
+
],
|
|
1458
|
+
]);
|
|
1459
|
+
const observedTestCaseIdsByRequirement = new Map<string, Set<number>>([
|
|
1460
|
+
['SR5304', new Set([101])],
|
|
1461
|
+
]);
|
|
1462
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1463
|
+
[
|
|
1464
|
+
101,
|
|
1465
|
+
{
|
|
1466
|
+
baseKeys: new Set(['SR5304']),
|
|
1467
|
+
fullCodes: new Set(['SR5304']),
|
|
1468
|
+
bugIds: new Set([55555]), // must be ignored in MEWP coverage mode
|
|
1469
|
+
},
|
|
1470
|
+
],
|
|
1471
|
+
]);
|
|
1472
|
+
|
|
1473
|
+
const rows = (resultDataProvider as any).buildMewpCoverageRows(
|
|
1474
|
+
requirements,
|
|
1475
|
+
requirementIndex,
|
|
1476
|
+
observedTestCaseIdsByRequirement,
|
|
1477
|
+
linkedRequirementsByTestCase,
|
|
1478
|
+
new Map(),
|
|
1479
|
+
new Map()
|
|
1480
|
+
);
|
|
1481
|
+
|
|
1482
|
+
expect(rows).toHaveLength(1);
|
|
1483
|
+
expect(rows[0]['L2 Run Status']).toBe('Fail');
|
|
1484
|
+
expect(rows[0]['Bug ID']).toBe('');
|
|
1485
|
+
expect(rows[0]['Bug Title']).toBe('');
|
|
1486
|
+
expect(rows[0]['Bug Responsibility']).toBe('');
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
it('should fallback bug responsibility from requirement when external bug row has unknown responsibility', () => {
|
|
1490
|
+
const requirements = [
|
|
1491
|
+
{
|
|
1492
|
+
requirementId: 'SR5305',
|
|
1493
|
+
baseKey: 'SR5305',
|
|
1494
|
+
title: 'Req 5305',
|
|
1495
|
+
subSystem: 'Auth',
|
|
1496
|
+
responsibility: 'IL',
|
|
1497
|
+
linkedTestCaseIds: [202],
|
|
1498
|
+
},
|
|
1499
|
+
];
|
|
1500
|
+
const requirementIndex = new Map([
|
|
1501
|
+
[
|
|
1502
|
+
'SR5305',
|
|
1503
|
+
new Map([
|
|
1504
|
+
[
|
|
1505
|
+
202,
|
|
1506
|
+
{
|
|
1507
|
+
passed: 0,
|
|
1508
|
+
failed: 1,
|
|
1509
|
+
notRun: 0,
|
|
1510
|
+
},
|
|
1511
|
+
],
|
|
1512
|
+
]),
|
|
1513
|
+
],
|
|
1514
|
+
]);
|
|
1515
|
+
const observedTestCaseIdsByRequirement = new Map<string, Set<number>>([
|
|
1516
|
+
['SR5305', new Set([202])],
|
|
1517
|
+
]);
|
|
1518
|
+
const linkedRequirementsByTestCase = new Map([
|
|
1519
|
+
[
|
|
1520
|
+
202,
|
|
1521
|
+
{
|
|
1522
|
+
baseKeys: new Set(['SR5305']),
|
|
1523
|
+
fullCodes: new Set(['SR5305']),
|
|
1524
|
+
bugIds: new Set(),
|
|
1525
|
+
},
|
|
1526
|
+
],
|
|
1527
|
+
]);
|
|
1528
|
+
const externalBugsByTestCase = new Map([
|
|
1529
|
+
[
|
|
1530
|
+
202,
|
|
1531
|
+
[
|
|
1532
|
+
{
|
|
1533
|
+
id: 99001,
|
|
1534
|
+
title: 'External bug without SAPWBS',
|
|
1535
|
+
responsibility: 'Unknown',
|
|
1536
|
+
requirementBaseKey: 'SR5305',
|
|
1537
|
+
},
|
|
1538
|
+
],
|
|
1539
|
+
],
|
|
1540
|
+
]);
|
|
1541
|
+
|
|
1542
|
+
const rows = (resultDataProvider as any).buildMewpCoverageRows(
|
|
1543
|
+
requirements,
|
|
1544
|
+
requirementIndex,
|
|
1545
|
+
observedTestCaseIdsByRequirement,
|
|
1546
|
+
linkedRequirementsByTestCase,
|
|
1547
|
+
new Map(),
|
|
1548
|
+
externalBugsByTestCase
|
|
1549
|
+
);
|
|
1550
|
+
|
|
1551
|
+
expect(rows).toHaveLength(1);
|
|
1552
|
+
expect(rows[0]['Bug ID']).toBe(99001);
|
|
1553
|
+
expect(rows[0]['Bug Responsibility']).toBe('Elisra');
|
|
1554
|
+
});
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
describe('getMewpInternalValidationFlatResults', () => {
|
|
1558
|
+
it('should skip test cases with no in-scope expected requirements and no linked requirements', async () => {
|
|
1559
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1560
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1561
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1562
|
+
{
|
|
1563
|
+
testPointsItems: [
|
|
1564
|
+
{ testCaseId: 101, testCaseName: 'TC 101' },
|
|
1565
|
+
{ testCaseId: 102, testCaseName: 'TC 102' },
|
|
1566
|
+
],
|
|
1567
|
+
testCasesItems: [
|
|
1568
|
+
{
|
|
1569
|
+
workItem: {
|
|
1570
|
+
id: 101,
|
|
1571
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1572
|
+
},
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
workItem: {
|
|
1576
|
+
id: 102,
|
|
1577
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1578
|
+
},
|
|
1579
|
+
},
|
|
1580
|
+
],
|
|
1581
|
+
},
|
|
1582
|
+
]);
|
|
1583
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1584
|
+
{
|
|
1585
|
+
workItemId: 5001,
|
|
1586
|
+
requirementId: 'SR0001',
|
|
1587
|
+
baseKey: 'SR0001',
|
|
1588
|
+
title: 'Req 1',
|
|
1589
|
+
responsibility: 'ESUK',
|
|
1590
|
+
linkedTestCaseIds: [101],
|
|
1591
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1592
|
+
},
|
|
1593
|
+
]);
|
|
1594
|
+
jest.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(
|
|
1595
|
+
new Map([[101, { baseKeys: new Set(['SR0001']), fullCodes: new Set(['SR0001']) }]])
|
|
1596
|
+
);
|
|
1597
|
+
jest
|
|
1598
|
+
.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps')
|
|
1599
|
+
.mockResolvedValueOnce([
|
|
1600
|
+
{
|
|
1601
|
+
stepId: '1',
|
|
1602
|
+
stepPosition: '1',
|
|
1603
|
+
action: '',
|
|
1604
|
+
expected: 'SR0001',
|
|
1605
|
+
isSharedStepTitle: false,
|
|
1606
|
+
},
|
|
1607
|
+
])
|
|
1608
|
+
.mockResolvedValueOnce([
|
|
1609
|
+
{
|
|
1610
|
+
stepId: '1',
|
|
1611
|
+
stepPosition: '1',
|
|
1612
|
+
action: '',
|
|
1613
|
+
expected: '',
|
|
1614
|
+
isSharedStepTitle: false,
|
|
1615
|
+
},
|
|
1616
|
+
]);
|
|
1617
|
+
|
|
1618
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
1619
|
+
'123',
|
|
1620
|
+
mockProjectName,
|
|
1621
|
+
[1]
|
|
1622
|
+
);
|
|
1623
|
+
|
|
1624
|
+
expect(result.rows).toHaveLength(2);
|
|
1625
|
+
expect(result.rows).toEqual(
|
|
1626
|
+
expect.arrayContaining([
|
|
1627
|
+
expect.objectContaining({
|
|
1628
|
+
'Test Case ID': 101,
|
|
1629
|
+
'Validation Status': 'Pass',
|
|
1630
|
+
'Mentioned but Not Linked': '',
|
|
1631
|
+
'Linked but Not Mentioned': '',
|
|
1632
|
+
}),
|
|
1633
|
+
expect.objectContaining({
|
|
1634
|
+
'Test Case ID': 102,
|
|
1635
|
+
'Validation Status': 'Pass',
|
|
1636
|
+
'Mentioned but Not Linked': '',
|
|
1637
|
+
'Linked but Not Mentioned': '',
|
|
1638
|
+
}),
|
|
1639
|
+
])
|
|
1640
|
+
);
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
it('should ignore non-L2 SR mentions and still report only real L2 discrepancies', async () => {
|
|
1644
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1645
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1646
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1647
|
+
{
|
|
1648
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1649
|
+
testCasesItems: [
|
|
1650
|
+
{
|
|
1651
|
+
workItem: {
|
|
1652
|
+
id: 101,
|
|
1653
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1654
|
+
},
|
|
1655
|
+
},
|
|
1656
|
+
],
|
|
1657
|
+
},
|
|
1658
|
+
]);
|
|
1659
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1660
|
+
{
|
|
1661
|
+
workItemId: 5001,
|
|
1662
|
+
requirementId: 'SR0001',
|
|
1663
|
+
baseKey: 'SR0001',
|
|
1664
|
+
title: 'Req 1',
|
|
1665
|
+
responsibility: 'ESUK',
|
|
1666
|
+
linkedTestCaseIds: [101],
|
|
1667
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1668
|
+
},
|
|
1669
|
+
]);
|
|
1670
|
+
jest.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(
|
|
1671
|
+
new Map()
|
|
1672
|
+
);
|
|
1673
|
+
jest.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1674
|
+
{
|
|
1675
|
+
stepId: '1',
|
|
1676
|
+
stepPosition: '1',
|
|
1677
|
+
action: '',
|
|
1678
|
+
expected: 'SR0001; SR9999',
|
|
1679
|
+
isSharedStepTitle: false,
|
|
1680
|
+
},
|
|
1681
|
+
]);
|
|
1682
|
+
|
|
1683
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
1684
|
+
'123',
|
|
1685
|
+
mockProjectName,
|
|
1686
|
+
[1]
|
|
1687
|
+
);
|
|
1688
|
+
|
|
1689
|
+
expect(result.rows).toHaveLength(1);
|
|
1690
|
+
expect(result.rows[0]).toEqual(
|
|
1691
|
+
expect.objectContaining({
|
|
1692
|
+
'Test Case ID': 101,
|
|
1693
|
+
'Mentioned but Not Linked': 'Step 1: SR0001',
|
|
1694
|
+
'Linked but Not Mentioned': '',
|
|
1695
|
+
'Validation Status': 'Fail',
|
|
1696
|
+
})
|
|
1697
|
+
);
|
|
1698
|
+
});
|
|
1699
|
+
|
|
1700
|
+
it('should emit Direction A rows with step context for missing sibling links', async () => {
|
|
1701
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1702
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1703
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1704
|
+
{
|
|
1705
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1706
|
+
testCasesItems: [
|
|
1707
|
+
{
|
|
1708
|
+
workItem: {
|
|
1709
|
+
id: 101,
|
|
1710
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1711
|
+
},
|
|
1712
|
+
},
|
|
1713
|
+
],
|
|
1714
|
+
},
|
|
1715
|
+
]);
|
|
1716
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1717
|
+
{
|
|
1718
|
+
workItemId: 5001,
|
|
1719
|
+
requirementId: 'SR0001',
|
|
1720
|
+
baseKey: 'SR0001',
|
|
1721
|
+
title: 'Req parent',
|
|
1722
|
+
responsibility: 'ESUK',
|
|
1723
|
+
linkedTestCaseIds: [101],
|
|
1724
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
workItemId: 5002,
|
|
1728
|
+
requirementId: 'SR0001-1',
|
|
1729
|
+
baseKey: 'SR0001',
|
|
1730
|
+
title: 'Req child',
|
|
1731
|
+
responsibility: 'ESUK',
|
|
1732
|
+
linkedTestCaseIds: [],
|
|
1733
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1734
|
+
},
|
|
1735
|
+
]);
|
|
1736
|
+
jest.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase').mockResolvedValueOnce(
|
|
1737
|
+
new Map([[101, { baseKeys: new Set(['SR0001']), fullCodes: new Set(['SR0001']) }]])
|
|
1738
|
+
);
|
|
1739
|
+
jest.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1740
|
+
{
|
|
1741
|
+
stepId: '2',
|
|
1742
|
+
stepPosition: '2',
|
|
1743
|
+
action: '',
|
|
1744
|
+
expected: 'SR0001; SR0002',
|
|
1745
|
+
isSharedStepTitle: false,
|
|
1746
|
+
},
|
|
1747
|
+
]);
|
|
1748
|
+
|
|
1749
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
1750
|
+
'123',
|
|
1751
|
+
mockProjectName,
|
|
1752
|
+
[1]
|
|
1753
|
+
);
|
|
1754
|
+
|
|
1755
|
+
expect(result.rows).toEqual(
|
|
1756
|
+
expect.arrayContaining([
|
|
1757
|
+
expect.objectContaining({
|
|
1758
|
+
'Test Case ID': 101,
|
|
1759
|
+
'Mentioned but Not Linked': expect.stringContaining('Step 2: SR0001-1'),
|
|
1760
|
+
'Validation Status': 'Fail',
|
|
1761
|
+
}),
|
|
1762
|
+
])
|
|
1763
|
+
);
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
it('should not duplicate Direction A discrepancy when same requirement is repeated in multiple steps', async () => {
|
|
1767
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1768
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1769
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1770
|
+
{
|
|
1771
|
+
testPointsItems: [{ testCaseId: 101, testCaseName: 'TC 101' }],
|
|
1772
|
+
testCasesItems: [
|
|
1773
|
+
{
|
|
1774
|
+
workItem: {
|
|
1775
|
+
id: 101,
|
|
1776
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
1777
|
+
},
|
|
1778
|
+
},
|
|
1779
|
+
],
|
|
1780
|
+
},
|
|
1781
|
+
]);
|
|
1782
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1783
|
+
{
|
|
1784
|
+
workItemId: 5001,
|
|
1785
|
+
requirementId: 'SR0001',
|
|
1786
|
+
baseKey: 'SR0001',
|
|
1787
|
+
title: 'Req 1',
|
|
1788
|
+
responsibility: 'ESUK',
|
|
1789
|
+
linkedTestCaseIds: [],
|
|
1790
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1791
|
+
},
|
|
1792
|
+
]);
|
|
1793
|
+
jest
|
|
1794
|
+
.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase')
|
|
1795
|
+
.mockResolvedValueOnce(new Map());
|
|
1796
|
+
jest.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
1797
|
+
{
|
|
1798
|
+
stepId: '1',
|
|
1799
|
+
stepPosition: '1',
|
|
1800
|
+
action: '',
|
|
1801
|
+
expected: 'SR0001',
|
|
1802
|
+
isSharedStepTitle: false,
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
stepId: '2',
|
|
1806
|
+
stepPosition: '2',
|
|
1807
|
+
action: '',
|
|
1808
|
+
expected: 'SR0001',
|
|
1809
|
+
isSharedStepTitle: false,
|
|
1810
|
+
},
|
|
1811
|
+
]);
|
|
1812
|
+
|
|
1813
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
1814
|
+
'123',
|
|
1815
|
+
mockProjectName,
|
|
1816
|
+
[1]
|
|
1817
|
+
);
|
|
1818
|
+
|
|
1819
|
+
expect(result.rows).toHaveLength(1);
|
|
1820
|
+
expect(result.rows[0]).toEqual(
|
|
1821
|
+
expect.objectContaining({
|
|
1822
|
+
'Test Case ID': 101,
|
|
1823
|
+
'Mentioned but Not Linked': 'Step 1: SR0001',
|
|
1824
|
+
'Linked but Not Mentioned': '',
|
|
1825
|
+
'Validation Status': 'Fail',
|
|
1826
|
+
})
|
|
1827
|
+
);
|
|
1828
|
+
});
|
|
1829
|
+
|
|
1830
|
+
it('should produce one detailed row per test case with correct bidirectional discrepancies', async () => {
|
|
1831
|
+
const mockDetailedStepsByTestCase = new Map<number, any[]>([
|
|
1832
|
+
[
|
|
1833
|
+
201,
|
|
1834
|
+
[
|
|
1835
|
+
{
|
|
1836
|
+
stepId: '1',
|
|
1837
|
+
stepPosition: '1',
|
|
1838
|
+
action: 'Validate parent SR0511 and SR0095 siblings',
|
|
1839
|
+
expected: '<b>sr0511</b>; SR0095-2,3; VVRM-05',
|
|
1840
|
+
isSharedStepTitle: false,
|
|
1841
|
+
},
|
|
1842
|
+
{
|
|
1843
|
+
stepId: '2',
|
|
1844
|
+
stepPosition: '2',
|
|
1845
|
+
action: 'Noisy requirement-like token should be ignored',
|
|
1846
|
+
expected: 'SR0511-V3.24',
|
|
1847
|
+
isSharedStepTitle: false,
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
stepId: '3',
|
|
1851
|
+
stepPosition: '3',
|
|
1852
|
+
action: 'Regression note',
|
|
1853
|
+
expected: 'Verification note only',
|
|
1854
|
+
isSharedStepTitle: false,
|
|
1855
|
+
},
|
|
1856
|
+
],
|
|
1857
|
+
],
|
|
1858
|
+
[
|
|
1859
|
+
202,
|
|
1860
|
+
[
|
|
1861
|
+
{
|
|
1862
|
+
stepId: '1',
|
|
1863
|
+
stepPosition: '1',
|
|
1864
|
+
action: 'Linked SR0200 exists but is not cleanly mentioned in expected result',
|
|
1865
|
+
expected: 'VVRM-22; SR0200 V3.1',
|
|
1866
|
+
isSharedStepTitle: false,
|
|
1867
|
+
},
|
|
1868
|
+
{
|
|
1869
|
+
stepId: '2',
|
|
1870
|
+
stepPosition: '2',
|
|
1871
|
+
action: 'Execution notes',
|
|
1872
|
+
expected: 'Notes without SR requirement token',
|
|
1873
|
+
isSharedStepTitle: false,
|
|
1874
|
+
},
|
|
1875
|
+
],
|
|
1876
|
+
],
|
|
1877
|
+
[
|
|
1878
|
+
203,
|
|
1879
|
+
[
|
|
1880
|
+
{
|
|
1881
|
+
stepId: '1',
|
|
1882
|
+
stepPosition: '1',
|
|
1883
|
+
action: 'Primary requirement validation for SR0100-1',
|
|
1884
|
+
expected: '<i>SR0100-1</i>',
|
|
1885
|
+
isSharedStepTitle: false,
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
stepId: '3',
|
|
1889
|
+
stepPosition: '3',
|
|
1890
|
+
action: 'Repeated mention should not create duplicate mismatch',
|
|
1891
|
+
expected: 'SR0100-1; SR0100-1',
|
|
1892
|
+
isSharedStepTitle: false,
|
|
1893
|
+
},
|
|
1894
|
+
],
|
|
1895
|
+
],
|
|
1896
|
+
]);
|
|
1897
|
+
const mockLinkedRequirementsByTestCase = new Map<number, any>([
|
|
1898
|
+
[
|
|
1899
|
+
201,
|
|
1900
|
+
{
|
|
1901
|
+
baseKeys: new Set(['SR0511', 'SR0095', 'SR8888']),
|
|
1902
|
+
fullCodes: new Set(['SR0511', 'SR0095-2', 'SR8888']),
|
|
1903
|
+
},
|
|
1904
|
+
],
|
|
1905
|
+
[
|
|
1906
|
+
202,
|
|
1907
|
+
{
|
|
1908
|
+
baseKeys: new Set(['SR0200']),
|
|
1909
|
+
fullCodes: new Set(['SR0200']),
|
|
1910
|
+
},
|
|
1911
|
+
],
|
|
1912
|
+
[
|
|
1913
|
+
203,
|
|
1914
|
+
{
|
|
1915
|
+
baseKeys: new Set(['SR0100']),
|
|
1916
|
+
fullCodes: new Set(['SR0100-1']),
|
|
1917
|
+
},
|
|
1918
|
+
],
|
|
1919
|
+
]);
|
|
1920
|
+
|
|
1921
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
1922
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
1923
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
1924
|
+
{
|
|
1925
|
+
testPointsItems: [
|
|
1926
|
+
{ testCaseId: 201, testCaseName: 'TC 201 - Mixed discrepancies' },
|
|
1927
|
+
{ testCaseId: 202, testCaseName: 'TC 202 - Link only' },
|
|
1928
|
+
{ testCaseId: 203, testCaseName: 'TC 203 - Fully valid' },
|
|
1929
|
+
],
|
|
1930
|
+
testCasesItems: [
|
|
1931
|
+
{
|
|
1932
|
+
workItem: {
|
|
1933
|
+
id: 201,
|
|
1934
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-201"></steps>' }],
|
|
1935
|
+
},
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
workItem: {
|
|
1939
|
+
id: 202,
|
|
1940
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-202"></steps>' }],
|
|
1941
|
+
},
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
workItem: {
|
|
1945
|
+
id: 203,
|
|
1946
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-203"></steps>' }],
|
|
1947
|
+
},
|
|
1948
|
+
},
|
|
1949
|
+
],
|
|
1950
|
+
},
|
|
1951
|
+
]);
|
|
1952
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
1953
|
+
{
|
|
1954
|
+
workItemId: 6001,
|
|
1955
|
+
requirementId: 'SR0511',
|
|
1956
|
+
baseKey: 'SR0511',
|
|
1957
|
+
title: 'Parent 0511',
|
|
1958
|
+
responsibility: 'ESUK',
|
|
1959
|
+
linkedTestCaseIds: [201],
|
|
1960
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
workItemId: 6002,
|
|
1964
|
+
requirementId: 'SR0511-1',
|
|
1965
|
+
baseKey: 'SR0511',
|
|
1966
|
+
title: 'Child 0511-1',
|
|
1967
|
+
responsibility: 'ESUK',
|
|
1968
|
+
linkedTestCaseIds: [],
|
|
1969
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1970
|
+
},
|
|
1971
|
+
{
|
|
1972
|
+
workItemId: 6003,
|
|
1973
|
+
requirementId: 'SR0511-2',
|
|
1974
|
+
baseKey: 'SR0511',
|
|
1975
|
+
title: 'Child 0511-2',
|
|
1976
|
+
responsibility: 'ESUK',
|
|
1977
|
+
linkedTestCaseIds: [],
|
|
1978
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
workItemId: 6004,
|
|
1982
|
+
requirementId: 'SR0095-2',
|
|
1983
|
+
baseKey: 'SR0095',
|
|
1984
|
+
title: 'SR0095 child 2',
|
|
1985
|
+
responsibility: 'ESUK',
|
|
1986
|
+
linkedTestCaseIds: [],
|
|
1987
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
workItemId: 6005,
|
|
1991
|
+
requirementId: 'SR0095-3',
|
|
1992
|
+
baseKey: 'SR0095',
|
|
1993
|
+
title: 'SR0095 child 3',
|
|
1994
|
+
responsibility: 'ESUK',
|
|
1995
|
+
linkedTestCaseIds: [],
|
|
1996
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
1997
|
+
},
|
|
1998
|
+
{
|
|
1999
|
+
workItemId: 6006,
|
|
2000
|
+
requirementId: 'SR0200',
|
|
2001
|
+
baseKey: 'SR0200',
|
|
2002
|
+
title: 'SR0200 standalone',
|
|
2003
|
+
responsibility: 'ESUK',
|
|
2004
|
+
linkedTestCaseIds: [202],
|
|
2005
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2006
|
+
},
|
|
2007
|
+
{
|
|
2008
|
+
workItemId: 6007,
|
|
2009
|
+
requirementId: 'SR0100-1',
|
|
2010
|
+
baseKey: 'SR0100',
|
|
2011
|
+
title: 'SR0100 child 1',
|
|
2012
|
+
responsibility: 'ESUK',
|
|
2013
|
+
linkedTestCaseIds: [203],
|
|
2014
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
2015
|
+
},
|
|
2016
|
+
]);
|
|
2017
|
+
jest
|
|
2018
|
+
.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase')
|
|
2019
|
+
.mockResolvedValueOnce(mockLinkedRequirementsByTestCase);
|
|
2020
|
+
jest
|
|
2021
|
+
.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps')
|
|
2022
|
+
.mockImplementation(async (...args: unknown[]) => {
|
|
2023
|
+
const stepsXml = String(args?.[0] || '');
|
|
2024
|
+
const testCaseMatch = /mock-steps-tc-(\d+)/i.exec(stepsXml);
|
|
2025
|
+
const testCaseId = Number(testCaseMatch?.[1] || 0);
|
|
2026
|
+
return mockDetailedStepsByTestCase.get(testCaseId) || [];
|
|
2027
|
+
});
|
|
2028
|
+
|
|
2029
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
2030
|
+
'123',
|
|
2031
|
+
mockProjectName,
|
|
2032
|
+
[1]
|
|
2033
|
+
);
|
|
2034
|
+
|
|
2035
|
+
expect(result.rows).toHaveLength(3);
|
|
2036
|
+
const byTestCase = new Map<number, any>(result.rows.map((row: any) => [row['Test Case ID'], row]));
|
|
2037
|
+
expect(new Set(result.rows.map((row: any) => row['Test Case ID']))).toEqual(new Set([201, 202, 203]));
|
|
2038
|
+
|
|
2039
|
+
expect(byTestCase.get(201)).toEqual(
|
|
2040
|
+
expect.objectContaining({
|
|
2041
|
+
'Test Case Title': 'TC 201 - Mixed discrepancies',
|
|
2042
|
+
'Mentioned but Not Linked': 'Step 1: SR0095-3, SR0511-1, SR0511-2',
|
|
2043
|
+
'Linked but Not Mentioned': 'SR8888',
|
|
2044
|
+
'Validation Status': 'Fail',
|
|
2045
|
+
})
|
|
2046
|
+
);
|
|
2047
|
+
expect(String(byTestCase.get(201)['Mentioned but Not Linked'] || '')).not.toContain('VVRM');
|
|
2048
|
+
|
|
2049
|
+
expect(byTestCase.get(202)).toEqual(
|
|
2050
|
+
expect.objectContaining({
|
|
2051
|
+
'Test Case Title': 'TC 202 - Link only',
|
|
2052
|
+
'Mentioned but Not Linked': '',
|
|
2053
|
+
'Linked but Not Mentioned': 'SR0200',
|
|
2054
|
+
'Validation Status': 'Fail',
|
|
2055
|
+
})
|
|
2056
|
+
);
|
|
2057
|
+
|
|
2058
|
+
expect(byTestCase.get(203)).toEqual(
|
|
2059
|
+
expect.objectContaining({
|
|
2060
|
+
'Test Case Title': 'TC 203 - Fully valid',
|
|
2061
|
+
'Mentioned but Not Linked': '',
|
|
2062
|
+
'Linked but Not Mentioned': '',
|
|
2063
|
+
'Validation Status': 'Pass',
|
|
2064
|
+
})
|
|
2065
|
+
);
|
|
2066
|
+
|
|
2067
|
+
expect((resultDataProvider as any).testStepParserHelper.parseTestSteps).toHaveBeenCalledTimes(3);
|
|
2068
|
+
const parseCalls = ((resultDataProvider as any).testStepParserHelper.parseTestSteps as jest.Mock).mock.calls;
|
|
2069
|
+
const parsedCaseIds = parseCalls
|
|
2070
|
+
.map(([xml]: [string]) => {
|
|
2071
|
+
const match = /mock-steps-tc-(\d+)/i.exec(String(xml || ''));
|
|
2072
|
+
return Number(match?.[1] || 0);
|
|
2073
|
+
})
|
|
2074
|
+
.filter((id: number) => Number.isFinite(id) && id > 0);
|
|
2075
|
+
expect(new Set(parsedCaseIds)).toEqual(new Set([201, 202, 203]));
|
|
2076
|
+
});
|
|
2077
|
+
|
|
2078
|
+
it('should parse TC-0042 mixed expected text and keep only valid SR requirement codes', async () => {
|
|
2079
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
2080
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
2081
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
2082
|
+
{
|
|
2083
|
+
testPointsItems: [{ testCaseId: 42, testCaseName: 'TC-0042' }],
|
|
2084
|
+
testCasesItems: [
|
|
2085
|
+
{
|
|
2086
|
+
workItem: {
|
|
2087
|
+
id: 42,
|
|
2088
|
+
workItemFields: [{ key: 'Steps', value: '<steps id="mock-steps-tc-0042"></steps>' }],
|
|
2089
|
+
},
|
|
2090
|
+
},
|
|
2091
|
+
],
|
|
2092
|
+
},
|
|
2093
|
+
]);
|
|
2094
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
2095
|
+
{
|
|
2096
|
+
workItemId: 7001,
|
|
2097
|
+
requirementId: 'SR0036',
|
|
2098
|
+
baseKey: 'SR0036',
|
|
2099
|
+
title: 'SR0036',
|
|
2100
|
+
responsibility: 'ESUK',
|
|
2101
|
+
linkedTestCaseIds: [],
|
|
2102
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2103
|
+
},
|
|
2104
|
+
{
|
|
2105
|
+
workItemId: 7002,
|
|
2106
|
+
requirementId: 'SR0215',
|
|
2107
|
+
baseKey: 'SR0215',
|
|
2108
|
+
title: 'SR0215',
|
|
2109
|
+
responsibility: 'ESUK',
|
|
2110
|
+
linkedTestCaseIds: [],
|
|
2111
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2112
|
+
},
|
|
2113
|
+
{
|
|
2114
|
+
workItemId: 7003,
|
|
2115
|
+
requirementId: 'SR0539',
|
|
2116
|
+
baseKey: 'SR0539',
|
|
2117
|
+
title: 'SR0539',
|
|
2118
|
+
responsibility: 'ESUK',
|
|
2119
|
+
linkedTestCaseIds: [],
|
|
2120
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
workItemId: 7004,
|
|
2124
|
+
requirementId: 'SR0348',
|
|
2125
|
+
baseKey: 'SR0348',
|
|
2126
|
+
title: 'SR0348',
|
|
2127
|
+
responsibility: 'ESUK',
|
|
2128
|
+
linkedTestCaseIds: [],
|
|
2129
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2130
|
+
},
|
|
2131
|
+
{
|
|
2132
|
+
workItemId: 7005,
|
|
2133
|
+
requirementId: 'SR0027',
|
|
2134
|
+
baseKey: 'SR0027',
|
|
2135
|
+
title: 'SR0027',
|
|
2136
|
+
responsibility: 'ESUK',
|
|
2137
|
+
linkedTestCaseIds: [],
|
|
2138
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2139
|
+
},
|
|
2140
|
+
{
|
|
2141
|
+
workItemId: 7006,
|
|
2142
|
+
requirementId: 'SR0041',
|
|
2143
|
+
baseKey: 'SR0041',
|
|
2144
|
+
title: 'SR0041',
|
|
2145
|
+
responsibility: 'ESUK',
|
|
2146
|
+
linkedTestCaseIds: [],
|
|
2147
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
workItemId: 7007,
|
|
2151
|
+
requirementId: 'SR0550',
|
|
2152
|
+
baseKey: 'SR0550',
|
|
2153
|
+
title: 'SR0550',
|
|
2154
|
+
responsibility: 'ESUK',
|
|
2155
|
+
linkedTestCaseIds: [],
|
|
2156
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2157
|
+
},
|
|
2158
|
+
{
|
|
2159
|
+
workItemId: 7008,
|
|
2160
|
+
requirementId: 'SR0550-2',
|
|
2161
|
+
baseKey: 'SR0550',
|
|
2162
|
+
title: 'SR0550-2',
|
|
2163
|
+
responsibility: 'ESUK',
|
|
2164
|
+
linkedTestCaseIds: [],
|
|
2165
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2\\MOP',
|
|
2166
|
+
},
|
|
2167
|
+
{
|
|
2168
|
+
workItemId: 7009,
|
|
2169
|
+
requirementId: 'SR0817',
|
|
2170
|
+
baseKey: 'SR0817',
|
|
2171
|
+
title: 'SR0817',
|
|
2172
|
+
responsibility: 'ESUK',
|
|
2173
|
+
linkedTestCaseIds: [],
|
|
2174
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2175
|
+
},
|
|
2176
|
+
{
|
|
2177
|
+
workItemId: 7010,
|
|
2178
|
+
requirementId: 'SR0818',
|
|
2179
|
+
baseKey: 'SR0818',
|
|
2180
|
+
title: 'SR0818',
|
|
2181
|
+
responsibility: 'ESUK',
|
|
2182
|
+
linkedTestCaseIds: [],
|
|
2183
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
workItemId: 7011,
|
|
2187
|
+
requirementId: 'SR0859',
|
|
2188
|
+
baseKey: 'SR0859',
|
|
2189
|
+
title: 'SR0859',
|
|
2190
|
+
responsibility: 'ESUK',
|
|
2191
|
+
linkedTestCaseIds: [],
|
|
2192
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2193
|
+
},
|
|
2194
|
+
]);
|
|
2195
|
+
jest
|
|
2196
|
+
.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase')
|
|
2197
|
+
.mockResolvedValueOnce(
|
|
2198
|
+
new Map([
|
|
2199
|
+
[
|
|
2200
|
+
42,
|
|
2201
|
+
{
|
|
2202
|
+
baseKeys: new Set([
|
|
2203
|
+
'SR0036',
|
|
2204
|
+
'SR0215',
|
|
2205
|
+
'SR0539',
|
|
2206
|
+
'SR0348',
|
|
2207
|
+
'SR0041',
|
|
2208
|
+
'SR0550',
|
|
2209
|
+
'SR0817',
|
|
2210
|
+
'SR0818',
|
|
2211
|
+
'SR0859',
|
|
2212
|
+
]),
|
|
2213
|
+
fullCodes: new Set([
|
|
2214
|
+
'SR0036',
|
|
2215
|
+
'SR0215',
|
|
2216
|
+
'SR0539',
|
|
2217
|
+
'SR0348',
|
|
2218
|
+
'SR0041',
|
|
2219
|
+
'SR0550',
|
|
2220
|
+
'SR0550-2',
|
|
2221
|
+
'SR0817',
|
|
2222
|
+
'SR0818',
|
|
2223
|
+
'SR0859',
|
|
2224
|
+
]),
|
|
2225
|
+
},
|
|
2226
|
+
],
|
|
2227
|
+
])
|
|
2228
|
+
);
|
|
2229
|
+
jest.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
2230
|
+
{
|
|
2231
|
+
stepId: '1',
|
|
2232
|
+
stepPosition: '1',
|
|
2233
|
+
action:
|
|
2234
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
|
2235
|
+
expected:
|
|
2236
|
+
'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)',
|
|
2237
|
+
isSharedStepTitle: false,
|
|
2238
|
+
},
|
|
2239
|
+
{
|
|
2240
|
+
stepId: '2',
|
|
2241
|
+
stepPosition: '2',
|
|
2242
|
+
action:
|
|
2243
|
+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.',
|
|
2244
|
+
expected:
|
|
2245
|
+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. (SR0539; SR0348; VVRM-1)',
|
|
2246
|
+
isSharedStepTitle: false,
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
stepId: '3',
|
|
2250
|
+
stepPosition: '3',
|
|
2251
|
+
action:
|
|
2252
|
+
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
|
|
2253
|
+
expected:
|
|
2254
|
+
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. (SR0027, SR0036; SR0041; VVRM-2)',
|
|
2255
|
+
isSharedStepTitle: false,
|
|
2256
|
+
},
|
|
2257
|
+
{
|
|
2258
|
+
stepId: '4',
|
|
2259
|
+
stepPosition: '4',
|
|
2260
|
+
action:
|
|
2261
|
+
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
|
2262
|
+
expected:
|
|
2263
|
+
'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. (SR0550-2)',
|
|
2264
|
+
isSharedStepTitle: false,
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
stepId: '5',
|
|
2268
|
+
stepPosition: '5',
|
|
2269
|
+
action: 'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.',
|
|
2270
|
+
expected: 'Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit. (VVRM-1)',
|
|
2271
|
+
isSharedStepTitle: false,
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
stepId: '6',
|
|
2275
|
+
stepPosition: '6',
|
|
2276
|
+
action:
|
|
2277
|
+
'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet consectetur adipisci velit.',
|
|
2278
|
+
expected:
|
|
2279
|
+
'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet consectetur adipisci velit. (SR0041; SR0550; VVRM-3)',
|
|
2280
|
+
isSharedStepTitle: false,
|
|
2281
|
+
},
|
|
2282
|
+
]);
|
|
2283
|
+
|
|
2284
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
2285
|
+
'123',
|
|
2286
|
+
mockProjectName,
|
|
2287
|
+
[1]
|
|
2288
|
+
);
|
|
2289
|
+
|
|
2290
|
+
expect(result.rows).toHaveLength(1);
|
|
2291
|
+
expect(result.rows[0]).toEqual(
|
|
2292
|
+
expect.objectContaining({
|
|
2293
|
+
'Test Case ID': 42,
|
|
2294
|
+
'Test Case Title': 'TC-0042',
|
|
2295
|
+
'Mentioned but Not Linked': 'Step 3: SR0027',
|
|
2296
|
+
'Linked but Not Mentioned': 'SR0817; SR0818; SR0859',
|
|
2297
|
+
'Validation Status': 'Fail',
|
|
2298
|
+
})
|
|
2299
|
+
);
|
|
2300
|
+
expect(String(result.rows[0]['Mentioned but Not Linked'] || '')).not.toContain('VVRM');
|
|
2301
|
+
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).not.toContain('VVRM');
|
|
2302
|
+
});
|
|
2303
|
+
});
|
|
2304
|
+
|
|
2305
|
+
describe('MEWP rel fallback scoping', () => {
|
|
2306
|
+
it('should fallback to previous Rel run when latest selected Rel has no run for a test case', async () => {
|
|
2307
|
+
const suites = [
|
|
2308
|
+
{ testSuiteId: 10, suiteName: 'Rel10 / Validation' },
|
|
2309
|
+
{ testSuiteId: 11, suiteName: 'Rel11 / Validation' },
|
|
2310
|
+
];
|
|
2311
|
+
const allSuites = [
|
|
2312
|
+
{ testSuiteId: 10, suiteName: 'Rel10 / Validation' },
|
|
2313
|
+
{ testSuiteId: 11, suiteName: 'Rel11 / Validation' },
|
|
2314
|
+
];
|
|
2315
|
+
const rawTestData = [
|
|
2316
|
+
{
|
|
2317
|
+
suiteName: 'Rel10 / Validation',
|
|
2318
|
+
testPointsItems: [
|
|
2319
|
+
{ testCaseId: 501, lastRunId: 100, lastResultId: 200 },
|
|
2320
|
+
{ testCaseId: 502, lastRunId: 300, lastResultId: 400 },
|
|
2321
|
+
],
|
|
2322
|
+
testCasesItems: [{ workItem: { id: 501 } }, { workItem: { id: 502 } }],
|
|
2323
|
+
},
|
|
2324
|
+
{
|
|
2325
|
+
suiteName: 'Rel11 / Validation',
|
|
2326
|
+
testPointsItems: [
|
|
2327
|
+
{ testCaseId: 501, lastRunId: 0, lastResultId: 0 },
|
|
2328
|
+
{ testCaseId: 502, lastRunId: 500, lastResultId: 600 },
|
|
2329
|
+
],
|
|
2330
|
+
testCasesItems: [{ workItem: { id: 501 } }, { workItem: { id: 502 } }],
|
|
2331
|
+
},
|
|
2332
|
+
];
|
|
2333
|
+
|
|
2334
|
+
const fetchSuitesSpy = jest
|
|
2335
|
+
.spyOn(resultDataProvider as any, 'fetchTestSuites')
|
|
2336
|
+
.mockResolvedValueOnce(suites)
|
|
2337
|
+
.mockResolvedValueOnce(allSuites);
|
|
2338
|
+
const fetchDataSpy = jest
|
|
2339
|
+
.spyOn(resultDataProvider as any, 'fetchTestData')
|
|
2340
|
+
.mockResolvedValueOnce(rawTestData);
|
|
2341
|
+
|
|
2342
|
+
const scoped = await (resultDataProvider as any).fetchMewpScopedTestData(
|
|
2343
|
+
'123',
|
|
2344
|
+
mockProjectName,
|
|
2345
|
+
[11],
|
|
2346
|
+
true
|
|
2347
|
+
);
|
|
2348
|
+
|
|
2349
|
+
expect(fetchSuitesSpy).toHaveBeenCalledTimes(2);
|
|
2350
|
+
expect(fetchDataSpy).toHaveBeenCalledTimes(1);
|
|
2351
|
+
expect(scoped).toHaveLength(1);
|
|
2352
|
+
const selectedPoints = scoped[0].testPointsItems;
|
|
2353
|
+
const tc501 = selectedPoints.find((item: any) => item.testCaseId === 501);
|
|
2354
|
+
const tc502 = selectedPoints.find((item: any) => item.testCaseId === 502);
|
|
2355
|
+
expect(tc501).toEqual(expect.objectContaining({ lastRunId: 100, lastResultId: 200 }));
|
|
2356
|
+
expect(tc502).toEqual(expect.objectContaining({ lastRunId: 500, lastResultId: 600 }));
|
|
2357
|
+
});
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2360
|
+
describe('MEWP external ingestion validation/parsing', () => {
|
|
2361
|
+
const validBugsSource = {
|
|
2362
|
+
name: 'bugs.xlsx',
|
|
2363
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/bugs/bugs.xlsx',
|
|
2364
|
+
sourceType: 'mewpExternalIngestion',
|
|
2365
|
+
};
|
|
2366
|
+
const validL3L4Source = {
|
|
2367
|
+
name: 'l3l4.xlsx',
|
|
2368
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/l3l4/l3l4.xlsx',
|
|
2369
|
+
sourceType: 'mewpExternalIngestion',
|
|
2370
|
+
};
|
|
2371
|
+
|
|
2372
|
+
it('should validate external files when required columns exist in A3 header row', async () => {
|
|
2373
|
+
const bugsBuffer = buildWorkbookBuffer([
|
|
2374
|
+
['', '', '', '', ''],
|
|
2375
|
+
['', '', '', '', ''],
|
|
2376
|
+
['Elisra_SortIndex', 'SR', 'TargetWorkItemId', 'Title', 'TargetState'],
|
|
2377
|
+
['101', 'SR0001', '9001', 'Bug one', 'Active'],
|
|
2378
|
+
]);
|
|
2379
|
+
const l3l4Buffer = buildWorkbookBuffer([
|
|
2380
|
+
['', '', '', '', '', '', '', ''],
|
|
2381
|
+
['', '', '', '', '', '', '', ''],
|
|
2382
|
+
[
|
|
2383
|
+
'SR',
|
|
2384
|
+
'AREA 34',
|
|
2385
|
+
'TargetWorkItemId Level 3',
|
|
2386
|
+
'TargetTitleLevel3',
|
|
2387
|
+
'TargetStateLevel 3',
|
|
2388
|
+
'TargetWorkItemIdLevel 4',
|
|
2389
|
+
'TargetTitleLevel4',
|
|
2390
|
+
'TargetStateLevel 4',
|
|
2391
|
+
],
|
|
2392
|
+
['SR0001', 'Level 3', '7001', 'Req L3', 'Active', '', '', ''],
|
|
2393
|
+
]);
|
|
2394
|
+
const axiosSpy = jest
|
|
2395
|
+
.spyOn(axios, 'get')
|
|
2396
|
+
.mockResolvedValueOnce({ data: bugsBuffer, headers: {} } as any)
|
|
2397
|
+
.mockResolvedValueOnce({ data: l3l4Buffer, headers: {} } as any);
|
|
2398
|
+
|
|
2399
|
+
const result = await (resultDataProvider as any).validateMewpExternalFiles({
|
|
2400
|
+
externalBugsFile: validBugsSource,
|
|
2401
|
+
externalL3L4File: validL3L4Source,
|
|
2402
|
+
});
|
|
2403
|
+
|
|
2404
|
+
expect(axiosSpy).toHaveBeenCalledTimes(2);
|
|
2405
|
+
expect(result.valid).toBe(true);
|
|
2406
|
+
expect(result.bugs).toEqual(
|
|
2407
|
+
expect.objectContaining({
|
|
2408
|
+
valid: true,
|
|
2409
|
+
headerRow: 'A3',
|
|
2410
|
+
matchedRequiredColumns: 5,
|
|
2411
|
+
})
|
|
2412
|
+
);
|
|
2413
|
+
expect(result.l3l4).toEqual(
|
|
2414
|
+
expect.objectContaining({
|
|
2415
|
+
valid: true,
|
|
2416
|
+
headerRow: 'A3',
|
|
2417
|
+
matchedRequiredColumns: 8,
|
|
2418
|
+
})
|
|
2419
|
+
);
|
|
2420
|
+
});
|
|
2421
|
+
|
|
2422
|
+
it('should accept A1 fallback header row for backward compatibility', async () => {
|
|
2423
|
+
const bugsBuffer = buildWorkbookBuffer([
|
|
2424
|
+
['Elisra_SortIndex', 'SR', 'TargetWorkItemId', 'Title', 'TargetState'],
|
|
2425
|
+
['101', 'SR0001', '9001', 'Bug one', 'Active'],
|
|
2426
|
+
]);
|
|
2427
|
+
jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: bugsBuffer, headers: {} } as any);
|
|
2428
|
+
|
|
2429
|
+
const result = await (resultDataProvider as any).validateMewpExternalFiles({
|
|
2430
|
+
externalBugsFile: validBugsSource,
|
|
2431
|
+
});
|
|
2432
|
+
|
|
2433
|
+
expect(result.valid).toBe(true);
|
|
2434
|
+
expect(result.bugs).toEqual(
|
|
2435
|
+
expect.objectContaining({
|
|
2436
|
+
valid: true,
|
|
2437
|
+
headerRow: 'A1',
|
|
2438
|
+
})
|
|
2439
|
+
);
|
|
2440
|
+
});
|
|
2441
|
+
|
|
2442
|
+
it('should fail validation when required columns are missing', async () => {
|
|
2443
|
+
const invalidBuffer = buildWorkbookBuffer([
|
|
2444
|
+
['', '', ''],
|
|
2445
|
+
['', '', ''],
|
|
2446
|
+
['Elisra_SortIndex', 'TargetWorkItemId', 'TargetState'],
|
|
2447
|
+
['101', '9001', 'Active'],
|
|
2448
|
+
]);
|
|
2449
|
+
jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: invalidBuffer, headers: {} } as any);
|
|
2450
|
+
|
|
2451
|
+
const result = await (resultDataProvider as any).validateMewpExternalFiles({
|
|
2452
|
+
externalBugsFile: validBugsSource,
|
|
2453
|
+
});
|
|
2454
|
+
|
|
2455
|
+
expect(result.valid).toBe(false);
|
|
2456
|
+
expect(result.bugs).toEqual(
|
|
2457
|
+
expect.objectContaining({
|
|
2458
|
+
valid: false,
|
|
2459
|
+
matchedRequiredColumns: 3,
|
|
2460
|
+
missingRequiredColumns: expect.arrayContaining(['SR', 'Title']),
|
|
2461
|
+
})
|
|
2462
|
+
);
|
|
2463
|
+
});
|
|
2464
|
+
|
|
2465
|
+
it('should reject files from non-dedicated bucket/object path', async () => {
|
|
2466
|
+
const result = await (resultDataProvider as any).validateMewpExternalFiles({
|
|
2467
|
+
externalBugsFile: {
|
|
2468
|
+
name: 'bugs.xlsx',
|
|
2469
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/other-prefix/bugs.xlsx',
|
|
2470
|
+
sourceType: 'mewpExternalIngestion',
|
|
2471
|
+
},
|
|
2472
|
+
});
|
|
2473
|
+
|
|
2474
|
+
expect(result.valid).toBe(false);
|
|
2475
|
+
expect(result.bugs?.message || '').toContain('Invalid object path');
|
|
2476
|
+
});
|
|
2477
|
+
|
|
2478
|
+
it('should filter external bugs by SR/state and dedupe by bug+requirement', async () => {
|
|
2479
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2480
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2481
|
+
{
|
|
2482
|
+
Elisra_SortIndex: '101',
|
|
2483
|
+
SR: 'SR0001',
|
|
2484
|
+
TargetWorkItemId: '9001',
|
|
2485
|
+
Title: 'Bug one',
|
|
2486
|
+
TargetState: 'Active',
|
|
2487
|
+
SAPWBS: 'ESUK',
|
|
2488
|
+
},
|
|
2489
|
+
{
|
|
2490
|
+
Elisra_SortIndex: '101',
|
|
2491
|
+
SR: 'SR0001',
|
|
2492
|
+
TargetWorkItemId: '9001',
|
|
2493
|
+
Title: 'Bug one duplicate',
|
|
2494
|
+
TargetState: 'Active',
|
|
2495
|
+
},
|
|
2496
|
+
{
|
|
2497
|
+
Elisra_SortIndex: '101',
|
|
2498
|
+
SR: '',
|
|
2499
|
+
TargetWorkItemId: '9002',
|
|
2500
|
+
Title: 'Missing SR',
|
|
2501
|
+
TargetState: 'Active',
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
Elisra_SortIndex: '101',
|
|
2505
|
+
SR: 'SR0002',
|
|
2506
|
+
TargetWorkItemId: '9003',
|
|
2507
|
+
Title: 'Closed bug',
|
|
2508
|
+
TargetState: 'Closed',
|
|
2509
|
+
},
|
|
2510
|
+
]);
|
|
2511
|
+
|
|
2512
|
+
const map = await (resultDataProvider as any).loadExternalBugsByTestCase(validBugsSource);
|
|
2513
|
+
const bugs = map.get(101) || [];
|
|
2514
|
+
|
|
2515
|
+
expect(bugs).toHaveLength(1);
|
|
2516
|
+
expect(bugs[0]).toEqual(
|
|
2517
|
+
expect.objectContaining({
|
|
2518
|
+
id: 9001,
|
|
2519
|
+
requirementBaseKey: 'SR0001',
|
|
2520
|
+
responsibility: 'ESUK',
|
|
2521
|
+
})
|
|
2522
|
+
);
|
|
2523
|
+
});
|
|
2524
|
+
|
|
2525
|
+
it('should require Elisra_SortIndex and ignore rows that only provide WorkItemId', async () => {
|
|
2526
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2527
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2528
|
+
{
|
|
2529
|
+
WorkItemId: '101',
|
|
2530
|
+
SR: 'SR0001',
|
|
2531
|
+
TargetWorkItemId: '9010',
|
|
2532
|
+
Title: 'Bug without Elisra_SortIndex',
|
|
2533
|
+
TargetState: 'Active',
|
|
2534
|
+
},
|
|
2535
|
+
]);
|
|
2536
|
+
|
|
2537
|
+
const map = await (resultDataProvider as any).loadExternalBugsByTestCase(validBugsSource);
|
|
2538
|
+
expect(map.size).toBe(0);
|
|
2539
|
+
});
|
|
2540
|
+
|
|
2541
|
+
it('should parse external L3/L4 file using AREA 34 semantics and terminal-state filtering', async () => {
|
|
2542
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2543
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2544
|
+
{
|
|
2545
|
+
SR: 'SR0001',
|
|
2546
|
+
'AREA 34': 'Level 4',
|
|
2547
|
+
'TargetWorkItemId Level 3': '7001',
|
|
2548
|
+
TargetTitleLevel3: 'L4 From Level3 Column',
|
|
2549
|
+
'TargetStateLevel 3': 'Active',
|
|
2550
|
+
},
|
|
2551
|
+
{
|
|
2552
|
+
SR: 'SR0001',
|
|
2553
|
+
'AREA 34': 'Level 3',
|
|
2554
|
+
'TargetWorkItemId Level 3': '7002',
|
|
2555
|
+
TargetTitleLevel3: 'L3 Requirement',
|
|
2556
|
+
'TargetStateLevel 3': 'Active',
|
|
2557
|
+
'TargetWorkItemIdLevel 4': '7003',
|
|
2558
|
+
TargetTitleLevel4: 'L4 Requirement',
|
|
2559
|
+
'TargetStateLevel 4': 'Closed',
|
|
2560
|
+
},
|
|
2561
|
+
]);
|
|
2562
|
+
|
|
2563
|
+
const map = await (resultDataProvider as any).loadExternalL3L4ByBaseKey(validL3L4Source);
|
|
2564
|
+
expect(map.get('SR0001')).toEqual([
|
|
2565
|
+
{ id: '7002', title: 'L3 Requirement', level: 'L3' },
|
|
2566
|
+
{ id: '7001', title: 'L4 From Level3 Column', level: 'L4' },
|
|
2567
|
+
]);
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2570
|
+
it('should exclude external open L3/L4 rows when SAPWBS resolves to ESUK', async () => {
|
|
2571
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2572
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2573
|
+
{
|
|
2574
|
+
SR: 'SR0001',
|
|
2575
|
+
'AREA 34': 'Level 3',
|
|
2576
|
+
'TargetWorkItemId Level 3': '7101',
|
|
2577
|
+
TargetTitleLevel3: 'L3 ESUK',
|
|
2578
|
+
'TargetStateLevel 3': 'Active',
|
|
2579
|
+
'TargetSapWbsLevel 3': 'ESUK',
|
|
2580
|
+
'TargetWorkItemIdLevel 4': '7201',
|
|
2581
|
+
TargetTitleLevel4: 'L4 IL',
|
|
2582
|
+
'TargetStateLevel 4': 'Active',
|
|
2583
|
+
'TargetSapWbsLevel 4': 'IL',
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
SR: 'SR0001',
|
|
2587
|
+
'AREA 34': 'Level 3',
|
|
2588
|
+
'TargetWorkItemId Level 3': '7102',
|
|
2589
|
+
TargetTitleLevel3: 'L3 IL',
|
|
2590
|
+
'TargetStateLevel 3': 'Active',
|
|
2591
|
+
'TargetSapWbsLevel 3': 'IL',
|
|
2592
|
+
},
|
|
2593
|
+
]);
|
|
2594
|
+
|
|
2595
|
+
const map = await (resultDataProvider as any).loadExternalL3L4ByBaseKey(validL3L4Source);
|
|
2596
|
+
expect(map.get('SR0001')).toEqual([
|
|
2597
|
+
{ id: '7102', title: 'L3 IL', level: 'L3' },
|
|
2598
|
+
{ id: '7201', title: 'L4 IL', level: 'L4' },
|
|
2599
|
+
]);
|
|
2600
|
+
});
|
|
2601
|
+
|
|
2602
|
+
it('should fallback L3/L4 SAPWBS exclusion from SR-mapped requirement when row SAPWBS is empty', async () => {
|
|
2603
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2604
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce([
|
|
2605
|
+
{
|
|
2606
|
+
SR: 'SR0001',
|
|
2607
|
+
'AREA 34': 'Level 3',
|
|
2608
|
+
'TargetWorkItemId Level 3': '7301',
|
|
2609
|
+
TargetTitleLevel3: 'L3 From ESUK Requirement',
|
|
2610
|
+
'TargetStateLevel 3': 'Active',
|
|
2611
|
+
'TargetSapWbsLevel 3': '',
|
|
2612
|
+
},
|
|
2613
|
+
{
|
|
2614
|
+
SR: 'SR0002',
|
|
2615
|
+
'AREA 34': 'Level 3',
|
|
2616
|
+
'TargetWorkItemId Level 3': '7302',
|
|
2617
|
+
TargetTitleLevel3: 'L3 From IL Requirement',
|
|
2618
|
+
'TargetStateLevel 3': 'Active',
|
|
2619
|
+
'TargetSapWbsLevel 3': '',
|
|
2620
|
+
},
|
|
2621
|
+
]);
|
|
2622
|
+
|
|
2623
|
+
const map = await (resultDataProvider as any).loadExternalL3L4ByBaseKey(
|
|
2624
|
+
validL3L4Source,
|
|
2625
|
+
new Map([
|
|
2626
|
+
['SR0001', 'ESUK'],
|
|
2627
|
+
['SR0002', 'IL'],
|
|
2628
|
+
])
|
|
2629
|
+
);
|
|
2630
|
+
|
|
2631
|
+
expect(map.has('SR0001')).toBe(false);
|
|
2632
|
+
expect(map.get('SR0002')).toEqual([{ id: '7302', title: 'L3 From IL Requirement', level: 'L3' }]);
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
it('should resolve bug responsibility from AreaPath when SAPWBS is empty', () => {
|
|
2636
|
+
const fromEsukAreaPath = (resultDataProvider as any).resolveBugResponsibility({
|
|
2637
|
+
'Custom.SAPWBS': '',
|
|
2638
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP\\ESUK',
|
|
2639
|
+
});
|
|
2640
|
+
const fromIlAreaPath = (resultDataProvider as any).resolveBugResponsibility({
|
|
2641
|
+
'Custom.SAPWBS': '',
|
|
2642
|
+
'System.AreaPath': 'MEWP\\Customer Requirements\\Level 2\\ATP',
|
|
2643
|
+
});
|
|
2644
|
+
const unknown = (resultDataProvider as any).resolveBugResponsibility({
|
|
2645
|
+
'Custom.SAPWBS': '',
|
|
2646
|
+
'System.AreaPath': 'MEWP\\Other\\Area',
|
|
2647
|
+
});
|
|
2648
|
+
|
|
2649
|
+
expect(fromEsukAreaPath).toBe('ESUK');
|
|
2650
|
+
expect(fromIlAreaPath).toBe('Elisra');
|
|
2651
|
+
expect(unknown).toBe('Unknown');
|
|
2652
|
+
});
|
|
2653
|
+
|
|
2654
|
+
it('should handle 1000 external bug rows and keep only in-scope parsed items', async () => {
|
|
2655
|
+
const rows: any[] = [];
|
|
2656
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2657
|
+
rows.push({
|
|
2658
|
+
Elisra_SortIndex: String(4000 + (i % 20)),
|
|
2659
|
+
SR: `SR${String(6000 + (i % 50)).padStart(4, '0')}`,
|
|
2660
|
+
TargetWorkItemId: String(900000 + i),
|
|
2661
|
+
Title: `Bug ${i}`,
|
|
2662
|
+
TargetState: i % 10 === 0 ? 'Closed' : 'Active',
|
|
2663
|
+
SAPWBS: i % 2 === 0 ? 'ESUK' : 'IL',
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2668
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce(rows);
|
|
2669
|
+
|
|
2670
|
+
const startedAt = Date.now();
|
|
2671
|
+
const map = await (resultDataProvider as any).loadExternalBugsByTestCase({
|
|
2672
|
+
name: 'bulk-bugs.xlsx',
|
|
2673
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/bugs/bulk-bugs.xlsx',
|
|
2674
|
+
sourceType: 'mewpExternalIngestion',
|
|
2675
|
+
});
|
|
2676
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2677
|
+
|
|
2678
|
+
const totalParsed = [...map.values()].reduce((sum, items) => sum + (items?.length || 0), 0);
|
|
2679
|
+
expect(totalParsed).toBe(900); // every 10th row is closed and filtered out
|
|
2680
|
+
expect(elapsedMs).toBeLessThan(5000);
|
|
2681
|
+
});
|
|
2682
|
+
|
|
2683
|
+
it('should handle 1000 external L3/L4 rows and map all active links', async () => {
|
|
2684
|
+
const rows: any[] = [];
|
|
2685
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2686
|
+
rows.push({
|
|
2687
|
+
SR: `SR${String(7000 + (i % 25)).padStart(4, '0')}`,
|
|
2688
|
+
'AREA 34': i % 3 === 0 ? 'Level 4' : 'Level 3',
|
|
2689
|
+
'TargetWorkItemId Level 3': String(800000 + i),
|
|
2690
|
+
TargetTitleLevel3: `L3/L4 Title ${i}`,
|
|
2691
|
+
'TargetStateLevel 3': i % 11 === 0 ? 'Resolved' : 'Active',
|
|
2692
|
+
'TargetWorkItemIdLevel 4': String(810000 + i),
|
|
2693
|
+
TargetTitleLevel4: `L4 Title ${i}`,
|
|
2694
|
+
'TargetStateLevel 4': i % 13 === 0 ? 'Closed' : 'Active',
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
const mewpExternalTableUtils = (resultDataProvider as any).mewpExternalTableUtils;
|
|
2699
|
+
jest.spyOn(mewpExternalTableUtils, 'loadExternalTableRows').mockResolvedValueOnce(rows);
|
|
2700
|
+
|
|
2701
|
+
const startedAt = Date.now();
|
|
2702
|
+
const map = await (resultDataProvider as any).loadExternalL3L4ByBaseKey({
|
|
2703
|
+
name: 'bulk-l3l4.xlsx',
|
|
2704
|
+
url: 'https://minio.local/mewp-external-ingestion/MEWP/mewp-external-ingestion/l3l4/bulk-l3l4.xlsx',
|
|
2705
|
+
sourceType: 'mewpExternalIngestion',
|
|
2706
|
+
});
|
|
2707
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2708
|
+
|
|
2709
|
+
const totalLinks = [...map.values()].reduce((sum, items) => sum + (items?.length || 0), 0);
|
|
2710
|
+
expect(totalLinks).toBeGreaterThan(700);
|
|
2711
|
+
expect(elapsedMs).toBeLessThan(5000);
|
|
2712
|
+
});
|
|
2713
|
+
});
|
|
2714
|
+
|
|
2715
|
+
describe('MEWP high-volume requirement token parsing', () => {
|
|
2716
|
+
it('should parse 1000 expected-result requirement tokens with noisy fragments', () => {
|
|
2717
|
+
const tokens: string[] = [];
|
|
2718
|
+
for (let i = 1; i <= 1000; i++) {
|
|
2719
|
+
const code = `SR${String(10000 + i)}`;
|
|
2720
|
+
tokens.push(code);
|
|
2721
|
+
if (i % 100 === 0) tokens.push(`${code} V3.24`);
|
|
2722
|
+
if (i % 125 === 0) tokens.push(`${code} VVRM2425`);
|
|
2723
|
+
}
|
|
2724
|
+
const sourceText = tokens.join('; ');
|
|
2725
|
+
|
|
2726
|
+
const startedAt = Date.now();
|
|
2727
|
+
const codes = (resultDataProvider as any).extractRequirementCodesFromText(sourceText) as Set<string>;
|
|
2728
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2729
|
+
|
|
2730
|
+
expect(codes.size).toBe(1000);
|
|
2731
|
+
expect(codes.has('SR10001')).toBe(true);
|
|
2732
|
+
expect(codes.has('SR11000')).toBe(true);
|
|
2733
|
+
expect(elapsedMs).toBeLessThan(3000);
|
|
1391
2734
|
});
|
|
1392
2735
|
});
|
|
1393
2736
|
|