fast-api-tester 1.0.4 ā 1.0.5
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/dist/apiEngine.d.ts +12 -26
- package/dist/apiEngine.d.ts.map +1 -1
- package/dist/apiEngine.js +144 -45
- package/dist/apiEngine.js.map +1 -1
- package/package.json +1 -1
package/dist/apiEngine.d.ts
CHANGED
|
@@ -21,9 +21,14 @@ export interface TestExecutionRecord {
|
|
|
21
21
|
errorMsg?: string;
|
|
22
22
|
timestamp: string;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
export interface RunSummaryHistory {
|
|
25
|
+
runId: string;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
total: number;
|
|
28
|
+
passed: number;
|
|
29
|
+
failed: number;
|
|
30
|
+
avgLatencyMs: number;
|
|
31
|
+
}
|
|
27
32
|
export declare class ApiResponse<T = unknown> implements ApiResponseData<T> {
|
|
28
33
|
statusCode: number;
|
|
29
34
|
headers: Record<string, string | string[] | undefined>;
|
|
@@ -31,52 +36,33 @@ export declare class ApiResponse<T = unknown> implements ApiResponseData<T> {
|
|
|
31
36
|
latencyMs: number;
|
|
32
37
|
lastExecutionRecord?: TestExecutionRecord;
|
|
33
38
|
constructor(data: ApiResponseData<T>);
|
|
34
|
-
/**
|
|
35
|
-
* Asserts that the response latency was strictly under the specified SLA limit (in ms).
|
|
36
|
-
* @throws Error if latency meets or exceeds the target SLA.
|
|
37
|
-
*/
|
|
38
39
|
expectLatencyUnder(maxLatencyMs: number): this;
|
|
39
40
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Fluent API Test Client with Automatic HTML Reporting
|
|
42
|
-
*/
|
|
43
41
|
export declare class ApiEngine {
|
|
44
42
|
private baseUrl;
|
|
45
43
|
private defaultHeaders;
|
|
46
44
|
private records;
|
|
47
45
|
private reportOutputPath;
|
|
46
|
+
private historyFilePath;
|
|
48
47
|
private autoReportEnabled;
|
|
49
|
-
/**
|
|
50
|
-
* Set the base URL for subsequent calls
|
|
51
|
-
*/
|
|
52
48
|
setBaseUrl(url: string): this;
|
|
53
|
-
/**
|
|
54
|
-
* Add default headers (e.g., Bearer tokens) across all requests
|
|
55
|
-
*/
|
|
56
49
|
setHeader(key: string, value: string): this;
|
|
57
|
-
/**
|
|
58
|
-
* Configure HTML reporting output path or disable it
|
|
59
|
-
*/
|
|
60
50
|
configureReport(options: {
|
|
61
51
|
enabled?: boolean;
|
|
62
52
|
outputPath?: string;
|
|
53
|
+
historyPath?: string;
|
|
63
54
|
}): this;
|
|
64
55
|
get<T = unknown>(path: string, options?: Omit<RequestOptions, 'body'>): Promise<ApiResponse<T>>;
|
|
65
56
|
post<T = unknown>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<ApiResponse<T>>;
|
|
66
57
|
put<T = unknown>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<ApiResponse<T>>;
|
|
67
58
|
patch<T = unknown>(path: string, body?: unknown, options?: Omit<RequestOptions, 'body'>): Promise<ApiResponse<T>>;
|
|
68
59
|
delete<T = unknown>(path: string, options?: Omit<RequestOptions, 'body'>): Promise<ApiResponse<T>>;
|
|
69
|
-
/**
|
|
70
|
-
* Core request runner using low-level undici.Pool
|
|
71
|
-
*/
|
|
72
60
|
request<T = unknown>(method: HttpMethod, path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
73
61
|
/**
|
|
74
|
-
*
|
|
62
|
+
* Persists current run to history file and returns all past execution history
|
|
75
63
|
*/
|
|
64
|
+
private updateAndGetHistory;
|
|
76
65
|
private generateHtmlReport;
|
|
77
|
-
/**
|
|
78
|
-
* Gracefully close connection pools & write auto-generated HTML report
|
|
79
|
-
*/
|
|
80
66
|
destroy(): Promise<void>;
|
|
81
67
|
}
|
|
82
68
|
//# sourceMappingURL=apiEngine.d.ts.map
|
package/dist/apiEngine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiEngine.d.ts","sourceRoot":"","sources":["../src/apiEngine.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,EAAE,CAAC,CAAC;IACR,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED
|
|
1
|
+
{"version":3,"file":"apiEngine.d.ts","sourceRoot":"","sources":["../src/apiEngine.ts"],"names":[],"mappings":"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"}
|
package/dist/apiEngine.js
CHANGED
|
@@ -2,15 +2,11 @@ import { Pool } from 'undici';
|
|
|
2
2
|
import { URL } from 'node:url';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
/**
|
|
6
|
-
* Fluent API Response class supporting chained SLA assertions
|
|
7
|
-
*/
|
|
8
5
|
export class ApiResponse {
|
|
9
6
|
statusCode;
|
|
10
7
|
headers;
|
|
11
8
|
body;
|
|
12
9
|
latencyMs;
|
|
13
|
-
// Internal reference for auto-recording SLA assertion outcomes
|
|
14
10
|
lastExecutionRecord;
|
|
15
11
|
constructor(data) {
|
|
16
12
|
this.statusCode = data.statusCode;
|
|
@@ -18,10 +14,6 @@ export class ApiResponse {
|
|
|
18
14
|
this.body = data.body;
|
|
19
15
|
this.latencyMs = data.latencyMs;
|
|
20
16
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Asserts that the response latency was strictly under the specified SLA limit (in ms).
|
|
23
|
-
* @throws Error if latency meets or exceeds the target SLA.
|
|
24
|
-
*/
|
|
25
17
|
expectLatencyUnder(maxLatencyMs) {
|
|
26
18
|
if (this.lastExecutionRecord) {
|
|
27
19
|
this.lastExecutionRecord.slaLimitMs = maxLatencyMs;
|
|
@@ -37,9 +29,6 @@ export class ApiResponse {
|
|
|
37
29
|
return this;
|
|
38
30
|
}
|
|
39
31
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Global connection pool map to keep sockets alive across tests
|
|
42
|
-
*/
|
|
43
32
|
const poolRegistry = new Map();
|
|
44
33
|
function getOrCreatePool(origin) {
|
|
45
34
|
let pool = poolRegistry.get(origin);
|
|
@@ -53,9 +42,6 @@ function getOrCreatePool(origin) {
|
|
|
53
42
|
}
|
|
54
43
|
return pool;
|
|
55
44
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Fluent API Test Client with Automatic HTML Reporting
|
|
58
|
-
*/
|
|
59
45
|
export class ApiEngine {
|
|
60
46
|
baseUrl = '';
|
|
61
47
|
defaultHeaders = {
|
|
@@ -63,32 +49,25 @@ export class ApiEngine {
|
|
|
63
49
|
};
|
|
64
50
|
records = [];
|
|
65
51
|
reportOutputPath = 'test-report.html';
|
|
52
|
+
historyFilePath = '.test-history.json';
|
|
66
53
|
autoReportEnabled = true;
|
|
67
|
-
/**
|
|
68
|
-
* Set the base URL for subsequent calls
|
|
69
|
-
*/
|
|
70
54
|
setBaseUrl(url) {
|
|
71
55
|
this.baseUrl = url.endsWith('/') ? url.slice(0, -1) : url;
|
|
72
56
|
return this;
|
|
73
57
|
}
|
|
74
|
-
/**
|
|
75
|
-
* Add default headers (e.g., Bearer tokens) across all requests
|
|
76
|
-
*/
|
|
77
58
|
setHeader(key, value) {
|
|
78
59
|
this.defaultHeaders[key.toLowerCase()] = value;
|
|
79
60
|
return this;
|
|
80
61
|
}
|
|
81
|
-
/**
|
|
82
|
-
* Configure HTML reporting output path or disable it
|
|
83
|
-
*/
|
|
84
62
|
configureReport(options) {
|
|
85
63
|
if (options.enabled !== undefined)
|
|
86
64
|
this.autoReportEnabled = options.enabled;
|
|
87
65
|
if (options.outputPath)
|
|
88
66
|
this.reportOutputPath = options.outputPath;
|
|
67
|
+
if (options.historyPath)
|
|
68
|
+
this.historyFilePath = options.historyPath;
|
|
89
69
|
return this;
|
|
90
70
|
}
|
|
91
|
-
// --- HTTP Verb Shortcuts ---
|
|
92
71
|
get(path, options) {
|
|
93
72
|
return this.request('GET', path, options);
|
|
94
73
|
}
|
|
@@ -104,24 +83,18 @@ export class ApiEngine {
|
|
|
104
83
|
delete(path, options) {
|
|
105
84
|
return this.request('DELETE', path, options);
|
|
106
85
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Core request runner using low-level undici.Pool
|
|
109
|
-
*/
|
|
110
86
|
async request(method, path, options = {}) {
|
|
111
87
|
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
|
112
88
|
const targetUrl = new URL(`${this.baseUrl}${fullPath}`);
|
|
113
|
-
// Append query parameters
|
|
114
89
|
if (options.query) {
|
|
115
90
|
Object.entries(options.query).forEach(([key, val]) => {
|
|
116
91
|
targetUrl.searchParams.append(key, String(val));
|
|
117
92
|
});
|
|
118
93
|
}
|
|
119
|
-
// Merge headers
|
|
120
94
|
const headers = {
|
|
121
95
|
...this.defaultHeaders,
|
|
122
96
|
...(options.headers || {}),
|
|
123
97
|
};
|
|
124
|
-
// Serialize body if present
|
|
125
98
|
let requestBody = undefined;
|
|
126
99
|
if (options.body !== undefined) {
|
|
127
100
|
if (typeof options.body === 'object') {
|
|
@@ -136,7 +109,6 @@ export class ApiEngine {
|
|
|
136
109
|
}
|
|
137
110
|
const pool = getOrCreatePool(targetUrl.origin);
|
|
138
111
|
const startTime = process.hrtime.bigint();
|
|
139
|
-
// High-performance direct pool dispatch
|
|
140
112
|
const response = await pool.request({
|
|
141
113
|
path: `${targetUrl.pathname}${targetUrl.search}`,
|
|
142
114
|
method: method,
|
|
@@ -145,10 +117,8 @@ export class ApiEngine {
|
|
|
145
117
|
headersTimeout: options.timeoutMs ?? 10_000,
|
|
146
118
|
bodyTimeout: options.timeoutMs ?? 10_000,
|
|
147
119
|
});
|
|
148
|
-
// Calculate latency in milliseconds
|
|
149
120
|
const endTime = process.hrtime.bigint();
|
|
150
121
|
const latencyMs = Number(endTime - startTime) / 1_000_000;
|
|
151
|
-
// Parse response body
|
|
152
122
|
const rawText = await response.body.text();
|
|
153
123
|
let parsedBody;
|
|
154
124
|
try {
|
|
@@ -159,7 +129,6 @@ export class ApiEngine {
|
|
|
159
129
|
}
|
|
160
130
|
const formattedLatency = Number(latencyMs.toFixed(2));
|
|
161
131
|
const passed = response.statusCode >= 200 && response.statusCode < 400;
|
|
162
|
-
// Track execution for auto report
|
|
163
132
|
const record = {
|
|
164
133
|
method,
|
|
165
134
|
endpoint: `${targetUrl.pathname}${targetUrl.search}`,
|
|
@@ -169,7 +138,6 @@ export class ApiEngine {
|
|
|
169
138
|
timestamp: new Date().toLocaleTimeString(),
|
|
170
139
|
};
|
|
171
140
|
this.records.push(record);
|
|
172
|
-
// Wrap output in the fluent ApiResponse class instance
|
|
173
141
|
const apiResponse = new ApiResponse({
|
|
174
142
|
statusCode: response.statusCode,
|
|
175
143
|
headers: response.headers,
|
|
@@ -180,15 +148,45 @@ export class ApiEngine {
|
|
|
180
148
|
return apiResponse;
|
|
181
149
|
}
|
|
182
150
|
/**
|
|
183
|
-
*
|
|
151
|
+
* Persists current run to history file and returns all past execution history
|
|
184
152
|
*/
|
|
153
|
+
updateAndGetHistory(currentSummary) {
|
|
154
|
+
const historyFile = path.resolve(this.historyFilePath);
|
|
155
|
+
let history = [];
|
|
156
|
+
if (fs.existsSync(historyFile)) {
|
|
157
|
+
try {
|
|
158
|
+
const content = fs.readFileSync(historyFile, 'utf-8');
|
|
159
|
+
history = JSON.parse(content);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
history = [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Add current run and keep last 10 executions
|
|
166
|
+
history.push(currentSummary);
|
|
167
|
+
if (history.length > 10) {
|
|
168
|
+
history = history.slice(-10);
|
|
169
|
+
}
|
|
170
|
+
fs.writeFileSync(historyFile, JSON.stringify(history, null, 2), 'utf-8');
|
|
171
|
+
return history;
|
|
172
|
+
}
|
|
185
173
|
generateHtmlReport() {
|
|
186
174
|
if (this.records.length === 0)
|
|
187
175
|
return;
|
|
188
176
|
const total = this.records.length;
|
|
189
177
|
const passed = this.records.filter((r) => r.passed).length;
|
|
190
178
|
const failed = total - passed;
|
|
191
|
-
const avgLatency = (this.records.reduce((acc, r) => acc + r.latencyMs, 0) / total).toFixed(1);
|
|
179
|
+
const avgLatency = Number((this.records.reduce((acc, r) => acc + r.latencyMs, 0) / total).toFixed(1));
|
|
180
|
+
const runTimestamp = new Date().toLocaleString();
|
|
181
|
+
const currentRunSummary = {
|
|
182
|
+
runId: `#${Date.now().toString().slice(-4)}`,
|
|
183
|
+
timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
184
|
+
total,
|
|
185
|
+
passed,
|
|
186
|
+
failed,
|
|
187
|
+
avgLatencyMs: avgLatency,
|
|
188
|
+
};
|
|
189
|
+
const historyData = this.updateAndGetHistory(currentRunSummary);
|
|
192
190
|
const tableRows = this.records
|
|
193
191
|
.map((r) => `
|
|
194
192
|
<tr>
|
|
@@ -203,65 +201,166 @@ export class ApiEngine {
|
|
|
203
201
|
</span>
|
|
204
202
|
${r.errorMsg ? `<div class="error-msg">${r.errorMsg}</div>` : ''}
|
|
205
203
|
</td>
|
|
204
|
+
</tr>`)
|
|
205
|
+
.join('');
|
|
206
|
+
const historyRows = historyData
|
|
207
|
+
.map((h) => `
|
|
208
|
+
<tr>
|
|
209
|
+
<td><strong>${h.runId}</strong></td>
|
|
210
|
+
<td>${h.timestamp}</td>
|
|
211
|
+
<td>${h.total}</td>
|
|
212
|
+
<td style="color: #16a34a; font-weight: bold;">${h.passed}</td>
|
|
213
|
+
<td style="color: #dc2626; font-weight: bold;">${h.failed}</td>
|
|
214
|
+
<td>${h.avgLatencyMs} ms</td>
|
|
206
215
|
</tr>`)
|
|
207
216
|
.join('');
|
|
208
217
|
const html = `<!DOCTYPE html>
|
|
209
218
|
<html lang="en">
|
|
210
219
|
<head>
|
|
211
220
|
<meta charset="UTF-8">
|
|
212
|
-
<title>fast-api-tester Execution Summary</title>
|
|
221
|
+
<title>fast-api-tester Analytics & Execution Summary</title>
|
|
222
|
+
<!-- CDN load Chart.js -->
|
|
223
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
213
224
|
<style>
|
|
214
225
|
*, *::before, *::after { box-sizing: border-box; }
|
|
215
226
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f8fafc; color: #1e293b; padding: 30px; margin: 0; }
|
|
216
227
|
.header { background: #0f172a; color: #fff; padding: 24px 28px; border-radius: 8px; margin-bottom: 24px; }
|
|
217
228
|
.header h1 { margin: 0 0 6px 0; font-size: 20pt; color: #38bdf8; }
|
|
218
229
|
.header p { margin: 0; color: #94a3b8; font-size: 10pt; }
|
|
230
|
+
|
|
219
231
|
.summary-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
|
|
220
232
|
.card { background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 18px; }
|
|
221
233
|
.card-label { font-size: 8pt; text-transform: uppercase; color: #64748b; font-weight: 600; margin-bottom: 4px; }
|
|
222
234
|
.card-value { font-size: 20pt; font-weight: 700; color: #0f172a; }
|
|
223
235
|
.card-value.pass { color: #16a34a; } .card-value.fail { color: #dc2626; } .card-value.sla { color: #2563eb; }
|
|
224
|
-
|
|
236
|
+
|
|
237
|
+
.charts-container { display: grid; grid-template-columns: 1fr 2fr; gap: 20px; margin-bottom: 24px; }
|
|
238
|
+
.chart-box { background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; height: 320px; }
|
|
239
|
+
.chart-box h3 { margin-top: 0; font-size: 11pt; color: #475569; margin-bottom: 15px; }
|
|
240
|
+
|
|
241
|
+
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; border: 1px solid #e2e8f0; overflow: hidden; margin-bottom: 24px; }
|
|
225
242
|
th { background: #f1f5f9; color: #475569; text-align: left; padding: 12px 14px; font-size: 9pt; text-transform: uppercase; border-bottom: 1px solid #e2e8f0; }
|
|
226
243
|
td { padding: 12px 14px; border-bottom: 1px solid #f1f5f9; font-size: 9.5pt; vertical-align: top; }
|
|
244
|
+
|
|
227
245
|
.badge { padding: 3px 8px; border-radius: 4px; font-size: 8pt; font-weight: 700; text-transform: uppercase; }
|
|
228
246
|
.badge-get { background: #e0f2fe; color: #0369a1; }
|
|
229
247
|
.badge-post { background: #dcfce7; color: #15803d; }
|
|
230
248
|
.badge-put { background: #fef3c7; color: #b45309; }
|
|
231
249
|
.badge-patch { background: #f3e8ff; color: #6b21a8; }
|
|
232
250
|
.badge-delete { background: #fee2e2; color: #b91c1c; }
|
|
251
|
+
|
|
233
252
|
.status-tag { font-weight: 700; padding: 3px 8px; border-radius: 4px; font-size: 8pt; display: inline-block; }
|
|
234
253
|
.status-pass { background: #dcfce7; color: #166534; }
|
|
235
254
|
.status-fail { background: #fee2e2; color: #991b1b; }
|
|
236
255
|
.error-msg { margin-top: 6px; font-family: monospace; font-size: 8.5pt; color: #dc2626; background: #fef2f2; padding: 6px 8px; border-radius: 4px; }
|
|
256
|
+
|
|
257
|
+
.section-title { font-size: 13pt; color: #0f172a; margin-bottom: 12px; }
|
|
237
258
|
</style>
|
|
238
259
|
</head>
|
|
239
260
|
<body>
|
|
240
261
|
<div class="header">
|
|
241
|
-
<h1>ā” fast-api-tester Execution Summary</h1>
|
|
242
|
-
<p>Generated on ${
|
|
262
|
+
<h1>ā” fast-api-tester Analytics & Execution Summary</h1>
|
|
263
|
+
<p>Generated on ${runTimestamp}</p>
|
|
243
264
|
</div>
|
|
265
|
+
|
|
244
266
|
<div class="summary-grid">
|
|
245
267
|
<div class="card"><div class="card-label">Total Requests</div><div class="card-value">${total}</div></div>
|
|
246
268
|
<div class="card"><div class="card-label">Passed</div><div class="card-value pass">${passed}</div></div>
|
|
247
269
|
<div class="card"><div class="card-label">Failed</div><div class="card-value fail">${failed}</div></div>
|
|
248
270
|
<div class="card"><div class="card-label">Avg Latency</div><div class="card-value sla">${avgLatency} ms</div></div>
|
|
249
271
|
</div>
|
|
272
|
+
|
|
273
|
+
<!-- Visual Analytics Section -->
|
|
274
|
+
<div class="charts-container">
|
|
275
|
+
<div class="chart-box">
|
|
276
|
+
<h3>Current Run Status (Pie Chart)</h3>
|
|
277
|
+
<div style="height: 230px; position: relative;">
|
|
278
|
+
<canvas id="pieChart"></canvas>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
<div class="chart-box">
|
|
282
|
+
<h3>Historical Execution Trend (Pass vs Fail)</h3>
|
|
283
|
+
<div style="height: 230px; position: relative;">
|
|
284
|
+
<canvas id="barChart"></canvas>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<h2 class="section-title">Current Test Suite Results</h2>
|
|
250
290
|
<table>
|
|
251
291
|
<thead>
|
|
252
292
|
<tr><th>Method</th><th>Endpoint</th><th>Status</th><th>Latency</th><th>SLA Target</th><th>Result</th></tr>
|
|
253
293
|
</thead>
|
|
254
294
|
<tbody>${tableRows}</tbody>
|
|
255
295
|
</table>
|
|
296
|
+
|
|
297
|
+
<h2 class="section-title">Historical Executions (Last 10 Runs)</h2>
|
|
298
|
+
<table>
|
|
299
|
+
<thead>
|
|
300
|
+
<tr><th>Run ID</th><th>Time</th><th>Total Tests</th><th>Passed</th><th>Failed</th><th>Avg Latency</th></tr>
|
|
301
|
+
</thead>
|
|
302
|
+
<tbody>${historyRows}</tbody>
|
|
303
|
+
</table>
|
|
304
|
+
|
|
305
|
+
<script>
|
|
306
|
+
// 1. Chart.js Pie Chart for Current Run
|
|
307
|
+
new Chart(document.getElementById('pieChart'), {
|
|
308
|
+
type: 'pie',
|
|
309
|
+
data: {
|
|
310
|
+
labels: ['Passed', 'Failed'],
|
|
311
|
+
datasets: [{
|
|
312
|
+
data: [${passed}, ${failed}],
|
|
313
|
+
backgroundColor: ['#22c55e', '#ef4444'],
|
|
314
|
+
borderWidth: 1
|
|
315
|
+
}]
|
|
316
|
+
},
|
|
317
|
+
options: {
|
|
318
|
+
responsive: true,
|
|
319
|
+
maintainAspectRatio: false,
|
|
320
|
+
plugins: {
|
|
321
|
+
legend: { position: 'bottom' }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// 2. Chart.js Bar Chart for Historical Trends
|
|
327
|
+
const historyData = ${JSON.stringify(historyData)};
|
|
328
|
+
new Chart(document.getElementById('barChart'), {
|
|
329
|
+
type: 'bar',
|
|
330
|
+
data: {
|
|
331
|
+
labels: historyData.map(h => h.runId + ' (' + h.timestamp + ')'),
|
|
332
|
+
datasets: [
|
|
333
|
+
{
|
|
334
|
+
label: 'Passed',
|
|
335
|
+
data: historyData.map(h => h.passed),
|
|
336
|
+
backgroundColor: '#22c55e',
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
label: 'Failed',
|
|
340
|
+
data: historyData.map(h => h.failed),
|
|
341
|
+
backgroundColor: '#ef4444',
|
|
342
|
+
}
|
|
343
|
+
]
|
|
344
|
+
},
|
|
345
|
+
options: {
|
|
346
|
+
responsive: true,
|
|
347
|
+
maintainAspectRatio: false,
|
|
348
|
+
scales: {
|
|
349
|
+
x: { stacked: true },
|
|
350
|
+
y: { stacked: true, beginAtZero: true }
|
|
351
|
+
},
|
|
352
|
+
plugins: {
|
|
353
|
+
legend: { position: 'bottom' }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
</script>
|
|
256
358
|
</body>
|
|
257
359
|
</html>`;
|
|
258
360
|
const fullPath = path.resolve(this.reportOutputPath);
|
|
259
361
|
fs.writeFileSync(fullPath, html, 'utf-8');
|
|
260
|
-
console.log(`\nš
|
|
362
|
+
console.log(`\nš Visual report generated: ${fullPath}`);
|
|
261
363
|
}
|
|
262
|
-
/**
|
|
263
|
-
* Gracefully close connection pools & write auto-generated HTML report
|
|
264
|
-
*/
|
|
265
364
|
async destroy() {
|
|
266
365
|
if (this.autoReportEnabled) {
|
|
267
366
|
this.generateHtmlReport();
|
package/dist/apiEngine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apiEngine.js","sourceRoot":"","sources":["../src/apiEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"apiEngine.js","sourceRoot":"","sources":["../src/apiEngine.ts"],"names":[],"mappings":"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"}
|