newman-reporter-dashboard-wrapper 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.
@@ -0,0 +1,456 @@
1
+ [
2
+ {
3
+ "runId": "RUN-683232",
4
+ "collectionName": "Public API Test Suite",
5
+ "timestamp": "2026-06-25 05:18:24",
6
+ "totalRequests": 2,
7
+ "passedAssertions": 4,
8
+ "failedAssertions": 0,
9
+ "avgResponseTime": 327,
10
+ "status": "Success",
11
+ "requests": [
12
+ {
13
+ "name": "Get All Posts",
14
+ "method": "GET",
15
+ "url": "https://jsonplaceholder.typicode.com/posts",
16
+ "statusCode": 0,
17
+ "responseTime": 0,
18
+ "assertions": []
19
+ },
20
+ {
21
+ "name": "Create New Post",
22
+ "method": "POST",
23
+ "url": "https://jsonplaceholder.typicode.com/posts",
24
+ "statusCode": 0,
25
+ "responseTime": 0,
26
+ "assertions": []
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "runId": "RUN-518049",
32
+ "collectionName": "Public API Test Suite",
33
+ "timestamp": "2026-06-25 05:19:13",
34
+ "totalRequests": 2,
35
+ "passedAssertions": 4,
36
+ "failedAssertions": 0,
37
+ "avgResponseTime": 469,
38
+ "status": "Success",
39
+ "requests": [
40
+ {
41
+ "name": "Get All Posts",
42
+ "method": "GET",
43
+ "url": "https://jsonplaceholder.typicode.com/posts",
44
+ "statusCode": 0,
45
+ "responseTime": 0,
46
+ "assertions": []
47
+ },
48
+ {
49
+ "name": "Create New Post",
50
+ "method": "POST",
51
+ "url": "https://jsonplaceholder.typicode.com/posts",
52
+ "statusCode": 0,
53
+ "responseTime": 0,
54
+ "assertions": []
55
+ }
56
+ ]
57
+ },
58
+ {
59
+ "runId": "RUN-854823",
60
+ "collectionName": "Public API Test Suite",
61
+ "timestamp": "2026-06-25 05:21:41",
62
+ "totalRequests": 2,
63
+ "passedAssertions": 4,
64
+ "failedAssertions": 0,
65
+ "avgResponseTime": 260,
66
+ "status": "Success",
67
+ "requests": [
68
+ {
69
+ "name": "Get All Posts",
70
+ "method": "GET",
71
+ "url": "https://jsonplaceholder.typicode.com/posts",
72
+ "statusCode": 0,
73
+ "responseTime": 0,
74
+ "assertions": []
75
+ },
76
+ {
77
+ "name": "Create New Post",
78
+ "method": "POST",
79
+ "url": "https://jsonplaceholder.typicode.com/posts",
80
+ "statusCode": 0,
81
+ "responseTime": 0,
82
+ "assertions": []
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "runId": "RUN-948995",
88
+ "collectionName": "Public API Test Suite",
89
+ "timestamp": "2026-06-25 06:12:26",
90
+ "totalRequests": 2,
91
+ "passedAssertions": 4,
92
+ "failedAssertions": 0,
93
+ "avgResponseTime": 302,
94
+ "status": "Success",
95
+ "requests": [
96
+ {
97
+ "name": "Get All Posts",
98
+ "method": "GET",
99
+ "url": "https://jsonplaceholder.typicode.com/posts",
100
+ "statusCode": 0,
101
+ "responseTime": 0,
102
+ "assertions": []
103
+ },
104
+ {
105
+ "name": "Create New Post",
106
+ "method": "POST",
107
+ "url": "https://jsonplaceholder.typicode.com/posts",
108
+ "statusCode": 0,
109
+ "responseTime": 0,
110
+ "assertions": []
111
+ }
112
+ ]
113
+ },
114
+ {
115
+ "runId": "RUN-759529",
116
+ "collectionName": "Public API Test Suite",
117
+ "timestamp": "2026-06-25 06:12:59",
118
+ "totalRequests": 2,
119
+ "passedAssertions": 4,
120
+ "failedAssertions": 0,
121
+ "avgResponseTime": 255,
122
+ "status": "Success",
123
+ "requests": [
124
+ {
125
+ "name": "Get All Posts",
126
+ "method": "GET",
127
+ "url": "https://jsonplaceholder.typicode.com/posts",
128
+ "statusCode": 0,
129
+ "responseTime": 0,
130
+ "assertions": []
131
+ },
132
+ {
133
+ "name": "Create New Post",
134
+ "method": "POST",
135
+ "url": "https://jsonplaceholder.typicode.com/posts",
136
+ "statusCode": 0,
137
+ "responseTime": 0,
138
+ "assertions": []
139
+ }
140
+ ]
141
+ },
142
+ {
143
+ "runId": "RUN-708198",
144
+ "collectionName": "Public API Test Suite",
145
+ "timestamp": "2026-06-25 06:14:17",
146
+ "totalRequests": 2,
147
+ "passedAssertions": 3,
148
+ "failedAssertions": 1,
149
+ "avgResponseTime": 487,
150
+ "status": "Failure",
151
+ "requests": [
152
+ {
153
+ "name": "Get All Posts",
154
+ "method": "GET",
155
+ "url": "https://jsonplaceholder.typicode.com/posts",
156
+ "statusCode": 0,
157
+ "responseTime": 0,
158
+ "assertions": []
159
+ },
160
+ {
161
+ "name": "Create New Post",
162
+ "method": "POST",
163
+ "url": "https://jsonplaceholder.typicode.com/posts",
164
+ "statusCode": 0,
165
+ "responseTime": 0,
166
+ "assertions": []
167
+ }
168
+ ]
169
+ },
170
+ {
171
+ "runId": "RUN-760015",
172
+ "collectionName": "Public API Test Suite",
173
+ "timestamp": "2026-06-25 06:18:20",
174
+ "totalRequests": 2,
175
+ "passedAssertions": 8,
176
+ "failedAssertions": 0,
177
+ "avgResponseTime": 545,
178
+ "status": "Success",
179
+ "requests": [
180
+ {
181
+ "name": "Get All Posts",
182
+ "method": "GET",
183
+ "url": "https://jsonplaceholder.typicode.com/posts",
184
+ "statusCode": 0,
185
+ "responseTime": 0,
186
+ "assertions": []
187
+ },
188
+ {
189
+ "name": "Create New Post",
190
+ "method": "POST",
191
+ "url": "https://jsonplaceholder.typicode.com/posts",
192
+ "statusCode": 0,
193
+ "responseTime": 0,
194
+ "assertions": []
195
+ }
196
+ ]
197
+ },
198
+ {
199
+ "runId": "RUN-766800",
200
+ "collectionName": "Public API Test Suite",
201
+ "timestamp": "2026-06-25 06:19:57",
202
+ "totalRequests": 2,
203
+ "passedAssertions": 8,
204
+ "failedAssertions": 0,
205
+ "avgResponseTime": 548,
206
+ "status": "Success",
207
+ "requests": [
208
+ {
209
+ "id": "623bbfa7-945e-4177-8908-d4095e3b523e",
210
+ "name": "Get All Posts",
211
+ "method": "GET",
212
+ "url": "https://jsonplaceholder.typicode.com/posts",
213
+ "statusCode": 200,
214
+ "responseTime": 315,
215
+ "assertions": [
216
+ {
217
+ "name": "Status code is 200",
218
+ "passed": true
219
+ },
220
+ {
221
+ "name": "Response time is within acceptable SLA limits",
222
+ "passed": true
223
+ },
224
+ {
225
+ "name": "Response is an array containing posts",
226
+ "passed": true
227
+ },
228
+ {
229
+ "name": "First post object complies with data schema contract",
230
+ "passed": true
231
+ }
232
+ ]
233
+ },
234
+ {
235
+ "id": "e99530c3-e9b2-48c5-a23e-3b4c6d946f46",
236
+ "name": "Create New Post",
237
+ "method": "POST",
238
+ "url": "https://jsonplaceholder.typicode.com/posts",
239
+ "statusCode": 201,
240
+ "responseTime": 780,
241
+ "assertions": [
242
+ {
243
+ "name": "Status code is 201 Created",
244
+ "passed": true
245
+ },
246
+ {
247
+ "name": "Response headers confirm JSON payload structure",
248
+ "passed": true
249
+ },
250
+ {
251
+ "name": "Response returns generated resource identity",
252
+ "passed": true
253
+ },
254
+ {
255
+ "name": "Response contains the complete mirrored request body data",
256
+ "passed": true
257
+ }
258
+ ]
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ "runId": "RUN-152281",
264
+ "collectionName": "Public API Test Suite",
265
+ "timestamp": "2026-06-25 06:24:56",
266
+ "totalRequests": 2,
267
+ "passedAssertions": 8,
268
+ "failedAssertions": 0,
269
+ "avgResponseTime": 315,
270
+ "status": "Success",
271
+ "requests": [
272
+ {
273
+ "id": "32d48e3e-2463-418e-81c6-9559fd9e3dcb",
274
+ "name": "Get All Posts",
275
+ "method": "GET",
276
+ "url": "https://jsonplaceholder.typicode.com/posts",
277
+ "statusCode": 200,
278
+ "responseTime": 323,
279
+ "assertions": [
280
+ {
281
+ "name": "Status code is 200",
282
+ "passed": true
283
+ },
284
+ {
285
+ "name": "Response time is within acceptable SLA limits",
286
+ "passed": true
287
+ },
288
+ {
289
+ "name": "Response is an array containing posts",
290
+ "passed": true
291
+ },
292
+ {
293
+ "name": "First post object complies with data schema contract",
294
+ "passed": true
295
+ }
296
+ ]
297
+ },
298
+ {
299
+ "id": "a148ca0b-10a2-41fb-bf76-65e34813babb",
300
+ "name": "Create New Post",
301
+ "method": "POST",
302
+ "url": "https://jsonplaceholder.typicode.com/posts",
303
+ "statusCode": 201,
304
+ "responseTime": 306,
305
+ "assertions": [
306
+ {
307
+ "name": "Status code is 201 Created",
308
+ "passed": true
309
+ },
310
+ {
311
+ "name": "Response headers confirm JSON payload structure",
312
+ "passed": true
313
+ },
314
+ {
315
+ "name": "Response returns generated resource identity",
316
+ "passed": true
317
+ },
318
+ {
319
+ "name": "Response contains the complete mirrored request body data",
320
+ "passed": true
321
+ }
322
+ ]
323
+ }
324
+ ]
325
+ },
326
+ {
327
+ "runId": "RUN-132160",
328
+ "collectionName": "Public API Test Suite",
329
+ "timestamp": "2026-06-25 06:25:33",
330
+ "totalRequests": 2,
331
+ "passedAssertions": 7,
332
+ "failedAssertions": 1,
333
+ "avgResponseTime": 401,
334
+ "status": "Failure",
335
+ "requests": [
336
+ {
337
+ "id": "e2c848fc-90eb-447f-a59d-76711e6d082e",
338
+ "name": "Get All Posts",
339
+ "method": "GET",
340
+ "url": "https://jsonplaceholder.typicode.com/posts",
341
+ "statusCode": 200,
342
+ "responseTime": 411,
343
+ "assertions": [
344
+ {
345
+ "name": "Status code is 200",
346
+ "passed": false,
347
+ "errorMessage": "expected response to have status code 201 but got 200"
348
+ },
349
+ {
350
+ "name": "Response time is within acceptable SLA limits",
351
+ "passed": true
352
+ },
353
+ {
354
+ "name": "Response is an array containing posts",
355
+ "passed": true
356
+ },
357
+ {
358
+ "name": "First post object complies with data schema contract",
359
+ "passed": true
360
+ }
361
+ ]
362
+ },
363
+ {
364
+ "id": "74b3409f-de1f-47a7-9994-7ebf08da94fe",
365
+ "name": "Create New Post",
366
+ "method": "POST",
367
+ "url": "https://jsonplaceholder.typicode.com/posts",
368
+ "statusCode": 201,
369
+ "responseTime": 390,
370
+ "assertions": [
371
+ {
372
+ "name": "Status code is 201 Created",
373
+ "passed": true
374
+ },
375
+ {
376
+ "name": "Response headers confirm JSON payload structure",
377
+ "passed": true
378
+ },
379
+ {
380
+ "name": "Response returns generated resource identity",
381
+ "passed": true
382
+ },
383
+ {
384
+ "name": "Response contains the complete mirrored request body data",
385
+ "passed": true
386
+ }
387
+ ]
388
+ }
389
+ ]
390
+ },
391
+ {
392
+ "runId": "RUN-975070",
393
+ "collectionName": "Public API Test Suite",
394
+ "timestamp": "2026-06-25 06:33:12",
395
+ "totalRequests": 2,
396
+ "passedAssertions": 7,
397
+ "failedAssertions": 1,
398
+ "avgResponseTime": 286,
399
+ "status": "Failure",
400
+ "requests": [
401
+ {
402
+ "id": "6e21365e-5752-4231-a7d9-bae2cef99003",
403
+ "name": "Get All Posts",
404
+ "method": "GET",
405
+ "url": "https://jsonplaceholder.typicode.com/posts",
406
+ "statusCode": 200,
407
+ "responseTime": 282,
408
+ "assertions": [
409
+ {
410
+ "name": "Status code is 200",
411
+ "passed": false,
412
+ "errorMessage": "expected response to have status code 201 but got 200"
413
+ },
414
+ {
415
+ "name": "Response time is within acceptable SLA limits",
416
+ "passed": true
417
+ },
418
+ {
419
+ "name": "Response is an array containing posts",
420
+ "passed": true
421
+ },
422
+ {
423
+ "name": "First post object complies with data schema contract",
424
+ "passed": true
425
+ }
426
+ ]
427
+ },
428
+ {
429
+ "id": "9e3c5316-f15f-47c1-9065-0704235bc6d5",
430
+ "name": "Create New Post",
431
+ "method": "POST",
432
+ "url": "https://jsonplaceholder.typicode.com/posts",
433
+ "statusCode": 201,
434
+ "responseTime": 289,
435
+ "assertions": [
436
+ {
437
+ "name": "Status code is 201 Created",
438
+ "passed": true
439
+ },
440
+ {
441
+ "name": "Response headers confirm JSON payload structure",
442
+ "passed": true
443
+ },
444
+ {
445
+ "name": "Response returns generated resource identity",
446
+ "passed": true
447
+ },
448
+ {
449
+ "name": "Response contains the complete mirrored request body data",
450
+ "passed": true
451
+ }
452
+ ]
453
+ }
454
+ ]
455
+ }
456
+ ]
@@ -0,0 +1,384 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Newman Orchestrator: Interactive Analytics</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <style>
10
+ body { background-color: #f4f6f9; font-family: system-ui, -apple-system, sans-serif; }
11
+ .card { border: none; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.03); }
12
+ .clickable-row { cursor: pointer; transition: background-color 0.2s; }
13
+ .clickable-row:hover { background-color: #f0f4f8 !important; }
14
+ .status-Success { background-color: #d1e7dd; color: #0f5132; padding: 4px 10px; border-radius: 20px; font-size: 0.82rem; font-weight: 600; }
15
+ .status-Warning { background-color: #fff3cd; color: #664d03; padding: 4px 10px; border-radius: 20px; font-size: 0.82rem; font-weight: 600; }
16
+ .status-Failure { background-color: #f8d7da; color: #842029; padding: 4px 10px; border-radius: 20px; font-size: 0.82rem; font-weight: 600; }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <div class="container-fluid py-4 px-5">
21
+ <div class="d-flex justify-content-between align-items-center mb-4">
22
+ <h2 class="fw-bold m-0">🚀 Newman Orchestrator Analytics</h2>
23
+ <span class="badge bg-dark px-3 py-2 fs-6">V1.0.0</span>
24
+ </div>
25
+
26
+ <div class="row g-3 mb-4">
27
+ <div class="col-md-4">
28
+ <div class="card p-3">
29
+ <small class="text-muted text-uppercase fw-bold">Run History</small>
30
+ <h3 id="stat-total-runs" class="fw-bold m-0">0 runs</h3>
31
+ </div>
32
+ </div>
33
+ <div class="col-md-4">
34
+ <div class="card p-3">
35
+ <small class="text-muted text-uppercase fw-bold">Success Pass Rate</small>
36
+ <h3 id="stat-success-rate" class="fw-bold m-0 text-success">0%</h3>
37
+ </div>
38
+ </div>
39
+ <div class="col-md-4">
40
+ <div class="card p-3">
41
+ <small class="text-muted text-uppercase fw-bold">Details Engine</small>
42
+ <h3 class="fw-bold m-0 text-primary">● Active</h3>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="row g-3 mb-4">
48
+ <div class="col-md-8">
49
+ <div class="card p-3 h-100">
50
+ <h6 class="fw-bold mb-3 text-secondary">Historical Pass / Fail Trend & Response Times</h6>
51
+ <canvas id="trendChart" height="120"></canvas>
52
+ </div>
53
+ </div>
54
+ <div class="col-md-4">
55
+ <div class="card p-3 h-100">
56
+ <canvas id="latencyChart"></canvas>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="card p-4">
62
+ <h5 class="fw-bold mb-3">Execution Ledger <span class="fs-6 fw-normal text-muted">(Click any row or graph coordinate to reveal request details)</span></h5>
63
+ <div class="table-responsive">
64
+ <table class="table table-hover align-middle m-0">
65
+ <thead class="table-light">
66
+ <tr>
67
+ <th>Run ID</th>
68
+ <th>Collection Target</th>
69
+ <th>Timestamp</th>
70
+ <th>Assertions (P/F)</th>
71
+ <th>Avg Response Time</th>
72
+ <th>Status</th>
73
+ </tr>
74
+ </thead>
75
+ <tbody id="execution-table-body"></tbody>
76
+ </table>
77
+ </div>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="offcanvas offcanvas-end style-drawer" style="width: 45%;" tabIndex="-1" id="detailsDrawer" aria-labelledby="detailsDrawerLabel">
82
+ <div class="offcanvas-header bg-dark text-white">
83
+ <h5 class="offcanvas-title fw-bold" id="detailsDrawerLabel">Run Details</h5>
84
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
85
+ </div>
86
+ <div class="offcanvas-body" id="drawer-content-body">
87
+ </div>
88
+ </div>
89
+
90
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
91
+ <script>
92
+ let cachedData = [];
93
+ const bsDrawer = new bootstrap.Offcanvas(document.getElementById('detailsDrawer'));
94
+
95
+ async function initMetricsEngine() {
96
+ try {
97
+ const res = await fetch('/api/history');
98
+ cachedData = await res.json();
99
+
100
+ if (!cachedData || cachedData.length === 0) {
101
+ console.warn("Database storage is currently empty.");
102
+ document.getElementById('execution-table-body').innerHTML =
103
+ `<tr><td colspan="6" class="text-center text-muted py-4">No data streams captured yet. Please run your collection suite to feed data.</td></tr>`;
104
+ return;
105
+ }
106
+
107
+ document.getElementById('stat-total-runs').innerText = `${cachedData.length} executions`;
108
+ const passCount = cachedData.filter(r => r.status !== 'Failure').length;
109
+ document.getElementById('stat-success-rate').innerText = `${Math.round((passCount / cachedData.length) * 100)}%`;
110
+
111
+ const tbody = document.getElementById('execution-table-body');
112
+ tbody.innerHTML = "";
113
+
114
+ cachedData.slice().reverse().forEach(run => {
115
+ const tr = document.createElement('tr');
116
+ tr.className = 'clickable-row';
117
+ tr.onclick = () => showDetailedRunBreakdown(run.runId);
118
+ tr.innerHTML = `
119
+ <td class="fw-bold text-primary">${run.runId}</td>
120
+ <td>${run.collectionName}</td>
121
+ <td class="text-muted">${run.timestamp}</td>
122
+ <td><span class="text-success">${run.passedAssertions} ✓</span> / <span class="text-danger">${run.failedAssertions} ✗</span></td>
123
+ <td><strong>${run.avgResponseTime}ms</strong></td>
124
+ <td><span class="status-${run.status}">${run.status}</span></td>
125
+ `;
126
+ tbody.appendChild(tr);
127
+ });
128
+
129
+ renderCharts(cachedData);
130
+ } catch (err) {
131
+ console.error("Dashboard engine rendering failure:", err);
132
+ }
133
+ }
134
+
135
+ // Heuristic AI Engine to generate free, context-aware QA advice for failing runs
136
+ function generateAIAdvice(run) {
137
+ let insights = [];
138
+ const highLatencyRequests = run.requests.filter(r => r.responseTime > 800);
139
+
140
+ // Captures requests that either have broken HTTP status codes OR have explicitly failed assertions
141
+ const failedRequests = run.requests.filter(r =>
142
+ r.statusCode >= 400 ||
143
+ (r.assertions && r.assertions.some(a => !a.passed))
144
+ );
145
+
146
+ // Scenario 1: Total Success Matrix
147
+ if (run.failedAssertions === 0 && run.status === 'Success' && highLatencyRequests.length === 0) {
148
+ return `
149
+ <div class="alert alert-success border-0 shadow-sm mb-3">
150
+ <h6 class="fw-bold mb-1">🤖 AI Advisor: System Healthy</h6>
151
+ <small class="d-block text-secondary">All endpoints responded within benchmark thresholds with 100% assertion parity. No regression risks detected. Ready for deployment pipeline integration.</small>
152
+ </div>`;
153
+ }
154
+
155
+ // Scenario 2: Global performance degradation
156
+ if (run.avgResponseTime > 1000) {
157
+ insights.push(`⚠️ <strong>Global Latency Warning:</strong> The collection's average response time is running slow at <strong>${run.avgResponseTime}ms</strong>. This suggests network congestion, slow environment proxies, or resource-heavy database lookups.`);
158
+ }
159
+
160
+ // Scenario 3: Individual latency outliers
161
+ if (highLatencyRequests.length > 0) {
162
+ highLatencyRequests.forEach(r => {
163
+ insights.push(`🐢 <strong>Endpoint Degraded:</strong> <code>[${r.method}] ${r.name}</code> took <strong>${r.responseTime}ms</strong>. This exceeds safe interactive API performance budgets (800ms). Consider applying data caching or optimizing server queries.`);
164
+ });
165
+ }
166
+
167
+ // Scenario 4: Broken tests / assertions
168
+ if (failedRequests.length > 0) {
169
+ failedRequests.forEach(r => {
170
+ const code = r.statusCode;
171
+ const brokenAssertions = r.assertions ? r.assertions.filter(a => !a.passed) : [];
172
+
173
+ if (code === 401 || code === 403) {
174
+ insights.push(`🔒 <strong>Authentication Fault on "${r.name}":</strong> Request dropped an access error (<strong>Status ${code}</strong>). Check if your environmental Authorization header tokens, API keys, or OAuth sessions expired.`);
175
+ } else if (code >= 500) {
176
+ insights.push(`💥 <strong>Server-Side Crash on "${r.name}":</strong> Backend returned a <strong>Status ${code}</strong> internal runtime error. Check your server logs or application container traces for active unhandled exceptions.`);
177
+ } else if (brokenAssertions.length > 0) {
178
+ // Pinpoint exactly which script validation assertion failed
179
+ brokenAssertions.forEach(a => {
180
+ let adviceString = `❌ <strong>Assertion Failed on "${r.name}":</strong> The check <code>"${a.name}"</code> collapsed.`;
181
+ if (a.errorMessage) {
182
+ adviceString += ` Backend unexpected value details: <em class="text-danger">${a.errorMessage}</em>`;
183
+ } else {
184
+ adviceString += ` Review your endpoint's response payload structure to ensure it matches the Postman test schema expectation.`;
185
+ }
186
+ insights.push(adviceString);
187
+ });
188
+ } else {
189
+ insights.push(`⚠️ <strong>Unexpected Code on "${r.name}":</strong> Returned a client-side warning error (<strong>Status ${code}</strong>). Verify parameter schema spelling parameters.`);
190
+ }
191
+ });
192
+ }
193
+
194
+ // Render the advice box matrix inside the view canvas layout
195
+ return `
196
+ <div class="card bg-light border-0 shadow-sm mb-4">
197
+ <div class="card-body">
198
+ <h6 class="fw-bold text-dark mb-3 d-flex align-items-center gap-2">
199
+ <span>🤖 AI QA Advisor Insights</span>
200
+ <span class="badge bg-danger font-monospace text-white" style="font-size:10px;">${run.failedAssertions} Failure(s) Detected</span>
201
+ </h6>
202
+ <ul class="small text-dark ps-3 m-0 d-flex flex-column gap-2">
203
+ ${insights.map(insight => `<li>${insight}</li>`).join('')}
204
+ </ul>
205
+ </div>
206
+ </div>`;
207
+ }
208
+
209
+ function showDetailedRunBreakdown(runId) {
210
+ const run = cachedData.find(r => r.runId === runId);
211
+ if (!run) return;
212
+
213
+ document.getElementById('detailsDrawerLabel').innerText = `Execution Report: ${run.runId}`;
214
+ const body = document.getElementById('drawer-content-body');
215
+
216
+ const aiAdviceHTML = generateAIAdvice(run);
217
+
218
+ let htmlHTML = `<div class="mb-4">
219
+ <h6><strong>Collection Suite:</strong> ${run.collectionName}</h6>
220
+ <p class="text-muted mb-1">Executed at: ${run.timestamp}</p>
221
+ <div class="badge bg-secondary p-2">Average Response Latency: ${run.avgResponseTime}ms</div>
222
+ </div>
223
+
224
+ ${aiAdviceHTML}
225
+ <hr/>`;
226
+
227
+ run.requests.forEach((req, idx) => {
228
+ const statusBadge = req.statusCode >= 200 && req.statusCode < 300 ? 'bg-success' : 'bg-danger';
229
+
230
+ htmlHTML += `
231
+ <div class="card p-3 mb-3 border">
232
+ <div class="d-flex justify-content-between align-items-center mb-2">
233
+ <h6 class="fw-bold text-dark m-0">${idx + 1}. ${req.name}</h6>
234
+ <span class="badge ${statusBadge}">${req.method} ${req.statusCode}</span>
235
+ </div>
236
+ <div class="text-muted font-monospace small mb-2 text-truncate">${req.url}</div>
237
+ <div class="small text-secondary mb-2">Response Time: <strong>${req.responseTime}ms</strong></div>
238
+
239
+ <div class="mt-2">
240
+ <h6>Assertions:</h6>
241
+ ${!req.assertions || req.assertions.length === 0 ? '<p class="text-muted small italic m-0">No assertions defined for this step.</p>' : ''}
242
+ ${req.assertions ? req.assertions.map(a => `
243
+ <div class="d-flex align-items-start gap-2 my-1">
244
+ <span>${a.passed ? '✅' : '❌'}</span>
245
+ <div>
246
+ <span class="${a.passed ? 'text-success' : 'text-danger fw-bold'}">${a.name}</span>
247
+ ${a.errorMessage ? `<div class="text-muted font-monospace text-xs" style="font-size:11px">${a.errorMessage}</div>` : ''}
248
+ </div>
249
+ </div>
250
+ `).join('') : ''}
251
+ </div>
252
+ </div>`;
253
+ });
254
+
255
+ body.innerHTML = htmlHTML;
256
+ bsDrawer.show();
257
+ }
258
+
259
+ function renderCharts(data) {
260
+ const existingTrendChart = Chart.getChart("trendChart");
261
+ if (existingTrendChart) existingTrendChart.destroy();
262
+
263
+ const existingLatencyChart = Chart.getChart("latencyChart");
264
+ if (existingLatencyChart) existingLatencyChart.destroy();
265
+
266
+ const ctx = document.getElementById('trendChart');
267
+ if (!ctx) return;
268
+
269
+ new Chart(ctx, {
270
+ type: 'bar',
271
+ data: {
272
+ labels: data.map(r => r.runId),
273
+ datasets: [
274
+ {
275
+ label: 'Passed Assertions',
276
+ data: data.map(r => r.passedAssertions),
277
+ backgroundColor: 'rgba(25, 135, 84, 0.6)',
278
+ borderColor: '#198754',
279
+ borderWidth: 1,
280
+ stack: 'assertions',
281
+ yAxisID: 'y'
282
+ },
283
+ {
284
+ label: 'Failed Assertions',
285
+ data: data.map(r => r.failedAssertions),
286
+ backgroundColor: 'rgba(220, 53, 69, 0.6)',
287
+ borderColor: '#dc3545',
288
+ borderWidth: 1,
289
+ stack: 'assertions',
290
+ yAxisID: 'y'
291
+ },
292
+ {
293
+ label: 'Avg Response Time (ms)',
294
+ data: data.map(r => r.avgResponseTime),
295
+ type: 'line',
296
+ borderColor: '#0d6efd',
297
+ backgroundColor: 'rgba(13, 110, 253, 0.1)',
298
+ tension: 0.3,
299
+ fill: true,
300
+ pointRadius: 5,
301
+ pointHoverRadius: 8,
302
+ yAxisID: 'y1'
303
+ }
304
+ ]
305
+ },
306
+ options: {
307
+ responsive: true,
308
+ plugins: {
309
+ legend: { position: 'top' },
310
+ tooltip: {
311
+ mode: 'index',
312
+ intersect: false,
313
+ callbacks: {
314
+ title: (context) => `Execution Details: ${context[0].label}`,
315
+ label: function(context) {
316
+ let label = context.dataset.label || '';
317
+ if (label) label += ': ';
318
+ if (context.datasetIndex === 2) {
319
+ label += `${context.raw} ms`;
320
+ } else {
321
+ label += `${context.raw} checks`;
322
+ }
323
+ return label;
324
+ },
325
+ footer: (context) => '💡 Click point to open full API request trace panel.'
326
+ }
327
+ }
328
+ },
329
+ scales: {
330
+ y: {
331
+ type: 'linear',
332
+ position: 'left',
333
+ title: { display: true, text: 'Assertion Check Quantities' },
334
+ grid: { drawOnChartArea: true }
335
+ },
336
+ y1: {
337
+ type: 'linear',
338
+ position: 'right',
339
+ title: { display: true, text: 'Latency Speed (ms)' },
340
+ grid: { drawOnChartArea: false },
341
+ ticks: { callback: (value) => `${value}ms` }
342
+ }
343
+ },
344
+ onClick: function(event, activeElements, chart) {
345
+ const points = chart.getElementsAtEventForMode(event, 'nearest', { intersect: true }, true);
346
+ if (points.length > 0) {
347
+ const dataIndex = points[0].index;
348
+ const selectedRunId = data.map(r => r.runId)[dataIndex];
349
+ if (selectedRunId) {
350
+ showDetailedRunBreakdown(selectedRunId);
351
+ }
352
+ }
353
+ }
354
+ }
355
+ });
356
+
357
+ const latencyCanvas = document.getElementById('latencyChart');
358
+ if (!latencyCanvas) return;
359
+
360
+ const latest = data[data.length - 1];
361
+ new Chart(latencyCanvas, {
362
+ type: 'doughnut',
363
+ data: {
364
+ labels: ['Passed Assertions', 'Failed Assertions'],
365
+ datasets: [{
366
+ data: [latest.passedAssertions, latest.failedAssertions],
367
+ backgroundColor: ['#198754', '#dc3545'],
368
+ borderWidth: 2,
369
+ hoverOffset: 4
370
+ }]
371
+ },
372
+ options: {
373
+ plugins: {
374
+ legend: { position: 'bottom' },
375
+ title: { display: true, text: `Latest Run State (${latest.runId})` }
376
+ }
377
+ }
378
+ });
379
+ }
380
+
381
+ window.onload = initMetricsEngine;
382
+ </script>
383
+ </body>
384
+ </html>
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env node
2
+ import newman from 'newman';
3
+ import express from 'express';
4
+ import open from 'open';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const HISTORY_FILE = path.join(__dirname, '.newman-history.json');
11
+ const SERVER_PORT = 8086;
12
+ function fetchLocalLedger() {
13
+ if (!fs.existsSync(HISTORY_FILE)) {
14
+ fs.writeFileSync(HISTORY_FILE, JSON.stringify([]));
15
+ }
16
+ try {
17
+ return JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf-8'));
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ }
23
+ function commitRunToHistory(metric) {
24
+ const history = fetchLocalLedger();
25
+ history.push(metric);
26
+ if (history.length > 50)
27
+ history.shift();
28
+ fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
29
+ console.log(`\x1b[32m✓ [LEDGER SUCCESS]\x1b[0m Appended run ${metric.runId} successfully.`);
30
+ }
31
+ function executePipeline() {
32
+ const collectionArg = process.argv[2];
33
+ if (!collectionArg) {
34
+ console.error("\x1b[31m❌ Usage Error:\x1b[0m Please specify your Postman collection file.");
35
+ process.exit(1);
36
+ }
37
+ const resolutionPath = path.resolve(collectionArg);
38
+ console.log(`\x1b[36m[ORCHESTRATOR]\x1b[0m Booting runtime stream for: [${path.basename(resolutionPath)}]`);
39
+ let totalLatency = 0;
40
+ let totalRequests = 0;
41
+ let passedAssertions = 0;
42
+ let failedAssertions = 0;
43
+ const detailedRequests = [];
44
+ // Tracks the active request execution block reference context
45
+ let currentRequestRef = null;
46
+ let collectionData;
47
+ try {
48
+ collectionData = JSON.parse(fs.readFileSync(resolutionPath, 'utf-8'));
49
+ }
50
+ catch (e) {
51
+ console.error(`\x1b[31m[ERROR]\x1b[0m Failed to parse collection file: ${e.message}`);
52
+ process.exit(1);
53
+ }
54
+ newman.run({
55
+ collection: collectionData,
56
+ reporters: 'cli'
57
+ })
58
+ .on('request', (err, args) => {
59
+ if (err || !args.response)
60
+ return;
61
+ totalRequests++;
62
+ totalLatency += args.response.responseTime;
63
+ // Create the active request record block during execution runtime
64
+ currentRequestRef = {
65
+ id: args.cursor.ref, // Unique internal step identifier
66
+ name: args.item?.name || 'Unnamed Request',
67
+ method: args.item?.request?.method || 'GET',
68
+ url: args.item?.request?.url?.toString() || '',
69
+ statusCode: args.response.code || 0,
70
+ responseTime: args.response.responseTime || 0,
71
+ assertions: []
72
+ };
73
+ detailedRequests.push(currentRequestRef);
74
+ })
75
+ .on('assertion', (err, args) => {
76
+ // Safe global status monitoring tracking counters
77
+ if (err)
78
+ failedAssertions++;
79
+ else
80
+ passedAssertions++;
81
+ // Map assertions actively straight to the current request reference payload array
82
+ if (currentRequestRef && currentRequestRef.id === args.cursor.ref) {
83
+ currentRequestRef.assertions.push({
84
+ name: args.assertion || 'Unnamed Checkpoint',
85
+ passed: !err,
86
+ errorMessage: err ? err.message : undefined
87
+ });
88
+ }
89
+ })
90
+ .on('done', (err, summary) => {
91
+ if (err || !summary) {
92
+ console.error("\x1b[31m[CRITICAL]\x1b[0m Collection run crashed.", err);
93
+ process.exit(1);
94
+ }
95
+ const calculatedAvg = totalRequests > 0 ? Math.round(totalLatency / totalRequests) : 0;
96
+ let healthRating = 'Success';
97
+ if (failedAssertions > 0)
98
+ healthRating = 'Failure';
99
+ else if (calculatedAvg > 1000)
100
+ healthRating = 'Warning';
101
+ const metricsPayload = {
102
+ runId: `RUN-${Math.floor(100000 + Math.random() * 900000)}`,
103
+ collectionName: summary.collection.name || 'Postman Collection',
104
+ timestamp: new Date().toISOString().replace('T', ' ').substring(0, 19),
105
+ totalRequests,
106
+ passedAssertions,
107
+ failedAssertions,
108
+ avgResponseTime: calculatedAvg,
109
+ status: healthRating,
110
+ requests: detailedRequests
111
+ };
112
+ commitRunToHistory(metricsPayload);
113
+ setTimeout(() => {
114
+ spinUpDashboardPortal();
115
+ }, 300);
116
+ });
117
+ }
118
+ function spinUpDashboardPortal() {
119
+ const app = express();
120
+ app.get('/api/history', (req, res) => {
121
+ res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, private');
122
+ res.json(fetchLocalLedger());
123
+ });
124
+ app.get('/', (req, res) => {
125
+ res.sendFile(path.join(__dirname, 'dashboard.html'));
126
+ });
127
+ app.listen(SERVER_PORT, async () => {
128
+ console.log(`\x1b[32m🚀 [PORTAL] Dashboard metrics serving at http://localhost:${SERVER_PORT}\x1b[0m`);
129
+ await open(`http://localhost:${SERVER_PORT}`);
130
+ });
131
+ }
132
+ executePipeline();
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "newman-reporter-dashboard-wrapper",
3
+ "version": "1.0.0",
4
+ "description": "Historical trend reporting dashboard for Postman/Newman executions",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "newman-dashboard": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc && copyfiles -u 1 src/dashboard.html dist/",
15
+ "prepare": "npm run build"
16
+ },
17
+ "dependencies": {
18
+ "express": "^4.19.2",
19
+ "newman": "^6.1.3",
20
+ "open": "^10.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/express": "^4.17.21",
24
+ "@types/newman": "*",
25
+ "@types/node": "^20.11.0",
26
+ "copyfiles": "^2.4.1",
27
+ "typescript": "^5.3.3"
28
+ }
29
+ }
package/readme.md ADDED
@@ -0,0 +1,20 @@
1
+ # Newman Dashboard Orchestrator 🚀
2
+
3
+ An interactive, real-time HTML5 analytics reporter dashboard for Postman collections executed via Newman. Includes a built-in, completely free, client-side **AI QA Advisor Insights** engine to instantly diagnose assertion and performance bottlenecks locally with zero external API dependencies.
4
+
5
+ ---
6
+
7
+ ## ⚡ Quick Start Guide
8
+
9
+ Get up and running with your interactive dashboard wrapper in under 2 minutes.
10
+
11
+ ### 1. Prerequisites
12
+ Ensure you have [Node.js](https://nodejs.org/) (v18 or higher) installed on your machine. You will also need a Postman collection exported as a `.json` file.
13
+
14
+ ### 2. Global Installation
15
+ Open your terminal and install the orchestrator utility globally:
16
+ ```bash
17
+ npm install -g newman-reporter-dashboard-orchestrator
18
+
19
+ How to run the newman and invoke dashboard
20
+ newman-dashboard my_postman_collection.json