@testsmith/perfornium 0.1.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.
Files changed (164) hide show
  1. package/README.md +360 -0
  2. package/dist/cli/cli.d.ts +2 -0
  3. package/dist/cli/cli.js +192 -0
  4. package/dist/cli/commands/distributed.d.ts +11 -0
  5. package/dist/cli/commands/distributed.js +179 -0
  6. package/dist/cli/commands/import.d.ts +23 -0
  7. package/dist/cli/commands/import.js +461 -0
  8. package/dist/cli/commands/init.d.ts +7 -0
  9. package/dist/cli/commands/init.js +923 -0
  10. package/dist/cli/commands/mock.d.ts +7 -0
  11. package/dist/cli/commands/mock.js +281 -0
  12. package/dist/cli/commands/report.d.ts +5 -0
  13. package/dist/cli/commands/report.js +70 -0
  14. package/dist/cli/commands/run.d.ts +12 -0
  15. package/dist/cli/commands/run.js +260 -0
  16. package/dist/cli/commands/validate.d.ts +3 -0
  17. package/dist/cli/commands/validate.js +35 -0
  18. package/dist/cli/commands/worker.d.ts +27 -0
  19. package/dist/cli/commands/worker.js +320 -0
  20. package/dist/config/index.d.ts +2 -0
  21. package/dist/config/index.js +20 -0
  22. package/dist/config/parser.d.ts +19 -0
  23. package/dist/config/parser.js +330 -0
  24. package/dist/config/types/global-config.d.ts +74 -0
  25. package/dist/config/types/global-config.js +2 -0
  26. package/dist/config/types/hooks.d.ts +58 -0
  27. package/dist/config/types/hooks.js +3 -0
  28. package/dist/config/types/import-types.d.ts +33 -0
  29. package/dist/config/types/import-types.js +2 -0
  30. package/dist/config/types/index.d.ts +11 -0
  31. package/dist/config/types/index.js +27 -0
  32. package/dist/config/types/load-config.d.ts +32 -0
  33. package/dist/config/types/load-config.js +9 -0
  34. package/dist/config/types/output-config.d.ts +10 -0
  35. package/dist/config/types/output-config.js +2 -0
  36. package/dist/config/types/report-config.d.ts +10 -0
  37. package/dist/config/types/report-config.js +2 -0
  38. package/dist/config/types/runtime-types.d.ts +6 -0
  39. package/dist/config/types/runtime-types.js +2 -0
  40. package/dist/config/types/scenario-config.d.ts +30 -0
  41. package/dist/config/types/scenario-config.js +2 -0
  42. package/dist/config/types/step-types.d.ts +139 -0
  43. package/dist/config/types/step-types.js +2 -0
  44. package/dist/config/types/test-configuration.d.ts +18 -0
  45. package/dist/config/types/test-configuration.js +2 -0
  46. package/dist/config/types/worker-config.d.ts +12 -0
  47. package/dist/config/types/worker-config.js +2 -0
  48. package/dist/config/validator.d.ts +19 -0
  49. package/dist/config/validator.js +198 -0
  50. package/dist/core/csv-data-provider.d.ts +47 -0
  51. package/dist/core/csv-data-provider.js +265 -0
  52. package/dist/core/hooks-manager.d.ts +33 -0
  53. package/dist/core/hooks-manager.js +129 -0
  54. package/dist/core/index.d.ts +5 -0
  55. package/dist/core/index.js +11 -0
  56. package/dist/core/script-executor.d.ts +14 -0
  57. package/dist/core/script-executor.js +290 -0
  58. package/dist/core/step-executor.d.ts +41 -0
  59. package/dist/core/step-executor.js +680 -0
  60. package/dist/core/test-runner.d.ts +34 -0
  61. package/dist/core/test-runner.js +465 -0
  62. package/dist/core/threshold-evaluator.d.ts +43 -0
  63. package/dist/core/threshold-evaluator.js +170 -0
  64. package/dist/core/virtual-user-pool.d.ts +42 -0
  65. package/dist/core/virtual-user-pool.js +136 -0
  66. package/dist/core/virtual-user.d.ts +51 -0
  67. package/dist/core/virtual-user.js +488 -0
  68. package/dist/distributed/coordinator.d.ts +34 -0
  69. package/dist/distributed/coordinator.js +158 -0
  70. package/dist/distributed/health-monitor.d.ts +18 -0
  71. package/dist/distributed/health-monitor.js +72 -0
  72. package/dist/distributed/load-distributor.d.ts +17 -0
  73. package/dist/distributed/load-distributor.js +106 -0
  74. package/dist/distributed/remote-worker.d.ts +37 -0
  75. package/dist/distributed/remote-worker.js +241 -0
  76. package/dist/distributed/result-aggregator.d.ts +43 -0
  77. package/dist/distributed/result-aggregator.js +146 -0
  78. package/dist/dsl/index.d.ts +3 -0
  79. package/dist/dsl/index.js +11 -0
  80. package/dist/dsl/test-builder.d.ts +111 -0
  81. package/dist/dsl/test-builder.js +514 -0
  82. package/dist/importers/har-importer.d.ts +17 -0
  83. package/dist/importers/har-importer.js +172 -0
  84. package/dist/importers/open-api-importer.d.ts +23 -0
  85. package/dist/importers/open-api-importer.js +181 -0
  86. package/dist/importers/wsdl-importer.d.ts +42 -0
  87. package/dist/importers/wsdl-importer.js +440 -0
  88. package/dist/index.d.ts +5 -0
  89. package/dist/index.js +17 -0
  90. package/dist/load-patterns/arrivals.d.ts +7 -0
  91. package/dist/load-patterns/arrivals.js +118 -0
  92. package/dist/load-patterns/base.d.ts +9 -0
  93. package/dist/load-patterns/base.js +2 -0
  94. package/dist/load-patterns/basic.d.ts +7 -0
  95. package/dist/load-patterns/basic.js +117 -0
  96. package/dist/load-patterns/stepping.d.ts +6 -0
  97. package/dist/load-patterns/stepping.js +122 -0
  98. package/dist/metrics/collector.d.ts +72 -0
  99. package/dist/metrics/collector.js +662 -0
  100. package/dist/metrics/types.d.ts +135 -0
  101. package/dist/metrics/types.js +2 -0
  102. package/dist/outputs/base.d.ts +7 -0
  103. package/dist/outputs/base.js +2 -0
  104. package/dist/outputs/csv.d.ts +13 -0
  105. package/dist/outputs/csv.js +163 -0
  106. package/dist/outputs/graphite.d.ts +13 -0
  107. package/dist/outputs/graphite.js +126 -0
  108. package/dist/outputs/influxdb.d.ts +12 -0
  109. package/dist/outputs/influxdb.js +82 -0
  110. package/dist/outputs/json.d.ts +14 -0
  111. package/dist/outputs/json.js +107 -0
  112. package/dist/outputs/streaming-csv.d.ts +37 -0
  113. package/dist/outputs/streaming-csv.js +254 -0
  114. package/dist/outputs/streaming-json.d.ts +43 -0
  115. package/dist/outputs/streaming-json.js +353 -0
  116. package/dist/outputs/webhook.d.ts +16 -0
  117. package/dist/outputs/webhook.js +96 -0
  118. package/dist/protocols/base.d.ts +33 -0
  119. package/dist/protocols/base.js +2 -0
  120. package/dist/protocols/rest/handler.d.ts +67 -0
  121. package/dist/protocols/rest/handler.js +776 -0
  122. package/dist/protocols/soap/handler.d.ts +12 -0
  123. package/dist/protocols/soap/handler.js +165 -0
  124. package/dist/protocols/web/core-web-vitals.d.ts +121 -0
  125. package/dist/protocols/web/core-web-vitals.js +373 -0
  126. package/dist/protocols/web/handler.d.ts +50 -0
  127. package/dist/protocols/web/handler.js +706 -0
  128. package/dist/recorder/native-recorder.d.ts +14 -0
  129. package/dist/recorder/native-recorder.js +533 -0
  130. package/dist/recorder/scenario-recorder.d.ts +55 -0
  131. package/dist/recorder/scenario-recorder.js +296 -0
  132. package/dist/reporting/constants.d.ts +94 -0
  133. package/dist/reporting/constants.js +82 -0
  134. package/dist/reporting/enhanced-html-generator.d.ts +55 -0
  135. package/dist/reporting/enhanced-html-generator.js +965 -0
  136. package/dist/reporting/generator.d.ts +42 -0
  137. package/dist/reporting/generator.js +1217 -0
  138. package/dist/reporting/statistics.d.ts +144 -0
  139. package/dist/reporting/statistics.js +742 -0
  140. package/dist/reporting/templates/enhanced-report.hbs +2812 -0
  141. package/dist/reporting/templates/html.hbs +2453 -0
  142. package/dist/utils/faker-manager.d.ts +55 -0
  143. package/dist/utils/faker-manager.js +166 -0
  144. package/dist/utils/file-manager.d.ts +33 -0
  145. package/dist/utils/file-manager.js +154 -0
  146. package/dist/utils/handlebars-manager.d.ts +42 -0
  147. package/dist/utils/handlebars-manager.js +172 -0
  148. package/dist/utils/logger.d.ts +16 -0
  149. package/dist/utils/logger.js +46 -0
  150. package/dist/utils/template.d.ts +80 -0
  151. package/dist/utils/template.js +513 -0
  152. package/dist/utils/test-output-writer.d.ts +56 -0
  153. package/dist/utils/test-output-writer.js +643 -0
  154. package/dist/utils/time.d.ts +3 -0
  155. package/dist/utils/time.js +23 -0
  156. package/dist/utils/timestamp-helper.d.ts +17 -0
  157. package/dist/utils/timestamp-helper.js +53 -0
  158. package/dist/workers/manager.d.ts +18 -0
  159. package/dist/workers/manager.js +95 -0
  160. package/dist/workers/server.d.ts +21 -0
  161. package/dist/workers/server.js +205 -0
  162. package/dist/workers/worker.d.ts +19 -0
  163. package/dist/workers/worker.js +147 -0
  164. package/package.json +102 -0
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ScenarioRecorder = void 0;
37
+ const events_1 = require("events");
38
+ const fs_1 = require("fs");
39
+ const yaml = __importStar(require("yaml"));
40
+ const logger_1 = require("../utils/logger");
41
+ class ScenarioRecorder extends events_1.EventEmitter {
42
+ constructor() {
43
+ super(...arguments);
44
+ this.recording = false;
45
+ this.currentScenario = null;
46
+ this.recordedRequests = [];
47
+ this.startTime = 0;
48
+ this.extractionRules = new Map();
49
+ }
50
+ startRecording(scenarioName, description) {
51
+ if (this.recording) {
52
+ throw new Error('Recording already in progress');
53
+ }
54
+ this.recording = true;
55
+ this.startTime = Date.now();
56
+ this.recordedRequests = [];
57
+ this.currentScenario = {
58
+ name: scenarioName,
59
+ description,
60
+ steps: []
61
+ };
62
+ logger_1.logger.info(`🔴 Started recording scenario: ${scenarioName}`);
63
+ this.emit('recording:started', scenarioName);
64
+ }
65
+ recordRequest(request) {
66
+ if (!this.recording) {
67
+ return;
68
+ }
69
+ request.timestamp = Date.now() - this.startTime;
70
+ this.recordedRequests.push(request);
71
+ this.detectVariables(request);
72
+ this.detectExtractions(request);
73
+ this.emit('request:recorded', request);
74
+ logger_1.logger.debug(`📝 Recorded ${request.method} ${request.path}`);
75
+ }
76
+ detectVariables(request) {
77
+ if (!this.currentScenario)
78
+ return;
79
+ const variables = {};
80
+ const urlPattern = /\{([^}]+)\}/g;
81
+ let match;
82
+ while ((match = urlPattern.exec(request.path)) !== null) {
83
+ variables[match[1]] = `{{${match[1]}}}`;
84
+ }
85
+ if (request.headers?.['Authorization']) {
86
+ variables['auth_token'] = request.headers['Authorization'];
87
+ }
88
+ if (Object.keys(variables).length > 0) {
89
+ this.currentScenario.variables = {
90
+ ...this.currentScenario.variables,
91
+ ...variables
92
+ };
93
+ }
94
+ }
95
+ detectExtractions(request) {
96
+ if (!request.response?.data || !this.currentScenario)
97
+ return;
98
+ const extractions = [];
99
+ const commonFields = [
100
+ 'id', 'token', 'access_token', 'refresh_token',
101
+ 'session_id', 'sessionId', 'user_id', 'userId'
102
+ ];
103
+ if (typeof request.response.data === 'object') {
104
+ for (const field of commonFields) {
105
+ if (this.hasNestedField(request.response.data, field)) {
106
+ const path = this.findJsonPath(request.response.data, field);
107
+ if (path) {
108
+ extractions.push({
109
+ from: request.path,
110
+ name: field,
111
+ expression: path,
112
+ type: 'json_path'
113
+ });
114
+ }
115
+ }
116
+ }
117
+ }
118
+ if (request.response.headers?.['set-cookie']) {
119
+ extractions.push({
120
+ from: request.path,
121
+ name: 'session_cookie',
122
+ expression: 'set-cookie',
123
+ type: 'cookie'
124
+ });
125
+ }
126
+ if (extractions.length > 0) {
127
+ this.currentScenario.extractions = [
128
+ ...(this.currentScenario.extractions || []),
129
+ ...extractions
130
+ ];
131
+ }
132
+ }
133
+ hasNestedField(obj, field) {
134
+ if (!obj || typeof obj !== 'object')
135
+ return false;
136
+ if (field in obj)
137
+ return true;
138
+ for (const value of Object.values(obj)) {
139
+ if (this.hasNestedField(value, field))
140
+ return true;
141
+ }
142
+ return false;
143
+ }
144
+ findJsonPath(obj, field, path = '$') {
145
+ if (!obj || typeof obj !== 'object')
146
+ return null;
147
+ if (field in obj) {
148
+ return `${path}.${field}`;
149
+ }
150
+ for (const [key, value] of Object.entries(obj)) {
151
+ const result = this.findJsonPath(value, field, `${path}.${key}`);
152
+ if (result)
153
+ return result;
154
+ }
155
+ return null;
156
+ }
157
+ stopRecording() {
158
+ if (!this.recording || !this.currentScenario) {
159
+ return null;
160
+ }
161
+ this.recording = false;
162
+ this.currentScenario.steps = this.optimizeSteps(this.recordedRequests);
163
+ const scenario = this.currentScenario;
164
+ this.currentScenario = null;
165
+ logger_1.logger.info(`⏹️ Stopped recording. Captured ${scenario.steps.length} steps`);
166
+ this.emit('recording:stopped', scenario);
167
+ return scenario;
168
+ }
169
+ optimizeSteps(requests) {
170
+ const optimized = [];
171
+ const seenUrls = new Set();
172
+ for (const request of requests) {
173
+ const urlKey = `${request.method}_${request.path}`;
174
+ if (!seenUrls.has(urlKey) || this.isImportantRequest(request)) {
175
+ optimized.push(this.cleanRequest(request));
176
+ seenUrls.add(urlKey);
177
+ }
178
+ }
179
+ return optimized;
180
+ }
181
+ isImportantRequest(request) {
182
+ const importantMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];
183
+ return importantMethods.includes(request.method);
184
+ }
185
+ cleanRequest(request) {
186
+ const cleaned = {
187
+ timestamp: request.timestamp,
188
+ method: request.method,
189
+ url: request.url,
190
+ path: request.path
191
+ };
192
+ const skipHeaders = [
193
+ 'user-agent', 'accept-encoding', 'connection',
194
+ 'content-length', 'host', 'cache-control'
195
+ ];
196
+ if (request.headers) {
197
+ cleaned.headers = Object.fromEntries(Object.entries(request.headers)
198
+ .filter(([key]) => !skipHeaders.includes(key.toLowerCase())));
199
+ }
200
+ if (request.params && Object.keys(request.params).length > 0) {
201
+ cleaned.params = request.params;
202
+ }
203
+ if (request.body) {
204
+ cleaned.body = request.body;
205
+ }
206
+ if (request.response) {
207
+ cleaned.response = {
208
+ status: request.response.status,
209
+ statusText: request.response.statusText,
210
+ duration: request.response.duration
211
+ };
212
+ }
213
+ return cleaned;
214
+ }
215
+ exportScenario(scenario, format) {
216
+ switch (format) {
217
+ case 'yaml':
218
+ return this.exportAsYAML(scenario);
219
+ case 'typescript':
220
+ return this.exportAsTypeScript(scenario);
221
+ case 'json':
222
+ return JSON.stringify(this.convertToTestConfig(scenario), null, 2);
223
+ default:
224
+ throw new Error(`Unsupported format: ${format}`);
225
+ }
226
+ }
227
+ exportAsYAML(scenario) {
228
+ const config = this.convertToTestConfig(scenario);
229
+ return yaml.stringify(config);
230
+ }
231
+ exportAsTypeScript(scenario) {
232
+ const config = this.convertToTestConfig(scenario);
233
+ return `import { TestConfiguration } from 'perfornium';
234
+
235
+ export const config: TestConfiguration = ${JSON.stringify(config, null, 2)
236
+ .replace(/"([^"]+)":/g, '$1:')
237
+ .replace(/"/g, "'")};
238
+
239
+ export default config;`;
240
+ }
241
+ convertToTestConfig(scenario) {
242
+ const steps = scenario.steps.map(step => ({
243
+ name: `${step.method} ${step.path}`,
244
+ type: 'rest',
245
+ method: step.method,
246
+ path: step.path,
247
+ ...(step.headers && { headers: step.headers }),
248
+ ...(step.params && { params: step.params }),
249
+ ...(step.body && {
250
+ [typeof step.body === 'object' ? 'json' : 'body']: step.body
251
+ }),
252
+ ...(scenario.extractions && {
253
+ extract: scenario.extractions
254
+ .filter(e => e.from === step.path)
255
+ .map(e => ({
256
+ name: e.name,
257
+ type: e.type,
258
+ expression: e.expression
259
+ }))
260
+ })
261
+ }));
262
+ return {
263
+ name: scenario.name,
264
+ description: scenario.description,
265
+ ...(scenario.baseURL && {
266
+ global: {
267
+ base_url: scenario.baseURL
268
+ }
269
+ }),
270
+ scenarios: [{
271
+ name: scenario.name,
272
+ ...(scenario.variables && { variables: scenario.variables }),
273
+ steps
274
+ }],
275
+ load: {
276
+ pattern: 'basic',
277
+ vus: 1,
278
+ duration: '1m'
279
+ }
280
+ };
281
+ }
282
+ saveToFile(scenario, filename, format) {
283
+ const content = this.exportScenario(scenario, format);
284
+ const extension = format === 'typescript' ? 'ts' : format;
285
+ const fullPath = filename.endsWith(`.${extension}`) ? filename : `${filename}.${extension}`;
286
+ (0, fs_1.writeFileSync)(fullPath, content);
287
+ logger_1.logger.success(`💾 Saved scenario to ${fullPath}`);
288
+ }
289
+ isRecording() {
290
+ return this.recording;
291
+ }
292
+ getRecordedRequestsCount() {
293
+ return this.recordedRequests.length;
294
+ }
295
+ }
296
+ exports.ScenarioRecorder = ScenarioRecorder;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Reporting and metrics constants
3
+ * Centralized configuration for all magic numbers
4
+ */
5
+ export declare const TIME_BUCKETS: {
6
+ readonly FINE: 1000;
7
+ readonly MEDIUM: 5000;
8
+ readonly COARSE: 10000;
9
+ };
10
+ export declare const PERCENTILES: {
11
+ STANDARD: number[];
12
+ EXTENDED: number[];
13
+ };
14
+ export declare const APDEX_DEFAULTS: {
15
+ readonly SATISFIED_THRESHOLD: 500;
16
+ readonly TOLERATING_MULTIPLIER: 4;
17
+ };
18
+ export declare const SLA_DEFAULTS: {
19
+ SUCCESS_RATE: number;
20
+ AVG_RESPONSE_TIME: number;
21
+ P95_RESPONSE_TIME: number;
22
+ P99_RESPONSE_TIME: number;
23
+ MIN_REQUESTS_PER_SECOND: number;
24
+ };
25
+ export declare const WEB_VITALS_THRESHOLDS: {
26
+ readonly LCP: {
27
+ readonly good: 2500;
28
+ readonly poor: 4000;
29
+ };
30
+ readonly FID: {
31
+ readonly good: 100;
32
+ readonly poor: 300;
33
+ };
34
+ readonly CLS: {
35
+ readonly good: 0.1;
36
+ readonly poor: 0.25;
37
+ };
38
+ readonly FCP: {
39
+ readonly good: 1800;
40
+ readonly poor: 3000;
41
+ };
42
+ readonly TTFB: {
43
+ readonly good: 800;
44
+ readonly poor: 1800;
45
+ };
46
+ readonly TTI: {
47
+ readonly good: 3800;
48
+ readonly poor: 7300;
49
+ };
50
+ readonly TBT: {
51
+ readonly good: 200;
52
+ readonly poor: 600;
53
+ };
54
+ readonly SPEED_INDEX: {
55
+ readonly good: 3400;
56
+ readonly poor: 5800;
57
+ };
58
+ readonly INP: {
59
+ readonly good: 200;
60
+ readonly poor: 500;
61
+ };
62
+ };
63
+ export declare const OUTLIER_DETECTION: {
64
+ readonly IQR_MULTIPLIER: 1.5;
65
+ readonly IQR_EXTREME_MULTIPLIER: 3;
66
+ readonly Z_SCORE_THRESHOLD: 3;
67
+ };
68
+ export declare const CONFIDENCE_INTERVALS: {
69
+ readonly LEVELS: readonly [0.9, 0.95, 0.99];
70
+ readonly DEFAULT_LEVEL: 0.95;
71
+ };
72
+ export declare const HEATMAP: {
73
+ readonly TIME_BUCKETS: 50;
74
+ readonly RESPONSE_TIME_BUCKETS: 20;
75
+ };
76
+ export declare const REPORT_SETTINGS: {
77
+ readonly MAX_RAW_DATA_ROWS: 1000;
78
+ readonly CHART_DATA_POINTS_LIMIT: 500;
79
+ readonly BATCH_SIZE: 10;
80
+ readonly FLUSH_INTERVAL: 5000;
81
+ };
82
+ export declare const STATUS_THRESHOLDS: {
83
+ readonly SUCCESS_RATE_GOOD: 99;
84
+ readonly SUCCESS_RATE_WARNING: 95;
85
+ };
86
+ export declare const RESPONSE_TIME_FIELD: "response_time";
87
+ export declare const DURATION_FIELD: "duration";
88
+ /**
89
+ * Get response time from a result, preferring response_time over duration
90
+ */
91
+ export declare function getResponseTime(result: {
92
+ response_time?: number;
93
+ duration?: number;
94
+ }): number;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * Reporting and metrics constants
4
+ * Centralized configuration for all magic numbers
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DURATION_FIELD = exports.RESPONSE_TIME_FIELD = exports.STATUS_THRESHOLDS = exports.REPORT_SETTINGS = exports.HEATMAP = exports.CONFIDENCE_INTERVALS = exports.OUTLIER_DETECTION = exports.WEB_VITALS_THRESHOLDS = exports.SLA_DEFAULTS = exports.APDEX_DEFAULTS = exports.PERCENTILES = exports.TIME_BUCKETS = void 0;
8
+ exports.getResponseTime = getResponseTime;
9
+ // Time bucket sizes (milliseconds)
10
+ exports.TIME_BUCKETS = {
11
+ FINE: 1000, // 1 second - for detailed analysis
12
+ MEDIUM: 5000, // 5 seconds - for timeline charts
13
+ COARSE: 10000, // 10 seconds - for trend analysis
14
+ };
15
+ // Standard percentiles to calculate
16
+ exports.PERCENTILES = {
17
+ STANDARD: [50, 90, 95, 99],
18
+ EXTENDED: [50, 90, 95, 99, 99.9, 99.99],
19
+ };
20
+ // Apdex score thresholds (in milliseconds)
21
+ exports.APDEX_DEFAULTS = {
22
+ SATISFIED_THRESHOLD: 500, // Requests under this are "satisfied"
23
+ TOLERATING_MULTIPLIER: 4, // Requests under threshold * 4 are "tolerating"
24
+ };
25
+ // SLA default thresholds
26
+ exports.SLA_DEFAULTS = {
27
+ SUCCESS_RATE: 95.0, // Minimum success rate percentage
28
+ AVG_RESPONSE_TIME: 2000, // Maximum average response time (ms)
29
+ P95_RESPONSE_TIME: 5000, // Maximum P95 response time (ms)
30
+ P99_RESPONSE_TIME: 10000, // Maximum P99 response time (ms)
31
+ MIN_REQUESTS_PER_SECOND: 0.1, // Minimum throughput (very low default - user should configure)
32
+ };
33
+ // Web Vitals thresholds (from Google)
34
+ exports.WEB_VITALS_THRESHOLDS = {
35
+ LCP: { good: 2500, poor: 4000 }, // Largest Contentful Paint (ms)
36
+ FID: { good: 100, poor: 300 }, // First Input Delay (ms)
37
+ CLS: { good: 0.1, poor: 0.25 }, // Cumulative Layout Shift (score)
38
+ FCP: { good: 1800, poor: 3000 }, // First Contentful Paint (ms)
39
+ TTFB: { good: 800, poor: 1800 }, // Time to First Byte (ms)
40
+ TTI: { good: 3800, poor: 7300 }, // Time to Interactive (ms)
41
+ TBT: { good: 200, poor: 600 }, // Total Blocking Time (ms)
42
+ SPEED_INDEX: { good: 3400, poor: 5800 }, // Speed Index (ms)
43
+ INP: { good: 200, poor: 500 }, // Interaction to Next Paint (ms)
44
+ };
45
+ // Outlier detection thresholds
46
+ exports.OUTLIER_DETECTION = {
47
+ IQR_MULTIPLIER: 1.5, // Standard IQR multiplier for mild outliers
48
+ IQR_EXTREME_MULTIPLIER: 3.0, // IQR multiplier for extreme outliers
49
+ Z_SCORE_THRESHOLD: 3.0, // Z-score threshold for outliers
50
+ };
51
+ // Confidence interval settings
52
+ exports.CONFIDENCE_INTERVALS = {
53
+ LEVELS: [0.90, 0.95, 0.99], // 90%, 95%, 99% confidence levels
54
+ DEFAULT_LEVEL: 0.95, // Default confidence level
55
+ };
56
+ // Heatmap settings
57
+ exports.HEATMAP = {
58
+ TIME_BUCKETS: 50, // Number of time buckets
59
+ RESPONSE_TIME_BUCKETS: 20, // Number of response time buckets
60
+ };
61
+ // Report generation settings
62
+ exports.REPORT_SETTINGS = {
63
+ MAX_RAW_DATA_ROWS: 1000, // Maximum raw data rows to include
64
+ CHART_DATA_POINTS_LIMIT: 500, // Maximum data points per chart
65
+ BATCH_SIZE: 10, // Default batch size for metrics collection
66
+ FLUSH_INTERVAL: 5000, // Default flush interval (ms)
67
+ };
68
+ // Status thresholds for visual indicators
69
+ exports.STATUS_THRESHOLDS = {
70
+ SUCCESS_RATE_GOOD: 99, // Green status
71
+ SUCCESS_RATE_WARNING: 95, // Yellow status
72
+ // Below WARNING is red/error status
73
+ };
74
+ // Response time field to use (standardized)
75
+ exports.RESPONSE_TIME_FIELD = 'response_time';
76
+ exports.DURATION_FIELD = 'duration';
77
+ /**
78
+ * Get response time from a result, preferring response_time over duration
79
+ */
80
+ function getResponseTime(result) {
81
+ return result.response_time ?? result.duration ?? 0;
82
+ }
@@ -0,0 +1,55 @@
1
+ import { TestResult, MetricsSummary } from '../metrics/types';
2
+ export interface HTMLReportConfig {
3
+ title?: string;
4
+ description?: string;
5
+ includeCharts?: boolean;
6
+ includeTimeline?: boolean;
7
+ includeErrorDetails?: boolean;
8
+ includeResponseTimes?: boolean;
9
+ templatePath?: string;
10
+ assetsInline?: boolean;
11
+ darkMode?: boolean;
12
+ sla?: {
13
+ successRate?: number;
14
+ avgResponseTime?: number;
15
+ p95ResponseTime?: number;
16
+ p99ResponseTime?: number;
17
+ minThroughput?: number;
18
+ };
19
+ apdexThreshold?: number;
20
+ }
21
+ export interface HTMLReportData {
22
+ testName: string;
23
+ summary: MetricsSummary;
24
+ results?: TestResult[];
25
+ config?: any;
26
+ metadata?: {
27
+ generated_at: string;
28
+ generated_by: string;
29
+ test_duration: string;
30
+ [key: string]: any;
31
+ };
32
+ }
33
+ export declare class EnhancedHTMLReportGenerator {
34
+ private config;
35
+ private templateCache;
36
+ private readonly DEFAULT_CONFIG;
37
+ constructor(config?: HTMLReportConfig);
38
+ private setupHandlebarsHelpers;
39
+ generate(data: HTMLReportData, filePath: string): Promise<string>;
40
+ private prepareReportData;
41
+ private prepareChartData;
42
+ private prepareTimelineData;
43
+ private prepareErrorAnalysis;
44
+ private prepareResponseTimeAnalysis;
45
+ private createResponseTimeHistogram;
46
+ private prepareWebVitalsCharts;
47
+ private getMetricColor;
48
+ private analyzeByPage;
49
+ private calculateAverageScore;
50
+ private generateColors;
51
+ private loadTemplate;
52
+ private getDefaultTemplatePath;
53
+ private formatDuration;
54
+ private getDefaultTemplate;
55
+ }