@testsmith/perfornium 0.6.4 → 0.6.6

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 (194) hide show
  1. package/dist/cli/cli.js +16 -1
  2. package/dist/cli/commands/distributed.js +2 -2
  3. package/dist/cli/commands/report.js +2 -2
  4. package/dist/cli/commands/run.js +2 -0
  5. package/dist/config/parser.js +2 -2
  6. package/dist/config/types/global-config.d.ts +82 -2
  7. package/dist/config/types/scenario-config.d.ts +2 -2
  8. package/dist/config/types/step-types.d.ts +1 -1
  9. package/dist/core/data/data-manager.d.ts +70 -0
  10. package/dist/core/data/data-manager.js +186 -0
  11. package/dist/core/data/data-provider.d.ts +85 -0
  12. package/dist/core/data/data-provider.js +468 -0
  13. package/dist/core/data/index.d.ts +8 -0
  14. package/dist/core/data/index.js +13 -0
  15. package/dist/core/execution/check-evaluator.d.ts +10 -0
  16. package/dist/core/execution/check-evaluator.js +79 -0
  17. package/dist/core/execution/data-extractor.d.ts +6 -0
  18. package/dist/core/execution/data-extractor.js +70 -0
  19. package/dist/core/execution/index.d.ts +3 -0
  20. package/dist/core/execution/index.js +9 -0
  21. package/dist/core/execution/json-payload-processor.d.ts +7 -0
  22. package/dist/core/execution/json-payload-processor.js +140 -0
  23. package/dist/core/factories/index.d.ts +2 -0
  24. package/dist/core/factories/index.js +7 -0
  25. package/dist/core/factories/output-handler-factory.d.ts +10 -0
  26. package/dist/core/factories/output-handler-factory.js +91 -0
  27. package/dist/core/factories/protocol-handler-factory.d.ts +12 -0
  28. package/dist/core/factories/protocol-handler-factory.js +96 -0
  29. package/dist/core/index.d.ts +3 -2
  30. package/dist/core/index.js +8 -3
  31. package/dist/core/reporting/dashboard-reporter.d.ts +17 -0
  32. package/dist/core/reporting/dashboard-reporter.js +127 -0
  33. package/dist/core/reporting/index.d.ts +1 -0
  34. package/dist/core/reporting/index.js +5 -0
  35. package/dist/core/step-executor.d.ts +6 -20
  36. package/dist/core/step-executor.js +72 -366
  37. package/dist/core/strategies/index.d.ts +2 -0
  38. package/dist/core/strategies/index.js +7 -0
  39. package/dist/core/strategies/scenario-selector.d.ts +13 -0
  40. package/dist/core/strategies/scenario-selector.js +37 -0
  41. package/dist/core/strategies/think-time-strategy.d.ts +15 -0
  42. package/dist/core/strategies/think-time-strategy.js +71 -0
  43. package/dist/core/test-runner.d.ts +4 -11
  44. package/dist/core/test-runner.js +105 -312
  45. package/dist/core/virtual-user.d.ts +7 -37
  46. package/dist/core/virtual-user.js +29 -269
  47. package/dist/dashboard/routes/api.d.ts +64 -0
  48. package/dist/dashboard/routes/api.js +569 -0
  49. package/dist/dashboard/routes/index.d.ts +2 -0
  50. package/dist/dashboard/routes/index.js +7 -0
  51. package/dist/dashboard/routes/static.d.ts +6 -0
  52. package/dist/dashboard/routes/static.js +76 -0
  53. package/dist/dashboard/server.d.ts +8 -84
  54. package/dist/dashboard/server.js +76 -2007
  55. package/dist/dashboard/services/file-scanner.d.ts +7 -0
  56. package/dist/dashboard/services/file-scanner.js +114 -0
  57. package/dist/dashboard/services/index.d.ts +5 -0
  58. package/dist/dashboard/services/index.js +13 -0
  59. package/dist/dashboard/services/influxdb-service.d.ts +41 -0
  60. package/dist/dashboard/services/influxdb-service.js +329 -0
  61. package/dist/dashboard/services/metrics-parser.d.ts +12 -0
  62. package/dist/dashboard/services/metrics-parser.js +209 -0
  63. package/dist/dashboard/services/results-manager.d.ts +17 -0
  64. package/dist/dashboard/services/results-manager.js +311 -0
  65. package/dist/dashboard/services/test-executor.d.ts +41 -0
  66. package/dist/dashboard/services/test-executor.js +250 -0
  67. package/dist/dashboard/services/workers-manager.d.ts +13 -0
  68. package/dist/dashboard/services/workers-manager.js +81 -0
  69. package/dist/dashboard/templates/index.html +122 -0
  70. package/dist/dashboard/templates/scripts/main.js +3280 -0
  71. package/dist/dashboard/templates/styles.css +402 -0
  72. package/dist/dashboard/types.d.ts +168 -0
  73. package/dist/dashboard/types.js +2 -0
  74. package/dist/distributed/result-aggregator.js +1 -3
  75. package/dist/metrics/batch/batch-processor.d.ts +27 -0
  76. package/dist/metrics/batch/batch-processor.js +85 -0
  77. package/dist/metrics/batch/index.d.ts +1 -0
  78. package/dist/metrics/batch/index.js +5 -0
  79. package/dist/metrics/collector.d.ts +46 -45
  80. package/dist/metrics/collector.js +179 -640
  81. package/dist/metrics/core/error-tracker.d.ts +9 -0
  82. package/dist/metrics/core/error-tracker.js +52 -0
  83. package/dist/metrics/core/index.d.ts +3 -0
  84. package/dist/metrics/core/index.js +9 -0
  85. package/dist/metrics/core/result-storage.d.ts +19 -0
  86. package/dist/metrics/core/result-storage.js +56 -0
  87. package/dist/metrics/core/statistics-engine.d.ts +27 -0
  88. package/dist/metrics/core/statistics-engine.js +91 -0
  89. package/dist/metrics/output/file-writer.d.ts +19 -0
  90. package/dist/metrics/output/file-writer.js +129 -0
  91. package/dist/metrics/output/index.d.ts +2 -0
  92. package/dist/metrics/output/index.js +10 -0
  93. package/dist/metrics/output/influxdb-writer.d.ts +89 -0
  94. package/dist/metrics/output/influxdb-writer.js +404 -0
  95. package/dist/metrics/realtime/dispatcher.d.ts +18 -0
  96. package/dist/metrics/realtime/dispatcher.js +45 -0
  97. package/dist/metrics/realtime/endpoints/graphite.d.ts +3 -0
  98. package/dist/metrics/realtime/endpoints/graphite.js +61 -0
  99. package/dist/metrics/realtime/endpoints/influxdb.d.ts +3 -0
  100. package/dist/metrics/realtime/endpoints/influxdb.js +35 -0
  101. package/dist/metrics/realtime/endpoints/webhook.d.ts +3 -0
  102. package/dist/metrics/realtime/endpoints/webhook.js +22 -0
  103. package/dist/metrics/realtime/endpoints/websocket.d.ts +3 -0
  104. package/dist/metrics/realtime/endpoints/websocket.js +25 -0
  105. package/dist/metrics/realtime/index.d.ts +5 -0
  106. package/dist/metrics/realtime/index.js +13 -0
  107. package/dist/metrics/reporting/index.d.ts +3 -0
  108. package/dist/metrics/reporting/index.js +9 -0
  109. package/dist/metrics/reporting/step-statistics.d.ts +6 -0
  110. package/dist/metrics/reporting/step-statistics.js +59 -0
  111. package/dist/metrics/reporting/summary-generator.d.ts +16 -0
  112. package/dist/metrics/reporting/summary-generator.js +46 -0
  113. package/dist/metrics/reporting/timeline-calculator.d.ts +7 -0
  114. package/dist/metrics/reporting/timeline-calculator.js +86 -0
  115. package/dist/metrics/types.d.ts +58 -0
  116. package/dist/outputs/csv.d.ts +2 -0
  117. package/dist/outputs/csv.js +21 -2
  118. package/dist/outputs/json.js +6 -2
  119. package/dist/protocols/rest/handler.d.ts +4 -53
  120. package/dist/protocols/rest/handler.js +73 -454
  121. package/dist/protocols/rest/request/auth-handler.d.ts +4 -0
  122. package/dist/protocols/rest/request/auth-handler.js +30 -0
  123. package/dist/protocols/rest/request/body-processor.d.ts +11 -0
  124. package/dist/protocols/rest/request/body-processor.js +62 -0
  125. package/dist/protocols/rest/request/index.d.ts +2 -0
  126. package/dist/protocols/rest/request/index.js +7 -0
  127. package/dist/protocols/rest/response/checks.d.ts +6 -0
  128. package/dist/protocols/rest/response/checks.js +71 -0
  129. package/dist/protocols/rest/response/index.d.ts +2 -0
  130. package/dist/protocols/rest/response/index.js +7 -0
  131. package/dist/protocols/rest/response/size-calculator.d.ts +12 -0
  132. package/dist/protocols/rest/response/size-calculator.js +64 -0
  133. package/dist/protocols/web/browser/highlight.d.ts +7 -0
  134. package/dist/protocols/web/browser/highlight.js +47 -0
  135. package/dist/protocols/web/browser/index.d.ts +4 -0
  136. package/dist/protocols/web/browser/index.js +11 -0
  137. package/dist/protocols/web/browser/manager.d.ts +20 -0
  138. package/dist/protocols/web/browser/manager.js +189 -0
  139. package/dist/protocols/web/browser/screenshot.d.ts +8 -0
  140. package/dist/protocols/web/browser/screenshot.js +69 -0
  141. package/dist/protocols/web/browser/storage.d.ts +5 -0
  142. package/dist/protocols/web/browser/storage.js +45 -0
  143. package/dist/protocols/web/commands/index.d.ts +5 -0
  144. package/dist/protocols/web/commands/index.js +11 -0
  145. package/dist/protocols/web/commands/interaction.d.ts +13 -0
  146. package/dist/protocols/web/commands/interaction.js +68 -0
  147. package/dist/protocols/web/commands/measurement.d.ts +16 -0
  148. package/dist/protocols/web/commands/measurement.js +33 -0
  149. package/dist/protocols/web/commands/navigation.d.ts +11 -0
  150. package/dist/protocols/web/commands/navigation.js +43 -0
  151. package/dist/protocols/web/commands/types.d.ts +12 -0
  152. package/dist/protocols/web/commands/types.js +2 -0
  153. package/dist/protocols/web/commands/verification.d.ts +12 -0
  154. package/dist/protocols/web/commands/verification.js +118 -0
  155. package/dist/protocols/web/handler.d.ts +19 -30
  156. package/dist/protocols/web/handler.js +164 -651
  157. package/dist/protocols/web/network/capture.d.ts +19 -0
  158. package/dist/protocols/web/network/capture.js +225 -0
  159. package/dist/protocols/web/network/filters.d.ts +5 -0
  160. package/dist/protocols/web/network/filters.js +49 -0
  161. package/dist/protocols/web/network/index.d.ts +4 -0
  162. package/dist/protocols/web/network/index.js +9 -0
  163. package/dist/protocols/web/network/types.d.ts +13 -0
  164. package/dist/protocols/web/network/types.js +2 -0
  165. package/dist/protocols/web/network/utils.d.ts +8 -0
  166. package/dist/protocols/web/network/utils.js +29 -0
  167. package/dist/recorder/continue-recorder.d.ts +11 -0
  168. package/dist/recorder/continue-recorder.js +872 -0
  169. package/dist/reporting/chart-data/index.d.ts +5 -0
  170. package/dist/reporting/chart-data/index.js +13 -0
  171. package/dist/reporting/chart-data/network.d.ts +25 -0
  172. package/dist/reporting/chart-data/network.js +78 -0
  173. package/dist/reporting/chart-data/scenario.d.ts +37 -0
  174. package/dist/reporting/chart-data/scenario.js +76 -0
  175. package/dist/reporting/chart-data/step-statistics.d.ts +24 -0
  176. package/dist/reporting/chart-data/step-statistics.js +94 -0
  177. package/dist/reporting/chart-data/throughput.d.ts +16 -0
  178. package/dist/reporting/chart-data/throughput.js +24 -0
  179. package/dist/reporting/chart-data/timeline.d.ts +17 -0
  180. package/dist/reporting/chart-data/timeline.js +46 -0
  181. package/dist/reporting/handlebars-helpers.d.ts +1 -0
  182. package/dist/reporting/handlebars-helpers.js +63 -0
  183. package/dist/reporting/{enhanced-html-generator.d.ts → html-generator.d.ts} +1 -1
  184. package/dist/reporting/{enhanced-html-generator.js → html-generator.js} +10 -7
  185. package/dist/reporting/templates/{enhanced-report.hbs → report.hbs} +9 -9
  186. package/dist/utils/data-utils.d.ts +17 -0
  187. package/dist/utils/data-utils.js +129 -0
  188. package/dist/utils/template.js +2 -2
  189. package/package.json +5 -2
  190. package/dist/core/csv-data-provider.d.ts +0 -47
  191. package/dist/core/csv-data-provider.js +0 -265
  192. package/dist/reporting/generator.d.ts +0 -42
  193. package/dist/reporting/generator.js +0 -1217
  194. package/dist/reporting/templates/html.hbs +0 -2453
