From 5ca8fd4095a0460a51c154a7e50ca43fbb68a6f3 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Fri, 23 Jan 2026 17:34:57 +0100 Subject: [PATCH] Fix crash when using invalid visibility condition type (#29150) --- .../lovelace/common/validate-condition.ts | 5 + .../common/validate-condition.test.ts | 111 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 test/panels/lovelace/common/validate-condition.test.ts diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index 39d239ef87..a375c15a03 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -105,6 +105,11 @@ function checkStateCondition( : UNKNOWN; let value = condition.state ?? condition.state_not; + // Guard against invalid/incomplete condition configuration + if (value === undefined) { + return false; + } + // Handle entity_id, UI should be updated for conditional card (filters does not have UI for now) if (Array.isArray(value)) { const entityValues = value diff --git a/test/panels/lovelace/common/validate-condition.test.ts b/test/panels/lovelace/common/validate-condition.test.ts new file mode 100644 index 0000000000..4b159d0490 --- /dev/null +++ b/test/panels/lovelace/common/validate-condition.test.ts @@ -0,0 +1,111 @@ +import { describe, it, expect } from "vitest"; +import { + checkConditionsMet, + validateConditionalConfig, +} from "../../../../src/panels/lovelace/common/validate-condition"; +import type { HomeAssistant } from "../../../../src/types"; + +const createMockHass = (states: Record = {}) => + ({ + states, + user: { id: "user1" }, + }) as unknown as HomeAssistant; + +describe("validateConditionalConfig", () => { + describe("state condition validation", () => { + it("should return true for valid state condition", () => { + const conditions = [ + { condition: "state", entity: "sensor.test", state: "on" }, + ] as any; + expect(validateConditionalConfig(conditions)).toBe(true); + }); + + it("should return false for state condition without state or state_not", () => { + const conditions = [{ condition: "state", entity: "sensor.test" }] as any; + expect(validateConditionalConfig(conditions)).toBe(false); + }); + }); + + describe("numeric_state condition validation", () => { + it("should return true for valid numeric_state condition", () => { + const conditions = [ + { condition: "numeric_state", entity: "sensor.test", above: 0 }, + ] as any; + expect(validateConditionalConfig(conditions)).toBe(true); + }); + }); +}); + +describe("checkConditionsMet", () => { + describe("state condition evaluation", () => { + it("should return true when state matches", () => { + const hass = createMockHass({ + "sensor.test": { state: "on" }, + }); + const conditions = [ + { condition: "state", entity: "sensor.test", state: "on" }, + ] as any; + expect(checkConditionsMet(conditions, hass)).toBe(true); + }); + + it("should return false when state does not match", () => { + const hass = createMockHass({ + "sensor.test": { state: "off" }, + }); + const conditions = [ + { condition: "state", entity: "sensor.test", state: "on" }, + ] as any; + expect(checkConditionsMet(conditions, hass)).toBe(false); + }); + + it("should return false for condition without state or state_not", () => { + const hass = createMockHass({ + "sensor.test": { state: "on" }, + }); + const conditions = [{ condition: "state", entity: "sensor.test" }] as any; + expect(checkConditionsMet(conditions, hass)).toBe(false); + }); + + it("should not crash with invalid condition type", () => { + const hass = createMockHass({ + "sensor.test": { state: "5" }, + }); + const conditions = [ + { condition: "numeric", entity: "sensor.test", above: 0 }, + ] as any; + // Should not throw - this was the bug + expect(() => checkConditionsMet(conditions, hass)).not.toThrow(); + expect(checkConditionsMet(conditions, hass)).toBe(false); + }); + }); + + describe("numeric_state condition evaluation", () => { + it("should return true when value is above threshold", () => { + const hass = createMockHass({ + "sensor.test": { state: "5" }, + }); + const conditions = [ + { condition: "numeric_state", entity: "sensor.test", above: 0 }, + ] as any; + expect(checkConditionsMet(conditions, hass)).toBe(true); + }); + }); + + describe("legacy conditions", () => { + it("should handle legacy state condition", () => { + const hass = createMockHass({ + "sensor.test": { state: "on" }, + }); + const conditions = [{ entity: "sensor.test", state: "on" }] as any; + expect(checkConditionsMet(conditions, hass)).toBe(true); + }); + + it("should return false for legacy condition without state", () => { + const hass = createMockHass({ + "sensor.test": { state: "on" }, + }); + const conditions = [{ entity: "sensor.test" }] as any; + expect(checkConditionsMet(conditions, hass)).toBe(false); + }); + }); +});