@avitalique/k6-html-reporter
Version:
A html reporter for k6
215 lines • 8.16 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generate = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const ejs_1 = __importDefault(require("ejs"));
const types_1 = require("./types");
const util_1 = require("./util");
function generate(options) {
const resolvedInputPath = path_1.default.resolve(process.cwd(), options.jsonFile);
const resolvedOutputPath = path_1.default.resolve(process.cwd(), options.output);
const jsonReport = readJsonReport(resolvedInputPath);
writeHtmlReport(jsonReport, resolvedOutputPath);
}
exports.generate = generate;
function readJsonReport(filePath) {
const resolvedPath = path_1.default.resolve(__dirname, filePath);
const rawData = fs_1.default.readFileSync(resolvedPath);
return JSON.parse(rawData.toString());
}
function writeHtmlReport(content, filePath) {
const time = new Date().toLocaleString();
const templatePath = path_1.default.resolve(__dirname, "../templates/template.ejs");
const checkRootGroupData = content["root_group"];
const metricsData = content["metrics"];
const { checkMetric, counterMetrics, trendMetrics, gaugeMetrics, rateMetrics, allThresholds, totalThresholdResult, } = mapMetrics(metricsData);
const checks = getChecks(checkRootGroupData).map((data) => {
const splitedPath = data.path.split("::");
splitedPath.shift();
return {
...data,
pathArray: splitedPath.join(" \u21C0 "),
};
});
// add pass rate to checks
checks.forEach((check) => {
check['passRate'] = '' + (0, util_1.calculatePassRate)(check['passes'], check['fails']) + '%';
});
// humanize counter metric values
counterMetrics.forEach((metric) => {
if (metric.contains === 'data') {
metric.values.count = (0, util_1.humanizeBytes)(metric.values.count);
metric.values.rate = (0, util_1.humanizeBytes)(metric.values.rate) + '/s';
}
else {
metric.values.rate = '' + (0, util_1.roundDecimal)(metric.values.rate, 2) + '/s';
}
});
// humanize trend metric values
trendMetrics.forEach((metric) => {
Object.keys(metric.values).forEach((key) => {
metric.values[key] = (0, util_1.humanizeDuration)(metric.values[key]);
});
});
// humanize rate metric values
rateMetrics.forEach((metric) => {
metric.values.rate = '' + (0, util_1.roundDecimal)(metric.values.rate, 3) + '/s';
});
ejs_1.default.renderFile(templatePath, {
checks,
rateMetrics: rateMetrics.sort(util_1.compareNameAscending),
checkMetric,
counterMetrics: counterMetrics.sort(util_1.compareNameAscending),
trendMetrics: trendMetrics.sort(util_1.compareNameAscending),
gaugeMetrics: gaugeMetrics.sort(util_1.compareNameAscending),
allThresholds,
totalThresholdResult,
time,
}, {}, function (err, str) {
if (err) {
console.error(err);
}
let html = ejs_1.default.render(str);
if (!fs_1.default.existsSync(filePath)) {
fs_1.default.mkdirSync(filePath, { recursive: true });
}
fs_1.default.writeFileSync(`${filePath}/report.html`, html);
console.log(`Report is created at ${filePath}`);
});
}
function mapMetrics(data) {
let checkMetric = {};
const counterMetrics = [];
const trendMetrics = [];
const gaugeMetrics = [];
const rateMetrics = [];
const allThresholds = [];
let totalThresholdResult = {
passes: 0,
fails: 0,
failedMetricsNum: 0,
};
Object.entries(data).forEach(([key, value]) => {
if (value.type === types_1.MetricsType.COUNTER) {
const { updatedThresholdResult, displayThreshold, metric } = handleMetricValues(key, value, totalThresholdResult);
totalThresholdResult = {
...totalThresholdResult,
...updatedThresholdResult,
};
if (displayThreshold) {
allThresholds.push(displayThreshold);
}
counterMetrics.push(metric);
}
else if (value.type === types_1.MetricsType.TREND) {
const { updatedThresholdResult, displayThreshold, metric } = handleMetricValues(key, value, totalThresholdResult);
totalThresholdResult = {
...totalThresholdResult,
...updatedThresholdResult,
};
if (displayThreshold) {
allThresholds.push(displayThreshold);
}
trendMetrics.push(metric);
}
else if (key === "checks") {
const { updatedThresholdResult, displayThreshold, metric } = handleMetricValues(key, value, totalThresholdResult);
totalThresholdResult = {
...totalThresholdResult,
...updatedThresholdResult,
};
if (displayThreshold) {
allThresholds.push(displayThreshold);
}
checkMetric = metric;
}
else if (value.type === types_1.MetricsType.GAUGE) {
const { updatedThresholdResult, displayThreshold, metric } = handleMetricValues(key, value, totalThresholdResult);
totalThresholdResult = {
...totalThresholdResult,
...updatedThresholdResult,
};
if (displayThreshold) {
allThresholds.push(displayThreshold);
}
gaugeMetrics.push(metric);
}
else if (value.type === types_1.MetricsType.RATE && key !== "checks") {
const { updatedThresholdResult, displayThreshold, metric } = handleMetricValues(key, value, totalThresholdResult);
totalThresholdResult = {
...totalThresholdResult,
...updatedThresholdResult,
};
if (displayThreshold) {
allThresholds.push(displayThreshold);
}
rateMetrics.push(metric);
}
});
return {
checkMetric,
counterMetrics,
trendMetrics,
gaugeMetrics,
rateMetrics,
allThresholds,
totalThresholdResult,
};
}
function handleMetricValues(key, value, currentTotalThresholdResult) {
const metric = {
name: key,
...value,
thresholdFailed: undefined,
};
const updatedThresholdResult = { ...currentTotalThresholdResult };
if (value.thresholds) {
const [passes, fails] = thresholdResult(value.thresholds);
if (fails > 0 && key !== "checks") {
updatedThresholdResult.failedMetricsNum++;
}
updatedThresholdResult.passes += passes;
updatedThresholdResult.fails += fails;
metric.thresholdFailed = fails > 0;
return {
displayThreshold: {
name: key,
thresholds: value.thresholds,
},
updatedThresholdResult,
metric,
};
}
return { updatedThresholdResult, metric };
}
function thresholdResult(thresholds) {
if (thresholds) {
const thresholdArr = Object.values(thresholds);
const passes = thresholdArr.filter((value) => value.ok === true).length;
const fails = thresholdArr.length - passes;
return [passes, fails];
}
}
function getChecks(data) {
let checksOutput = [];
findChecksRecursively(data);
function findChecksRecursively(data) {
if (data.groups.length === 0 && data.checks.length === 0) {
return;
}
if (data.checks.length > 0) {
Object.values(data.checks).forEach((value) => {
checksOutput.push(value);
});
}
for (let item in data.groups) {
findChecksRecursively(data.groups[item]);
}
}
return checksOutput;
}
//# sourceMappingURL=reporter.js.map