@@ -0,0 +1,402 @@
1
+ :root {
2
+ --bg-primary: #0f0f23;
3
+ --bg-secondary: #1a1a2e;
4
+ --bg-card: rgba(255, 255, 255, 0.03);
5
+ --border: rgba(255, 255, 255, 0.1);
6
+ --text-primary: #e2e8f0;
7
+ --text-secondary: #9ca3af;
8
+ --accent-cyan: #00d4ff;
9
+ --accent-purple: #9c40ff;
10
+ --success: #22c55e;
11
+ --warning: #eab308;
12
+ --error: #ef4444;
13
+ }
14
+
15
+ * { margin: 0; padding: 0; box-sizing: border-box; }
16
+
17
+ body {
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
19
+ background: var(--bg-primary);
20
+ color: var(--text-primary);
21
+ min-height: 100vh;
22
+ }
23
+
24
+ /* Header */
25
+ .header {
26
+ background: linear-gradient(135deg, rgba(0, 212, 255, 0.1), rgba(156, 64, 255, 0.1));
27
+ border-bottom: 1px solid var(--border);
28
+ padding: 16px 24px;
29
+ display: flex;
30
+ justify-content: space-between;
31
+ align-items: center;
32
+ position: sticky;
33
+ top: 0;
34
+ z-index: 100;
35
+ backdrop-filter: blur(10px);
36
+ }
37
+
38
+ .logo {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 12px;
42
+ font-size: 22px;
43
+ font-weight: 700;
44
+ color: white;
45
+ }
46
+
47
+ .logo svg { width: 36px; height: 36px; }
48
+
49
+ /* Container */
50
+ .container { max-width: 1800px; margin: 0 auto; padding: 24px; }
51
+
52
+ /* Tabs */
53
+ .tabs { display: flex; gap: 8px; margin-bottom: 24px; flex-wrap: wrap; }
54
+
55
+ .tab {
56
+ padding: 10px 20px;
57
+ background: var(--bg-card);
58
+ border: 1px solid var(--border);
59
+ border-radius: 8px;
60
+ color: var(--text-secondary);
61
+ cursor: pointer;
62
+ transition: all 0.2s;
63
+ font-size: 14px;
64
+ font-weight: 500;
65
+ }
66
+
67
+ .tab:hover { border-color: var(--accent-cyan); color: white; }
68
+ .tab.active {
69
+ background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple));
70
+ border-color: transparent;
71
+ color: white;
72
+ }
73
+
74
+ /* Panels */
75
+ .panel { display: none; }
76
+ .panel.active { display: block; }
77
+
78
+ /* Grid layouts */
79
+ .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; grid-auto-flow: dense; }
80
+ .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; grid-auto-flow: dense; }
81
+ .grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; grid-auto-flow: dense; }
82
+ .grid-6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px; grid-auto-flow: dense; }
83
+
84
+ @media (max-width: 1400px) { .grid-6 { grid-template-columns: repeat(3, 1fr); } }
85
+ @media (max-width: 1200px) { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
86
+ @media (max-width: 900px) {
87
+ .grid-2, .grid-3 { grid-template-columns: 1fr; }
88
+ .grid-4, .grid-6 { grid-template-columns: repeat(2, 1fr); }
89
+ }
90
+
91
+ /* Cards */
92
+ .card {
93
+ background: var(--bg-card);
94
+ border: 1px solid var(--border);
95
+ border-radius: 12px;
96
+ padding: 20px;
97
+ margin-bottom: 20px;
98
+ }
99
+
100
+ .card-header {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ margin-bottom: 16px;
105
+ }
106
+
107
+ .card h3 { font-size: 16px; font-weight: 600; color: var(--accent-cyan); }
108
+ .card-full { grid-column: 1 / -1; }
109
+
110
+ /* Metric cards */
111
+ .metric-card {
112
+ background: var(--bg-secondary);
113
+ border: 1px solid var(--border);
114
+ border-radius: 10px;
115
+ padding: 16px;
116
+ text-align: center;
117
+ }
118
+
119
+ .metric-card .value {
120
+ font-size: 28px;
121
+ font-weight: 700;
122
+ background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple));
123
+ -webkit-background-clip: text;
124
+ -webkit-text-fill-color: transparent;
125
+ background-clip: text;
126
+ }
127
+
128
+ .metric-card .label {
129
+ font-size: 12px;
130
+ color: var(--text-secondary);
131
+ margin-top: 4px;
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.5px;
134
+ }
135
+
136
+ .metric-card .change { font-size: 11px; margin-top: 4px; }
137
+ .metric-card .change.up { color: var(--error); }
138
+ .metric-card .change.down { color: var(--success); }
139
+
140
+ /* Charts */
141
+ .chart-container { position: relative; height: 280px; width: 100%; }
142
+ .chart-container.tall { height: 380px; }
143
+ .chart-container.short { height: 200px; }
144
+ .chart-container canvas { display: block; }
145
+
146
+ /* Expandable chart cards */
147
+ .card.expandable {
148
+ position: relative;
149
+ }
150
+ .card.expandable .expand-btn {
151
+ position: absolute;
152
+ top: 12px;
153
+ right: 12px;
154
+ width: 28px;
155
+ height: 28px;
156
+ padding: 0;
157
+ background: var(--bg-secondary);
158
+ border: 1px solid var(--border);
159
+ border-radius: 6px;
160
+ color: var(--text-secondary);
161
+ cursor: pointer;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ opacity: 0;
166
+ transition: opacity 0.2s, background 0.2s, color 0.2s;
167
+ z-index: 10;
168
+ }
169
+ .card.expandable:hover .expand-btn {
170
+ opacity: 1;
171
+ }
172
+ .card.expandable .expand-btn:hover {
173
+ background: var(--bg-tertiary);
174
+ color: var(--text-primary);
175
+ border-color: var(--primary);
176
+ }
177
+ .card.expandable .expand-btn svg {
178
+ width: 16px;
179
+ height: 16px;
180
+ }
181
+ /* Chart Modal */
182
+ .chart-modal-overlay {
183
+ position: fixed;
184
+ top: 0;
185
+ left: 0;
186
+ right: 0;
187
+ bottom: 0;
188
+ background: rgba(0, 0, 0, 0.85);
189
+ z-index: 1000;
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ padding: 40px;
194
+ opacity: 0;
195
+ visibility: hidden;
196
+ transition: opacity 0.2s, visibility 0.2s;
197
+ }
198
+ .chart-modal-overlay.active {
199
+ opacity: 1;
200
+ visibility: visible;
201
+ }
202
+ .chart-modal {
203
+ background: var(--bg-primary);
204
+ border-radius: 12px;
205
+ width: 90vw;
206
+ max-width: 1400px;
207
+ max-height: 85vh;
208
+ display: flex;
209
+ flex-direction: column;
210
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
211
+ }
212
+ .chart-modal-header {
213
+ display: flex;
214
+ justify-content: space-between;
215
+ align-items: center;
216
+ padding: 16px 20px;
217
+ border-bottom: 1px solid var(--border);
218
+ }
219
+ .chart-modal-header h3 {
220
+ margin: 0;
221
+ font-size: 16px;
222
+ color: var(--text-primary);
223
+ }
224
+ .chart-modal-close {
225
+ background: none;
226
+ border: none;
227
+ color: var(--text-secondary);
228
+ cursor: pointer;
229
+ padding: 8px;
230
+ border-radius: 6px;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ }
235
+ .chart-modal-close:hover {
236
+ background: var(--bg-secondary);
237
+ color: var(--text-primary);
238
+ }
239
+ .chart-modal-body {
240
+ flex: 1;
241
+ padding: 20px;
242
+ min-height: 0;
243
+ }
244
+ .chart-modal-body .chart-container {
245
+ height: 100%;
246
+ min-height: 500px;
247
+ }
248
+ .chart-modal-body canvas {
249
+ width: 100% !important;
250
+ height: 100% !important;
251
+ }
252
+
253
+ /* Badges */
254
+ .live-badge {
255
+ display: inline-flex;
256
+ align-items: center;
257
+ gap: 6px;
258
+ padding: 4px 12px;
259
+ background: rgba(34, 197, 94, 0.2);
260
+ border-radius: 20px;
261
+ font-size: 12px;
262
+ color: var(--success);
263
+ font-weight: 500;
264
+ }
265
+
266
+ .live-badge::before {
267
+ content: '';
268
+ width: 8px;
269
+ height: 8px;
270
+ background: var(--success);
271
+ border-radius: 50%;
272
+ animation: pulse 1.5s infinite;
273
+ }
274
+
275
+ @keyframes pulse {
276
+ 0%, 100% { opacity: 1; transform: scale(1); }
277
+ 50% { opacity: 0.5; transform: scale(0.9); }
278
+ }
279
+
280
+ .status-badge { padding: 4px 10px; border-radius: 4px; font-size: 12px; font-weight: 500; }
281
+ .status-badge.good { background: rgba(34, 197, 94, 0.2); color: var(--success); }
282
+ .status-badge.warn { background: rgba(234, 179, 8, 0.2); color: var(--warning); }
283
+ .status-badge.bad { background: rgba(239, 68, 68, 0.2); color: var(--error); }
284
+
285
+ /* Tables */
286
+ table { width: 100%; border-collapse: collapse; }
287
+ th, td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); }
288
+ th { color: var(--text-secondary); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
289
+ tr:hover { background: rgba(255, 255, 255, 0.02); }
290
+ .clickable { color: var(--accent-cyan); cursor: pointer; }
291
+ .clickable:hover { text-decoration: underline; }
292
+
293
+ /* Buttons */
294
+ .btn {
295
+ padding: 10px 20px;
296
+ border: none;
297
+ border-radius: 8px;
298
+ font-weight: 600;
299
+ cursor: pointer;
300
+ transition: all 0.2s;
301
+ font-size: 14px;
302
+ }
303
+
304
+ .btn-primary {
305
+ background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple));
306
+ color: white;
307
+ }
308
+
309
+ .btn-primary:hover { opacity: 0.9; transform: translateY(-1px); }
310
+ .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
311
+ .btn-secondary { background: var(--bg-secondary); border: 1px solid var(--border); color: white; }
312
+ .btn-danger { background: var(--error); color: white; }
313
+ .btn-sm { padding: 6px 12px; font-size: 12px; }
314
+
315
+ /* Empty state */
316
+ .empty-state { text-align: center; padding: 60px 20px; color: var(--text-secondary); }
317
+ .empty-state h3 { color: var(--text-primary); margin-bottom: 8px; font-size: 18px; }
318
+ .empty-state code { background: var(--bg-secondary); padding: 2px 8px; border-radius: 4px; font-size: 13px; }
319
+
320
+ /* Progress bar */
321
+ .progress-bar { height: 8px; background: var(--bg-secondary); border-radius: 4px; overflow: hidden; }
322
+ .progress-bar .fill { height: 100%; background: linear-gradient(90deg, var(--accent-cyan), var(--accent-purple)); transition: width 0.3s; }
323
+
324
+ /* Test list */
325
+ .test-list { max-height: 500px; overflow-y: auto; }
326
+
327
+ .test-item {
328
+ display: flex;
329
+ justify-content: space-between;
330
+ align-items: center;
331
+ padding: 12px 16px;
332
+ border-bottom: 1px solid var(--border);
333
+ transition: background 0.2s;
334
+ }
335
+
336
+ .test-item:hover { background: rgba(255, 255, 255, 0.02); }
337
+ .test-item .test-info { flex: 1; }
338
+ .test-item .test-name { font-weight: 500; color: var(--text-primary); }
339
+ .test-item .test-path { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }
340
+
341
+ .test-type {
342
+ padding: 2px 8px;
343
+ border-radius: 4px;
344
+ font-size: 11px;
345
+ font-weight: 500;
346
+ text-transform: uppercase;
347
+ }
348
+
349
+ .test-type.api { background: rgba(0, 212, 255, 0.2); color: var(--accent-cyan); }
350
+ .test-type.web { background: rgba(156, 64, 255, 0.2); color: var(--accent-purple); }
351
+
352
+ /* Console output */
353
+ .console-output {
354
+ background: #0d1117;
355
+ border: 1px solid var(--border);
356
+ border-radius: 8px;
357
+ padding: 16px;
358
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
359
+ font-size: 12px;
360
+ max-height: 400px;
361
+ overflow-y: auto;
362
+ white-space: pre-wrap;
363
+ word-break: break-all;
364
+ }
365
+
366
+ .console-output .line { padding: 2px 0; }
367
+ .console-output .error { color: var(--error); }
368
+ .console-output .success { color: var(--success); }
369
+
370
+ /* Section tabs */
371
+ .section-tabs {
372
+ display: flex;
373
+ gap: 4px;
374
+ margin-bottom: 16px;
375
+ border-bottom: 1px solid var(--border);
376
+ padding-bottom: 8px;
377
+ }
378
+
379
+ .section-tab {
380
+ padding: 8px 16px;
381
+ background: none;
382
+ border: none;
383
+ color: var(--text-secondary);
384
+ cursor: pointer;
385
+ font-size: 13px;
386
+ font-weight: 500;
387
+ border-bottom: 2px solid transparent;
388
+ margin-bottom: -9px;
389
+ transition: all 0.2s;
390
+ }
391
+
392
+ .section-tab:hover { color: var(--text-primary); }
393
+ .section-tab.active { color: var(--accent-cyan); border-bottom-color: var(--accent-cyan); }
394
+
395
+ /* Step stats table */
396
+ .step-stats-table { font-size: 13px; }
397
+ .step-stats-table th { font-size: 10px; padding: 8px 6px; white-space: nowrap; }
398
+ .step-stats-table th:nth-child(n+4) { text-align: right; }
399
+ .step-stats-table td { padding: 8px 6px; }
400
+ .step-stats-table td:nth-child(n+4) { text-align: right; font-family: 'Monaco', 'Menlo', monospace; font-size: 12px; }
401
+
402
+ .back-btn { margin-bottom: 20px; }
@@ -0,0 +1,168 @@
1
+ import { ChildProcess } from 'child_process';
2
+ export interface DashboardOptions {
3
+ port: number;
4
+ resultsDir: string;
5
+ testsDir?: string;
6
+ workersFile?: string;
7
+ }
8
+ export interface TestResultSummary {
9
+ total_requests: number;
10
+ successful_requests: number;
11
+ failed_requests: number;
12
+ avg_response_time: number;
13
+ min_response_time: number;
14
+ max_response_time: number;
15
+ p50_response_time: number;
16
+ p75_response_time: number;
17
+ p90_response_time: number;
18
+ p95_response_time: number;
19
+ p99_response_time: number;
20
+ requests_per_second: number;
21
+ error_rate: number;
22
+ success_rate: number;
23
+ }
24
+ export interface ErrorDetail {
25
+ scenario: string;
26
+ action: string;
27
+ status?: number;
28
+ error: string;
29
+ request_url?: string;
30
+ count: number;
31
+ }
32
+ export interface NetworkCall {
33
+ id: string;
34
+ vuId: number;
35
+ url: string;
36
+ method: string;
37
+ status: number;
38
+ statusText?: string;
39
+ duration: number;
40
+ size: number;
41
+ type: string;
42
+ success: boolean;
43
+ error?: string;
44
+ timestamp: number;
45
+ requestHeaders?: Record<string, string>;
46
+ requestBody?: string;
47
+ responseHeaders?: Record<string, string>;
48
+ responseBody?: string;
49
+ }
50
+ export interface TestResult {
51
+ id: string;
52
+ name: string;
53
+ timestamp: string;
54
+ duration: number;
55
+ summary: TestResultSummary;
56
+ scenarios: any[];
57
+ step_statistics?: any[];
58
+ timeline_data?: any[];
59
+ vu_ramp_up?: any[];
60
+ response_time_distribution?: any[];
61
+ timeseries?: any[];
62
+ error_details?: ErrorDetail[];
63
+ network_calls?: any[];
64
+ infrastructure_metrics?: Record<string, InfrastructureMetrics[]>;
65
+ /** Individual test results (can come from file or InfluxDB) */
66
+ results?: any[];
67
+ raw?: any;
68
+ }
69
+ export interface TestFile {
70
+ name: string;
71
+ path: string;
72
+ relativePath: string;
73
+ type: 'api' | 'web' | 'mixed';
74
+ lastModified: string;
75
+ }
76
+ export interface LiveTestMetrics {
77
+ requests: number;
78
+ errors: number;
79
+ avgResponseTime: number;
80
+ currentVUs: number;
81
+ p50ResponseTime?: number;
82
+ p90ResponseTime?: number;
83
+ p95ResponseTime?: number;
84
+ p99ResponseTime?: number;
85
+ minResponseTime?: number;
86
+ maxResponseTime?: number;
87
+ requestsPerSecond?: number;
88
+ successRate?: number;
89
+ }
90
+ export interface StepStats {
91
+ stepName: string;
92
+ scenario: string;
93
+ requests: number;
94
+ errors: number;
95
+ avgResponseTime: number;
96
+ p50?: number;
97
+ p95?: number;
98
+ p99?: number;
99
+ successRate: number;
100
+ }
101
+ export interface ResponseTimePoint {
102
+ timestamp: number;
103
+ value: number;
104
+ success: boolean;
105
+ stepName?: string;
106
+ }
107
+ export interface TopError {
108
+ scenario: string;
109
+ action: string;
110
+ status?: number;
111
+ error: string;
112
+ url?: string;
113
+ count: number;
114
+ }
115
+ export interface HistoryPoint {
116
+ timestamp: number;
117
+ requests: number;
118
+ errors: number;
119
+ avgResponseTime: number;
120
+ p95ResponseTime: number;
121
+ p99ResponseTime: number;
122
+ vus: number;
123
+ rps: number;
124
+ }
125
+ export interface InfrastructureMetrics {
126
+ host: string;
127
+ timestamp: string;
128
+ interval_seconds: number;
129
+ metrics: {
130
+ cpu?: {
131
+ usage_percent: number;
132
+ };
133
+ memory?: {
134
+ used_mb: number;
135
+ total_mb: number;
136
+ usage_percent: number;
137
+ };
138
+ disk?: {
139
+ used_gb: number;
140
+ total_gb: number;
141
+ usage_percent: number;
142
+ path: string;
143
+ };
144
+ network?: {
145
+ bytes_in: number;
146
+ bytes_out: number;
147
+ interface: string;
148
+ };
149
+ };
150
+ }
151
+ export interface LiveTest {
152
+ id: string;
153
+ name: string;
154
+ startTime: Date;
155
+ status: 'running' | 'completed' | 'failed';
156
+ metrics: LiveTestMetrics;
157
+ stepStats: StepStats[];
158
+ responseTimes: ResponseTimePoint[];
159
+ topErrors: TopError[];
160
+ history: HistoryPoint[];
161
+ networkCalls?: NetworkCall[];
162
+ infrastructure?: Map<string, InfrastructureMetrics[]>;
163
+ }
164
+ export interface RunningProcess {
165
+ process: ChildProcess;
166
+ testId: string;
167
+ output: string[];
168
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -109,8 +109,6 @@ class ResultAggregator {
109
109
  totalVUs = new Set(this.results.map(r => r.vu_id)).size;
110
110
  }
111
111
  }
