From 0fe2f2a8518b9bc2adb84784da1dd8e4cc090cbf Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Wed, 26 Nov 2025 08:08:42 +0100 Subject: [PATCH] Config UI: order meters (#25558) --- .../js/components/Energyflow/Energyflow.vue | 1 + assets/js/views/Config.vue | 6 +- tests/config-ext-meter.spec.ts | 75 +++++++++++++++++-- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/assets/js/components/Energyflow/Energyflow.vue b/assets/js/components/Energyflow/Energyflow.vue index aa90796cc..1ff226a40 100644 --- a/assets/js/components/Energyflow/Energyflow.vue +++ b/assets/js/components/Energyflow/Energyflow.vue @@ -182,6 +182,7 @@ :power="c.power" :powerUnit="powerUnit" icon="vehicle" + data-testid="energyflow-entry-consumer" :iconProps="{ names: [c.icon || 'generic'] }" /> diff --git a/assets/js/views/Config.vue b/assets/js/views/Config.vue index 036b39250..db33e87ba 100644 --- a/assets/js/views/Config.vue +++ b/assets/js/views/Config.vue @@ -748,11 +748,13 @@ export default defineComponent({ const response = await api.get("/config/loadpoints"); this.loadpoints = response.data || []; }, - getMetersByNames(names: string[] | null) { + getMetersByNames(names: string[] | null): ConfigMeter[] { if (!names || !this.meters) { return []; } - return this.meters.filter((m) => names.includes(m.name)); + return names + .map((name) => this.meters.find((m) => m.name === name)) + .filter((m): m is ConfigMeter => m !== undefined); }, getMeterById(id?: number) { if (!id || !this.meters) { diff --git a/tests/config-ext-meter.spec.ts b/tests/config-ext-meter.spec.ts index fa3d6a4ad..fbaff1388 100644 --- a/tests/config-ext-meter.spec.ts +++ b/tests/config-ext-meter.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from "@playwright/test"; +import { test, expect, type Page } from "@playwright/test"; import { start, stop, restart, baseUrl } from "./evcc"; import { editorClear, @@ -9,17 +9,32 @@ import { } from "./utils"; const CONFIG_GRID_ONLY = "config-grid-only.evcc.yaml"; +const CONFIG_BASICS = "basics.evcc.yaml"; test.use({ baseURL: baseUrl() }); -test.beforeEach(async () => { - await start(CONFIG_GRID_ONLY); -}); -test.afterEach(async () => { - await stop(); -}); +async function createExtMeter(page: Page, title: string, power: string) { + const modal = page.getByTestId("meter-modal"); + + await page.getByRole("button", { name: "Add additional meter" }).click(); + await expectModalVisible(modal); + await modal.getByRole("button", { name: "Add regular consumer" }).click(); + await modal.getByLabel("Title").fill(title); + await modal.getByLabel("Usage").selectOption("battery"); + await modal.getByLabel("Manufacturer").selectOption("Demo battery"); + await modal.getByLabel("Power").fill(power); + await modal.getByRole("button", { name: "Save" }).click(); + await expectModalHidden(modal); +} test.describe("ext meter", async () => { + test.beforeEach(async () => { + await start(CONFIG_GRID_ONLY); + }); + test.afterEach(async () => { + await stop(); + }); + test("template-based ext meter", async ({ page }) => { await page.goto("/#/config"); await enableExperimental(page, false); @@ -161,3 +176,49 @@ test.describe("ext meter", async () => { await expect(page.getByTestId("ext")).toHaveCount(0); }); }); + +test.describe("ext meter order", async () => { + test.beforeEach(async () => { + await start(CONFIG_BASICS); + }); + test.afterEach(async () => { + await stop(); + }); + + test("ensure order is preserved", async ({ page }) => { + await page.goto("/#/config"); + await enableExperimental(page, false); + await expect(page.getByTestId("ext")).toHaveCount(0); + + // Create meters + await createExtMeter(page, "Meter 1", "10"); + await createExtMeter(page, "Meter 2", "20"); + await createExtMeter(page, "Meter 3", "30"); + + // Verify order in config UI + const extMeters = page.getByTestId("ext"); + await expect(extMeters).toHaveCount(3); + await expect(extMeters.nth(0)).toContainText("Meter 1"); + await expect(extMeters.nth(1)).toContainText("Meter 2"); + await expect(extMeters.nth(2)).toContainText("Meter 3"); + + // Restart and check order is preserved in both UIs + await restart(CONFIG_BASICS); + + // Check config UI + await page.goto("/#/config"); + await expect(extMeters).toHaveCount(3); + await expect(extMeters.nth(0)).toContainText("Meter 1"); + await expect(extMeters.nth(1)).toContainText("Meter 2"); + await expect(extMeters.nth(2)).toContainText("Meter 3"); + + // Verify order in main UI consumer dropdown + await page.goto("/#/"); + await page.getByTestId("energyflow").click(); + await page.getByRole("button", { name: "Consumption" }).click(); + const consumers = await page.getByTestId("energyflow-entry-consumer"); + await expect(consumers.nth(0)).toContainText("Meter 1"); + await expect(consumers.nth(1)).toContainText("Meter 2"); + await expect(consumers.nth(2)).toContainText("Meter 3"); + }); +});