zephyr-enterprise-tools 1.2.6 → 1.2.7
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/mcp-server.js +3 -2
- package/package.json +1 -1
- package/zephyr-enterprise-tools.d.ts +8 -1
- package/zephyr-enterprise-tools.js +82 -7
package/mcp-server.js
CHANGED
|
@@ -20,12 +20,13 @@ import QualityGates from './zephyr-enterprise-tools.js';
|
|
|
20
20
|
const TOOLS = [
|
|
21
21
|
{
|
|
22
22
|
name: 'release_readiness',
|
|
23
|
-
description: 'Run all 4 quality gates to
|
|
23
|
+
description: 'Run all 4 quality gates with calculation details and action items. Optionally scope Test Plan Analysis to a Zephyr ZQL expression. Returns GO, CONDITIONAL GO, or NO GO status.',
|
|
24
24
|
inputSchema: {
|
|
25
25
|
type: 'object',
|
|
26
26
|
properties: {
|
|
27
27
|
projectId: { type: 'number', description: 'Zephyr project ID' },
|
|
28
28
|
releaseId: { type: 'number', description: 'Zephyr release ID' },
|
|
29
|
+
query: { type: 'string', description: 'Optional Zephyr ZQL expression for Test Plan Analysis, e.g. priority = "P1"' },
|
|
29
30
|
},
|
|
30
31
|
required: ['projectId', 'releaseId'],
|
|
31
32
|
},
|
|
@@ -265,7 +266,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
265
266
|
|
|
266
267
|
switch (name) {
|
|
267
268
|
case 'release_readiness':
|
|
268
|
-
result = await tools.runAllGates(projectId, releaseId);
|
|
269
|
+
result = await tools.runAllGates(projectId, releaseId, { query: args.query });
|
|
269
270
|
break;
|
|
270
271
|
|
|
271
272
|
case 'requirement_coverage':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zephyr-enterprise-tools",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Comprehensive Zephyr Enterprise Tools - Release Readiness, Project Health, Test Analytics & More",
|
|
5
5
|
"main": "zephyr-enterprise-tools.js",
|
|
6
6
|
"types": "zephyr-enterprise-tools.d.ts",
|
|
@@ -70,8 +70,15 @@ export interface ReleaseReadinessResult {
|
|
|
70
70
|
projectId: number;
|
|
71
71
|
releaseId: number;
|
|
72
72
|
timestamp: string;
|
|
73
|
+
report: {
|
|
74
|
+
projectName: string;
|
|
75
|
+
releaseName: string;
|
|
76
|
+
generatedAt: string;
|
|
77
|
+
fileName: string;
|
|
78
|
+
};
|
|
73
79
|
overallStatus: GateStatus;
|
|
74
80
|
summary: { passed: number; failed: number; conditional: number };
|
|
81
|
+
details: Record<string, unknown>;
|
|
75
82
|
recommendation: string;
|
|
76
83
|
gates: {
|
|
77
84
|
requirementCoverage: RequirementCoverageResult;
|
|
@@ -185,7 +192,7 @@ export declare class QualityGates {
|
|
|
185
192
|
testPlanAnalysisGate(projectId: number, releaseId: number, options?: TestPlanOptions): Promise<TestPlanResult>;
|
|
186
193
|
testExecutionGate(projectId: number, releaseId: number): Promise<TestExecutionResult>;
|
|
187
194
|
defectQualityGate(projectId: number, releaseId: number): Promise<DefectQualityResult>;
|
|
188
|
-
runAllGates(projectId: number, releaseId: number): Promise<ReleaseReadinessResult>;
|
|
195
|
+
runAllGates(projectId: number, releaseId: number, options?: TestPlanOptions): Promise<ReleaseReadinessResult>;
|
|
189
196
|
|
|
190
197
|
// Analytics & Insights
|
|
191
198
|
getProjectHealth(projectId: number, releaseId: number): Promise<ProjectHealthResult>;
|
|
@@ -540,13 +540,20 @@ export class QualityGates {
|
|
|
540
540
|
|
|
541
541
|
// ─── Run All Gates ──────────────────────────────────────────────────────────
|
|
542
542
|
|
|
543
|
-
async runAllGates(projectId, releaseId) {
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
543
|
+
async runAllGates(projectId, releaseId, options = {}) {
|
|
544
|
+
const { query } = options;
|
|
545
|
+
const [results, project, releases] = await Promise.all([
|
|
546
|
+
Promise.all([
|
|
547
|
+
this.requirementCoverageGate(projectId, releaseId),
|
|
548
|
+
this.testPlanAnalysisGate(projectId, releaseId, { query }),
|
|
549
|
+
this.testExecutionGate(projectId, releaseId),
|
|
550
|
+
this.defectQualityGate(projectId, releaseId),
|
|
551
|
+
]),
|
|
552
|
+
this.GET(`/project/${projectId}`).catch(() => ({})),
|
|
553
|
+
this.GET('/release', { projectid: projectId, isaliasallowed: false }).catch(() => []),
|
|
549
554
|
]);
|
|
555
|
+
const release = Array.isArray(releases) ? releases.find(item => Number(item.id) === Number(releaseId)) : undefined;
|
|
556
|
+
const generatedAt = new Date().toISOString();
|
|
550
557
|
|
|
551
558
|
const gates = {
|
|
552
559
|
requirementCoverage: results[0],
|
|
@@ -576,7 +583,13 @@ export class QualityGates {
|
|
|
576
583
|
return {
|
|
577
584
|
projectId,
|
|
578
585
|
releaseId,
|
|
579
|
-
timestamp:
|
|
586
|
+
timestamp: generatedAt,
|
|
587
|
+
report: {
|
|
588
|
+
projectName: project.name || `Project ${projectId}`,
|
|
589
|
+
releaseName: release?.name || `Release ${releaseId}`,
|
|
590
|
+
generatedAt,
|
|
591
|
+
fileName: this.getReportFileName(project.name || `Project ${projectId}`, release?.name || `Release ${releaseId}`, generatedAt),
|
|
592
|
+
},
|
|
580
593
|
overallStatus,
|
|
581
594
|
summary: {
|
|
582
595
|
passed: passedGates,
|
|
@@ -585,10 +598,72 @@ export class QualityGates {
|
|
|
585
598
|
total: 4,
|
|
586
599
|
},
|
|
587
600
|
gates,
|
|
601
|
+
details: this.getReadinessDetails(gates, query),
|
|
588
602
|
recommendation: this.getRecommendation(overallStatus, gates),
|
|
589
603
|
};
|
|
590
604
|
}
|
|
591
605
|
|
|
606
|
+
getReportFileName(projectName, releaseName, generatedAt) {
|
|
607
|
+
const safeSegment = value => String(value)
|
|
608
|
+
.trim()
|
|
609
|
+
.replace(/[^a-zA-Z0-9]+/g, '_')
|
|
610
|
+
.replace(/^_+|_+$/g, '');
|
|
611
|
+
const timestamp = new Date(generatedAt).toISOString();
|
|
612
|
+
const date = timestamp.slice(0, 10);
|
|
613
|
+
const time = timestamp.slice(11, 19).replace(/:/g, '-');
|
|
614
|
+
|
|
615
|
+
return `Release_Rediness_${safeSegment(projectName)}_${safeSegment(releaseName)}_${date}_${time}_UTC.pdf`;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
getReadinessDetails(gates, query) {
|
|
619
|
+
const plan = gates.testPlanAnalysis;
|
|
620
|
+
const coverage = gates.requirementCoverage;
|
|
621
|
+
const execution = gates.testExecution;
|
|
622
|
+
const defects = gates.defectQuality;
|
|
623
|
+
const planAnalysis = plan.analysis;
|
|
624
|
+
const remainingToPlan = Math.max(0, planAnalysis.testcasePlanning.totalTestcases - planAnalysis.testcasePlanning.plannedTestcases);
|
|
625
|
+
const remainingToExecute = execution.incompleteTests;
|
|
626
|
+
const additionalTestsForGo = Math.max(0, Math.ceil((0.97 * execution.totalPlannedTests) - execution.completedTests));
|
|
627
|
+
|
|
628
|
+
return {
|
|
629
|
+
testPlanAnalysis: {
|
|
630
|
+
scope: query ? "ZQL-filtered test cases" : "Release-wide",
|
|
631
|
+
query: query || undefined,
|
|
632
|
+
calculation: {
|
|
633
|
+
testcasePlanning: `${planAnalysis.testcasePlanning.plannedTestcases} / ${planAnalysis.testcasePlanning.totalTestcases} = ${planAnalysis.testcasePlanning.percentage}%`,
|
|
634
|
+
executionAssignment: `${planAnalysis.executionAssignment.assignedExecutions} / ${planAnalysis.executionAssignment.totalExecutions} = ${planAnalysis.executionAssignment.percentage}%`,
|
|
635
|
+
overallPlanning: `(${planAnalysis.testcasePlanning.percentage}% + ${planAnalysis.executionAssignment.percentage}%) / 2 = ${plan.overallPlanningPercentage}%`,
|
|
636
|
+
},
|
|
637
|
+
actionItems: remainingToPlan > 0
|
|
638
|
+
? [`Plan the remaining ${remainingToPlan} test cases.`, "Maintain execution assignment as tests are planned."]
|
|
639
|
+
: ["Maintain complete test planning and execution assignment."],
|
|
640
|
+
},
|
|
641
|
+
requirementCoverage: {
|
|
642
|
+
scope: "Release-wide",
|
|
643
|
+
calculation: `${coverage.coveredRequirements} / ${coverage.totalRequirements} = ${coverage.coveragePercentage}%`,
|
|
644
|
+
uncoveredRequirementIds: [],
|
|
645
|
+
detailAvailability: "Zephyr release summaries provide the uncovered count but not requirement IDs.",
|
|
646
|
+
actionItems: coverage.notCoveredRequirements > 0
|
|
647
|
+
? [`Create and map tests for ${coverage.notCoveredRequirements} uncovered requirements.`, "Confirm requirement mappings before release sign-off."]
|
|
648
|
+
: ["Maintain requirement-to-test traceability through release sign-off."],
|
|
649
|
+
},
|
|
650
|
+
testExecution: {
|
|
651
|
+
scope: "Release-wide",
|
|
652
|
+
calculation: `${execution.completedTests} / ${execution.totalPlannedTests} = ${execution.executionPercentage}%`,
|
|
653
|
+
actionItems: remainingToExecute > 0
|
|
654
|
+
? [`Execute or resolve ${remainingToExecute} incomplete tests.`, `Complete at least ${additionalTestsForGo} additional tests to reach the 97% GO threshold.`, ...(execution.breakdown.failed > 0 ? [`Investigate and rerun ${execution.breakdown.failed} failed test(s).`] : [])]
|
|
655
|
+
: ["Maintain execution completion through release sign-off."],
|
|
656
|
+
},
|
|
657
|
+
defectQuality: {
|
|
658
|
+
scope: "Release-wide",
|
|
659
|
+
defects: defects.breakdown,
|
|
660
|
+
actionItems: defects.unresolvedDefects > 0
|
|
661
|
+
? ["Resolve all blocker defects.", "Reduce unresolved high-risk defects to 10 or fewer before approval."]
|
|
662
|
+
: ["Verify resolved defect fixes with targeted regression coverage.", "Confirm product-owner approval for any Won't Fix resolutions."],
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
592
667
|
getRecommendation(status, gates) {
|
|
593
668
|
const issues = [];
|
|
594
669
|
|