diff --git a/assets/js/components/Config/CreateFlowStatus.vue b/assets/js/components/Config/CreateFlowStatus.vue new file mode 100644 index 000000000..73687ee92 --- /dev/null +++ b/assets/js/components/Config/CreateFlowStatus.vue @@ -0,0 +1,31 @@ + + + diff --git a/assets/js/components/Config/LoadpointModal.vue b/assets/js/components/Config/LoadpointModal.vue index a0abd1cff..3f2703bd0 100644 --- a/assets/js/components/Config/LoadpointModal.vue +++ b/assets/js/components/Config/LoadpointModal.vue @@ -9,7 +9,22 @@ @close="onClose" @dismiss="onDismiss" > -
+ + + +
-

- {{ $t("config.loadpoint.defaultsHint") }} - - {{ $t("config.loadpoint.defaultsHintLink") }} . -

-
+
{{ $t("config.loadpoint.chargingTitle") }}
@@ -625,10 +634,12 @@ import api from "@/api"; import GenericModal from "../Helper/GenericModal.vue"; import deepClone from "@/utils/deepClone"; import deepEqual from "@/utils/deepEqual"; +import sleep from "@/utils/sleep"; import formatter, { POWER_UNIT } from "@/mixins/formatter"; import DeviceRefBox from "./DeviceRefBox.vue"; import NewDeviceButton from "./NewDeviceButton.vue"; import InvalidReferenceAlert from "./InvalidReferenceAlert.vue"; +import CreateFlowStatus from "./CreateFlowStatus.vue"; import { handleError, customChargerName, createDeviceUtils } from "./DeviceModal"; import { getModal, openModal, replaceModal, closeModal } from "@/configModal"; import { @@ -688,6 +699,7 @@ export default { DeviceRefBox, NewDeviceButton, InvalidReferenceAlert, + CreateFlowStatus, }, mixins: [formatter], props: { @@ -706,12 +718,12 @@ export default { data() { return { isModalVisible: false, - showAllSettings: false, saving: false, values: deepClone(defaultValues) as ConfigLoadpoint, chargerPower: "11kw", solarMode: "default", - created: false, + autoCreate: false, + autoCreateInProgress: false, tab: "solar", powerUnit: POWER_UNIT, }; @@ -724,7 +736,10 @@ export default { return getModal("loadpoint")?.type as LoadpointType | undefined; }, modalTitle() { - if (this.isNew) { + if (this.autoCreate && !this.autoCreateInProgress) { + return this.$t(`config.loadpoint.createdTitle.${this.loadpointType}`); + } + if (this.isNew || this.autoCreate) { return this.$t(`config.loadpoint.titleAdd.${this.loadpointType || "unknown"}`); } return this.$t(`config.loadpoint.titleEdit.${this.loadpointType || "unknown"}`); @@ -751,24 +766,24 @@ export default { }, chargerStatus() { if (!this.chargerValues || !this.values.charger) { - return {}; + return null; } - return this.chargerValues[this.values.charger] || {}; + return this.chargerValues[this.values.charger] || null; }, chargerSupports1p3p() { - return this.chargerStatus.phases1p3p?.value || false; + return this.chargerStatus?.phases1p3p?.value || false; }, chargerIsSinglePhase() { - return this.chargerStatus.singlePhase?.value || false; + return this.chargerStatus?.singlePhase?.value || false; }, chargerIsIntegratedDevice() { - return this.chargerStatus.integratedDevice?.value || false; + return this.chargerStatus?.integratedDevice?.value || false; }, chargerIsSwitchDevice() { - return this.chargerStatus.switchDevice?.value || false; + return this.chargerStatus?.switchDevice?.value || false; }, chargerIsHeating() { - return this.chargerStatus.heating?.value === true; + return this.chargerStatus?.heating?.value === true; }, meterTitle() { const name = this.values.meter; @@ -857,6 +872,12 @@ export default { } } }, + id(newId) { + // replaceModal swaps the id in place (after create) without a remount + if (this.isModalVisible && newId !== undefined && this.values?.id !== newId) { + this.loadConfiguration(); + } + }, chargerPower(value) { if (value === "11kw") { this.values.minCurrent = 6; @@ -881,8 +902,8 @@ export default { methods: { reset() { this.values = deepClone(defaultValues); - this.created = false; - this.showAllSettings = false; + this.autoCreate = false; + this.autoCreateInProgress = false; this.updatePhases(); }, async loadConfiguration() { @@ -922,18 +943,30 @@ export default { } }, async create() { - this.saving = true; + this.autoCreate = true; + this.autoCreateInProgress = true; + await sleep(1000); // ux pause try { - await api.post("config/loadpoints", this.values); - this.created = true; - this.emitChanged("added"); - // the created response has no id to compare against on next open, so - // reset explicitly instead of relying on the isModalVisible watcher - this.reset(); + for (let i = 0; !this.chargerStatus && i < 100; i++) { + await sleep(50); + } + const res = await api.post("config/loadpoints", this.values); + this.$emit("changed", { action: "added" }); + // edit mode of the newly loadpoint + replaceModal("loadpoint", { + id: res.data?.id, + type: this.loadpointType || undefined, + }); + this.autoCreateInProgress = false; } catch (e) { + this.autoCreate = false; + this.autoCreateInProgress = false; handleError(e, "create failed"); } - this.saving = false; + }, + async finish() { + await closeModal({ action: "added" }); + this.reset(); }, onOpen() { this.isModalVisible = true; @@ -942,12 +975,17 @@ export default { this.isModalVisible = false; }, async onDismiss() { - if (!this.values.id && !this.created) { + // closeModal already cleared the route id, so check values.id, not isNew + if (!this.values.id && !this.autoCreate) { await this.cleanupDevice("charger", this.values.charger, this.chargers); await this.cleanupDevice("meter", this.values.meter, this.meters); this.$emit("dismissed"); this.reset(); + return; } + // keep values so reopening the same loadpoint stays populated + this.autoCreate = false; + this.autoCreateInProgress = false; }, async cleanupDevice(type: DeviceType, name: string, list: { name: string; id: number }[]) { const id = list.find((d) => d.name === name)?.id; @@ -965,7 +1003,11 @@ export default { type: this.loadpointType || undefined, }); if (result.action === "added" && result.name) { + const instantCreate = this.isNew && !this.values.charger; this.values.charger = result.name; + if (instantCreate) { + await this.create(); + } } else if (result.action === "removed") { this.values.charger = ""; } diff --git a/assets/js/components/Helper/SuccessCheck.vue b/assets/js/components/Helper/SuccessCheck.vue new file mode 100644 index 000000000..524121b18 --- /dev/null +++ b/assets/js/components/Helper/SuccessCheck.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/i18n/de.json b/i18n/de.json index 174dc711e..6dbc30210 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -143,16 +143,16 @@ "template": "Hersteller", "titleAdd": { "charging": "Wallbox hinzufügen", - "heating": "Heizung hinzufügen" + "heating": "Heizgerät hinzufügen" }, "titleEdit": { "charging": "Wallbox bearbeiten", - "heating": "Heizung bearbeiten" + "heating": "Heizgerät bearbeiten" }, "type": { "custom": { "charging": "Benutzerdefinierte Wallbox", - "heating": "Benutzerdefinierte Heizung" + "heating": "Benutzerdefiniertes Heizgerät" }, "heatpump": "Benutzerdefinierte Wärmepumpe", "sgready": "Benutzerdefinierte Wärmepumpe (sg-ready über Plugins)", @@ -396,17 +396,18 @@ "loadpoint": { "addCharger": { "charging": "Wallbox hinzufügen", - "heating": "Heizung hinzufügen" + "heating": "Heizgerät hinzufügen" }, "addMeter": "Zusätzlichen Energiezähler hinzufügen", + "advancedConfig": "Erweiterte Konfiguration", "cancel": "Abbrechen", "chargerError": { "charging": "Wallbox muss konfiguriert sein.", - "heating": "Heizung muss konfiguriert sein." + "heating": "Heizgerät muss konfiguriert sein." }, "chargerLabel": { "charging": "Wallbox", - "heating": "Heizung" + "heating": "Heizgerät" }, "chargerPower11kw": "11 kW", "chargerPower11kwHelp": "Verwendet einen Strombereich von 6 bis 16 A.", @@ -420,14 +421,21 @@ "circuitInvalid": "Stromkreis existiert nicht", "circuitLabel": "Stromkreis", "circuitUnassigned": "nicht zugewiesen", + "created": "„{name}“ wurde angelegt und verwendet sinnvolle Standardwerte.", + "createdTitle": { + "charging": "Ladepunkt erstellt", + "heating": "Heizung erstellt" + }, + "creating": { + "charging": "Wallbox gespeichert. Ladepunkt wird erstellt …", + "heating": "Heizgerät gespeichert. Heizung wird erstellt …" + }, "defaultModeHelp": { "charging": "Lademodus beim Anschließen des Fahrzeugs.", "heating": "Wird beim Systemstart gesetzt." }, "defaultModeHelpKeep": "Zuletzt ausgewählter Modus wird beibehalten.", "defaultModeLabel": "Standard-Modus", - "defaultsHint": "Standardmodus, PV-Überschussverhalten und elektrische Details verwenden sinnvolle Standardwerte.", - "defaultsHintLink": "Einstellungen anpassen", "delete": "Löschen", "electricalSubtitle": "Im Zweifelsfall Elektriker fragen.", "electricalTitle": "Elektrik", @@ -444,7 +452,7 @@ "noVehicles": "Keine Fahrzeuge konfiguriert.", "option": { "charging": "Ladepunkt hinzufügen", - "heating": "Heizungsgerät hinzufügen" + "heating": "Heizung hinzufügen" }, "phases1p": "1-phasig", "phases3p": "3-phasig", @@ -485,12 +493,12 @@ "titleAdd": { "charging": "Ladepunkt hinzufügen", "heating": "Heizung hinzufügen", - "unknown": "Wallbox oder Heizung hinzufügen" + "unknown": "Ladepunkt oder Heizung hinzufügen" }, "titleEdit": { "charging": "Ladepunkt bearbeiten", "heating": "Heizung bearbeiten", - "unknown": "Wallbox oder Heizung bearbeiten" + "unknown": "Ladepunkt oder Heizung bearbeiten" }, "titleExample": { "charging": "Garage, Carport, etc.", @@ -508,7 +516,7 @@ "addAdditional": "Zusätzlichen Zähler hinzufügen", "addConsumer": "Verbraucher hinzufügen", "addGrid": "Netzzähler hinzufügen", - "addLoadpoint": "Wallbox oder Heizung hinzufügen", + "addLoadpoint": "Ladepunkt oder Heizung hinzufügen", "addPvBattery": "PV oder Speicher hinzufügen", "addTariffs": "Tarife hinzufügen", "addVehicle": "Fahrzeug hinzufügen", @@ -801,7 +809,7 @@ "general": "Allgemein", "grid": "Netzanschluss", "integrations": "Integrationen", - "loadpoints": "Laden & Heizen", + "loadpoints": "Ladepunkte & Heizungen", "meter": "PV & Batterie", "services": "Dienste", "system": "System", diff --git a/i18n/en.json b/i18n/en.json index 478b6a62f..077bfd20e 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -143,16 +143,16 @@ "template": "Manufacturer", "titleAdd": { "charging": "Add Charger", - "heating": "Add Heater" + "heating": "Add Heating Device" }, "titleEdit": { "charging": "Edit Charger", - "heating": "Edit Heater" + "heating": "Edit Heating Device" }, "type": { "custom": { "charging": "User-defined charger", - "heating": "User-defined heater" + "heating": "User-defined heating device" }, "heatpump": "User-defined heat pump", "sgready": "User-defined heat pump (sg-ready via plugins)", @@ -396,17 +396,18 @@ "loadpoint": { "addCharger": { "charging": "Add charger", - "heating": "Add heater" + "heating": "Add heating device" }, "addMeter": "Add dedicated energy meter", + "advancedConfig": "Advanced configuration", "cancel": "Cancel", "chargerError": { "charging": "Configuring a charger is required.", - "heating": "Configuring a heater is required." + "heating": "Configuring a heating device is required." }, "chargerLabel": { "charging": "Charger", - "heating": "Heater" + "heating": "Heating device" }, "chargerPower11kw": "11 kW", "chargerPower11kwHelp": "Will use a current range of 6 to 16 A.", @@ -420,14 +421,21 @@ "circuitInvalid": "Circuit does not exist", "circuitLabel": "Circuit", "circuitUnassigned": "unassigned", + "created": "“{name}” was created and uses sensible defaults.", + "createdTitle": { + "charging": "Charging point created", + "heating": "Heater created" + }, + "creating": { + "charging": "Charger saved. Creating charging point …", + "heating": "Heating device saved. Creating heater …" + }, "defaultModeHelp": { "charging": "Charging mode when connecting the vehicle.", "heating": "Is set when on system start." }, "defaultModeHelpKeep": "Keeps the last selected mode.", "defaultModeLabel": "Default mode", - "defaultsHint": "Default mode, solar behaviour and electrical details use sensible defaults.", - "defaultsHintLink": "Adjust settings", "delete": "Delete", "electricalSubtitle": "When in doubt, ask your electrician.", "electricalTitle": "Electrics", @@ -444,7 +452,7 @@ "noVehicles": "No vehicles are configured.", "option": { "charging": "Add charging point", - "heating": "Add heating device" + "heating": "Add heater" }, "phases1p": "1-phase", "phases3p": "3-phase", @@ -484,13 +492,13 @@ "thresholdEnableLabel": "Enable grid power", "titleAdd": { "charging": "Add Charging Point", - "heating": "Add Heating Device", - "unknown": "Add Charger or Heater" + "heating": "Add Heater", + "unknown": "Add Charging Point or Heater" }, "titleEdit": { "charging": "Edit Charging Point", - "heating": "Edit Heating Device", - "unknown": "Edit Charger or Heater" + "heating": "Edit Heater", + "unknown": "Edit Charging Point or Heater" }, "titleExample": { "charging": "Garage, Carport, etc.", @@ -508,7 +516,7 @@ "addAdditional": "Add additional meter", "addConsumer": "Add consumer", "addGrid": "Add grid meter", - "addLoadpoint": "Add charger or heater", + "addLoadpoint": "Add charging point or heater", "addPvBattery": "Add solar or battery", "addTariffs": "Add tariffs", "addVehicle": "Add vehicle", @@ -801,7 +809,7 @@ "general": "General", "grid": "Grid", "integrations": "Integrations", - "loadpoints": "Charging & Heating", + "loadpoints": "Charging points & Heaters", "meter": "Solar & Battery", "services": "Services", "system": "System", diff --git a/server/http_config_loadpoint_handler.go b/server/http_config_loadpoint_handler.go index 87f71fcb9..97f820336 100644 --- a/server/http_config_loadpoint_handler.go +++ b/server/http_config_loadpoint_handler.go @@ -189,7 +189,9 @@ func newLoadpointHandler() http.HandlerFunc { setConfigDirty() - w.WriteHeader(http.StatusOK) + jsonWrite(w, struct { + ID int `json:"id"` + }{ID: conf.ID}) } } diff --git a/tests/boost.spec.ts b/tests/boost.spec.ts index bf0668185..9d38af800 100644 --- a/tests/boost.spec.ts +++ b/tests/boost.spec.ts @@ -5,6 +5,7 @@ import { expectModalVisible, newLoadpoint, addDemoCharger, + finishLoadpoint, ChargerStatus, } from "./utils"; test.use({ baseURL: baseUrl() }); @@ -120,9 +121,7 @@ test.describe("boost", async () => { await page.goto("/#/config"); await newLoadpoint(page, "New Charger"); await addDemoCharger(page, undefined, ChargerStatus.Connected); - const lpModal = page.getByTestId("loadpoint-modal"); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); // restart evcc to apply new loadpoint await restart(CONFIG_BATTERY); diff --git a/tests/config-circuit.spec.ts b/tests/config-circuit.spec.ts index 7aaffddf9..2a0d2e49b 100644 --- a/tests/config-circuit.spec.ts +++ b/tests/config-circuit.spec.ts @@ -51,7 +51,7 @@ test.describe("circuit", async () => { // add loadpoint and charger const lpModal = page.getByTestId("loadpoint-modal"); - await page.getByRole("button", { name: "Add charger or heater" }).click(); + await page.getByRole("button", { name: "Add charging point or heater" }).click(); await expectModalVisible(lpModal); await lpModal.getByRole("button", { name: "Add charging point" }).click(); await lpModal.getByLabel("Title").fill("Carport"); @@ -69,6 +69,7 @@ test.describe("circuit", async () => { await expectModalVisible(lpModal); // no load management, no circuits + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await expect(lpModal.getByLabel("Circuit")).not.toBeVisible(); await lpModal.getByRole("button", { name: "Save" }).click(); diff --git a/tests/config-fatals.spec.ts b/tests/config-fatals.spec.ts index e624bc454..38f004136 100644 --- a/tests/config-fatals.spec.ts +++ b/tests/config-fatals.spec.ts @@ -69,6 +69,7 @@ test.describe("fatal config handling", async () => { await addDemoCharger(page); // add shelly meter + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByRole("button", { name: "Add dedicated energy meter" }).click(); const meterModal = page.getByTestId("meter-modal"); await expectModalVisible(meterModal); diff --git a/tests/config-invalid-references.spec.ts b/tests/config-invalid-references.spec.ts index ab4839ceb..6d122cfd7 100644 --- a/tests/config-invalid-references.spec.ts +++ b/tests/config-invalid-references.spec.ts @@ -45,6 +45,7 @@ test.describe("invalid references", async () => { const lpModal = page.getByTestId("loadpoint-modal"); await newLoadpoint(page, "Test Carport"); await addDemoCharger(page); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); // Wait for circuit field to be available and assign to circuit main await expect(lpModal.getByLabel("Circuit")).toBeVisible(); @@ -118,6 +119,7 @@ test.describe("invalid references", async () => { // Create loadpoint with demo charger and assign vehicle await newLoadpoint(page, "Garage"); await addDemoCharger(page); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await expect(lpModal.getByLabel("Default vehicle")).toBeVisible(); await lpModal.getByLabel("Default vehicle").selectOption("Legacy Vehicle"); await lpModal.getByRole("button", { name: "Save" }).click(); diff --git a/tests/config-loadpoint.spec.ts b/tests/config-loadpoint.spec.ts index f9d770cec..934f4413a 100644 --- a/tests/config-loadpoint.spec.ts +++ b/tests/config-loadpoint.spec.ts @@ -10,6 +10,7 @@ import { addDemoMeter, addVehicle, newLoadpoint, + finishLoadpoint, } from "./utils"; const CONFIG_ONE_LP = "config-one-lp.evcc.yaml"; @@ -43,6 +44,12 @@ test.describe("charging loadpoint", async () => { await chargerModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); + + // instant-create success screen + await expect(lpModal.getByRole("heading", { name: "Charging point created" })).toBeVisible(); + await expect(lpModal).toContainText("“Solar Carport” was created and uses sensible defaults."); + + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await expect(lpModal.getByLabel("Title")).toHaveValue("Solar Carport"); // verify defaults await expect(lpModal.getByLabel("Default mode")).toHaveValue(""); @@ -146,8 +153,7 @@ test.describe("charging loadpoint", async () => { // add loadpoint via UI await newLoadpoint(page, "Garage"); await addDemoCharger(page); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); // two loadpoints await expect(page.getByTestId("loadpoint")).toHaveCount(2); @@ -203,8 +209,7 @@ test.describe("charging loadpoint", async () => { await chargerModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); await expect(page.getByTestId("loadpoint")).toHaveCount(1); await expect(page.getByTestId("loadpoint")).toContainText("11.0 kW"); @@ -221,8 +226,7 @@ test.describe("charging loadpoint", async () => { await chargerModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); // two loadpoints, each with its own distinct charger await expect(page.getByTestId("loadpoint")).toHaveCount(2); @@ -251,6 +255,7 @@ test.describe("charging loadpoint", async () => { // add loadpoint > no vehicle option await newLoadpoint(page, LP_1); await addDemoCharger(page); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await expect(lpModal.getByLabel("Default vehicle")).not.toBeVisible(); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); @@ -278,6 +283,7 @@ test.describe("charging loadpoint", async () => { // add second loadpoint await newLoadpoint(page, LP_2); await addDemoCharger(page); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByLabel("Default vehicle").selectOption(VEHICLE_2); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); @@ -313,6 +319,7 @@ test.describe("charging loadpoint", async () => { const lpModal = page.getByTestId("loadpoint-modal"); await newLoadpoint(page, "Carport"); await addDemoCharger(page); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByLabel("Default mode").selectOption("---"); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); @@ -356,10 +363,14 @@ test.describe("charging loadpoint", async () => { await addDemoCharger(page); const lpModal = page.getByTestId("loadpoint-modal"); await expectModalVisible(lpModal); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByLabel("Default vehicle").selectOption("Porsche"); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); + await restart(); + await page.reload(); + // delete vehicle await page.getByTestId("vehicle").nth(0).getByRole("button", { name: "edit" }).click(); const vehicleModal = page.getByTestId("vehicle-modal"); @@ -385,8 +396,7 @@ test.describe("charging loadpoint", async () => { await newLoadpoint(page, "Garage"); await addDemoCharger(page); const lpModal = page.getByTestId("loadpoint-modal"); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); // delete charger await page.getByTestId("loadpoint").getByRole("button", { name: "edit" }).click(); @@ -397,7 +407,9 @@ test.describe("charging loadpoint", async () => { await chargerModal.getByRole("button", { name: "Delete" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); - await expect(lpModal.getByRole("heading", { name: "Edit Charger or Heater" })).toBeVisible(); + await expect( + lpModal.getByRole("heading", { name: "Edit Charging Point or Heater" }) + ).toBeVisible(); // close modal before restarting await lpModal.getByRole("button", { name: "Close" }).click(); @@ -426,6 +438,8 @@ test.describe("charging loadpoint", async () => { const lpModal = page.getByTestId("loadpoint-modal"); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); + await restart(); + await page.reload(); await expect(page.getByTestId("loadpoint")).toContainText("11.0 kW"); // delete charger @@ -504,6 +518,7 @@ power: await expectModalVisible(lpModal); // add user-defined meter + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByRole("button", { name: "Add dedicated energy meter" }).click(); const meterModal = page.getByTestId("meter-modal"); await expectModalVisible(meterModal); @@ -585,6 +600,7 @@ power: await expectModalHidden(chargerModal); await expectModalVisible(lpModal); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); const modeSelect = lpModal.getByLabel("Default mode"); await expect(modeSelect.getByRole("option", { name: "Solar", exact: true })).toHaveCount(1); await expect(modeSelect.getByRole("option", { name: "Min+Solar" })).toHaveCount(0); @@ -611,10 +627,8 @@ test.describe("heating loadpoint", async () => { const lpModal = page.getByTestId("loadpoint-modal"); await addDemoCharger(page, LoadpointType.Heating); - // check heading - await expect(lpModal.getByRole("heading", { name: "Add Heating Device" })).toBeVisible(); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await expect(lpModal.getByRole("heading", { name: "Heater created" })).toBeVisible(); + await finishLoadpoint(page); // restart edit await restart(); @@ -624,7 +638,7 @@ test.describe("heating loadpoint", async () => { await expect(lpEntry).toContainText("Wärmepumpe"); await lpEntry.getByRole("button", { name: "edit" }).click(); await expectModalVisible(lpModal); - await expect(lpModal.getByRole("heading", { name: "Edit Heating Device" })).toBeVisible(); + await expect(lpModal.getByRole("heading", { name: "Edit Heater" })).toBeVisible(); await lpModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(lpModal); @@ -650,12 +664,12 @@ test.describe("heating loadpoint", async () => { // add loadpoint await newLoadpoint(page, "Wärmepumpe", LoadpointType.Heating); const lpModal = page.getByTestId("loadpoint-modal"); - await lpModal.getByRole("button", { name: "Add heater" }).click(); + await lpModal.getByRole("button", { name: "Add heating device" }).click(); // add user-defined heat pump const modal = page.getByTestId("charger-modal"); await expectModalVisible(modal); - await modal.getByLabel("Manufacturer").selectOption("User-defined heater"); + await modal.getByLabel("Manufacturer").selectOption("User-defined heating device"); await modal.getByLabel("Manufacturer").selectOption("User-defined heat pump"); await modal .getByLabel("Manufacturer") @@ -706,8 +720,7 @@ temp: await expectModalVisible(lpModal); // create - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); await expect(page.getByTestId("loadpoint")).toHaveCount(1); const lpEntry = page.getByTestId("loadpoint").first(); @@ -724,8 +737,7 @@ temp: await newLoadpoint(page, "Wärmepumpe", LoadpointType.Heating); await addDemoCharger(page, LoadpointType.Heating); const lpModal = page.getByTestId("loadpoint-modal"); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); // delete heater await page.getByTestId("loadpoint").getByRole("button", { name: "edit" }).click(); @@ -736,9 +748,11 @@ temp: await chargerModal.getByRole("button", { name: "Delete" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); - await expect(lpModal.getByRole("heading", { name: "Edit Charger or Heater" })).toBeVisible(); + await expect( + lpModal.getByRole("heading", { name: "Edit Charging Point or Heater" }) + ).toBeVisible(); await expect(lpModal.getByRole("button", { name: "Add charging point" })).toBeVisible(); - await expect(lpModal.getByRole("button", { name: "Add heating device" })).toBeVisible(); + await expect(lpModal.getByRole("button", { name: "Add heater" })).toBeVisible(); }); }); diff --git a/tests/config-ocpp.spec.ts b/tests/config-ocpp.spec.ts index afb9bbca8..9b31bc0d3 100644 --- a/tests/config-ocpp.spec.ts +++ b/tests/config-ocpp.spec.ts @@ -116,7 +116,7 @@ test.describe("ocpp", () => { await page.goto("/#/config"); // Open loadpoint modal and select charging point type - await page.getByRole("button", { name: "Add charger or heater" }).click(); + await page.getByRole("button", { name: "Add charging point or heater" }).click(); const lpModal = page.getByTestId("loadpoint-modal"); await expectModalVisible(lpModal); await lpModal.getByRole("button", { name: "Charging point" }).click(); diff --git a/tests/config-onboarding.spec.ts b/tests/config-onboarding.spec.ts index cf2e97464..d63e778c9 100644 --- a/tests/config-onboarding.spec.ts +++ b/tests/config-onboarding.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from "@playwright/test"; import { start, stop, restart, baseUrl } from "./evcc"; -import { expectModalHidden, expectModalVisible } from "./utils"; +import { expectModalHidden, expectModalVisible, finishLoadpoint } from "./utils"; test.use({ baseURL: baseUrl() }); @@ -49,8 +49,7 @@ test.describe("onboarding", async () => { await chargerModal.getByRole("button", { name: "Save" }).click(); await expectModalHidden(chargerModal); await expectModalVisible(lpModal); - await lpModal.getByRole("button", { name: "Save" }).click(); - await expectModalHidden(lpModal); + await finishLoadpoint(page); await restart(); diff --git a/tests/hems.spec.ts b/tests/hems.spec.ts index 6434a8f90..fef6dbbf4 100644 --- a/tests/hems.spec.ts +++ b/tests/hems.spec.ts @@ -385,6 +385,7 @@ limit: await addDemoCharger(page); const lpModal = page.getByTestId("loadpoint-modal"); await expectModalVisible(lpModal); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); const circuitOptions = lpModal.getByLabel("Circuit").getByRole("option"); await expect(circuitOptions).toHaveText(["---", "House [main]"]); }); diff --git a/tests/utils.ts b/tests/utils.ts index 8c57c67cc..bf9412879 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -51,7 +51,9 @@ export async function addDemoCharger( ): Promise { const lpModal = page.getByTestId("loadpoint-modal"); await lpModal - .getByRole("button", { name: type === LoadpointType.Heating ? "Add heater" : "Add charger" }) + .getByRole("button", { + name: type === LoadpointType.Heating ? "Add heating device" : "Add charger", + }) .click(); const modal = page.getByTestId("charger-modal"); @@ -69,6 +71,7 @@ export async function addDemoCharger( export async function addDemoMeter(page: Page, power = "0"): Promise { const lpModal = page.getByTestId("loadpoint-modal"); + await lpModal.getByRole("link", { name: "Advanced configuration" }).click(); await lpModal.getByRole("button", { name: "Add dedicated energy meter" }).click(); const modal = page.getByTestId("meter-modal"); @@ -96,16 +99,22 @@ export async function newLoadpoint( type: LoadpointType = LoadpointType.Charging ): Promise { const lpModal = page.getByTestId("loadpoint-modal"); - await page.getByRole("button", { name: "Add charger or heater" }).click(); + await page.getByRole("button", { name: "Add charging point or heater" }).click(); await expectModalVisible(lpModal); await lpModal .getByRole("button", { - name: type === LoadpointType.Heating ? "Add heating device" : "Add charging point", + name: type === LoadpointType.Heating ? "Add heater" : "Add charging point", }) .click(); await lpModal.getByLabel("Title").fill(title); } +export async function finishLoadpoint(page: Page): Promise { + const lpModal = page.getByTestId("loadpoint-modal"); + await lpModal.getByText("Close", { exact: true }).click(); + await expectModalHidden(lpModal); +} + export async function dragElement( page: Page, sourceElement: Locator,