super-api-tester-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.
package/bin/cli.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * super-api-tester-dashboard-wrapper
5
+ * Executable CLI Entry Point
6
+ */
7
+
8
+ // Bypasses argument filtering to execute the live streaming runner instantly
9
+ require('../src/runner.js');
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "super-api-tester-dashboard-wrapper",
3
+ "version": "1.0.0",
4
+ "description": "The official real-time SSE live-streaming dashboard UI wrapper for super-api-tester suites",
5
+ "main": "src/runner.js",
6
+ "bin": {
7
+ "super-api-dashboard": "./bin/cli.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src"
12
+ ],
13
+ "keywords": [
14
+ "super-api-tester",
15
+ "dashboard",
16
+ "realtime",
17
+ "sse",
18
+ "automation",
19
+ "qa",
20
+ "contract-testing"
21
+ ],
22
+ "author": "Kushan Shalindra Amarasiri",
23
+ "license": "ISC",
24
+ "dependencies": {
25
+ "express": "^4.19.2"
26
+ },
27
+ "peerDependencies": {
28
+ "super-api-tester": "^1.0.0"
29
+ },
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ }
33
+ }
package/readme.md ADDED
@@ -0,0 +1,65 @@
1
+ # super-api-tester-dashboard-wrapper
2
+
3
+ A real-time, hands-free automation streaming pipeline and interactive visual dashboard wrapper for the `super-api-tester` contract framework.
4
+
5
+ This wrapper monitors your Vitest contract automation metrics, maintains an in-memory execution history ledger, and handles real-time Server-Sent Events (SSE) data streams to populate a beautiful, zero-lag browser interface complete with interactive telemetry metrics and individual endpoint latency profiling.
6
+
7
+ ---
8
+
9
+ ## šŸš€ Features
10
+
11
+ * **⚔ Zero-Lag Live Streaming:** Utilizes Server-Sent Events (SSE) to push test metrics instantly from your runtime compiler loop straight to your browser.
12
+ * **šŸ“Š Interactive Trend Charts:** Integrated Chart.js dashboards showing overall execution trends (Passed vs. Contract Breaches) over structural interval cycles.
13
+ * **ā±ļø Performance Profiling:** Automatically highlights your Top 5 slowest routing endpoints in a responsive bar graph matrix.
14
+ * **šŸ” Click-to-Drilldown History:** Click any specific API route row in the log console to instantly pull up its isolated, historical latency tracking profile over past runs.
15
+ * **šŸ’¾ Refresh Persistence:** The backend keeps an active internal snapshot ledger. Refreshing your browser will **never** wipe your charts or show blank metrics placeholders.
16
+ * **ā° Embedded Automation Scheduler:** Operates background cron intervals (defaulted to 5 minutes) to execute checks hands-free from a single terminal window.
17
+
18
+ ---
19
+
20
+ ## šŸ› ļø Installation
21
+
22
+ Navigate to your active `super-api-tester` project directory and install the wrapper as a development dependency:
23
+
24
+ ```bash
25
+ npm install -D super-api-tester-dashboard-wrapper
26
+ āš™ļø Project Setup Configuration
27
+ 1. Update package.json Scripts
28
+ Open your main testing repository's package.json file and merge these scripts into your existing configuration:
29
+
30
+ JSON
31
+ {
32
+ "name": "your-testing-project",
33
+ "type": "module",
34
+ "scripts": {
35
+ "test": "vitest run",
36
+ "test:watch": "vitest --reporter=default --reporter=json --outputFile=./test-results.json",
37
+ "dashboard": "super-api-dashboard"
38
+ },
39
+ "dependencies": {
40
+ "super-api-tester": "^1.0.3"
41
+ },
42
+ "devDependencies": {
43
+ "super-api-tester-dashboard-wrapper": "^1.1.0"
44
+ }
45
+ }
46
+ 2. Verify Your Test Base URL Alignment
47
+ To avoid encountering HTTP 405 Method Not Allowed errors during execution loops, ensure your super-api-tester instance points directly to the active API routing gateway subdomain, rather than a landing documentation URL layer:
48
+
49
+ JavaScript
50
+ // āœ… Correct Target Configuration Structure
51
+ const api = new ApiTester({
52
+ baseUrl: '[https://api.restful-api.dev](https://api.restful-api.dev)'
53
+ });
54
+ šŸ’» Usage
55
+ You no longer need to manage multiple terminal windows to run your dashboard and watch files simultaneously! Simply run the unified dashboard boot command:
56
+
57
+ Bash
58
+ npm run dashboard
59
+ This launches the internal monitoring engine and outputs your confirmation hook:
60
+
61
+ Plaintext
62
+ šŸ–„ļø super-api-tester Live Console: http://localhost:3000
63
+ šŸš€ Automated scheduling sequence armed.
64
+ šŸ•’ Triggering automated contract check...
65
+ Open your browser and navigate to http://localhost:3000 to view your live telemetry suite streaming in real time.
package/src/index.html ADDED
@@ -0,0 +1,227 @@
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>super-api-tester Live Console</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <style>
10
+ body { background-color: #0b0f19; }
11
+ .neon-glow-pass { text-shadow: 0 0 15px rgba(16, 185, 129, 0.3); }
12
+ .neon-glow-fail { text-shadow: 0 0 15px rgba(244, 63, 94, 0.3); }
13
+ </style>
14
+ </head>
15
+ <body class="text-slate-300 font-sans antialiased min-h-screen p-4 sm:p-8">
16
+
17
+ <div class="max-w-6xl mx-auto space-y-8">
18
+
19
+ <header class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b border-slate-800 pb-6">
20
+ <div>
21
+ <div class="flex items-center gap-3">
22
+ <h1 class="text-2xl font-extrabold tracking-tight text-white flex items-center gap-2">
23
+ <svg class="w-6 h-6 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2z"></path></svg>
24
+ super-api-tester
25
+ </h1>
26
+ <span class="bg-slate-800 text-slate-400 text-xs px-2 py-0.5 rounded font-mono border border-slate-700">Live Console</span>
27
+ </div>
28
+ <p class="text-sm text-slate-400 mt-1">Real-time contract automation streaming dashboard wrapper pipeline</p>
29
+ </div>
30
+ <div class="flex items-center gap-2 bg-slate-900 border border-slate-800 px-3 py-1.5 rounded-lg w-fit">
31
+ <span id="status-dot" class="w-2.5 h-2.5 rounded-full bg-yellow-500 animate-pulse"></span>
32
+ <span id="status-text" class="text-xs font-mono font-bold uppercase tracking-wider text-yellow-500">Connecting</span>
33
+ </div>
34
+ </header>
35
+
36
+ <section class="grid grid-cols-1 sm:grid-cols-3 gap-6">
37
+ <div class="bg-[#0f1422] border border-slate-800 p-6 rounded-xl relative overflow-hidden">
38
+ <div class="text-xs font-bold text-slate-500 uppercase tracking-wider">Total Executed Suites</div>
39
+ <div id="total-suites" class="text-4xl font-black text-white mt-2 font-mono">--</div>
40
+ </div>
41
+ <div class="bg-[#0f1422] border border-slate-800 p-6 rounded-xl border-l-4 border-l-emerald-500 relative overflow-hidden">
42
+ <div class="text-xs font-bold text-slate-500 uppercase tracking-wider">Passed Metrics</div>
43
+ <div id="passed-tests" class="text-4xl font-black text-emerald-400 mt-2 font-mono neon-glow-pass">--</div>
44
+ </div>
45
+ <div class="bg-[#0f1422] border border-slate-800 p-6 rounded-xl border-l-4 border-l-rose-500 relative overflow-hidden">
46
+ <div class="text-xs font-bold text-slate-500 uppercase tracking-wider">Contract Breaches</div>
47
+ <div id="failed-tests" class="text-4xl font-black text-rose-500 mt-2 font-mono neon-glow-fail">--</div>
48
+ </div>
49
+ </section>
50
+
51
+ <section class="grid grid-cols-1 md:grid-cols-2 gap-6">
52
+ <div class="bg-[#0f1422] border border-slate-800 p-5 rounded-xl space-y-3">
53
+ <h4 class="text-xs font-bold text-slate-400 uppercase tracking-wider">Suite Execution Trends History</h4>
54
+ <div class="h-56 w-full relative"><canvas id="lineChart"></canvas></div>
55
+ </div>
56
+ <div class="bg-[#0f1422] border border-slate-800 p-5 rounded-xl space-y-3">
57
+ <h4 class="text-xs font-bold text-slate-400 uppercase tracking-wider">Slowest Endpoints Response Speed (Top 5)</h4>
58
+ <div class="h-56 w-full relative"><canvas id="barChart"></canvas></div>
59
+ </div>
60
+ </section>
61
+
62
+ <main class="bg-[#0f1422] border border-slate-800 rounded-xl overflow-hidden shadow-2xl">
63
+ <div class="px-6 py-4 bg-slate-900/40 border-b border-slate-800 flex items-center justify-between">
64
+ <h3 class="font-bold text-white text-sm tracking-wide uppercase">Active Spec Target Suite Telemetry (Click Row to View History)</h3>
65
+ <span id="runtime-clock" class="text-xs font-mono text-slate-500">Last updated: Never</span>
66
+ </div>
67
+ <div id="log-container" class="p-6 divide-y divide-slate-800/60 font-mono text-sm space-y-4"></div>
68
+ </main>
69
+
70
+ <section id="drilldown-panel" class="hidden bg-[#0f1422] border border-slate-700 rounded-xl p-6 space-y-4 shadow-2xl border-t-4 border-t-blue-500">
71
+ <div class="flex items-center justify-between">
72
+ <div>
73
+ <h4 id="drilldown-title" class="text-white font-bold font-sans text-base">Endpoint Performance Profile</h4>
74
+ <p id="drilldown-subtitle" class="text-xs text-slate-500 mt-0.5 font-mono"></p>
75
+ </div>
76
+ <button onclick="document.getElementById('drilldown-panel').classList.add('hidden')" class="text-xs tracking-wider border border-slate-800 bg-slate-900 text-slate-400 hover:text-white px-3 py-1 rounded">Close</button>
77
+ </div>
78
+ <div class="h-48 w-full relative">
79
+ <canvas id="endpointHistoryChart"></canvas>
80
+ </div>
81
+ </section>
82
+ </div>
83
+
84
+ <script>
85
+ const logContainer = document.getElementById('log-container');
86
+ let lineChartInstance = null, barChartInstance = null, endpointChartInstance = null;
87
+ let globalHistoryLedger = [];
88
+
89
+ function renderInterface(payload) {
90
+ if (!payload || !payload.latest) return;
91
+ const metrics = payload.latest;
92
+ globalHistoryLedger = payload.history || [];
93
+
94
+ document.getElementById('runtime-clock').innerText = `Last updated: ${new Date().toLocaleTimeString()}`;
95
+ document.getElementById('total-suites').innerText = metrics.numTotalTestSuites || 0;
96
+ document.getElementById('passed-tests').innerText = metrics.numPassedTests || 0;
97
+ document.getElementById('failed-tests').innerText = metrics.numFailedTests || 0;
98
+
99
+ // 1. Refresh System Line Trends Charts
100
+ if (lineChartInstance) {
101
+ lineChartInstance.data.labels = globalHistoryLedger.map(r => r.timestamp);
102
+ lineChartInstance.data.datasets[0].data = globalHistoryLedger.map(r => r.numPassedTests);
103
+ lineChartInstance.data.datasets[1].data = globalHistoryLedger.map(r => r.numFailedTests);
104
+ lineChartInstance.update();
105
+ }
106
+
107
+ // 2. Map Rows Log Console Matrix
108
+ logContainer.innerHTML = '';
109
+ let speedMetricsList = [];
110
+
111
+ metrics.testResults?.forEach(suite => {
112
+ suite.assertionResults?.forEach(assertion => {
113
+ const isPassed = assertion.status === 'passed';
114
+ const statusColor = isPassed ? 'text-emerald-400' : 'text-rose-500';
115
+ const row = document.createElement('div');
116
+
117
+ row.className = "pt-4 pb-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 border-b border-slate-800/40 last:border-0 hover:bg-slate-900/30 px-3 rounded-lg cursor-pointer transition";
118
+
119
+ // Click execution engine binder
120
+ row.onclick = () => showEndpointDrilldown(assertion.fullName);
121
+
122
+ speedMetricsList.push({
123
+ label: assertion.fullName.length > 15 ? assertion.fullName.substring(0, 14) + '..' : assertion.fullName,
124
+ duration: assertion.duration || 0
125
+ });
126
+
127
+ row.innerHTML = `
128
+ <div class="flex-1 min-w-0">
129
+ <span class="text-slate-200 font-sans font-medium block truncate">${assertion.fullName}</span>
130
+ <span class="text-xs text-slate-500 block mt-0.5 truncate">${suite.name}</span>
131
+ </div>
132
+ <span class="text-xs font-mono font-bold px-2 py-1 bg-slate-900 border border-slate-800 rounded ${statusColor}">
133
+ ${assertion.duration || 0}ms
134
+ </span>
135
+ `;
136
+ logContainer.appendChild(row);
137
+ });
138
+ });
139
+
140
+ // 3. Sync System Bar Performance Profile
141
+ speedMetricsList.sort((a, b) => b.duration - a.duration);
142
+ if (barChartInstance) {
143
+ barChartInstance.data.labels = speedMetricsList.slice(0, 5).map(i => i.label);
144
+ barChartInstance.data.datasets[0].data = speedMetricsList.slice(0, 5).map(i => i.duration);
145
+ barChartInstance.update();
146
+ }
147
+ }
148
+
149
+ // Extracts all matching historical latency targets for an item when clicked
150
+ function showEndpointDrilldown(fullName) {
151
+ const container = document.getElementById('drilldown-panel');
152
+ document.getElementById('drilldown-title').innerText = fullName;
153
+ document.getElementById('drilldown-subtitle').innerText = `Historical Latency Over Last ${globalHistoryLedger.length} Runs`;
154
+ container.classList.remove('hidden');
155
+
156
+ const historyPoints = globalHistoryLedger.map(run => {
157
+ let matchingAssertion = null;
158
+ run.testResults.forEach(s => {
159
+ s.assertionResults.forEach(a => {
160
+ if (a.fullName === fullName) matchingAssertion = a;
161
+ });
162
+ });
163
+ return {
164
+ timestamp: run.timestamp,
165
+ duration: matchingAssertion ? matchingAssertion.duration : 0
166
+ };
167
+ });
168
+
169
+ if (endpointChartInstance) endpointChartInstance.destroy();
170
+
171
+ const ctx = document.getElementById('endpointHistoryChart').getContext('2d');
172
+ endpointChartInstance = new Chart(ctx, {
173
+ type: 'line',
174
+ data: {
175
+ labels: historyPoints.map(p => p.timestamp),
176
+ datasets: [{
177
+ label: 'Duration (ms)',
178
+ data: historyPoints.map(p => p.duration),
179
+ borderColor: '#3b82f6',
180
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
181
+ fill: true,
182
+ tension: 0.1
183
+ }]
184
+ },
185
+ options: {
186
+ responsive: true,
187
+ maintainAspectRatio: false,
188
+ plugins: { legend: { display: false } },
189
+ scales: {
190
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
191
+ y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }, beginAtZero: true }
192
+ }
193
+ }
194
+ });
195
+ container.scrollIntoView({ behavior: 'smooth' });
196
+ }
197
+
198
+ // Chart Initialization Setup Loops
199
+ function initCharts() {
200
+ lineChartInstance = new Chart(document.getElementById('lineChart').getContext('2d'), {
201
+ type: 'line',
202
+ data: { labels: [], datasets: [{ label: 'Pass', data: [], borderColor: '#10b981', tension: 0.2 }, { label: 'Fail', data: [], borderColor: '#f43f5e', tension: 0.2 }] },
203
+ options: { responsive: true, maintainAspectRatio: false, scales: { x: { ticks: { color: '#64748b' } }, y: { beginAtZero: true, ticks: { color: '#64748b' } } } }
204
+ });
205
+
206
+ barChartInstance = new Chart(document.getElementById('barChart').getContext('2d'), {
207
+ type: 'bar',
208
+ data: { labels: [], datasets: [{ data: [], backgroundColor: '#3b82f6' }] },
209
+ options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true } } }
210
+ });
211
+ }
212
+
213
+ // Fetch snapshot directly on page layout render to completely prevent visual lag or empty states
214
+ initCharts();
215
+ fetch('/api/snapshot').then(r => r.json()).then(renderInterface);
216
+
217
+ // Connect the continuous stream pipeline
218
+ const eventSource = new EventSource('/stream');
219
+ eventSource.onopen = () => {
220
+ document.getElementById('status-dot').className = "w-2.5 h-2.5 rounded-full bg-emerald-500 animate-pulse";
221
+ document.getElementById('status-text').className = "text-xs font-mono font-bold text-emerald-400";
222
+ document.getElementById('status-text').innerText = "Live Streaming";
223
+ };
224
+ eventSource.onmessage = (e) => renderInterface(JSON.parse(e.data));
225
+ </script>
226
+ </body>
227
+ </html>
package/src/runner.js ADDED
@@ -0,0 +1,98 @@
1
+ const express = require('express');
2
+ const { exec } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const app = express();
7
+ const PORT = process.env.PORT || 3000;
8
+ const RESULTS_PATH = path.join(process.cwd(), 'test-results.json');
9
+
10
+ // Global Backend Memory Stores to persist data through browser refreshes
11
+ let currentTelemetryData = null;
12
+ let historicalRunsLedger = [];
13
+ const MAX_HISTORICAL_RUNS = 20;
14
+
15
+ // Helper to safely fetch, clean, and digest local metrics data
16
+ function loadTelemetryFromDisk() {
17
+ if (fs.existsSync(RESULTS_PATH)) {
18
+ try {
19
+ const rawData = fs.readFileSync(RESULTS_PATH, 'utf-8');
20
+ if (!rawData.trim()) return null;
21
+
22
+ const parsed = JSON.parse(rawData);
23
+ currentTelemetryData = parsed;
24
+
25
+ // Append runtime data into historical tracks matrix
26
+ const timestamp = new Date().toLocaleTimeString();
27
+ historicalRunsLedger.push({
28
+ timestamp,
29
+ numPassedTests: parsed.numPassedTests || 0,
30
+ numFailedTests: parsed.numFailedTests || 0,
31
+ testResults: parsed.testResults || []
32
+ });
33
+
34
+ // Bound size bounds
35
+ if (historicalRunsLedger.length > MAX_HISTORICAL_RUNS) {
36
+ historicalRunsLedger.shift();
37
+ }
38
+ return parsed;
39
+ } catch (e) {
40
+ return null;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+
46
+ // Bootstrap raw baseline load instantly on process boot
47
+ loadTelemetryFromDisk();
48
+
49
+ app.get('/', (req, res) => {
50
+ res.sendFile(path.join(__dirname, 'index.html'));
51
+ });
52
+
53
+ // REST API endpoint to pull absolute system memory instantly on reload
54
+ app.get('/api/snapshot', (req, res) => {
55
+ res.json({
56
+ latest: currentTelemetryData,
57
+ history: historicalRunsLedger
58
+ });
59
+ });
60
+
61
+ // Real-Time SSE Live Gate
62
+ app.get('/stream', (req, res) => {
63
+ res.setHeader('Content-Type', 'text/event-stream');
64
+ res.setHeader('Cache-Control', 'no-cache');
65
+ res.setHeader('Connection', 'keep-alive');
66
+
67
+ // Immediately stream back current cache frames so it isn't blank on connection hook
68
+ if (currentTelemetryData) {
69
+ res.write(`data: ${JSON.stringify({ latest: currentTelemetryData, history: historicalRunsLedger })}\n\n`);
70
+ }
71
+
72
+ const watcher = fs.watch(RESULTS_PATH, (eventType) => {
73
+ if (eventType === 'change') {
74
+ // Small timeout delay to let file write streams unlock safely
75
+ setTimeout(() => {
76
+ const freshlyLoaded = loadTelemetryFromDisk();
77
+ if (freshlyLoaded && !res.writableEnded) {
78
+ res.write(`data: ${JSON.stringify({ latest: freshlyLoaded, history: historicalRunsLedger })}\n\n`);
79
+ }
80
+ }, 150);
81
+ }
82
+ });
83
+
84
+ req.on('close', () => watcher.close());
85
+ });
86
+
87
+ function runTestPipeline() {
88
+ console.log(`\nšŸ•’ [${new Date().toLocaleTimeString()}] Triggering automated contract check...`);
89
+ const testExecution = exec('npx vitest run --reporter=default --reporter=json --outputFile=./test-results.json');
90
+ testExecution.stdout.on('data', (data) => process.stdout.write(data));
91
+ testExecution.stderr.on('data', (data) => process.stderr.write(data));
92
+ }
93
+
94
+ app.listen(PORT, () => {
95
+ console.log(`šŸ–„ļø super-api-tester Live Console: http://localhost:${PORT}`);
96
+ runTestPipeline();
97
+ setInterval(runTestPipeline, 120000); // Trigger every 5 minutes
98
+ });