zephyr-enterprise-tools 1.0.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/LICENSE +21 -0
- package/README.md +260 -0
- package/cli.js +465 -0
- package/package.json +53 -0
- package/zephyr-enterprise-tools.js +1129 -0
package/cli.js
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zephyr Enterprise Tools CLI
|
|
5
|
+
*
|
|
6
|
+
* Run release readiness checks and analytics from the command line.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* export ZEPHYR_BASE_URL="https://your-zephyr.com/flex/services/rest/latest"
|
|
10
|
+
* export ZEPHYR_USERNAME="your-username"
|
|
11
|
+
* export ZEPHYR_PASSWORD="your-password"
|
|
12
|
+
*
|
|
13
|
+
* zephyr-tools --project 364 --release 4312
|
|
14
|
+
* zephyr-tools -p 364 -r 4312 -t project-health
|
|
15
|
+
* zephyr-tools -p 364 -r 4312 -t search-tests -q "login"
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import QualityGates from './zephyr-enterprise-tools.js';
|
|
19
|
+
|
|
20
|
+
// ─── Parse Arguments ──────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
function parseArgs() {
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
const options = {
|
|
25
|
+
projectId: null,
|
|
26
|
+
releaseId: null,
|
|
27
|
+
tool: 'release-readiness',
|
|
28
|
+
gate: null, // Legacy support
|
|
29
|
+
format: 'table',
|
|
30
|
+
help: false,
|
|
31
|
+
query: '',
|
|
32
|
+
days: 30,
|
|
33
|
+
limit: 50,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < args.length; i++) {
|
|
37
|
+
const arg = args[i];
|
|
38
|
+
switch (arg) {
|
|
39
|
+
case '-p':
|
|
40
|
+
case '--project':
|
|
41
|
+
options.projectId = Number(args[++i]);
|
|
42
|
+
break;
|
|
43
|
+
case '-r':
|
|
44
|
+
case '--release':
|
|
45
|
+
options.releaseId = Number(args[++i]);
|
|
46
|
+
break;
|
|
47
|
+
case '-t':
|
|
48
|
+
case '--tool':
|
|
49
|
+
options.tool = args[++i];
|
|
50
|
+
break;
|
|
51
|
+
case '-g':
|
|
52
|
+
case '--gate':
|
|
53
|
+
// Legacy support - map to tool
|
|
54
|
+
options.gate = args[++i];
|
|
55
|
+
break;
|
|
56
|
+
case '-q':
|
|
57
|
+
case '--query':
|
|
58
|
+
options.query = args[++i] || '';
|
|
59
|
+
break;
|
|
60
|
+
case '-d':
|
|
61
|
+
case '--days':
|
|
62
|
+
options.days = parseInt(args[++i], 10) || 30;
|
|
63
|
+
break;
|
|
64
|
+
case '-l':
|
|
65
|
+
case '--limit':
|
|
66
|
+
options.limit = parseInt(args[++i], 10) || 50;
|
|
67
|
+
break;
|
|
68
|
+
case '--json':
|
|
69
|
+
options.format = 'json';
|
|
70
|
+
break;
|
|
71
|
+
case '--table':
|
|
72
|
+
options.format = 'table';
|
|
73
|
+
break;
|
|
74
|
+
case '-h':
|
|
75
|
+
case '--help':
|
|
76
|
+
options.help = true;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Legacy gate support
|
|
82
|
+
if (options.gate) {
|
|
83
|
+
options.tool = options.gate === 'all' ? 'release-readiness' : options.gate;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return options;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
function showHelp() {
|
|
92
|
+
console.log(`
|
|
93
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
94
|
+
║ ZEPHYR ENTERPRISE TOOLS CLI v1.0.0 ║
|
|
95
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
96
|
+
|
|
97
|
+
USAGE:
|
|
98
|
+
zephyr-tools -p <projectId> -r <releaseId> [options]
|
|
99
|
+
|
|
100
|
+
REQUIRED:
|
|
101
|
+
-p, --project <id> Project ID
|
|
102
|
+
-r, --release <id> Release ID
|
|
103
|
+
|
|
104
|
+
OPTIONS:
|
|
105
|
+
-t, --tool <name> Tool to run (default: release-readiness)
|
|
106
|
+
-q, --query <text> Search query (for search-tests)
|
|
107
|
+
-d, --days <n> Number of days for trends/activity (default: 30)
|
|
108
|
+
-l, --limit <n> Max results to return (default: 50)
|
|
109
|
+
--json Output as JSON
|
|
110
|
+
--table Output as table (default)
|
|
111
|
+
-h, --help Show this help
|
|
112
|
+
|
|
113
|
+
AVAILABLE TOOLS:
|
|
114
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
115
|
+
│ RELEASE READINESS (Quality Gates) │
|
|
116
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
117
|
+
│ release-readiness Run all 4 quality gates (default) │
|
|
118
|
+
│ requirement-coverage Requirement coverage gate only │
|
|
119
|
+
│ test-plan Test plan analysis gate only │
|
|
120
|
+
│ test-execution Test execution gate only │
|
|
121
|
+
│ defect-quality Defect quality gate only │
|
|
122
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
123
|
+
│ ANALYTICS & INSIGHTS │
|
|
124
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
125
|
+
│ project-health Overall project health score and metrics │
|
|
126
|
+
│ test-coverage Detailed test coverage analysis │
|
|
127
|
+
│ failed-tests List and analyze failed tests │
|
|
128
|
+
│ req-coverage Requirements with/without test coverage │
|
|
129
|
+
│ test-trends Test execution trends over time │
|
|
130
|
+
│ search-tests Search test cases by query │
|
|
131
|
+
│ user-activity User activity and productivity metrics │
|
|
132
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
133
|
+
|
|
134
|
+
ENVIRONMENT VARIABLES:
|
|
135
|
+
ZEPHYR_BASE_URL Zephyr API base URL (required)
|
|
136
|
+
ZEPHYR_USERNAME Username for Basic auth
|
|
137
|
+
ZEPHYR_PASSWORD Password for Basic auth
|
|
138
|
+
ZEPHYR_TOKEN Bearer token (alternative to username/password)
|
|
139
|
+
|
|
140
|
+
EXAMPLES:
|
|
141
|
+
# Run all quality gates (release readiness)
|
|
142
|
+
zephyr-tools -p 364 -r 4312
|
|
143
|
+
|
|
144
|
+
# Check project health
|
|
145
|
+
zephyr-tools -p 364 -r 4312 -t project-health
|
|
146
|
+
|
|
147
|
+
# Get failed tests as JSON
|
|
148
|
+
zephyr-tools -p 364 -r 4312 -t failed-tests --json
|
|
149
|
+
|
|
150
|
+
# Search for test cases containing "login"
|
|
151
|
+
zephyr-tools -p 364 -r 4312 -t search-tests -q "login"
|
|
152
|
+
|
|
153
|
+
# Get test trends for last 14 days
|
|
154
|
+
zephyr-tools -p 364 -r 4312 -t test-trends -d 14
|
|
155
|
+
|
|
156
|
+
# Get user activity report
|
|
157
|
+
zephyr-tools -p 364 -r 4312 -t user-activity
|
|
158
|
+
|
|
159
|
+
QUALITY GATE THRESHOLDS:
|
|
160
|
+
Requirement Coverage: ≥70% = GO
|
|
161
|
+
Test Plan Analysis: <80% = NO GO, 80-90% = CONDITIONAL, ≥90% = GO
|
|
162
|
+
Test Execution: <90% = NO GO, 90-97% = CONDITIONAL, ≥97% = GO
|
|
163
|
+
Defect Quality: Blocker >0 = NO GO, High-risk >10 = NO GO
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ─── Formatters ───────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
function formatTable(report) {
|
|
170
|
+
const { gates, overallStatus, summary, recommendation } = report;
|
|
171
|
+
|
|
172
|
+
const statusIcon = (s) => s === 'GO' ? '🟢' : s === 'CONDITIONAL GO' ? '🟡' : s === 'NO GO' ? '🔴' : '⚪';
|
|
173
|
+
|
|
174
|
+
console.log('\n' + '═'.repeat(80));
|
|
175
|
+
console.log(' RELEASE READINESS REPORT');
|
|
176
|
+
console.log('═'.repeat(80));
|
|
177
|
+
console.log(`Project: ${report.projectId} | Release: ${report.releaseId} | ${report.timestamp}`);
|
|
178
|
+
console.log('─'.repeat(80));
|
|
179
|
+
|
|
180
|
+
console.log('\n┌─────────────────────────┬──────────┬───────────┬─────────────────────────┐');
|
|
181
|
+
console.log('│ Gate │ Score │ Status │ Threshold │');
|
|
182
|
+
console.log('├─────────────────────────┼──────────┼───────────┼─────────────────────────┤');
|
|
183
|
+
|
|
184
|
+
// Gate 1
|
|
185
|
+
const g1 = gates.requirementCoverage;
|
|
186
|
+
console.log(`│ Requirement Coverage │ ${String(g1.coveragePercentage + '%').padEnd(8)} │ ${statusIcon(g1.status)} ${g1.status.padEnd(7)} │ ≥70% coverage │`);
|
|
187
|
+
|
|
188
|
+
// Gate 2
|
|
189
|
+
const g2 = gates.testPlanAnalysis;
|
|
190
|
+
console.log(`│ Test Plan Analysis │ ${String(g2.overallPlanningPercentage + '%').padEnd(8)} │ ${statusIcon(g2.status)} ${g2.status.padEnd(7)} │ ≥90% planned & assigned │`);
|
|
191
|
+
|
|
192
|
+
// Gate 3
|
|
193
|
+
const g3 = gates.testExecution;
|
|
194
|
+
console.log(`│ Test Execution │ ${String(g3.executionPercentage + '%').padEnd(8)} │ ${statusIcon(g3.status)} ${g3.status.padEnd(7)} │ ≥97% executed │`);
|
|
195
|
+
|
|
196
|
+
// Gate 4
|
|
197
|
+
const g4 = gates.defectQuality;
|
|
198
|
+
const defectScore = `${g4.breakdown?.blocker?.count || 0}B/${g4.breakdown?.highRisk?.count || 0}H`;
|
|
199
|
+
console.log(`│ Defect Quality │ ${defectScore.padEnd(8)} │ ${statusIcon(g4.status)} ${g4.status.padEnd(7)} │ 0 blocker, ≤10 high │`);
|
|
200
|
+
|
|
201
|
+
console.log('└─────────────────────────┴──────────┴───────────┴─────────────────────────┘');
|
|
202
|
+
|
|
203
|
+
console.log('\n' + '─'.repeat(80));
|
|
204
|
+
console.log(`OVERALL: ${statusIcon(overallStatus)} ${overallStatus} (${summary.passed}/4 passed, ${summary.failed} failed, ${summary.conditional} conditional)`);
|
|
205
|
+
console.log('─'.repeat(80));
|
|
206
|
+
console.log('\n' + recommendation);
|
|
207
|
+
console.log('\n' + '═'.repeat(80) + '\n');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function formatSingleGate(result) {
|
|
211
|
+
const statusIcon = (s) => s === 'GO' ? '🟢' : s === 'CONDITIONAL GO' ? '🟡' : s === 'NO GO' ? '🔴' : '⚪';
|
|
212
|
+
|
|
213
|
+
console.log('\n' + '─'.repeat(60));
|
|
214
|
+
console.log(`${result.gate.toUpperCase()} GATE`);
|
|
215
|
+
console.log('─'.repeat(60));
|
|
216
|
+
console.log(`Status: ${statusIcon(result.status)} ${result.status}`);
|
|
217
|
+
console.log(`Message: ${result.statusMessage}`);
|
|
218
|
+
|
|
219
|
+
if (result.coveragePercentage !== undefined) {
|
|
220
|
+
console.log(`Coverage: ${result.coveragePercentage}% (${result.coveredRequirements}/${result.totalRequirements})`);
|
|
221
|
+
}
|
|
222
|
+
if (result.overallPlanningPercentage !== undefined) {
|
|
223
|
+
console.log(`Planning: ${result.overallPlanningPercentage}%`);
|
|
224
|
+
}
|
|
225
|
+
if (result.executionPercentage !== undefined) {
|
|
226
|
+
console.log(`Execution: ${result.executionPercentage}% (${result.completedTests}/${result.totalPlannedTests})`);
|
|
227
|
+
const b = result.breakdown;
|
|
228
|
+
console.log(`Breakdown: ✅${b.passed} ❌${b.failed} ⏸️${b.blocked} 🔄${b.wip} ⏳${b.notExecuted}`);
|
|
229
|
+
}
|
|
230
|
+
if (result.breakdown?.blocker !== undefined) {
|
|
231
|
+
console.log(`Defects: ${result.breakdown.blocker.count} blocker, ${result.breakdown.highRisk.count} high-risk, ${result.breakdown.lowRisk.count} low-risk`);
|
|
232
|
+
}
|
|
233
|
+
console.log('─'.repeat(60) + '\n');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ─── Generic Result Formatter ─────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
function formatGenericResult(result) {
|
|
239
|
+
const tool = result.tool || 'Result';
|
|
240
|
+
console.log('\n' + '═'.repeat(70));
|
|
241
|
+
console.log(` ${tool.toUpperCase()}`);
|
|
242
|
+
console.log('═'.repeat(70));
|
|
243
|
+
console.log(`Project: ${result.projectId} | Release: ${result.releaseId} | ${result.timestamp}`);
|
|
244
|
+
console.log('─'.repeat(70));
|
|
245
|
+
|
|
246
|
+
// Health score display
|
|
247
|
+
if (result.healthScore !== undefined) {
|
|
248
|
+
const icon = result.healthStatus === 'HEALTHY' ? '🟢' :
|
|
249
|
+
result.healthStatus === 'MODERATE' ? '🟡' :
|
|
250
|
+
result.healthStatus === 'AT RISK' ? '🟠' : '🔴';
|
|
251
|
+
console.log(`\nHealth Score: ${icon} ${result.healthScore}/100 (${result.healthStatus})`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Summary section
|
|
255
|
+
if (result.summary) {
|
|
256
|
+
console.log('\n📊 SUMMARY:');
|
|
257
|
+
for (const [key, value] of Object.entries(result.summary)) {
|
|
258
|
+
if (typeof value === 'object') continue;
|
|
259
|
+
console.log(` ${formatKey(key)}: ${value}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Metrics section
|
|
264
|
+
if (result.metrics) {
|
|
265
|
+
console.log('\n📈 METRICS:');
|
|
266
|
+
for (const [category, metrics] of Object.entries(result.metrics)) {
|
|
267
|
+
console.log(` ${formatKey(category)}:`);
|
|
268
|
+
for (const [key, value] of Object.entries(metrics)) {
|
|
269
|
+
console.log(` ${formatKey(key)}: ${value}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Team summary
|
|
275
|
+
if (result.teamSummary) {
|
|
276
|
+
console.log('\n👥 TEAM SUMMARY:');
|
|
277
|
+
for (const [key, value] of Object.entries(result.teamSummary)) {
|
|
278
|
+
console.log(` ${formatKey(key)}: ${value}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Users list
|
|
283
|
+
if (result.users && result.users.length > 0) {
|
|
284
|
+
console.log('\n👤 USERS:');
|
|
285
|
+
console.log(' ┌─────────────────────────┬────────┬────────┬────────┬────────┐');
|
|
286
|
+
console.log(' │ User │ Assign │ Exec │ Pass% │ Comp% │');
|
|
287
|
+
console.log(' ├─────────────────────────┼────────┼────────┼────────┼────────┤');
|
|
288
|
+
for (const user of result.users.slice(0, 10)) {
|
|
289
|
+
const name = (user.name || 'Unknown').substring(0, 21).padEnd(23);
|
|
290
|
+
console.log(` │ ${name} │ ${String(user.assigned).padEnd(6)} │ ${String(user.executed).padEnd(6)} │ ${String(user.passRate + '%').padEnd(6)} │ ${String(user.completionRate + '%').padEnd(6)} │`);
|
|
291
|
+
}
|
|
292
|
+
console.log(' └─────────────────────────┴────────┴────────┴────────┴────────┘');
|
|
293
|
+
if (result.users.length > 10) console.log(` ... and ${result.users.length - 10} more users`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Failed tests list
|
|
297
|
+
if (result.failedTests && result.failedTests.length > 0) {
|
|
298
|
+
console.log('\n❌ FAILED TESTS:');
|
|
299
|
+
for (const test of result.failedTests.slice(0, 15)) {
|
|
300
|
+
console.log(` • [${test.testcaseKey || test.testcaseId}] ${test.testcaseName}`);
|
|
301
|
+
if (test.executedBy) console.log(` Tester: ${test.executedBy}`);
|
|
302
|
+
}
|
|
303
|
+
if (result.failedTests.length > 15) console.log(` ... and ${result.failedTests.length - 15} more failed tests`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Search results
|
|
307
|
+
if (result.results && Array.isArray(result.results)) {
|
|
308
|
+
console.log(`\n🔍 RESULTS (${result.returned || result.results.length} of ${result.totalMatches || result.results.length}):`);
|
|
309
|
+
for (const item of result.results.slice(0, 15)) {
|
|
310
|
+
console.log(` • [${item.key || item.id}] ${item.name}`);
|
|
311
|
+
if (item.status) console.log(` Status: ${item.status}`);
|
|
312
|
+
}
|
|
313
|
+
if (result.results.length > 15) console.log(` ... and ${result.results.length - 15} more results`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Daily trends
|
|
317
|
+
if (result.dailyTrends && result.dailyTrends.length > 0) {
|
|
318
|
+
console.log('\n📅 DAILY TRENDS (last 10 days):');
|
|
319
|
+
console.log(' ┌────────────┬───────┬────────┬────────┬─────────┐');
|
|
320
|
+
console.log(' │ Date │ Total │ Passed │ Failed │ Blocked │');
|
|
321
|
+
console.log(' ├────────────┼───────┼────────┼────────┼─────────┤');
|
|
322
|
+
for (const day of result.dailyTrends.slice(-10)) {
|
|
323
|
+
console.log(` │ ${day.date} │ ${String(day.total).padEnd(5)} │ ${String(day.passed).padEnd(6)} │ ${String(day.failed).padEnd(6)} │ ${String(day.blocked).padEnd(7)} │`);
|
|
324
|
+
}
|
|
325
|
+
console.log(' └────────────┴───────┴────────┴────────┴─────────┘');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Recommendations
|
|
329
|
+
if (result.recommendations) {
|
|
330
|
+
console.log('\n💡 RECOMMENDATIONS:');
|
|
331
|
+
for (const rec of result.recommendations) {
|
|
332
|
+
console.log(` • ${rec}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Insight
|
|
337
|
+
if (result.insight) {
|
|
338
|
+
console.log(`\n💡 INSIGHT: ${result.insight}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Message
|
|
342
|
+
if (result.message) {
|
|
343
|
+
console.log(`\n📝 ${result.message}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Status
|
|
347
|
+
if (result.status && !result.healthStatus) {
|
|
348
|
+
const icon = result.status === 'GO' || result.status === 'ADEQUATE' ? '🟢' :
|
|
349
|
+
result.status === 'CONDITIONAL GO' || result.status === 'PARTIAL' ? '🟡' : '🔴';
|
|
350
|
+
console.log(`\nStatus: ${icon} ${result.status}`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
console.log('\n' + '═'.repeat(70) + '\n');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function formatKey(key) {
|
|
357
|
+
return key
|
|
358
|
+
.replace(/([A-Z])/g, ' $1')
|
|
359
|
+
.replace(/_/g, ' ')
|
|
360
|
+
.replace(/^\w/, c => c.toUpperCase())
|
|
361
|
+
.trim();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
async function main() {
|
|
367
|
+
const options = parseArgs();
|
|
368
|
+
|
|
369
|
+
if (options.help) {
|
|
370
|
+
showHelp();
|
|
371
|
+
process.exit(0);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!options.projectId || !options.releaseId) {
|
|
375
|
+
console.error('Error: --project and --release are required.\n');
|
|
376
|
+
showHelp();
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
const tools = new QualityGates({
|
|
382
|
+
baseUrl: process.env.ZEPHYR_BASE_URL,
|
|
383
|
+
username: process.env.ZEPHYR_USERNAME,
|
|
384
|
+
password: process.env.ZEPHYR_PASSWORD,
|
|
385
|
+
token: process.env.ZEPHYR_TOKEN,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
let result;
|
|
389
|
+
const { projectId, releaseId, tool, query, days, limit } = options;
|
|
390
|
+
|
|
391
|
+
switch (tool) {
|
|
392
|
+
// Quality Gates (Release Readiness)
|
|
393
|
+
case 'release-readiness':
|
|
394
|
+
case 'all':
|
|
395
|
+
result = await tools.runAllGates(projectId, releaseId);
|
|
396
|
+
break;
|
|
397
|
+
case 'requirement-coverage':
|
|
398
|
+
result = await tools.requirementCoverageGate(projectId, releaseId);
|
|
399
|
+
break;
|
|
400
|
+
case 'test-plan':
|
|
401
|
+
result = await tools.testPlanAnalysisGate(projectId, releaseId);
|
|
402
|
+
break;
|
|
403
|
+
case 'test-execution':
|
|
404
|
+
result = await tools.testExecutionGate(projectId, releaseId);
|
|
405
|
+
break;
|
|
406
|
+
case 'defect-quality':
|
|
407
|
+
result = await tools.defectQualityGate(projectId, releaseId);
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
// Analytics & Insights
|
|
411
|
+
case 'project-health':
|
|
412
|
+
result = await tools.getProjectHealth(projectId, releaseId);
|
|
413
|
+
break;
|
|
414
|
+
case 'test-coverage':
|
|
415
|
+
result = await tools.getTestCoverage(projectId, releaseId);
|
|
416
|
+
break;
|
|
417
|
+
case 'failed-tests':
|
|
418
|
+
result = await tools.getFailedTests(projectId, releaseId, { limit });
|
|
419
|
+
break;
|
|
420
|
+
case 'req-coverage':
|
|
421
|
+
result = await tools.getRequirementCoverage(projectId, releaseId);
|
|
422
|
+
break;
|
|
423
|
+
case 'test-trends':
|
|
424
|
+
result = await tools.getTestCaseTrends(projectId, releaseId, { days });
|
|
425
|
+
break;
|
|
426
|
+
case 'search-tests':
|
|
427
|
+
result = await tools.searchTestCases(projectId, releaseId, { query, limit });
|
|
428
|
+
break;
|
|
429
|
+
case 'user-activity':
|
|
430
|
+
result = await tools.getUserActivity(projectId, releaseId, { days });
|
|
431
|
+
break;
|
|
432
|
+
|
|
433
|
+
default:
|
|
434
|
+
console.error(`Unknown tool: ${tool}`);
|
|
435
|
+
console.error('Use --help to see available tools.');
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (options.format === 'json') {
|
|
440
|
+
console.log(JSON.stringify(result, null, 2));
|
|
441
|
+
} else {
|
|
442
|
+
// Use appropriate formatter
|
|
443
|
+
if (tool === 'release-readiness' || tool === 'all') {
|
|
444
|
+
formatTable(result);
|
|
445
|
+
} else if (['requirement-coverage', 'test-plan', 'test-execution', 'defect-quality'].includes(tool)) {
|
|
446
|
+
formatSingleGate(result);
|
|
447
|
+
} else {
|
|
448
|
+
formatGenericResult(result);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Exit code based on status
|
|
453
|
+
const status = result.overallStatus || result.status || result.healthStatus;
|
|
454
|
+
if (status === 'NO GO' || status === 'CRITICAL') process.exit(2);
|
|
455
|
+
if (status === 'CONDITIONAL GO' || status === 'AT RISK') process.exit(1);
|
|
456
|
+
process.exit(0);
|
|
457
|
+
|
|
458
|
+
} catch (err) {
|
|
459
|
+
console.error(`Error: ${err.message}`);
|
|
460
|
+
if (process.env.DEBUG) console.error(err.stack);
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zephyr-enterprise-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Comprehensive Zephyr Enterprise Tools - Release Readiness, Project Health, Test Analytics & More",
|
|
5
|
+
"main": "zephyr-enterprise-tools.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./zephyr-enterprise-tools.js",
|
|
8
|
+
"./cli": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"zephyr-gates": "cli.js",
|
|
12
|
+
"zephyr-tools": "cli.js"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "node cli.js",
|
|
17
|
+
"test": "node cli.js --help"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"zephyr",
|
|
21
|
+
"zephyr-enterprise",
|
|
22
|
+
"quality-gates",
|
|
23
|
+
"release-readiness",
|
|
24
|
+
"testing",
|
|
25
|
+
"qa",
|
|
26
|
+
"test-management",
|
|
27
|
+
"test-analytics",
|
|
28
|
+
"project-health",
|
|
29
|
+
"test-coverage",
|
|
30
|
+
"ci-cd",
|
|
31
|
+
"devops",
|
|
32
|
+
"mcp"
|
|
33
|
+
],
|
|
34
|
+
"author": "Govind Drolia",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/govind1072/zephyr-Enterprise-tools.git"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/govind1072/zephyr-Enterprise-tools/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/govind1072/zephyr-Enterprise-tools#readme",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"zephyr-enterprise-tools.js",
|
|
49
|
+
"cli.js",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
]
|
|
53
|
+
}
|