blaze-performance-tester 3.2.23 → 3.2.25
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/cli.js +260 -317
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5571,6 +5571,164 @@ try {
|
|
|
5571
5571
|
console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
|
|
5572
5572
|
process.exit(1);
|
|
5573
5573
|
}
|
|
5574
|
+
var i18n = {
|
|
5575
|
+
"en-US": {
|
|
5576
|
+
rtl: false,
|
|
5577
|
+
title: "Blaze Core Performance Analysis",
|
|
5578
|
+
targetScript: "Target Script",
|
|
5579
|
+
generatedAt: "Generated At",
|
|
5580
|
+
rampUp: "Ramp-Up",
|
|
5581
|
+
rampDown: "Ramp-Down",
|
|
5582
|
+
metrics: {
|
|
5583
|
+
apdex: "Apdex Score (T: 50ms)",
|
|
5584
|
+
throughput: "Throughput",
|
|
5585
|
+
bandwidth: "Network Bandwidth",
|
|
5586
|
+
ttfb: "Avg Time To First Byte",
|
|
5587
|
+
dns: "DNS Lookup",
|
|
5588
|
+
tcp: "TCP Connect",
|
|
5589
|
+
tls: "TLS Handshake",
|
|
5590
|
+
stability: "Stability (Std Dev)",
|
|
5591
|
+
good: "(Good)"
|
|
5592
|
+
},
|
|
5593
|
+
verdict: "\u{1F916} AI Stress-Testing Agent Verdict",
|
|
5594
|
+
health: {
|
|
5595
|
+
title: "Blaze Run Health Score",
|
|
5596
|
+
subtitle: "Composite Run Quality Engine Rating"
|
|
5597
|
+
},
|
|
5598
|
+
matrix: {
|
|
5599
|
+
title: "\u{1F4CB} HTTP Response Matrix",
|
|
5600
|
+
info: "Informational",
|
|
5601
|
+
success: "Successful",
|
|
5602
|
+
redirects: "Redirects",
|
|
5603
|
+
clientErr: "Client Error",
|
|
5604
|
+
serverErr: "Server Error",
|
|
5605
|
+
breakdown: "\u{1F522} HTTP Status Code Breakdown Rate",
|
|
5606
|
+
code: "Code"
|
|
5607
|
+
},
|
|
5608
|
+
rca: "\u26A1 Gemini 2.5 Flash Autonomous Root Cause Analysis (RCA)",
|
|
5609
|
+
charts: {
|
|
5610
|
+
volume: "\u{1F4CA} Throughput Velocity & Workload Volume",
|
|
5611
|
+
concurrency: "\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast",
|
|
5612
|
+
status: "\u{1F369} HTTP Status Code Proportions",
|
|
5613
|
+
scatter: "\u{1F4C8} Time-to-First-Byte Scatter Plot"
|
|
5614
|
+
},
|
|
5615
|
+
telemetry: {
|
|
5616
|
+
title: "Telemetry Matrix Logs",
|
|
5617
|
+
threads: "Concurrency (Threads)",
|
|
5618
|
+
throughput: "Throughput",
|
|
5619
|
+
avgLatency: "Avg Latency",
|
|
5620
|
+
p95: "P95 Latency",
|
|
5621
|
+
errors: "Error Rate",
|
|
5622
|
+
status: "Status",
|
|
5623
|
+
pass: "SLO PASS",
|
|
5624
|
+
fail: "SLO BREACH"
|
|
5625
|
+
}
|
|
5626
|
+
},
|
|
5627
|
+
"es-ES": {
|
|
5628
|
+
rtl: false,
|
|
5629
|
+
title: "An\xE1lisis de Rendimiento Blaze Core",
|
|
5630
|
+
targetScript: "Script Objetivo",
|
|
5631
|
+
generatedAt: "Generado el",
|
|
5632
|
+
rampUp: "Aceleraci\xF3n",
|
|
5633
|
+
rampDown: "Desaceleraci\xF3n",
|
|
5634
|
+
metrics: {
|
|
5635
|
+
apdex: "Puntuaci\xF3n Apdex (T: 50ms)",
|
|
5636
|
+
throughput: "Rendimiento",
|
|
5637
|
+
bandwidth: "Ancho de Banda",
|
|
5638
|
+
ttfb: "Tiempo Prom. Hasta el 1er Byte",
|
|
5639
|
+
dns: "B\xFAsqueda DNS",
|
|
5640
|
+
tcp: "Conexi\xF3n TCP",
|
|
5641
|
+
tls: "Apret\xF3n de Manos TLS",
|
|
5642
|
+
stability: "Estabilidad (Desv. Est.)",
|
|
5643
|
+
good: "(Bueno)"
|
|
5644
|
+
},
|
|
5645
|
+
verdict: "\u{1F916} Veredicto del Agente de IA",
|
|
5646
|
+
health: {
|
|
5647
|
+
title: "Puntuaci\xF3n de Salud de la Ejecuci\xF3n",
|
|
5648
|
+
subtitle: "Calificaci\xF3n Compuesta del Motor de Calidad"
|
|
5649
|
+
},
|
|
5650
|
+
matrix: {
|
|
5651
|
+
title: "\u{1F4CB} Matriz de Respuesta HTTP",
|
|
5652
|
+
info: "Informativo",
|
|
5653
|
+
success: "Exitoso",
|
|
5654
|
+
redirects: "Redirecciones",
|
|
5655
|
+
clientErr: "Error del Cliente",
|
|
5656
|
+
serverErr: "Error del Servidor",
|
|
5657
|
+
breakdown: "\u{1F522} Tasa de Desglose de C\xF3digos HTTP",
|
|
5658
|
+
code: "C\xF3digo"
|
|
5659
|
+
},
|
|
5660
|
+
rca: "\u26A1 An\xE1lisis de Causa Ra\xEDz (RCA) de Gemini 2.5 Flash",
|
|
5661
|
+
charts: {
|
|
5662
|
+
volume: "\u{1F4CA} Velocidad de Rendimiento y Carga de Trabajo",
|
|
5663
|
+
concurrency: "\u{1F504} Concurrencia (VUs) vs Tendencia TTF",
|
|
5664
|
+
status: "\u{1F369} Proporciones de C\xF3digos HTTP",
|
|
5665
|
+
scatter: "\u{1F4C8} Gr\xE1fico de Dispersi\xF3n de Tiempo de Respuesta"
|
|
5666
|
+
},
|
|
5667
|
+
telemetry: {
|
|
5668
|
+
title: "Registros de la Matriz de Telemetr\xEDa",
|
|
5669
|
+
threads: "Concurrencia (Hilos)",
|
|
5670
|
+
throughput: "Rendimiento",
|
|
5671
|
+
avgLatency: "Latencia Promedio",
|
|
5672
|
+
p95: "Latencia P95",
|
|
5673
|
+
errors: "Tasa de Error",
|
|
5674
|
+
status: "Estado",
|
|
5675
|
+
pass: "PASA SLO",
|
|
5676
|
+
fail: "FALLA SLO"
|
|
5677
|
+
}
|
|
5678
|
+
},
|
|
5679
|
+
"ar-AE": {
|
|
5680
|
+
rtl: true,
|
|
5681
|
+
title: "\u062A\u062D\u0644\u064A\u0644 \u0623\u062F\u0627\u0621 \u0628\u0644\u064A\u0632 \u0627\u0644\u0623\u0633\u0627\u0633\u064A",
|
|
5682
|
+
targetScript: "\u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u0646\u0635\u064A \u0627\u0644\u0645\u0633\u062A\u0647\u062F\u0641",
|
|
5683
|
+
generatedAt: "\u062A\u0645 \u0627\u0644\u0625\u0646\u0634\u0627\u0621 \u0641\u064A",
|
|
5684
|
+
rampUp: "\u0627\u0644\u062A\u0635\u0639\u064A\u062F",
|
|
5685
|
+
rampDown: "\u0627\u0644\u062A\u062E\u0641\u064A\u0636",
|
|
5686
|
+
metrics: {
|
|
5687
|
+
apdex: "\u0646\u0642\u0627\u0637 Apdex",
|
|
5688
|
+
throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
|
|
5689
|
+
bandwidth: "\u0639\u0631\u0636 \u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0644\u0644\u0634\u0628\u0643\u0629",
|
|
5690
|
+
ttfb: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A",
|
|
5691
|
+
dns: "\u0628\u062D\u062B DNS",
|
|
5692
|
+
tcp: "\u0627\u062A\u0635\u0627\u0644 TCP",
|
|
5693
|
+
tls: "\u0645\u0635\u0627\u0641\u062D\u0629 TLS",
|
|
5694
|
+
stability: "\u0627\u0644\u0627\u0633\u062A\u0642\u0631\u0627\u0631 (\u0627\u0644\u0627\u0646\u062D\u0631\u0627\u0641 \u0627\u0644\u0645\u0639\u064A\u0627\u0631\u064A)",
|
|
5695
|
+
good: "(\u062C\u064A\u062F)"
|
|
5696
|
+
},
|
|
5697
|
+
verdict: "\u{1F916} \u062D\u0643\u0645 \u0648\u0643\u064A\u0644 \u0627\u062E\u062A\u0628\u0627\u0631 \u0627\u0644\u062C\u0647\u062F \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A",
|
|
5698
|
+
health: {
|
|
5699
|
+
title: "\u0646\u0642\u0627\u0637 \u0635\u062D\u0629 \u062A\u0634\u063A\u064A\u0644 \u0628\u0644\u064A\u0632",
|
|
5700
|
+
subtitle: "\u0627\u0644\u062A\u0642\u064A\u064A\u0645 \u0627\u0644\u0645\u0631\u0643\u0628 \u0644\u0645\u062D\u0631\u0643 \u0627\u0644\u062C\u0648\u062F\u0629"
|
|
5701
|
+
},
|
|
5702
|
+
matrix: {
|
|
5703
|
+
title: "\u{1F4CB} \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0633\u062A\u062C\u0627\u0628\u0629 HTTP",
|
|
5704
|
+
info: "\u0625\u0639\u0644\u0627\u0645\u064A",
|
|
5705
|
+
success: "\u0646\u0627\u062C\u062D",
|
|
5706
|
+
redirects: "\u0625\u0639\u0627\u062F\u0629 \u062A\u0648\u062C\u064A\u0647",
|
|
5707
|
+
clientErr: "\u062E\u0637\u0623 \u0627\u0644\u0639\u0645\u064A\u0644",
|
|
5708
|
+
serverErr: "\u062E\u0637\u0623 \u0627\u0644\u062E\u0627\u062F\u0645",
|
|
5709
|
+
breakdown: "\u{1F522} \u0645\u0639\u062F\u0644 \u062A\u0648\u0632\u064A\u0639 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
|
|
5710
|
+
code: "\u0627\u0644\u0631\u0645\u0632"
|
|
5711
|
+
},
|
|
5712
|
+
rca: "\u26A1 \u0627\u0644\u062A\u062D\u0644\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0642\u0644 \u0644\u0644\u0623\u0633\u0628\u0627\u0628 \u0627\u0644\u062C\u0630\u0631\u064A\u0629 (RCA) \u0639\u0628\u0631 Gemini 2.5",
|
|
5713
|
+
charts: {
|
|
5714
|
+
volume: "\u{1F4CA} \u0633\u0631\u0639\u0629 \u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629 \u0648\u062D\u062C\u0645 \u0639\u0628\u0621 \u0627\u0644\u0639\u0645\u0644",
|
|
5715
|
+
concurrency: "\u{1F504} \u0627\u0644\u062A\u0632\u0627\u0645\u0646 \u0627\u0644\u0646\u0634\u0637 (VUs) \u0645\u0642\u0627\u0628\u0644 \u0627\u062A\u062C\u0627\u0647 TTF \u0648\u062A\u0648\u0642\u0639\u0627\u062A \u0627\u0644\u062D\u0645\u0644",
|
|
5716
|
+
status: "\u{1F369} \u0646\u0633\u0628 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
|
|
5717
|
+
scatter: "\u{1F4C8} \u0645\u062E\u0637\u0637 \u0627\u0644\u062A\u0634\u062A\u062A \u0644\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A"
|
|
5718
|
+
},
|
|
5719
|
+
telemetry: {
|
|
5720
|
+
title: "\u0633\u062C\u0644\u0627\u062A \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0644\u0642\u064A\u0627\u0633 \u0639\u0646 \u0628\u064F\u0639\u062F",
|
|
5721
|
+
threads: "\u0627\u0644\u062A\u0632\u0627\u0645\u0646 (\u0627\u0644\u062E\u064A\u0648\u0637)",
|
|
5722
|
+
throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
|
|
5723
|
+
avgLatency: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0643\u0645\u0648\u0646",
|
|
5724
|
+
p95: "\u0643\u0645\u0648\u0646 P95",
|
|
5725
|
+
errors: "\u0645\u0639\u062F\u0644 \u0627\u0644\u062E\u0637\u0623",
|
|
5726
|
+
status: "\u0627\u0644\u062D\u0627\u0644\u0629",
|
|
5727
|
+
pass: "\u0646\u062C\u0627\u062D",
|
|
5728
|
+
fail: "\u0641\u0634\u0644"
|
|
5729
|
+
}
|
|
5730
|
+
}
|
|
5731
|
+
};
|
|
5574
5732
|
var mathUtils = {
|
|
5575
5733
|
mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
|
|
5576
5734
|
stdDev: (arr, mean) => {
|
|
@@ -5808,7 +5966,7 @@ async function runCli() {
|
|
|
5808
5966
|
const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", {
|
|
5809
5967
|
describe: "Natural language scenario to generate the test script",
|
|
5810
5968
|
type: "string"
|
|
5811
|
-
}).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).help(false).strict(false).argv;
|
|
5969
|
+
}).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).option("locale", { type: "string", description: "Dashboard UI Locale (e.g. en-US, es-ES, ar-AE)" }).help(false).strict(false).argv;
|
|
5812
5970
|
const args = process.argv.slice(2);
|
|
5813
5971
|
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
5814
5972
|
printUsage();
|
|
@@ -5845,7 +6003,7 @@ async function runCli() {
|
|
|
5845
6003
|
process.exit(1);
|
|
5846
6004
|
}
|
|
5847
6005
|
console.log(`Starting performance test using script: ${config.targetScript}`);
|
|
5848
|
-
console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s`);
|
|
6006
|
+
console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s, Locale: ${config.locale}`);
|
|
5849
6007
|
if (config.isCorrelate) {
|
|
5850
6008
|
await executeTestPipeline(config);
|
|
5851
6009
|
}
|
|
@@ -5873,6 +6031,7 @@ function parseArguments(args) {
|
|
|
5873
6031
|
let outputHtml = "blaze-dashboard.html";
|
|
5874
6032
|
let baselinePath = null;
|
|
5875
6033
|
let optionValue = null;
|
|
6034
|
+
let locale = "en-US";
|
|
5876
6035
|
const threadsIdx = args.indexOf("--threads");
|
|
5877
6036
|
if (threadsIdx !== -1)
|
|
5878
6037
|
threads = parseInt(args[threadsIdx + 1], 10);
|
|
@@ -5887,7 +6046,7 @@ function parseArguments(args) {
|
|
|
5887
6046
|
rampDownSec = parseInt(args[rampDownIdx + 1], 10);
|
|
5888
6047
|
const apdexIdx = args.indexOf("--target-apdex");
|
|
5889
6048
|
if (apdexIdx !== -1)
|
|
5890
|
-
targetApdex = parseFloat(apdexIdx + 1);
|
|
6049
|
+
targetApdex = parseFloat(args[apdexIdx + 1]);
|
|
5891
6050
|
const errorIdx = args.indexOf("--target-error");
|
|
5892
6051
|
if (errorIdx !== -1)
|
|
5893
6052
|
targetErrorRate = parseFloat(args[errorIdx + 1]);
|
|
@@ -5904,6 +6063,15 @@ function parseArguments(args) {
|
|
|
5904
6063
|
if (optionIdx !== -1 && args[optionIdx + 1]) {
|
|
5905
6064
|
optionValue = args[optionIdx + 1];
|
|
5906
6065
|
}
|
|
6066
|
+
const localeIdx = args.findIndex((arg) => arg.startsWith("--locale="));
|
|
6067
|
+
if (localeIdx !== -1) {
|
|
6068
|
+
locale = args[localeIdx].split("=")[1];
|
|
6069
|
+
} else {
|
|
6070
|
+
const standaloneLocaleIdx = args.indexOf("--locale");
|
|
6071
|
+
if (standaloneLocaleIdx !== -1 && args[standaloneLocaleIdx + 1]) {
|
|
6072
|
+
locale = args[standaloneLocaleIdx + 1];
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
5907
6075
|
const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
|
|
5908
6076
|
if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic)
|
|
5909
6077
|
threads = positionalNums[0];
|
|
@@ -5933,7 +6101,6 @@ function parseArguments(args) {
|
|
|
5933
6101
|
isCorrelate,
|
|
5934
6102
|
isSeo,
|
|
5935
6103
|
isCrossBrowser,
|
|
5936
|
-
// Include in configuration object
|
|
5937
6104
|
clusterLogs,
|
|
5938
6105
|
threads,
|
|
5939
6106
|
durationSec,
|
|
@@ -5946,7 +6113,9 @@ function parseArguments(args) {
|
|
|
5946
6113
|
maxAllowedLatencyMs,
|
|
5947
6114
|
outputHtml,
|
|
5948
6115
|
baselinePath,
|
|
5949
|
-
optionValue
|
|
6116
|
+
optionValue,
|
|
6117
|
+
locale
|
|
6118
|
+
// Include locale in configuration
|
|
5950
6119
|
};
|
|
5951
6120
|
}
|
|
5952
6121
|
async function runBlazeCoreEngine(config) {
|
|
@@ -6329,6 +6498,18 @@ function generateFinalAgentReport(history, score) {
|
|
|
6329
6498
|
console.log("=======================================================\n");
|
|
6330
6499
|
}
|
|
6331
6500
|
function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", baselineData = null) {
|
|
6501
|
+
const dict = i18n[config.locale] || i18n["en-US"];
|
|
6502
|
+
const dir = dict.rtl ? "rtl" : "ltr";
|
|
6503
|
+
const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
|
|
6504
|
+
year: "numeric",
|
|
6505
|
+
month: "long",
|
|
6506
|
+
day: "numeric",
|
|
6507
|
+
hour: "2-digit",
|
|
6508
|
+
minute: "2-digit",
|
|
6509
|
+
second: "2-digit",
|
|
6510
|
+
timeZoneName: "short"
|
|
6511
|
+
});
|
|
6512
|
+
const formattedDate = formatter.format(/* @__PURE__ */ new Date());
|
|
6332
6513
|
const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
|
|
6333
6514
|
try {
|
|
6334
6515
|
fs.writeFileSync(summaryArtifactPath, JSON.stringify(cards, null, 2), "utf-8");
|
|
@@ -6367,9 +6548,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6367
6548
|
<h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
|
|
6368
6549
|
\u{1F4CA} Baseline Regression Comparison (Delta Engine)
|
|
6369
6550
|
</h3>
|
|
6370
|
-
<p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
6371
|
-
Comparing current execution metrics directly against reference payload from baseline source.
|
|
6372
|
-
</p>
|
|
6373
6551
|
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
|
|
6374
6552
|
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6375
6553
|
<div class="card-title">Apdex Shift</div>
|
|
@@ -6408,17 +6586,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6408
6586
|
<h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
|
|
6409
6587
|
\u{1F310} Cross-Browser Rendering Performance Matrix
|
|
6410
6588
|
</h3>
|
|
6411
|
-
<
|
|
6412
|
-
Synthetic user journey benchmarks across Blink, WebKit, and Gecko execution contexts.
|
|
6413
|
-
</p>
|
|
6414
|
-
<table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: left; font-size: 14px;">
|
|
6589
|
+
<table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: start; font-size: 14px;">
|
|
6415
6590
|
<thead>
|
|
6416
6591
|
<tr style="border-bottom: 1px solid #1e293b;">
|
|
6417
6592
|
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Browser Engine</th>
|
|
6418
6593
|
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">FCP</th>
|
|
6419
6594
|
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">LCP</th>
|
|
6420
6595
|
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Journey Duration</th>
|
|
6421
|
-
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">
|
|
6596
|
+
<th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Status</th>
|
|
6422
6597
|
</tr>
|
|
6423
6598
|
</thead>
|
|
6424
6599
|
<tbody>
|
|
@@ -6433,19 +6608,19 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6433
6608
|
logClustersHtml = `
|
|
6434
6609
|
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
6435
6610
|
<h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
|
|
6436
|
-
\u{1F9E0} Semantic Log Clustering
|
|
6611
|
+
\u{1F9E0} Semantic Log Clustering
|
|
6437
6612
|
</h3>
|
|
6438
6613
|
<div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
6439
|
-
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures
|
|
6614
|
+
Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures.
|
|
6440
6615
|
</div>
|
|
6441
6616
|
|
|
6442
6617
|
<table style="width: 100%; border-collapse: collapse;">
|
|
6443
6618
|
<thead>
|
|
6444
6619
|
<tr style="border-bottom: 1px solid #1e293b;">
|
|
6445
|
-
<th style="width: 8%; padding: 0.75rem; text-align:
|
|
6446
|
-
<th style="width: 22%; padding: 0.75rem; text-align:
|
|
6447
|
-
<th style="width: 10%; padding: 0.75rem; text-align:
|
|
6448
|
-
<th style="width: 60%; padding: 0.75rem; text-align:
|
|
6620
|
+
<th style="width: 8%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
|
|
6621
|
+
<th style="width: 22%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Category</th>
|
|
6622
|
+
<th style="width: 10%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
|
|
6623
|
+
<th style="width: 60%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Blueprint</th>
|
|
6449
6624
|
</tr>
|
|
6450
6625
|
</thead>
|
|
6451
6626
|
<tbody>
|
|
@@ -6455,8 +6630,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6455
6630
|
catColor = "#c084fc";
|
|
6456
6631
|
if (c.category === "Product_Error")
|
|
6457
6632
|
catColor = "#f87171";
|
|
6458
|
-
if (c.category === "Environment_Infrastructure_Error")
|
|
6459
|
-
catColor = "#fb923c";
|
|
6460
6633
|
let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
|
|
6461
6634
|
const rawTemplate = c.template || "";
|
|
6462
6635
|
const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
|
|
@@ -6465,15 +6638,12 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6465
6638
|
<td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
|
|
6466
6639
|
<td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
|
|
6467
6640
|
<td style="padding: 1.25rem 0.75rem;">
|
|
6468
|
-
<span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;
|
|
6641
|
+
<span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;">${c.severity}</span>
|
|
6469
6642
|
</td>
|
|
6470
6643
|
<td style="padding: 1.25rem 0.75rem;">
|
|
6471
|
-
<div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-
|
|
6644
|
+
<div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-inline-start: 3px solid #1e293b; margin-bottom: 0.5rem;">
|
|
6472
6645
|
<code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
|
|
6473
6646
|
</div>
|
|
6474
|
-
<div style="color: #64748b; font-size: 0.8rem; font-family: monospace; padding-left: 0.2rem; line-height: 1.4;">
|
|
6475
|
-
<span style="color: #475569;">Real Example Trace:</span> ${rawTemplate}
|
|
6476
|
-
</div>
|
|
6477
6647
|
</td>
|
|
6478
6648
|
</tr>`;
|
|
6479
6649
|
}).join("")}
|
|
@@ -6481,124 +6651,26 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6481
6651
|
</table>
|
|
6482
6652
|
</div>`;
|
|
6483
6653
|
}
|
|
6484
|
-
let seoImpactSectionHtml = "";
|
|
6485
|
-
if (config.isSeo) {
|
|
6486
|
-
const finalLatency = history[history.length - 1]?.avgLatency || cards.ttfb;
|
|
6487
|
-
const seoMetrics = calculateSeoImpactMetrics(finalLatency);
|
|
6488
|
-
seoImpactSectionHtml = `
|
|
6489
|
-
<div class="card" style="margin-top: 2rem; background: #111a2e; border: 1px solid #1e3a8a;">
|
|
6490
|
-
<h3 style="color: #f43f5e; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
|
|
6491
|
-
\u{1F50E} SEO Rank Impact & Visibility Loss Predictor
|
|
6492
|
-
</h3>
|
|
6493
|
-
<p style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
|
|
6494
|
-
Translates response time infrastructure load stress into Core Web Vitals ranking drops and organic search volume degradation metrics.
|
|
6495
|
-
</p>
|
|
6496
|
-
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
|
6497
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b; text-align: center;">
|
|
6498
|
-
<div class="card-title" style="color: #94a3b8;">Est. Organic Traffic Loss</div>
|
|
6499
|
-
<div style="font-size: 2rem; font-weight: bold; color: ${seoMetrics.color};">
|
|
6500
|
-
${seoMetrics.trafficLoss > 0 ? "-" : ""}${seoMetrics.trafficLoss}%
|
|
6501
|
-
</div>
|
|
6502
|
-
<div style="font-size: 0.8rem; color: #64748b; margin-top: 0.25rem;">compared to 200ms sweet-spot</div>
|
|
6503
|
-
</div>
|
|
6504
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6505
|
-
<div class="card-title">Search Visibility Status</div>
|
|
6506
|
-
<div style="font-size: 1.25rem; font-weight: bold; color: ${seoMetrics.color}; margin-top: 0.4rem;">${seoMetrics.status}</div>
|
|
6507
|
-
<div style="font-size: 0.8rem; color: #94a3b8; margin-top: 0.25rem;">Target Latency: ${finalLatency.toFixed(1)}ms</div>
|
|
6508
|
-
</div>
|
|
6509
|
-
<div style="background: #090d16; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6510
|
-
<div class="card-title">Googlebot Crawl Budget Impact</div>
|
|
6511
|
-
<div style="font-size: 0.85rem; color: #e2e8f0; line-height: 1.5; margin-top: 0.4rem;">${seoMetrics.crawlPenalty}</div>
|
|
6512
|
-
</div>
|
|
6513
|
-
</div>
|
|
6514
|
-
</div>`;
|
|
6515
|
-
}
|
|
6516
|
-
const liveAdjusterHtml = `
|
|
6517
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
6518
|
-
<h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 1rem;">
|
|
6519
|
-
\u{1F39B}\uFE0F Live Concurrency Adjuster (Mid-Test Simulation)
|
|
6520
|
-
</h3>
|
|
6521
|
-
<div style="display: flex; gap: 2rem; align-items: center; flex-wrap: wrap;">
|
|
6522
|
-
<div style="flex: 1; min-width: 280px;">
|
|
6523
|
-
<label for="concurrencySlider" style="display: block; font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.5rem;">
|
|
6524
|
-
Scale Virtual Users (VUs): <strong id="sliderValue" style="color: #38bdf8;">${config.threads}</strong> threads
|
|
6525
|
-
</label>
|
|
6526
|
-
<input type="range" id="concurrencySlider" min="5" max="${config.maxThreads || 300}" step="5" value="${config.threads}" style="width: 100%; accent-color: #38bdf8; cursor: pointer;">
|
|
6527
|
-
</div>
|
|
6528
|
-
<div style="display: flex; gap: 1.5rem; text-align: center;">
|
|
6529
|
-
<div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6530
|
-
<div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Throughput</div>
|
|
6531
|
-
<div id="simThroughput" style="font-size: 1.2rem; font-weight: bold; color: #f97316;">${cards.throughput.toFixed(0)}/s</div>
|
|
6532
|
-
</div>
|
|
6533
|
-
<div style="background: #090d16; padding: 0.75rem 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6534
|
-
<div style="font-size: 0.7rem; color: #64748b; text-transform: uppercase;">Sim. Latency</div>
|
|
6535
|
-
<div id="simLatency" style="font-size: 1.2rem; font-weight: bold; color: #10b981;">${cards.ttfb.toFixed(1)}ms</div>
|
|
6536
|
-
</div>
|
|
6537
|
-
</div>
|
|
6538
|
-
</div>
|
|
6539
|
-
</div>`;
|
|
6540
|
-
const rightSizingHtml = `
|
|
6541
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
|
|
6542
|
-
<h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem;">
|
|
6543
|
-
\u2601\uFE0F Infrastructure Right-Sizing & Cloud Cost Projections
|
|
6544
|
-
</h3>
|
|
6545
|
-
<div style="grid-template-columns: repeat(3, 1fr); display: grid; gap: 1rem; margin-top: 1rem;">
|
|
6546
|
-
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6547
|
-
<div class="card-title">Est. Monthly Cloud Cost</div>
|
|
6548
|
-
<div class="card-value green">$${estimatedMonthlyCost} <span class="unit">/mo</span></div>
|
|
6549
|
-
</div>
|
|
6550
|
-
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6551
|
-
<div class="card-title">Recommended CPU Right-Sizing</div>
|
|
6552
|
-
<div class="card-value purple">${recommendedCpuCores} <span class="unit">vCores</span></div>
|
|
6553
|
-
</div>
|
|
6554
|
-
<div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
|
|
6555
|
-
<div class="card-title">Recommended Memory Allocation</div>
|
|
6556
|
-
<div class="card-value orange">${recommendedRamGb} <span class="unit">GB RAM</span></div>
|
|
6557
|
-
</div>
|
|
6558
|
-
</div>
|
|
6559
|
-
</div>`;
|
|
6560
|
-
const nlpQueryEngineHtml = `
|
|
6561
|
-
<div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #0ea5e9; border-left: 4px solid #0ea5e9;">
|
|
6562
|
-
<h3 style="color: #0ea5e9; display: flex; align-items: center; gap: 0.5rem; font-size: 1.2rem; margin-bottom: 0.5rem; border-bottom: none; padding-bottom: 0;">
|
|
6563
|
-
\u{1F916} Natural Language Telemetry Insights Query Engine
|
|
6564
|
-
</h3>
|
|
6565
|
-
<p style="color: #94a3b8; font-size: 0.9rem; margin: 0 0 1rem 0;">
|
|
6566
|
-
Ask questions of your load test results using natural phrasing to isolate and filter metric rows dynamically.
|
|
6567
|
-
</p>
|
|
6568
|
-
<div style="display: flex; gap: 0.75rem; margin-bottom: 0.75rem;">
|
|
6569
|
-
<input type="text" id="nlpQueryInput" placeholder="e.g., show me tiers where latency > 40ms or error rate > 0%"
|
|
6570
|
-
style="flex: 1; background: #090d16; border: 1px solid #1e293b; border-radius: 6px; padding: 0.75rem 1rem; color: #ffffff; font-size: 0.95rem; outline: none; transition: border-color 0.2s;">
|
|
6571
|
-
<button id="nlpQueryBtn" style="background: #0ea5e9; color: #ffffff; border: none; border-radius: 6px; padding: 0.75rem 1.5rem; font-weight: 600; cursor: pointer; transition: background 0.2s;">
|
|
6572
|
-
Query Matrix
|
|
6573
|
-
</button>
|
|
6574
|
-
</div>
|
|
6575
|
-
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
|
6576
|
-
<span style="font-size: 0.8rem; color: #64748b; font-weight: bold; text-transform: uppercase; letter-spacing: 0.05em;">Suggestions:</span>
|
|
6577
|
-
<button onclick="setNlpQuery('Show P95 latency > 50ms')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">P95 Latency > 50ms</button>
|
|
6578
|
-
<button onclick="setNlpQuery('Find tiers with errors > 0%')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Errors > 0%</button>
|
|
6579
|
-
<button onclick="setNlpQuery('High concurrency > 25 VUs')" style="background: #1e293b; color: #cbd5e1; border: 1px solid #334155; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Concurrency > 25 VUs</button>
|
|
6580
|
-
<button onclick="setNlpQuery('')" style="background: #1c1917; color: #f43f5e; border: 1px solid #44403c; border-radius: 4px; padding: 0.25rem 0.5rem; font-size: 0.8rem; cursor: pointer;">Reset Filter</button>
|
|
6581
|
-
</div>
|
|
6582
|
-
<div id="nlpQueryFeedback" style="margin-top: 0.75rem; font-size: 0.85rem; color: #38bdf8;"></div>
|
|
6583
|
-
</div>`;
|
|
6584
6654
|
const breakdownRates = responseMatrix.breakdownRates || {};
|
|
6585
6655
|
const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
|
|
6586
6656
|
const rate = breakdownRates[code] || 0;
|
|
6587
6657
|
const displayColor = rate > 0 ? code >= 500 ? "#f87171" : "#fde047" : "#64748b";
|
|
6588
6658
|
return `
|
|
6589
6659
|
<div style="background: #090d16; border: 1px solid #1e293b; border-radius: 4px; padding: 0.4rem 0.2rem; text-align: center;">
|
|
6590
|
-
<div style="font-size: 0.7rem; color: #64748b; font-weight: bold;"
|
|
6660
|
+
<div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">${dict.matrix.code} ${code}</div>
|
|
6591
6661
|
<div style="font-size: 0.85rem; font-weight: bold; color: ${displayColor};">${rate.toFixed(2)}%</div>
|
|
6592
6662
|
</div>
|
|
6593
6663
|
`;
|
|
6594
6664
|
}).join("");
|
|
6595
6665
|
const gaugeColor = cards.healthScore >= 90 ? "#10b981" : cards.healthScore >= 70 ? "#f59e0b" : "#ef4444";
|
|
6596
|
-
const htmlContent = `<!DOCTYPE html
|
|
6666
|
+
const htmlContent = `<!DOCTYPE html>
|
|
6667
|
+
<html lang="${config.locale}" dir="${dir}">
|
|
6668
|
+
<head>
|
|
6597
6669
|
<meta charset="UTF-8">
|
|
6598
|
-
<title
|
|
6670
|
+
<title>${dict.title}</title>
|
|
6599
6671
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
6600
6672
|
<style>
|
|
6601
|
-
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
6673
|
+
body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
|
|
6602
6674
|
.container { max-width: 1300px; margin: 0 auto; }
|
|
6603
6675
|
.header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
|
|
6604
6676
|
h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
|
|
@@ -6608,11 +6680,11 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6608
6680
|
.metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
|
|
6609
6681
|
.card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
|
|
6610
6682
|
.card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
|
|
6611
|
-
.card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-
|
|
6683
|
+
.card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; }
|
|
6612
6684
|
.card-value.green { color: #10b981; }
|
|
6613
6685
|
.card-value.orange { color: #f97316; }
|
|
6614
6686
|
.card-value.purple { color: #a855f7; }
|
|
6615
|
-
.card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-
|
|
6687
|
+
.card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-inline-start: 0.4rem; display: inline-block; }
|
|
6616
6688
|
|
|
6617
6689
|
.top-layout-grid { display: grid; grid-template-columns: 1fr 280px 380px; gap: 1.5rem; margin-bottom: 2rem; }
|
|
6618
6690
|
.verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
|
|
@@ -6637,7 +6709,6 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6637
6709
|
.badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
|
|
6638
6710
|
.matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
|
|
6639
6711
|
|
|
6640
|
-
/* Cross-browser engine badge utility styles */
|
|
6641
6712
|
.engine-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-weight: 600; font-size: 12px; }
|
|
6642
6713
|
.badge-blink { background: #dbeafe; color: #1e40af; }
|
|
6643
6714
|
.badge-webkit { background: #fef3c7; color: #92400e; }
|
|
@@ -6652,7 +6723,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6652
6723
|
.card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
|
|
6653
6724
|
h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
|
|
6654
6725
|
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
|
|
6655
|
-
th, td { padding: 0.75rem; text-align:
|
|
6726
|
+
th, td { padding: 0.75rem; text-align: start; border-bottom: 1px solid #1e293b; }
|
|
6656
6727
|
th { color: #94a3b8; font-weight: 600; }
|
|
6657
6728
|
.badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
|
|
6658
6729
|
.badge-success { background: #16a34a; color: #f0fdf4; }
|
|
@@ -6660,41 +6731,41 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6660
6731
|
</style></head><body>
|
|
6661
6732
|
<div class="container">
|
|
6662
6733
|
<div class="header">
|
|
6663
|
-
<h1>\u{1F525}
|
|
6664
|
-
<div class="meta"
|
|
6734
|
+
<h1>\u{1F525} ${dict.title}</h1>
|
|
6735
|
+
<div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
|
|
6665
6736
|
</div>
|
|
6666
6737
|
|
|
6667
6738
|
<div class="cards-wrapper">
|
|
6668
6739
|
<div class="metric-card">
|
|
6669
|
-
<div class="card-title"
|
|
6670
|
-
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext"
|
|
6740
|
+
<div class="card-title">${dict.metrics.apdex}</div>
|
|
6741
|
+
<div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">${dict.metrics.good}</span></div>
|
|
6671
6742
|
</div>
|
|
6672
6743
|
<div class="metric-card">
|
|
6673
|
-
<div class="card-title"
|
|
6744
|
+
<div class="card-title">${dict.metrics.throughput}</div>
|
|
6674
6745
|
<div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
|
|
6675
6746
|
</div>
|
|
6676
6747
|
<div class="metric-card">
|
|
6677
|
-
<div class="card-title"
|
|
6748
|
+
<div class="card-title">${dict.metrics.bandwidth}</div>
|
|
6678
6749
|
<div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
|
|
6679
6750
|
</div>
|
|
6680
6751
|
<div class="metric-card">
|
|
6681
|
-
<div class="card-title"
|
|
6752
|
+
<div class="card-title">${dict.metrics.ttfb}</div>
|
|
6682
6753
|
<div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
|
|
6683
6754
|
</div>
|
|
6684
6755
|
<div class="metric-card">
|
|
6685
|
-
<div class="card-title"
|
|
6756
|
+
<div class="card-title">${dict.metrics.dns}</div>
|
|
6686
6757
|
<div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
|
|
6687
6758
|
</div>
|
|
6688
6759
|
<div class="metric-card">
|
|
6689
|
-
<div class="card-title"
|
|
6760
|
+
<div class="card-title">${dict.metrics.tcp}</div>
|
|
6690
6761
|
<div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
|
|
6691
6762
|
</div>
|
|
6692
6763
|
<div class="metric-card">
|
|
6693
|
-
<div class="card-title"
|
|
6764
|
+
<div class="card-title">${dict.metrics.tls}</div>
|
|
6694
6765
|
<div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
|
|
6695
6766
|
</div>
|
|
6696
6767
|
<div class="metric-card">
|
|
6697
|
-
<div class="card-title"
|
|
6768
|
+
<div class="card-title">${dict.metrics.stability}</div>
|
|
6698
6769
|
<div class="card-value">±${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
|
|
6699
6770
|
</div>
|
|
6700
6771
|
</div>
|
|
@@ -6704,13 +6775,13 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6704
6775
|
|
|
6705
6776
|
<div class="top-layout-grid" style="margin-top: 2rem;">
|
|
6706
6777
|
<div class="verdict-box">
|
|
6707
|
-
<div class="verdict-title"
|
|
6778
|
+
<div class="verdict-title">${dict.verdict}</div>
|
|
6708
6779
|
<div class="verdict-text">${verdictReason}</div>
|
|
6709
6780
|
<div class="verdict-waves">${waveListItems}</div>
|
|
6710
6781
|
</div>
|
|
6711
6782
|
|
|
6712
6783
|
<div class="gauge-container-box">
|
|
6713
|
-
<div class="gauge-title"
|
|
6784
|
+
<div class="gauge-title">${dict.health.title}</div>
|
|
6714
6785
|
<div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
|
|
6715
6786
|
<svg width="140" height="140" viewBox="0 0 140 140" style="transform: rotate(-90deg);">
|
|
6716
6787
|
<circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
|
|
@@ -6724,35 +6795,35 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6724
6795
|
</div>
|
|
6725
6796
|
</div>
|
|
6726
6797
|
<div style="margin-top: 0.75rem; font-size: 0.8rem; color: #94a3b8; font-weight: 500;">
|
|
6727
|
-
|
|
6798
|
+
${dict.health.subtitle}
|
|
6728
6799
|
</div>
|
|
6729
6800
|
</div>
|
|
6730
6801
|
|
|
6731
6802
|
<div class="matrix-box">
|
|
6732
|
-
<div class="matrix-title"
|
|
6803
|
+
<div class="matrix-title">${dict.matrix.title}</div>
|
|
6733
6804
|
<div class="matrix-row">
|
|
6734
|
-
<div class="matrix-label"
|
|
6805
|
+
<div class="matrix-label">${dict.matrix.info} <span class="matrix-badge badge-1xx">1xx</span></div>
|
|
6735
6806
|
<div class="matrix-value">${responseMatrix.total1xx}</div>
|
|
6736
6807
|
</div>
|
|
6737
6808
|
<div class="matrix-row">
|
|
6738
|
-
<div class="matrix-label"
|
|
6809
|
+
<div class="matrix-label">${dict.matrix.success} <span class="matrix-badge badge-2xx">2xx</span></div>
|
|
6739
6810
|
<div class="matrix-value">${responseMatrix.total2xx}</div>
|
|
6740
6811
|
</div>
|
|
6741
6812
|
<div class="matrix-row">
|
|
6742
|
-
<div class="matrix-label"
|
|
6813
|
+
<div class="matrix-label">${dict.matrix.redirects} <span class="matrix-badge badge-3xx">3xx</span></div>
|
|
6743
6814
|
<div class="matrix-value">${responseMatrix.total3xx}</div>
|
|
6744
6815
|
</div>
|
|
6745
6816
|
<div class="matrix-row">
|
|
6746
|
-
<div class="matrix-label"
|
|
6817
|
+
<div class="matrix-label">${dict.matrix.clientErr} <span class="matrix-badge badge-4xx">4xx</span></div>
|
|
6747
6818
|
<div class="matrix-value">${responseMatrix.total4xx}</div>
|
|
6748
6819
|
</div>
|
|
6749
6820
|
<div class="matrix-row">
|
|
6750
|
-
<div class="matrix-label"
|
|
6821
|
+
<div class="matrix-label">${dict.matrix.serverErr} <span class="matrix-badge badge-5xx">5xx</span></div>
|
|
6751
6822
|
<div class="matrix-value">${responseMatrix.total5xx}</div>
|
|
6752
6823
|
</div>
|
|
6753
6824
|
|
|
6754
6825
|
<div class="matrix-title" style="margin-top: 1.5rem; font-size: 0.85rem; border-top: 1px solid #1e293b; padding-top: 1rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">
|
|
6755
|
-
|
|
6826
|
+
${dict.matrix.breakdown}
|
|
6756
6827
|
</div>
|
|
6757
6828
|
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
|
|
6758
6829
|
${breakdownGridHtml}
|
|
@@ -6762,7 +6833,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6762
6833
|
|
|
6763
6834
|
<div class="card" style="margin-top: 2rem; background: #0c172b; border: 1px solid #7c3aed;">
|
|
6764
6835
|
<h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
|
|
6765
|
-
|
|
6836
|
+
${dict.rca}
|
|
6766
6837
|
</h3>
|
|
6767
6838
|
<div style="font-size: 0.95rem; line-height: 1.6; color: #cbd5e1; padding: 0.5rem 0; white-space: pre-wrap; word-break: break-word;">
|
|
6768
6839
|
${geminiAnalysisHtml}
|
|
@@ -6771,47 +6842,45 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6771
6842
|
|
|
6772
6843
|
<div class="charts-grid" style="margin-top: 2rem;">
|
|
6773
6844
|
<div class="graph-card">
|
|
6774
|
-
<div class="graph-title"
|
|
6845
|
+
<div class="graph-title">${dict.charts.volume}</div>
|
|
6775
6846
|
<div style="height: 280px; position: relative;">
|
|
6776
6847
|
<canvas id="volumeChart"></canvas>
|
|
6777
6848
|
</div>
|
|
6778
6849
|
</div>
|
|
6779
6850
|
|
|
6780
6851
|
<div class="graph-card">
|
|
6781
|
-
<div class="graph-title"
|
|
6852
|
+
<div class="graph-title">${dict.charts.concurrency}</div>
|
|
6782
6853
|
<div style="height: 280px; position: relative;">
|
|
6783
6854
|
<canvas id="concurrencyChart"></canvas>
|
|
6784
6855
|
</div>
|
|
6785
6856
|
</div>
|
|
6786
6857
|
|
|
6787
6858
|
<div class="graph-card">
|
|
6788
|
-
<div class="graph-title"
|
|
6859
|
+
<div class="graph-title">${dict.charts.status}</div>
|
|
6789
6860
|
<div style="height: 280px; position: relative;">
|
|
6790
6861
|
<canvas id="statusDonutChart"></canvas>
|
|
6791
6862
|
</div>
|
|
6792
6863
|
</div>
|
|
6793
6864
|
|
|
6794
6865
|
<div class="graph-card">
|
|
6795
|
-
<div class="graph-title"
|
|
6866
|
+
<div class="graph-title">${dict.charts.scatter}</div>
|
|
6796
6867
|
<div style="height: 280px; position: relative;">
|
|
6797
6868
|
<canvas id="ttfbScatterChart"></canvas>
|
|
6798
6869
|
</div>
|
|
6799
6870
|
</div>
|
|
6800
6871
|
</div>
|
|
6801
|
-
|
|
6802
|
-
${nlpQueryEngineHtml}
|
|
6803
6872
|
|
|
6804
6873
|
<div class="card" style="margin-top: 2rem;">
|
|
6805
|
-
<h3
|
|
6874
|
+
<h3>${dict.telemetry.title}</h3>
|
|
6806
6875
|
<table>
|
|
6807
6876
|
<thead>
|
|
6808
6877
|
<tr>
|
|
6809
|
-
<th
|
|
6810
|
-
<th
|
|
6811
|
-
<th
|
|
6812
|
-
<th
|
|
6813
|
-
<th
|
|
6814
|
-
<th
|
|
6878
|
+
<th>${dict.telemetry.threads}</th>
|
|
6879
|
+
<th>${dict.telemetry.throughput}</th>
|
|
6880
|
+
<th>${dict.telemetry.avgLatency}</th>
|
|
6881
|
+
<th>${dict.telemetry.p95}</th>
|
|
6882
|
+
<th>${dict.telemetry.errors}</th>
|
|
6883
|
+
<th>${dict.telemetry.status}</th>
|
|
6815
6884
|
</tr>
|
|
6816
6885
|
</thead>
|
|
6817
6886
|
<tbody id="telemetryTableBody">
|
|
@@ -6819,103 +6888,23 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6819
6888
|
const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
|
|
6820
6889
|
return `<tr>
|
|
6821
6890
|
<td><strong>${h.threads}</strong></td>
|
|
6822
|
-
<td>${h.throughput.toFixed(0)}
|
|
6891
|
+
<td>${h.throughput.toFixed(0)} /s</td>
|
|
6823
6892
|
<td>${h.avgLatency.toFixed(1)} ms</td>
|
|
6824
6893
|
<td>${h.p95Latency.toFixed(1)} ms</td>
|
|
6825
6894
|
<td>${(h.errorRate * 100).toFixed(2)}%</td>
|
|
6826
|
-
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ?
|
|
6895
|
+
<td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? dict.telemetry.pass : dict.telemetry.fail}</span></td>
|
|
6827
6896
|
</tr>`;
|
|
6828
6897
|
}).join("")}
|
|
6829
6898
|
</tbody>
|
|
6830
6899
|
</table>
|
|
6831
6900
|
</div>
|
|
6832
|
-
${seoImpactSectionHtml}
|
|
6833
|
-
${liveAdjusterHtml}
|
|
6834
|
-
${rightSizingHtml}
|
|
6835
6901
|
${logClustersHtml}
|
|
6836
6902
|
</div>
|
|
6837
6903
|
|
|
6838
6904
|
<script>
|
|
6839
|
-
const telemetryHistory = ${JSON.stringify(history)};
|
|
6840
6905
|
const labels = ${JSON.stringify(labels)};
|
|
6841
|
-
const
|
|
6842
|
-
const
|
|
6843
|
-
|
|
6844
|
-
const slider = document.getElementById('concurrencySlider');
|
|
6845
|
-
const sliderVal = document.getElementById('sliderValue');
|
|
6846
|
-
const simThroughput = document.getElementById('simThroughput');
|
|
6847
|
-
const simLatency = document.getElementById('simLatency');
|
|
6848
|
-
|
|
6849
|
-
if (slider) {
|
|
6850
|
-
slider.addEventListener('input', (e) => {
|
|
6851
|
-
const val = parseInt(e.target.value, 10);
|
|
6852
|
-
sliderVal.textContent = val;
|
|
6853
|
-
const factor = val / ${config.threads || 10};
|
|
6854
|
-
simThroughput.textContent = (baseThroughput * Math.min(factor, 2.5)).toFixed(0) + '/s';
|
|
6855
|
-
simLatency.textContent = (baseLatency * Math.pow(factor, 0.8)).toFixed(1) + 'ms';
|
|
6856
|
-
});
|
|
6857
|
-
}
|
|
6858
|
-
|
|
6859
|
-
function executeNlpQuery(queryString) {
|
|
6860
|
-
const q = queryString.toLowerCase().trim();
|
|
6861
|
-
const rows = document.querySelectorAll('#telemetryTableBody tr');
|
|
6862
|
-
const feedback = document.getElementById('nlpQueryFeedback');
|
|
6863
|
-
|
|
6864
|
-
let filterFn = (row, data) => true;
|
|
6865
|
-
const numMatch = q.match(/(d+(?:.d+)?)/);
|
|
6866
|
-
const targetNum = numMatch ? parseFloat(numMatch[1]) : null;
|
|
6867
|
-
|
|
6868
|
-
const isGreater = q.includes('>') || q.includes('greater') || q.includes('above') || q.includes('more');
|
|
6869
|
-
const isLess = q.includes('<') || q.includes('less') || q.includes('below') || q.includes('under');
|
|
6870
|
-
|
|
6871
|
-
if (q.includes('latency') || q.includes('p95') || q.includes('ms')) {
|
|
6872
|
-
if (targetNum !== null) {
|
|
6873
|
-
filterFn = (row, data) => isLess ? (data.p95Latency < targetNum) : (data.p95Latency > targetNum);
|
|
6874
|
-
} else {
|
|
6875
|
-
filterFn = (row, data) => data.p95Latency > 0;
|
|
6876
|
-
}
|
|
6877
|
-
} else if (q.includes('error') || q.includes('fail') || q.includes('%')) {
|
|
6878
|
-
if (targetNum !== null) {
|
|
6879
|
-
const searchVal = q.includes('%') ? targetNum / 100 : targetNum;
|
|
6880
|
-
filterFn = (row, data) => isLess ? (data.errorRate < searchVal) : (data.errorRate > searchVal);
|
|
6881
|
-
} else {
|
|
6882
|
-
filterFn = (row, data) => data.errorRate > 0;
|
|
6883
|
-
}
|
|
6884
|
-
} else if (q.includes('concurrency') || q.includes('thread') || q.includes('vu')) {
|
|
6885
|
-
if (targetNum !== null) {
|
|
6886
|
-
filterFn = (row, data) => isLess ? (data.threads < targetNum) : (data.threads > targetNum);
|
|
6887
|
-
}
|
|
6888
|
-
}
|
|
6889
|
-
|
|
6890
|
-
let matchCount = 0;
|
|
6891
|
-
rows.forEach((row, idx) => {
|
|
6892
|
-
const data = telemetryHistory[idx];
|
|
6893
|
-
if (!data) return;
|
|
6894
|
-
if (filterFn(row, data)) {
|
|
6895
|
-
row.style.display = '';
|
|
6896
|
-
row.style.background = q !== '' ? 'rgba(14, 165, 233, 0.15)' : '';
|
|
6897
|
-
matchCount++;
|
|
6898
|
-
} else {
|
|
6899
|
-
row.style.display = 'none';
|
|
6900
|
-
}
|
|
6901
|
-
});
|
|
6902
|
-
|
|
6903
|
-
feedback.innerHTML = q === '' ? '' : '\u{1F50D} Match verification resolved <strong>' + matchCount + '</strong> parameters inside telemetry array.';
|
|
6904
|
-
}
|
|
6905
|
-
|
|
6906
|
-
function setNlpQuery(str) {
|
|
6907
|
-
const input = document.getElementById('nlpQueryInput');
|
|
6908
|
-
input.value = str;
|
|
6909
|
-
executeNlpQuery(str);
|
|
6910
|
-
}
|
|
6911
|
-
|
|
6912
|
-
document.getElementById('nlpQueryBtn').addEventListener('click', () => {
|
|
6913
|
-
executeNlpQuery(document.getElementById('nlpQueryInput').value);
|
|
6914
|
-
});
|
|
6915
|
-
|
|
6916
|
-
document.getElementById('nlpQueryInput').addEventListener('keyup', (e) => {
|
|
6917
|
-
if (e.key === 'Enter') executeNlpQuery(e.target.value);
|
|
6918
|
-
});
|
|
6906
|
+
const successRequestsData = ${JSON.stringify(successRequestsData)};
|
|
6907
|
+
const failedWorkloadsData = ${JSON.stringify(failedWorkloadsData)};
|
|
6919
6908
|
|
|
6920
6909
|
new Chart(document.getElementById('volumeChart'), {
|
|
6921
6910
|
type: 'bar',
|
|
@@ -6923,16 +6912,16 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6923
6912
|
labels: labels,
|
|
6924
6913
|
datasets: [
|
|
6925
6914
|
{
|
|
6926
|
-
label: '
|
|
6927
|
-
data:
|
|
6915
|
+
label: '${dict.matrix.success}',
|
|
6916
|
+
data: successRequestsData,
|
|
6928
6917
|
backgroundColor: '#10b981',
|
|
6929
6918
|
stack: 'requests',
|
|
6930
6919
|
barPercentage: 0.95,
|
|
6931
6920
|
categoryPercentage: 0.95
|
|
6932
6921
|
},
|
|
6933
6922
|
{
|
|
6934
|
-
label: '
|
|
6935
|
-
data:
|
|
6923
|
+
label: '${dict.matrix.serverErr}',
|
|
6924
|
+
data: failedWorkloadsData,
|
|
6936
6925
|
backgroundColor: '#ef4444',
|
|
6937
6926
|
stack: 'requests',
|
|
6938
6927
|
barPercentage: 0.95,
|
|
@@ -6955,50 +6944,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
6955
6944
|
|
|
6956
6945
|
const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
|
|
6957
6946
|
const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
let extendedLabels = [...labels];
|
|
6961
|
-
let extendedThreads = [...baseActiveThreads];
|
|
6962
|
-
let extendedTtf = [...baseTtfTrend];
|
|
6963
|
-
let aiForecastData = [];
|
|
6964
|
-
|
|
6965
|
-
if (historicalCount >= 2) {
|
|
6966
|
-
let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
|
|
6967
|
-
for (let i = 0; i < historicalCount; i++) {
|
|
6968
|
-
sumX += i;
|
|
6969
|
-
sumY += baseTtfTrend[i];
|
|
6970
|
-
sumXY += i * baseTtfTrend[i];
|
|
6971
|
-
sumXX += i * i;
|
|
6972
|
-
}
|
|
6973
|
-
const denominator = (historicalCount * sumXX - sumX * sumX);
|
|
6974
|
-
const slope = denominator === 0 ? 0 : (historicalCount * sumXY - sumX * sumY) / denominator;
|
|
6975
|
-
const intercept = (sumY - slope * sumX) / historicalCount;
|
|
6976
|
-
|
|
6977
|
-
for (let i = 0; i < historicalCount; i++) {
|
|
6978
|
-
aiForecastData.push(Number((slope * i + intercept).toFixed(1)));
|
|
6979
|
-
}
|
|
6980
|
-
|
|
6981
|
-
const lastThreads = baseActiveThreads[historicalCount - 1] || 10;
|
|
6982
|
-
const threadStep = ${config.stepSize || 15};
|
|
6983
|
-
|
|
6984
|
-
for (let i = 1; i <= 3; i++) {
|
|
6985
|
-
extendedLabels.push('+' + (i * 5) + 's (AI Forecast)');
|
|
6986
|
-
extendedThreads.push(lastThreads + (threadStep * i));
|
|
6987
|
-
extendedTtf.push(null);
|
|
6988
|
-
aiForecastData.push(Number((slope * (historicalCount + i - 1) + intercept).toFixed(1)));
|
|
6989
|
-
}
|
|
6990
|
-
} else {
|
|
6991
|
-
aiForecastData = [...baseTtfTrend];
|
|
6992
|
-
}
|
|
6993
|
-
|
|
6947
|
+
|
|
6994
6948
|
new Chart(document.getElementById('concurrencyChart'), {
|
|
6995
6949
|
type: 'line',
|
|
6996
6950
|
data: {
|
|
6997
|
-
labels:
|
|
6951
|
+
labels: labels,
|
|
6998
6952
|
datasets: [
|
|
6999
6953
|
{
|
|
7000
|
-
label: '
|
|
7001
|
-
data:
|
|
6954
|
+
label: 'VUs',
|
|
6955
|
+
data: baseActiveThreads,
|
|
7002
6956
|
borderColor: '#38bdf8',
|
|
7003
6957
|
backgroundColor: 'rgba(56, 189, 248, 0.1)',
|
|
7004
6958
|
borderWidth: 2.5,
|
|
@@ -7007,25 +6961,14 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7007
6961
|
yAxisID: 'yThreads'
|
|
7008
6962
|
},
|
|
7009
6963
|
{
|
|
7010
|
-
label: '
|
|
7011
|
-
data:
|
|
6964
|
+
label: 'TTF',
|
|
6965
|
+
data: baseTtfTrend,
|
|
7012
6966
|
borderColor: '#f43f5e',
|
|
7013
6967
|
borderWidth: 2,
|
|
7014
6968
|
borderDash: [5, 5],
|
|
7015
6969
|
pointRadius: 3,
|
|
7016
6970
|
fill: false,
|
|
7017
6971
|
yAxisID: 'yTtf'
|
|
7018
|
-
},
|
|
7019
|
-
{
|
|
7020
|
-
label: '\u{1F52E} AI Capacity Forecast Line',
|
|
7021
|
-
data: aiForecastData,
|
|
7022
|
-
borderColor: '#a855f7',
|
|
7023
|
-
borderWidth: 2,
|
|
7024
|
-
borderDash: [3, 3],
|
|
7025
|
-
pointRadius: 4,
|
|
7026
|
-
pointBackgroundColor: '#a855f7',
|
|
7027
|
-
fill: false,
|
|
7028
|
-
yAxisID: 'yTtf'
|
|
7029
6972
|
}
|
|
7030
6973
|
]
|
|
7031
6974
|
},
|
|
@@ -7037,8 +6980,8 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7037
6980
|
},
|
|
7038
6981
|
scales: {
|
|
7039
6982
|
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
7040
|
-
yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' }
|
|
7041
|
-
yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' }
|
|
6983
|
+
yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' } },
|
|
6984
|
+
yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' } }
|
|
7042
6985
|
}
|
|
7043
6986
|
}
|
|
7044
6987
|
});
|
|
@@ -7046,7 +6989,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7046
6989
|
new Chart(document.getElementById('statusDonutChart'), {
|
|
7047
6990
|
type: 'doughnut',
|
|
7048
6991
|
data: {
|
|
7049
|
-
labels: ['1xx
|
|
6992
|
+
labels: ['1xx', '2xx', '3xx', '4xx', '5xx'],
|
|
7050
6993
|
datasets: [{
|
|
7051
6994
|
data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
|
|
7052
6995
|
backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
|
|
@@ -7068,7 +7011,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7068
7011
|
type: 'scatter',
|
|
7069
7012
|
data: {
|
|
7070
7013
|
datasets: [{
|
|
7071
|
-
label: '
|
|
7014
|
+
label: 'Delay',
|
|
7072
7015
|
data: ${JSON.stringify(scatterPoints)},
|
|
7073
7016
|
backgroundColor: '#a855f7',
|
|
7074
7017
|
pointRadius: 4,
|
|
@@ -7082,15 +7025,15 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7082
7025
|
legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
|
|
7083
7026
|
},
|
|
7084
7027
|
scales: {
|
|
7085
|
-
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }
|
|
7086
|
-
y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' }
|
|
7028
|
+
x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
|
|
7029
|
+
y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
|
|
7087
7030
|
}
|
|
7088
7031
|
}
|
|
7089
7032
|
});
|
|
7090
7033
|
</script></body></html>`;
|
|
7091
7034
|
try {
|
|
7092
7035
|
fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
|
|
7093
|
-
console.log(`\u{1F4CA} Standalone Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
|
|
7036
|
+
console.log(`\u{1F4CA} Standalone Localized Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
|
|
7094
7037
|
} catch (err) {
|
|
7095
7038
|
console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
|
|
7096
7039
|
}
|
|
@@ -7098,7 +7041,7 @@ function exportHtmlDashboard(history, config, verdictReason, semanticReport, res
|
|
|
7098
7041
|
function printUsage() {
|
|
7099
7042
|
console.log(`Blaze Core Performance Test CLI Engine
|
|
7100
7043
|
Options:
|
|
7101
|
-
--threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --option elif`);
|
|
7044
|
+
--threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --locale <lang_code> | --option elif`);
|
|
7102
7045
|
}
|
|
7103
7046
|
runCli().catch(console.error);
|
|
7104
7047
|
export {
|