1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-02-15 07:25:54 +00:00

Add tests for energy chart functions (#29504)

This commit is contained in:
Petar Petrov
2026-02-09 15:07:11 +01:00
committed by GitHub
parent d47738aa24
commit aa13c6fa53

View File

@@ -1,7 +1,12 @@
import { assert, describe, it } from "vitest";
import type { LineSeriesOption } from "echarts/charts";
import type { BarSeriesOption, LineSeriesOption } from "echarts/charts";
import { fillLineGaps } from "../../../../../../src/panels/lovelace/cards/energy/common/energy-chart-options";
import {
fillDataGapsAndRoundCaps,
fillLineGaps,
getCompareTransform,
getSuggestedMax,
} from "../../../../../../src/panels/lovelace/cards/energy/common/energy-chart-options";
// Helper to get x value from either [x,y] or {value: [x,y]} format
function getX(item: any): number {
@@ -13,6 +18,76 @@ function getY(item: any): number {
return item?.value?.[1] ?? item?.[1];
}
describe("getSuggestedMax", () => {
it("returns end date unchanged for 5minute period", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("5minute", end, false);
assert.equal(result.getTime(), end.getTime());
});
it("returns end date unchanged when noRounding is true", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("hour", end, true);
assert.equal(result.getTime(), end.getTime());
});
it("rounds down to start of hour for hour period", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("hour", end, false);
assert.equal(result.getMinutes(), 0);
assert.equal(result.getSeconds(), 0);
assert.equal(result.getMilliseconds(), 0);
assert.equal(result.getHours(), 14);
});
it("rounds down to start of day for day period", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("day", end, false);
assert.equal(result.getHours(), 0);
assert.equal(result.getMinutes(), 0);
assert.equal(result.getDate(), 15);
});
it("rounds down to start of day for week period", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("week", end, false);
assert.equal(result.getHours(), 0);
assert.equal(result.getMinutes(), 0);
assert.equal(result.getDate(), 15);
});
it("rounds down to start of month for month period", () => {
const end = new Date("2024-03-15T14:37:22.000");
const result = getSuggestedMax("month", end, false);
assert.equal(result.getDate(), 1);
assert.equal(result.getHours(), 0);
assert.equal(result.getMonth(), 2); // March = 2
});
it("corrects DST edge case when hour is 0 for day period", () => {
// Simulate a time that lands exactly on midnight (e.g. DST adjustment)
const end = new Date("2024-03-15T00:00:00.000");
const result = getSuggestedMax("day", end, false);
// Should subtract an hour first, landing on previous day
assert.equal(result.getDate(), 14);
assert.equal(result.getHours(), 0);
});
it("does not apply DST correction when hour is nonzero", () => {
const end = new Date("2024-03-15T10:30:00.000");
const result = getSuggestedMax("day", end, false);
assert.equal(result.getDate(), 15);
assert.equal(result.getHours(), 0);
});
it("does not mutate the input date", () => {
const end = new Date("2024-03-15T14:37:22.000");
const originalTime = end.getTime();
getSuggestedMax("month", end, false);
assert.equal(end.getTime(), originalTime);
});
});
describe("fillLineGaps", () => {
it("fills gaps in datasets with missing timestamps", () => {
const datasets: LineSeriesOption[] = [
@@ -197,3 +272,264 @@ describe("fillLineGaps", () => {
assert.equal(secondItem.itemStyle.color, "red");
});
});
// Helper to get bar data item
function getBarItem(dataset: BarSeriesOption, index: number): any {
const dp = dataset.data![index];
return dp && typeof dp === "object" && "value" in dp ? dp : { value: dp };
}
describe("fillDataGapsAndRoundCaps", () => {
it("fills missing buckets with zero values", () => {
// When a dataset has entries at some but not all bucket positions,
// the function splices in zero-value entries to align them
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [
[1000, 10],
[3000, 30],
],
},
{
type: "bar",
stack: "a",
data: [
[1000, 100],
[2000, 200],
[3000, 300],
],
},
];
fillDataGapsAndRoundCaps(datasets);
// First dataset should now have 3 entries with bucket 2000 filled in
assert.equal(datasets[0].data!.length, 3);
const filled = getBarItem(datasets[0], 1);
assert.equal(filled.value[0], 2000);
assert.equal(filled.value[1], 0);
assert.equal(filled.itemStyle.borderWidth, 0);
});
it("sets borderWidth 0 on zero-value existing entries", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [
[1000, 0],
[2000, 5],
],
},
];
fillDataGapsAndRoundCaps(datasets);
const zeroItem = getBarItem(datasets[0], 0);
assert.equal(zeroItem.itemStyle.borderWidth, 0);
});
it("rounds caps on top positive bar in stack", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [[1000, 10]],
},
{
type: "bar",
stack: "a",
data: [[1000, 20]],
},
];
fillDataGapsAndRoundCaps(datasets);
// Last dataset (topmost positive) gets rounded top caps
// Iteration is reverse so first positive hit from the end gets the cap
const topItem = getBarItem(datasets[1], 0);
assert.deepEqual(topItem.itemStyle.borderRadius, [4, 4, 0, 0]);
// Bottom dataset should NOT have rounded caps
const bottomItem = getBarItem(datasets[0], 0);
assert.equal(bottomItem.itemStyle?.borderRadius, undefined);
});
it("rounds caps on bottom negative bar in stack", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [[1000, -10]],
},
{
type: "bar",
stack: "a",
data: [[1000, -20]],
},
];
fillDataGapsAndRoundCaps(datasets);
// Last dataset (bottommost negative) gets rounded bottom caps
const bottomItem = getBarItem(datasets[1], 0);
assert.deepEqual(bottomItem.itemStyle.borderRadius, [0, 0, 4, 4]);
// First dataset should NOT have rounded caps
const topItem = getBarItem(datasets[0], 0);
assert.equal(topItem.itemStyle?.borderRadius, undefined);
});
it("handles different stacks independently", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [[1000, 10]],
},
{
type: "bar",
stack: "b",
data: [[1000, 20]],
},
];
fillDataGapsAndRoundCaps(datasets);
// Both should get caps since they're in different stacks
const itemA = getBarItem(datasets[0], 0);
assert.deepEqual(itemA.itemStyle.borderRadius, [4, 4, 0, 0]);
const itemB = getBarItem(datasets[1], 0);
assert.deepEqual(itemB.itemStyle.borderRadius, [4, 4, 0, 0]);
});
it("handles object-format data items", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [{ value: [1000, 10] }],
},
{
type: "bar",
stack: "a",
data: [{ value: [2000, 20] }],
},
];
fillDataGapsAndRoundCaps(datasets);
// Both datasets should now have 2 entries
assert.equal(datasets[0].data!.length, 2);
assert.equal(datasets[1].data!.length, 2);
});
it("handles empty datasets", () => {
const datasets: BarSeriesOption[] = [
{
type: "bar",
stack: "a",
data: [],
},
];
fillDataGapsAndRoundCaps(datasets);
assert.equal(datasets[0].data!.length, 0);
});
});
describe("getCompareTransform", () => {
it("returns identity transform when no compareStart", () => {
const start = new Date("2024-03-01");
const transform = getCompareTransform(start);
const testDate = new Date("2024-03-15T12:00:00");
assert.equal(transform(testDate).getTime(), testDate.getTime());
});
it("returns identity transform when compareStart is undefined", () => {
const start = new Date("2024-03-01");
const transform = getCompareTransform(start, undefined);
const testDate = new Date("2024-03-15T12:00:00");
assert.equal(transform(testDate).getTime(), testDate.getTime());
});
it("shifts by years when start is at year boundary", () => {
const start = new Date("2024-01-01T00:00:00");
const compareStart = new Date("2023-01-01T00:00:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2023-06-15T12:00:00");
const result = transform(testDate);
// Should shift forward by 1 year
assert.equal(result.getFullYear(), 2024);
assert.equal(result.getMonth(), 5); // June
assert.equal(result.getDate(), 15);
});
it("shifts by months when start is at month boundary", () => {
const start = new Date("2024-03-01T00:00:00");
const compareStart = new Date("2024-01-01T00:00:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2024-01-15T12:00:00");
const result = transform(testDate);
// Should shift forward by 2 months
assert.equal(result.getMonth(), 2); // March
assert.equal(result.getDate(), 15);
});
it("shifts by days when start is at day boundary", () => {
const start = new Date("2024-03-15T00:00:00");
const compareStart = new Date("2024-03-08T00:00:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2024-03-08T14:30:00");
const result = transform(testDate);
// Should shift forward by 7 days
assert.equal(result.getDate(), 15);
assert.equal(result.getHours(), 14);
assert.equal(result.getMinutes(), 30);
});
it("falls back to millisecond offset for non-aligned starts", () => {
const start = new Date("2024-03-15T10:30:00");
const compareStart = new Date("2024-03-14T10:30:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2024-03-14T12:00:00");
const result = transform(testDate);
const expectedOffset = start.getTime() - compareStart.getTime();
assert.equal(result.getTime(), testDate.getTime() + expectedOffset);
});
it("prefers year shift over month shift when both apply", () => {
// Jan 1 is both start-of-year and start-of-month
const start = new Date("2024-01-01T00:00:00");
const compareStart = new Date("2022-01-01T00:00:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2022-07-01T00:00:00");
const result = transform(testDate);
// Should shift by 2 years (year check comes first)
assert.equal(result.getFullYear(), 2024);
assert.equal(result.getMonth(), 6); // July
});
it("uses month shift when start is at month but not year boundary", () => {
const start = new Date("2024-06-01T00:00:00");
const compareStart = new Date("2024-03-01T00:00:00");
const transform = getCompareTransform(start, compareStart);
const testDate = new Date("2024-03-20T08:00:00");
const result = transform(testDate);
// Should shift by 3 months
assert.equal(result.getMonth(), 5); // June
assert.equal(result.getDate(), 20);
});
});