feat(statistics): show rolling account usage

This commit is contained in:
Sucukdeluxe
2026-08-21 08:30:50 +02:00
parent e24f685a71
commit 4de4f4a0be
5 changed files with 173 additions and 26 deletions
+87 -9
View File
@@ -9,8 +9,9 @@ import {
type StatisticsMetric
} from "../src/renderer/views/statistics/statistics-model";
import {
StatisticsContent,
StatisticsSidebar,
StatisticsContent,
StatisticsSidebar,
StatisticsSidebarStatus,
StatisticsView,
type StatisticsViewActions
} from "../src/renderer/views/statistics/StatisticsView";
@@ -210,7 +211,63 @@ describe("statistics model", () => {
["realdebrid", 500]
]);
expect(model.providers.map((row) => [row.completed, row.failed])).toEqual([[0, 1], [1, 0]]);
});
});
it("shows rolling account traffic with unavailable day-scoped metrics", () => {
const snapshot = createSnapshot();
snapshot.stats.statistics = {
version: 2,
startedAt: now - (2 * 60 * 60 * 1_000),
days: [],
minutes: []
};
snapshot.stats.rolling24Hours = {
from: now - (24 * 60 * 60 * 1_000),
to: now,
downloadedBytes: 125,
accounts: [
{ id: "rdw_two", provider: "realdebrid", label: "Secondary", bytes: 75 },
{ id: "rdw_one", provider: "realdebrid", label: "Primary", bytes: 50 }
]
};
const model = buildStatisticsViewModel(snapshot, "last24", now);
expect(model.metrics.downloadedBytes).toMatchObject({ value: 125, available: true });
expectUnavailable(model.metrics.files);
expectUnavailable(model.metrics.successRate);
expectUnavailable(model.metrics.averageSpeedBps);
expectUnavailable(model.metrics.errors);
expect(model.usageKind).toBe("accounts");
expect(model.providerScope).toBe("last24");
expect(model.providers.map((row) => [row.id, row.label, row.bytes])).toEqual([
["rdw_two", "Real-Debrid · Secondary", 75],
["rdw_one", "Real-Debrid · Primary", 50]
]);
expect(model.providers.map((row) => [row.completed, row.failed])).toEqual([[null, null], [null, null]]);
expect(model.message).toContain("seit Beginn der Aufzeichnung");
});
it("shows a complete rolling description after 24 hours of statistics coverage", () => {
const snapshot = createSnapshot();
snapshot.stats.statistics = {
version: 2,
startedAt: now - (25 * 60 * 60 * 1_000),
days: [],
minutes: []
};
snapshot.stats.rolling24Hours = {
from: now - (24 * 60 * 60 * 1_000),
to: now,
downloadedBytes: 0,
accounts: []
};
const model = buildStatisticsViewModel(snapshot, "last24", now);
expect(model.message).toContain("vergangenen 24 Stunden");
expect(model.message).not.toContain("seit Beginn der Aufzeichnung");
});
it("treats a stale daily key as a genuine zero today without stale provider rows", () => {
const snapshot = createSnapshot();
@@ -340,7 +397,7 @@ describe("statistics view", () => {
const html = renderToStaticMarkup(<StatisticsSidebar actions={createActions()} model={model} />);
expect(html).toContain("ui-sliding-selection ui-sliding-selection-vertical");
expect(html.match(/data-sliding-selection-item="true"/g)).toHaveLength(5);
expect(html.match(/data-sliding-selection-item="true"/g)).toHaveLength(6);
expect(html.match(/data-sliding-selection-active="true"/g)).toHaveLength(1);
});
@@ -360,8 +417,8 @@ describe("statistics view", () => {
for (const marker of ["statistics-sidebar", "statistics-kpis", "statistics-chart"]) {
expect(html.match(new RegExp(`data-visual-region=\\"${marker}\\"`, "g"))).toHaveLength(1);
}
for (const label of ["Sitzung", "Heute", "Sieben Tage", "30 Tage", "Gesamt"]) {
expect(html).toContain(`>${label}<`);
for (const label of ["Sitzung", "Heute", "Letzte 24 Stunden", "Sieben Tage", "30 Tage", "Gesamt"]) {
expect(html).toContain(`>${label}<`);
}
expect(html).toContain("Bestehender Bandbreitenverlauf");
expect(html).not.toContain("downloads-toolbar");
@@ -410,7 +467,7 @@ describe("statistics view", () => {
expect(findButton(failedContent, "Fehler zurücksetzen").props.disabled).toBe(false);
});
it("keeps the empty provider state inside the ARIA table as a row and spanning cell", () => {
it("keeps the empty provider state inside the ARIA table as a row and spanning cell", () => {
const html = renderToStaticMarkup(
<StatisticsContent
actions={createActions()}
@@ -420,8 +477,29 @@ describe("statistics view", () => {
);
expect(html).toContain('class="statistics-provider-empty" role="row"');
expect(html).toContain('aria-colspan="3" role="cell"');
});
expect(html).toContain('aria-colspan="3" role="cell"');
});
it("renders the rolling range as an account table with its own empty state", () => {
const snapshot = createSnapshot();
snapshot.stats.rolling24Hours = {
from: now - (24 * 60 * 60 * 1_000),
to: now,
downloadedBytes: 0,
accounts: []
};
const model = buildStatisticsViewModel(snapshot, "last24", now);
const html = renderToStaticMarkup(<>
<StatisticsSidebarStatus model={model} />
<StatisticsContent actions={createActions()} chart={<div />} model={model} />
</>);
expect(html).toContain("<h3>Accounts</h3>");
expect(html).toContain('aria-label="Account-Nutzung"');
expect(html).toContain('<span role="columnheader">Account</span>');
expect(html).toContain("In den vergangenen 24 Stunden wurde noch kein Account-Traffic erfasst.");
expect(html).toContain("Accounts: 0");
});
});
describe("bandwidth chart palette", () => {
+2
View File
@@ -87,6 +87,8 @@ describe("visual fixtures", () => {
const update = createVisualFixture("update");
expect(Object.keys(empty.snapshot.session.packages)).toHaveLength(0);
expect(Object.keys(dense.snapshot.session.packages).length).toBeGreaterThan(1);
expect(dense.snapshot.stats.rolling24Hours?.accounts).toHaveLength(2);
expect(dense.snapshot.stats.statistics?.minutes).toEqual([]);
expect(update.update.latestTag).toBe("v9.9.9");
expect(createVisualFixture("dense")).toEqual(dense);
});
+20 -1
View File
@@ -496,8 +496,27 @@ function createDenseSnapshot(): UiSnapshot {
}
}
]
},
rolling24Hours: {
from: 1786226400000,
to: 1786312800000,
downloadedBytes: 541165879488,
accounts: [
{
id: "rdw_visual_primary",
provider: "realdebrid",
label: "xSucukDE",
bytes: 328565653504
},
{
id: "dl_visual_secondary",
provider: "debridlink",
label: "Debrid-Link Key 2",
bytes: 212600225984
}
]
}
};
};
snapshot.speedText = "12,0 MB/s";
snapshot.etaText = "00:17:24";
snapshot.canStart = true;