mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-02 22:27:23 +00:00
* Add time-based conditional visibility for cards * Move clearTimeout outside of scheduleUpdate * Add time string validation * Add time string validation * Remove runtime validation as config shouldnt allow bad values * Fix for midnight crossing * Cap timeout to 32-bit signed integer * Add listener tests * Additional tests * Format
438 lines
10 KiB
TypeScript
438 lines
10 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
extractMediaQueries,
|
|
extractTimeConditions,
|
|
} from "../../../src/common/condition/extract";
|
|
import type {
|
|
Condition,
|
|
TimeCondition,
|
|
ScreenCondition,
|
|
OrCondition,
|
|
AndCondition,
|
|
NotCondition,
|
|
} from "../../../src/panels/lovelace/common/validate-condition";
|
|
|
|
describe("extractMediaQueries", () => {
|
|
it("should extract single media query", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(max-width: 600px)"]);
|
|
});
|
|
|
|
it("should extract multiple media queries", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "screen",
|
|
media_query: "(min-width: 1200px)",
|
|
} as ScreenCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(max-width: 600px)", "(min-width: 1200px)"]);
|
|
});
|
|
|
|
it("should return empty array when no screen conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "state",
|
|
entity: "light.living_room",
|
|
state: "on",
|
|
},
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should ignore screen conditions without media_query", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "screen",
|
|
} as ScreenCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should extract from nested or conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "state",
|
|
entity: "light.living_room",
|
|
state: "on",
|
|
},
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(max-width: 600px)"]);
|
|
});
|
|
|
|
it("should extract from nested and conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "and",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(orientation: portrait)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
],
|
|
} as AndCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(orientation: portrait)"]);
|
|
});
|
|
|
|
it("should extract from nested not conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "not",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(prefers-color-scheme: dark)",
|
|
} as ScreenCondition,
|
|
],
|
|
} as NotCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(prefers-color-scheme: dark)"]);
|
|
});
|
|
|
|
it("should extract from deeply nested conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "and",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "not",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(orientation: landscape)",
|
|
} as ScreenCondition,
|
|
],
|
|
} as NotCondition,
|
|
],
|
|
} as AndCondition,
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(max-width: 600px)", "(orientation: landscape)"]);
|
|
});
|
|
|
|
it("should handle empty conditions array", () => {
|
|
const result = extractMediaQueries([]);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should handle mixed conditions with nesting", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(min-width: 1200px)",
|
|
} as ScreenCondition,
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractMediaQueries(conditions);
|
|
|
|
expect(result).toEqual(["(max-width: 600px)", "(min-width: 1200px)"]);
|
|
});
|
|
});
|
|
|
|
describe("extractTimeConditions", () => {
|
|
it("should extract single time condition", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
before: "17:00",
|
|
} as TimeCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
before: "17:00",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("should extract multiple time conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "time",
|
|
before: "17:00",
|
|
weekdays: ["mon", "tue", "wed", "thu", "fri"],
|
|
} as TimeCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toMatchObject({ condition: "time", after: "08:00" });
|
|
expect(result[1]).toMatchObject({
|
|
condition: "time",
|
|
before: "17:00",
|
|
weekdays: ["mon", "tue", "wed", "thu", "fri"],
|
|
});
|
|
});
|
|
|
|
it("should return empty array when no time conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "state",
|
|
entity: "light.living_room",
|
|
state: "on",
|
|
},
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should extract from nested or conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "state",
|
|
entity: "light.living_room",
|
|
state: "on",
|
|
},
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("should extract from nested and conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "and",
|
|
conditions: [
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "time",
|
|
weekdays: ["sat", "sun"],
|
|
} as TimeCondition,
|
|
],
|
|
} as AndCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
condition: "time",
|
|
weekdays: ["sat", "sun"],
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("should extract from nested not conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "not",
|
|
conditions: [
|
|
{
|
|
condition: "time",
|
|
after: "22:00",
|
|
before: "06:00",
|
|
} as TimeCondition,
|
|
],
|
|
} as NotCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
condition: "time",
|
|
after: "22:00",
|
|
before: "06:00",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("should extract from deeply nested conditions", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "and",
|
|
conditions: [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "not",
|
|
conditions: [
|
|
{
|
|
condition: "time",
|
|
weekdays: ["sat", "sun"],
|
|
} as TimeCondition,
|
|
],
|
|
} as NotCondition,
|
|
],
|
|
} as AndCondition,
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toMatchObject({ condition: "time", after: "08:00" });
|
|
expect(result[1]).toMatchObject({
|
|
condition: "time",
|
|
weekdays: ["sat", "sun"],
|
|
});
|
|
});
|
|
|
|
it("should handle empty conditions array", () => {
|
|
const result = extractTimeConditions([]);
|
|
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it("should handle mixed conditions with nesting", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
} as TimeCondition,
|
|
{
|
|
condition: "screen",
|
|
media_query: "(max-width: 600px)",
|
|
} as ScreenCondition,
|
|
{
|
|
condition: "or",
|
|
conditions: [
|
|
{
|
|
condition: "time",
|
|
before: "22:00",
|
|
} as TimeCondition,
|
|
],
|
|
} as OrCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result).toHaveLength(2);
|
|
expect(result[0]).toMatchObject({ condition: "time", after: "08:00" });
|
|
expect(result[1]).toMatchObject({ condition: "time", before: "22:00" });
|
|
});
|
|
|
|
it("should preserve all time condition properties", () => {
|
|
const conditions: Condition[] = [
|
|
{
|
|
condition: "time",
|
|
after: "08:00",
|
|
before: "17:00",
|
|
weekdays: ["mon", "tue", "wed", "thu", "fri"],
|
|
} as TimeCondition,
|
|
];
|
|
|
|
const result = extractTimeConditions(conditions);
|
|
|
|
expect(result[0]).toEqual({
|
|
condition: "time",
|
|
after: "08:00",
|
|
before: "17:00",
|
|
weekdays: ["mon", "tue", "wed", "thu", "fri"],
|
|
});
|
|
});
|
|
});
|