mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-10-20 08:31:56 +00:00
61 lines
3.8 KiB
TypeScript
61 lines
3.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { signalHours, signalBinLabel, signalHistogramOption, signalTrendOption, signalCoverageOption } from "../../../src/features/stats/signal";
|
|
import { readChartColors } from "../../../src/features/stats/chartTheme";
|
|
import type { SignalStats } from "../../../src/features/stats/types";
|
|
import i18n from "../../../src/i18n";
|
|
|
|
const en = i18n.getFixedT("en");
|
|
const fr = i18n.getFixedT("fr");
|
|
|
|
const hour = 3_600_000;
|
|
export const signalFixture: SignalStats = {
|
|
since: hour / 2, until: 3 * hour + hour / 2, receptions: 100,
|
|
snr: { samples: 80, average: 0, histogram: [{ lower: null, upper: -30, count: 2 }, { lower: -30, upper: -25, count: 78 }, { lower: 30, upper: null, count: 0 }] },
|
|
rssi: { samples: 90, average: -102.5, histogram: [{ lower: -110, upper: -100, count: 90 }] },
|
|
hourly: [
|
|
{ hour: 0, receptions: 50, snrSamples: 50, snrAverage: 0, rssiSamples: 50, rssiAverage: -100 },
|
|
{ hour: 2 * hour, receptions: 40, snrSamples: 30, snrAverage: 0, rssiSamples: 40, rssiAverage: -105.625 },
|
|
{ hour: 3 * hour, receptions: 10, snrSamples: 0, snrAverage: null, rssiSamples: 0, rssiAverage: null },
|
|
],
|
|
};
|
|
|
|
describe("signal analytics", () => {
|
|
it("keeps UTC window edges, true zeros, missing hours and missing readings distinct", () => {
|
|
const before = JSON.stringify(signalFixture);
|
|
const hours = signalHours(signalFixture);
|
|
expect(hours.map((h) => h.hour)).toEqual([0, hour, 2 * hour, 3 * hour]);
|
|
expect(hours.map((h) => h.snrAverage)).toEqual([0, null, 0, null]);
|
|
expect(hours.map((h) => h.receptions)).toEqual([50, null, 40, 10]);
|
|
expect(JSON.stringify(signalFixture)).toBe(before);
|
|
expect(signalHours(undefined)).toEqual([]);
|
|
expect(signalHours({ ...signalFixture, until: 30 * 24 * hour + hour / 2, hourly: [] })).toHaveLength(721);
|
|
});
|
|
it("labels exact half-open bins including overflow without assigning quality ratings", () => {
|
|
expect(signalBinLabel({ lower: null, upper: -30, count: 2 }, en)).toBe("< -30");
|
|
expect(signalBinLabel({ lower: -30, upper: -25, count: 2 }, en)).toBe("-30 to < -25");
|
|
expect(signalBinLabel({ lower: 30, upper: null, count: 2 }, en)).toBe("≥ 30");
|
|
const colors = readChartColors();
|
|
expect(signalHistogramOption(signalFixture.snr, "SNR", "dB", colors, en)).toMatchObject({ animation: false, tooltip: { renderMode: "richText" }, aria: { enabled: true } });
|
|
expect(signalTrendOption(signalHours(signalFixture), "snr", colors, en)).toMatchObject({ animation: false, series: [{ connectNulls: false, showSymbol: true, data: [[0, 0], [hour, null], [2 * hour, 0], [3 * hour, null]] }] });
|
|
});
|
|
|
|
it("translates plotted text without changing bin boundaries, coverage counts or UTC gaps", () => {
|
|
const before = JSON.stringify(signalFixture), colors = readChartColors();
|
|
expect(signalHistogramOption(signalFixture.snr, "SNR", "dB", colors, fr)).toMatchObject({
|
|
tooltip: { renderMode: "richText" },
|
|
aria: { label: { description: expect.stringContaining("Distribution du SNR en dB") } },
|
|
xAxis: { data: ["< -30 dB", "-30 à < -25 dB", "≥ 30 dB"] },
|
|
series: [{ name: "Échantillons SNR", data: [{ value: 2 }, { value: 78 }, { value: 0 }] }],
|
|
});
|
|
expect(signalCoverageOption(signalFixture, colors, fr)).toMatchObject({
|
|
series: [{ name: "Disponibles", data: [80, 90] }, { name: "Indisponibles", data: [20, 10] }],
|
|
});
|
|
const trend = signalTrendOption(signalHours(signalFixture), "snr", colors, fr);
|
|
expect(trend).toMatchObject({ useUTC: true, series: [{ connectNulls: false, data: [[0, 0], [hour, null], [2 * hour, 0], [3 * hour, null]] }] });
|
|
const tooltip = trend.tooltip as { valueFormatter: (value: unknown) => string };
|
|
expect(tooltip.valueFormatter(null)).toBe("Aucun échantillon");
|
|
expect(tooltip.valueFormatter(0)).toBe("0.00 dB");
|
|
expect(JSON.stringify(signalFixture)).toBe(before);
|
|
});
|
|
});
|