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:
parent
ba0eafb132
commit
714c847e2e
3 changed files with 81 additions and 1 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
56
tests/config-required-field.spec.ts
Normal file
56
tests/config-required-field.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
20
tests/config-required-field.tpl.yaml
Normal file
20
tests/config-required-field.tpl.yaml
Normal 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 }}
|
||||
Loading…
Add table
Add a link
Reference in a new issue