Skip to content

Commit 0bf9975

Browse files
committed
Validate gallery card configs
1 parent 0bc1af7 commit 0bf9975

7 files changed

Lines changed: 87 additions & 3 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
2+
import { getCardElementClass } from "../../../src/panels/lovelace/create-element/create-card-element";
3+
4+
export const validateCardConfig = async (config: LovelaceCardConfig) => {
5+
const cardClass = await getCardElementClass(config.type);
6+
7+
if (cardClass.getConfigElement) {
8+
const editor = await cardClass.getConfigElement();
9+
editor.setConfig(config);
10+
} else {
11+
cardClass.getConfigForm?.().assertConfig?.(config);
12+
}
13+
14+
new cardClass().setConfig(config);
15+
};

gallery/src/components/demo-card.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import type { LovelaceCardConfig } from "../../../src/data/lovelace/config/card"
77
import "../../../src/panels/lovelace/cards/hui-card";
88
import type { HuiCard } from "../../../src/panels/lovelace/cards/hui-card";
99
import type { HomeAssistant } from "../../../src/types";
10+
import { validateCardConfig } from "../common/validate-card-config";
1011

1112
export interface DemoCardConfig<
1213
T extends LovelaceCardConfig = LovelaceCardConfig,
1314
> {
1415
heading: string;
1516
config: T;
17+
expectConfigError?: boolean;
1618
}
1719

1820
@customElement("demo-card")
@@ -32,6 +34,21 @@ class DemoCard extends LitElement {
3234
dump([config]).trim()
3335
);
3436

37+
protected async firstUpdated() {
38+
try {
39+
await validateCardConfig(this.config.config);
40+
} catch (err) {
41+
if (this.config.expectConfigError) {
42+
return;
43+
}
44+
throw err;
45+
}
46+
47+
if (this.config.expectConfigError) {
48+
throw new Error(`Expected config error for ${this.config.heading}`);
49+
}
50+
}
51+
3552
render() {
3653
return html`
3754
<h2>

gallery/src/pages/lovelace/entity-button-card.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ const CONFIGS = [
6464
config: {
6565
type: "button",
6666
entity: "light.bed_light",
67-
service: "light.toggle",
67+
tap_action: {
68+
action: "perform-action",
69+
perform_action: "light.toggle",
70+
},
6871
},
6972
},
7073
{

gallery/src/pages/lovelace/entity-filter-card.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,15 @@ const INVALID_CONFIGS = [
295295
{
296296
heading: "Error: Entities must be specified",
297297
config: { type: "entity-filter" },
298+
expectConfigError: true,
298299
},
299300
{
300301
heading: "Error: Incorrect filter config",
301302
config: {
302303
type: "entity-filter",
303304
entities: ["sensor.gas_station_lowest_price"],
304305
},
306+
expectConfigError: true,
305307
},
306308
] satisfies DemoCardConfig<
307309
| Pick<EntityFilterCardConfig, "type">

gallery/src/pages/lovelace/gauge-card.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const CONFIGS = [
4343
config: {
4444
type: "gauge",
4545
entity: "sensor.outside_temperature",
46-
unit_of_measurement: "C",
46+
unit: "C",
4747
name: "Outside Temperature",
4848
},
4949
},

gallery/src/pages/lovelace/picture-card.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ const CONFIGS = [
3737
heading: "Error: Image required",
3838
config: {
3939
type: "picture",
40-
entity: "person.paulus",
4140
},
41+
expectConfigError: true,
4242
},
4343
] satisfies DemoCardConfig<PictureCardConfig>[];
4444

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
import { validateCardConfig } from "../../gallery/src/common/validate-card-config";
3+
4+
vi.hoisted(() => {
5+
Object.assign(globalThis, {
6+
__STATIC_PATH__: "/",
7+
__BUILD__: "modern",
8+
__VERSION__: "test",
9+
__BACKWARDS_COMPAT__: false,
10+
__SUPERVISOR__: false,
11+
__NAMESPACE__: "frontend",
12+
});
13+
});
14+
15+
describe("validateCardConfig", () => {
16+
it("accepts valid card configs", async () => {
17+
await expect(
18+
validateCardConfig({
19+
type: "button",
20+
entity: "light.bed_light",
21+
tap_action: {
22+
action: "perform-action",
23+
perform_action: "light.toggle",
24+
},
25+
})
26+
).resolves.toBeUndefined();
27+
});
28+
29+
it.each([
30+
{
31+
type: "button",
32+
entity: "light.bed_light",
33+
service: "light.toggle",
34+
},
35+
{
36+
type: "gauge",
37+
entity: "sensor.outside_temperature",
38+
unit_of_measurement: "C",
39+
},
40+
{
41+
type: "picture",
42+
entity: "person.paulus",
43+
},
44+
])("rejects invalid $type card configs", async (config) => {
45+
await expect(validateCardConfig(config)).rejects.toThrow();
46+
});
47+
});

0 commit comments

Comments
 (0)