Config: enforce required fields when forcing save past a failed test (#29982)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
andig 2026-05-18 20:05:18 +02:00 • committed by GitHub
parent ba0eafb132
commit 714c847e2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 81 additions and 1 deletions

View file

@ -512,6 +512,11 @@ export default defineComponent({
},
values: {
handler() {
// a prior test result no longer matches the edited config:
// revert "Save anyway" back to "Validate & save"
if (this.test.isError || this.test.isSuccess) {
this.test = initialTestState();
}
this.updateServiceValues();
},
deep: true,
@ -689,7 +694,6 @@ export default defineComponent({
this.testDevice,
this.$refs["form"] as HTMLFormElement
);
console.log("test result", success);
if (!success) {
return;
}

View file

@ -0,0 +1,56 @@
import { test, expect } from "@playwright/test";
import { start, stop, baseUrl } from "./evcc";
import { expectModalVisible } from "./utils";
test.use({ baseURL: baseUrl() });
const templateFlags = [
"--disable-auth",
"--template-type",
"meter",
"--template",
"tests/config-required-field.tpl.yaml",
];
test.beforeEach(async () => {
await start(undefined, undefined, templateFlags);
});
test.afterEach(async () => {
await stop();
});
test.describe("required field validation", async () => {
// regression test for evcc-io/evcc#29919: clearing a required field after a
// failed connection test must not allow saving via the "Save anyway" button
test("cannot save with empty required field after failed test", async ({ page }) => {
await page.goto("/#/config");
await page.getByRole("button", { name: "Add grid meter" }).click();
const meterModal = page.getByTestId("meter-modal");
await expectModalVisible(meterModal);
await meterModal.getByLabel("Manufacturer").selectOption("Required Field Demo");
// fill the required field so the connection test runs (and then fails)
await meterModal.getByLabel("Secret").fill("some-secret");
await meterModal.getByRole("button", { name: "Validate & save" }).click();
// failed test exposes the "Save anyway" force-save button
const forceSave = meterModal.getByRole("button", { name: "Save anyway" });
await expect(forceSave).toBeVisible();
// clearing the field invalidates the stale test result: the button must
// revert to "Validate & save" instead of staying in force-save mode
await meterModal.getByLabel("Secret").fill("");
await expect(forceSave).not.toBeVisible();
const validateSave = meterModal.getByRole("button", { name: "Validate & save" });
await expect(validateSave).toBeVisible();
// attempting to save with the empty required field is blocked by validation
await validateSave.click();
await expectModalVisible(meterModal);
await expect(meterModal.getByLabel("Secret")).toHaveJSProperty("validity.valid", false);
// nothing was persisted
await expect(page.getByTestId("grid")).toHaveCount(0);
});
});

View file

@ -0,0 +1,20 @@
template: required-field-demo
group: generic
products:
- description:
generic: Required Field Demo
params:
- name: usage
choice: ["grid"]
- name: secret
description:
generic: Secret
required: true
# unreachable uri so the connection test always fails, exposing the
# "Save anyway" button (see tests/config-required-field.spec.ts)
render: |
type: custom
power:
source: http
uri: http://localhost:1/{{ .secret }}