@elisra-devops/docgen-data-provider 1.91.0 → 1.92.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/modules/ResultDataProvider.d.ts +0 -1
- package/bin/modules/ResultDataProvider.js +34 -24
- package/bin/modules/ResultDataProvider.js.map +1 -1
- package/bin/tests/modules/ResultDataProvider.test.js +61 -2
- package/bin/tests/modules/ResultDataProvider.test.js.map +1 -1
- package/package.json +1 -1
- package/src/modules/ResultDataProvider.ts +43 -28
- package/src/tests/modules/ResultDataProvider.test.ts +70 -2
package/package.json
CHANGED
|
@@ -886,8 +886,10 @@ export default class ResultDataProvider {
|
|
|
886
886
|
}
|
|
887
887
|
|
|
888
888
|
// Direction A logic:
|
|
889
|
-
// 1) Base mention ("SR0054") is parent-level
|
|
890
|
-
//
|
|
889
|
+
// 1) Base mention ("SR0054") is parent-level and considered covered only when
|
|
890
|
+
// the whole family is covered across scoped test cases:
|
|
891
|
+
// - if family has children, all children must be linked;
|
|
892
|
+
// - if family has no children, the base item itself must be linked.
|
|
891
893
|
// 2) Child mention ("SR0054-1") is exact-match and checked across scoped test cases.
|
|
892
894
|
const missingBaseWhenFamilyUncovered = new Set<string>();
|
|
893
895
|
const missingSpecificMentionedNoFamily = new Set<string>();
|
|
@@ -900,9 +902,18 @@ export default class ResultDataProvider {
|
|
|
900
902
|
if (familyCodes?.size) {
|
|
901
903
|
const familyLinkedCodes = linkedFamilyCodesAcrossTestCases.get(baseKey) || new Set<string>();
|
|
902
904
|
|
|
903
|
-
// Base mention ("SR0054")
|
|
905
|
+
// Base mention ("SR0054") requires full family coverage across selected test cases.
|
|
904
906
|
if (hasBaseMention) {
|
|
905
|
-
|
|
907
|
+
const normalizedFamilyMembers = [...familyCodes]
|
|
908
|
+
.map((code) => this.normalizeMewpRequirementCodeWithSuffix(code))
|
|
909
|
+
.filter((code) => !!code);
|
|
910
|
+
const specificFamilyMembers = normalizedFamilyMembers.filter((code) => /-\d+$/.test(code));
|
|
911
|
+
const requiredFamilyMembers =
|
|
912
|
+
specificFamilyMembers.length > 0 ? specificFamilyMembers : normalizedFamilyMembers;
|
|
913
|
+
const isWholeFamilyCovered = requiredFamilyMembers.every((memberCode) =>
|
|
914
|
+
familyLinkedCodes.has(memberCode)
|
|
915
|
+
);
|
|
916
|
+
if (!isWholeFamilyCovered) {
|
|
906
917
|
missingBaseWhenFamilyUncovered.add(baseKey);
|
|
907
918
|
}
|
|
908
919
|
}
|
|
@@ -936,15 +947,26 @@ export default class ResultDataProvider {
|
|
|
936
947
|
if (!baseKey) return false;
|
|
937
948
|
return !mentionedBaseKeys.has(baseKey);
|
|
938
949
|
});
|
|
939
|
-
const mentionedButNotLinkedByStep = new Map<
|
|
950
|
+
const mentionedButNotLinkedByStep = new Map<
|
|
951
|
+
string,
|
|
952
|
+
{ baseIds: Set<string>; specificIds: Set<string> }
|
|
953
|
+
>();
|
|
940
954
|
const appendMentionedButNotLinked = (requirementId: string, stepRef: string) => {
|
|
941
955
|
const normalizedRequirementId = this.normalizeMewpRequirementCodeWithSuffix(requirementId);
|
|
942
956
|
if (!normalizedRequirementId) return;
|
|
943
957
|
const normalizedStepRef = String(stepRef || 'Step ?').trim() || 'Step ?';
|
|
944
958
|
if (!mentionedButNotLinkedByStep.has(normalizedStepRef)) {
|
|
945
|
-
mentionedButNotLinkedByStep.set(normalizedStepRef,
|
|
959
|
+
mentionedButNotLinkedByStep.set(normalizedStepRef, {
|
|
960
|
+
baseIds: new Set<string>(),
|
|
961
|
+
specificIds: new Set<string>(),
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
const entry = mentionedButNotLinkedByStep.get(normalizedStepRef)!;
|
|
965
|
+
if (/-\d+$/.test(normalizedRequirementId)) {
|
|
966
|
+
entry.specificIds.add(normalizedRequirementId);
|
|
967
|
+
} else {
|
|
968
|
+
entry.baseIds.add(normalizedRequirementId);
|
|
946
969
|
}
|
|
947
|
-
mentionedButNotLinkedByStep.get(normalizedStepRef)!.add(normalizedRequirementId);
|
|
948
970
|
};
|
|
949
971
|
|
|
950
972
|
const sortedMissingSpecificMentionedNoFamily = [...missingSpecificMentionedNoFamily].sort((a, b) =>
|
|
@@ -979,8 +1001,19 @@ export default class ResultDataProvider {
|
|
|
979
1001
|
if (stepOrderA !== stepOrderB) return stepOrderA - stepOrderB;
|
|
980
1002
|
return String(a[0]).localeCompare(String(b[0]));
|
|
981
1003
|
})
|
|
982
|
-
.map(([stepRef,
|
|
983
|
-
|
|
1004
|
+
.map(([stepRef, requirementIdsByType]) => {
|
|
1005
|
+
const specificCodes = [...requirementIdsByType.specificIds].sort((a, b) =>
|
|
1006
|
+
this.compareMewpRequirementCodes(a, b)
|
|
1007
|
+
);
|
|
1008
|
+
const specificFamilies = new Set<string>(
|
|
1009
|
+
specificCodes.map((code) => this.toRequirementKey(code)).filter((code) => !!code)
|
|
1010
|
+
);
|
|
1011
|
+
const baseCodes = [...requirementIdsByType.baseIds]
|
|
1012
|
+
.filter((baseCode) => !specificFamilies.has(baseCode))
|
|
1013
|
+
.sort((a, b) => this.compareMewpRequirementCodes(a, b));
|
|
1014
|
+
const displayCodes = [...specificCodes, ...baseCodes];
|
|
1015
|
+
if (displayCodes.length === 0) return `${stepRef}:`;
|
|
1016
|
+
return `${stepRef}: ${displayCodes.join('; ')}`.trim();
|
|
984
1017
|
})
|
|
985
1018
|
.join('\n')
|
|
986
1019
|
.trim();
|
|
@@ -2645,19 +2678,6 @@ export default class ResultDataProvider {
|
|
|
2645
2678
|
return left.raw.localeCompare(right.raw);
|
|
2646
2679
|
}
|
|
2647
2680
|
|
|
2648
|
-
private formatStepScopedRequirementGroups(stepRef: string, requirementIds: Iterable<string>): string {
|
|
2649
|
-
const groupedRequirementList = this.formatRequirementCodesGroupedByFamily(requirementIds);
|
|
2650
|
-
if (!groupedRequirementList) return `${stepRef}:`;
|
|
2651
|
-
|
|
2652
|
-
const groupedLines = groupedRequirementList
|
|
2653
|
-
.split('\n')
|
|
2654
|
-
.map((line) => String(line || '').trim())
|
|
2655
|
-
.filter((line) => line.length > 0);
|
|
2656
|
-
|
|
2657
|
-
if (groupedLines.length <= 1) return `${stepRef}: ${groupedLines[0] || ''}`.trim();
|
|
2658
|
-
return `${stepRef}:\n${groupedLines.map((line) => `- ${line}`).join('\n')}`.trim();
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
2681
|
private formatRequirementCodesGroupedByFamily(codes: Iterable<string>): string {
|
|
2662
2682
|
const byBaseKey = new Map<string, Set<string>>();
|
|
2663
2683
|
for (const rawCode of codes || []) {
|
|
@@ -2675,12 +2695,7 @@ export default class ResultDataProvider {
|
|
|
2675
2695
|
.map(([baseKey, members]) => {
|
|
2676
2696
|
const sortedMembers = [...members].sort((a, b) => this.compareMewpRequirementCodes(a, b));
|
|
2677
2697
|
if (sortedMembers.length <= 1) return sortedMembers[0];
|
|
2678
|
-
|
|
2679
|
-
const nonBaseMembers = sortedMembers.filter((member) => member !== baseKey);
|
|
2680
|
-
if (nonBaseMembers.length > 0) {
|
|
2681
|
-
return `${baseKey}: ${nonBaseMembers.join(', ')}`;
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2698
|
+
// Direction B display is family-level when multiple members exist.
|
|
2684
2699
|
return baseKey;
|
|
2685
2700
|
})
|
|
2686
2701
|
.join('\n');
|
|
@@ -2003,6 +2003,72 @@ describe('ResultDataProvider', () => {
|
|
|
2003
2003
|
);
|
|
2004
2004
|
});
|
|
2005
2005
|
|
|
2006
|
+
it('should keep explicit child IDs when multiple specifically mentioned children are missing', async () => {
|
|
2007
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
2008
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
2009
|
+
jest.spyOn(resultDataProvider as any, 'fetchTestData').mockResolvedValueOnce([
|
|
2010
|
+
{
|
|
2011
|
+
testPointsItems: [{ testCaseId: 111, testCaseName: 'TC 111' }],
|
|
2012
|
+
testCasesItems: [
|
|
2013
|
+
{
|
|
2014
|
+
workItem: {
|
|
2015
|
+
id: 111,
|
|
2016
|
+
workItemFields: [{ key: 'Steps', value: '<steps></steps>' }],
|
|
2017
|
+
},
|
|
2018
|
+
},
|
|
2019
|
+
],
|
|
2020
|
+
},
|
|
2021
|
+
]);
|
|
2022
|
+
jest.spyOn(resultDataProvider as any, 'fetchMewpL2Requirements').mockResolvedValueOnce([
|
|
2023
|
+
{
|
|
2024
|
+
workItemId: 5101,
|
|
2025
|
+
requirementId: 'SR0054-1',
|
|
2026
|
+
baseKey: 'SR0054',
|
|
2027
|
+
title: 'Req child 1',
|
|
2028
|
+
responsibility: 'ESUK',
|
|
2029
|
+
linkedTestCaseIds: [],
|
|
2030
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
workItemId: 5102,
|
|
2034
|
+
requirementId: 'SR0054-2',
|
|
2035
|
+
baseKey: 'SR0054',
|
|
2036
|
+
title: 'Req child 2',
|
|
2037
|
+
responsibility: 'ESUK',
|
|
2038
|
+
linkedTestCaseIds: [],
|
|
2039
|
+
areaPath: 'MEWP\\Customer Requirements\\Level 2',
|
|
2040
|
+
},
|
|
2041
|
+
]);
|
|
2042
|
+
jest
|
|
2043
|
+
.spyOn(resultDataProvider as any, 'buildLinkedRequirementsByTestCase')
|
|
2044
|
+
.mockResolvedValueOnce(new Map([[111, { baseKeys: new Set<string>(), fullCodes: new Set<string>() }]]));
|
|
2045
|
+
jest.spyOn((resultDataProvider as any).testStepParserHelper, 'parseTestSteps').mockResolvedValueOnce([
|
|
2046
|
+
{
|
|
2047
|
+
stepId: '4',
|
|
2048
|
+
stepPosition: '4',
|
|
2049
|
+
action: '',
|
|
2050
|
+
expected: 'SR0054-1; SR0054-2',
|
|
2051
|
+
isSharedStepTitle: false,
|
|
2052
|
+
},
|
|
2053
|
+
]);
|
|
2054
|
+
|
|
2055
|
+
const result = await (resultDataProvider as any).getMewpInternalValidationFlatResults(
|
|
2056
|
+
'123',
|
|
2057
|
+
mockProjectName,
|
|
2058
|
+
[1]
|
|
2059
|
+
);
|
|
2060
|
+
|
|
2061
|
+
expect(result.rows).toHaveLength(1);
|
|
2062
|
+
expect(result.rows[0]).toEqual(
|
|
2063
|
+
expect.objectContaining({
|
|
2064
|
+
'Test Case ID': 111,
|
|
2065
|
+
'Mentioned but Not Linked': 'Step 4: SR0054-1; SR0054-2',
|
|
2066
|
+
'Linked but Not Mentioned': '',
|
|
2067
|
+
'Validation Status': 'Fail',
|
|
2068
|
+
})
|
|
2069
|
+
);
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2006
2072
|
it('should pass when a base SR mention is fully covered by its only linked child', async () => {
|
|
2007
2073
|
jest.spyOn(resultDataProvider as any, 'fetchTestPlanName').mockResolvedValueOnce('Plan A');
|
|
2008
2074
|
jest.spyOn(resultDataProvider as any, 'fetchTestSuites').mockResolvedValueOnce([{ testSuiteId: 1 }]);
|
|
@@ -2260,7 +2326,9 @@ describe('ResultDataProvider', () => {
|
|
|
2260
2326
|
|
|
2261
2327
|
expect(result.rows).toHaveLength(1);
|
|
2262
2328
|
expect(String(result.rows[0]['Mentioned but Not Linked'] || '')).toBe('');
|
|
2263
|
-
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).toContain('SR0054
|
|
2329
|
+
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).toContain('SR0054');
|
|
2330
|
+
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).not.toContain('SR0054-1');
|
|
2331
|
+
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).not.toContain('SR0054-2');
|
|
2264
2332
|
expect(String(result.rows[0]['Linked but Not Mentioned'] || '')).toContain('SR0100-1');
|
|
2265
2333
|
});
|
|
2266
2334
|
|
|
@@ -2671,7 +2739,7 @@ describe('ResultDataProvider', () => {
|
|
|
2671
2739
|
expect(byTestCase.get(201)).toEqual(
|
|
2672
2740
|
expect.objectContaining({
|
|
2673
2741
|
'Test Case Title': 'TC 201 - Mixed discrepancies',
|
|
2674
|
-
'Mentioned but Not Linked': 'Step 1: SR0095-3',
|
|
2742
|
+
'Mentioned but Not Linked': 'Step 1: SR0095-3; SR0511',
|
|
2675
2743
|
'Linked but Not Mentioned': 'SR8888',
|
|
2676
2744
|
'Validation Status': 'Fail',
|
|
2677
2745
|
})
|