112
- // Duration in seconds for report display
113
- const totalDurationSeconds = duration / 1000;
114
112
  // Sort VU ramp-up events by start time for proper chart rendering
115
113
  const sortedVURampUp = [...this.vuRampUpEvents].sort((a, b) => a.start_time - b.start_time);
116
114
  return {
@@ -123,7 +121,7 @@ class ResultAggregator {
123
121
  start_time: this.startTime,
124
122
  end_time: this.endTime,
125
123
  duration,
126
- total_duration: totalDurationSeconds,
124
+ total_duration: duration, // Store in milliseconds for consistency
127
125
  total_virtual_users: totalVUs,
128
126
  peak_virtual_users: totalVUs,
129
127
  successful_requests: successfulRequests,
@@ -0,0 +1,27 @@
1
+ import { TestResult } from '../types';
2
+ export interface BatchConfig {
3
+ batchSize: number;
4
+ intervalMs?: number;
5
+ maxBufferSize: number;
6
+ }
7
+ export interface BatchFlushHandler {
8
+ (batch: TestResult[], batchNumber: number): Promise<void>;
9
+ }
10
+ export declare class BatchProcessor {
11
+ private config;
12
+ private buffer;
13
+ private batchTimer;
14
+ private batchCounter;
15
+ private onFlush;
16
+ constructor(config?: Partial<BatchConfig>);
17
+ setFlushHandler(handler: BatchFlushHandler): void;
18
+ start(): void;
19
+ stop(): void;
20
+ reset(): void;
21
+ private startTimer;
22
+ add(result: TestResult): void;
23
+ flush(): Promise<void>;
24
+ finalize(): Promise<void>;
25
+ getBatchCounter(): number;
26
+ getBufferSize(): number;
27
+ }
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchProcessor = void 0;
4
+ const logger_1 = require("../../utils/logger");
5
+ class BatchProcessor {
6
+ constructor(config = {}) {
7
+ this.buffer = [];
8
+ this.batchTimer = null;
9
+ this.batchCounter = 0;
10
+ this.onFlush = null;
11
+ this.config = {
12
+ batchSize: config.batchSize || 10,
13
+ intervalMs: config.intervalMs,
14
+ maxBufferSize: config.maxBufferSize || 1000
15
+ };
16
+ }
17
+ setFlushHandler(handler) {
18
+ this.onFlush = handler;
19
+ }
20
+ start() {
21
+ if (this.config.intervalMs) {
22
+ this.startTimer();
23
+ }
24
+ }
25
+ stop() {
26
+ if (this.batchTimer) {
27
+ clearInterval(this.batchTimer);
28
+ this.batchTimer = null;
29
+ }
30
+ }
31
+ reset() {
32
+ this.stop();
33
+ this.buffer = [];
34
+ this.batchCounter = 0;
35
+ }
36
+ startTimer() {
37
+ this.batchTimer = setInterval(() => {
38
+ if (this.buffer.length > 0) {
39
+ this.flush();
40
+ }
41
+ }, this.config.intervalMs);
42
+ }
43
+ add(result) {
44
+ // Safety limit: force flush if buffer exceeds max size
45
+ if (this.buffer.length >= this.config.maxBufferSize) {
46
+ this.flush();
47
+ }
48
+ this.buffer.push(result);
49
+ // Check if we should flush based on batch size (if not using intervals)
50
+ if (!this.config.intervalMs) {
51
+ if (this.buffer.length >= this.config.batchSize) {
52
+ this.flush();
53
+ }
54
+ }
55
+ }
56
+ async flush() {
57
+ if (this.buffer.length === 0)
58
+ return;
59
+ const batch = [...this.buffer];
60
+ this.buffer = [];
61
+ this.batchCounter++;
62
+ logger_1.logger.debug(`Flushing batch #${this.batchCounter} with ${batch.length} results`);
63
+ if (this.onFlush) {
64
+ try {
65
+ await this.onFlush(batch, this.batchCounter);
66
+ }
67
+ catch (error) {
68
+ logger_1.logger.error('Failed to flush metrics batch:', error);
69
+ }
70
+ }
71
+ }
72
+ async finalize() {
73
+ this.stop();
74
+ if (this.buffer.length > 0) {
75
+ await this.flush();
76
+ }
77
+ }
78
+ getBatchCounter() {
79
+ return this.batchCounter;
80
+ }
81
+ getBufferSize() {
82
+ return this.buffer.length;
83
+ }
84
+ }
85
+ exports.BatchProcessor = BatchProcessor;
@@ -0,0 +1 @@
1
+ export { BatchProcessor, BatchConfig, BatchFlushHandler } from './batch-processor';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchProcessor = void 0;
4
+ var batch_processor_1 = require("./batch-processor");
5
+ Object.defineProperty(exports, "BatchProcessor", { enumerable: true, get: function () { return batch_processor_1.BatchProcessor; } });