evcc-io/tests/backup-restore.spec.ts
2026-04-23 09:39:08 +00:00

249 lines
11 KiB
TypeScript

import { test, expect } from "@playwright/test";
import { start, stop, baseUrl, restart } from "./evcc";
import { openMoreMenu, expectModalVisible, expectModalHidden } from "./utils";
import fs from "fs";
import path from "path";
test.use({ baseURL: baseUrl() });
test.describe("reset", async () => {
test("reset sessions", async ({ page }) => {
await start(undefined, "sessions.sql");
// check sessions
await page.goto("/#/sessions?year=2023&month=5");
await expect(page.getByTestId("sessions-entry")).toHaveCount(4);
// open backup & restore modal
await openMoreMenu(page);
await page.getByRole("link", { name: "Configuration" }).click();
await page.getByRole("button", { name: "Backup & Restore" }).click();
const modal = page.getByTestId("backup-restore-modal");
await expectModalVisible(modal);
// reset
await expect(modal.getByRole("button", { name: "Reset..." })).toBeDisabled();
await modal.getByRole("checkbox", { name: "Charging sessions" }).check();
await modal.getByRole("button", { name: "Reset..." }).click();
const confirmModal = page.getByTestId("backup-restore-confirm-modal");
await expectModalVisible(confirmModal);
await expect(confirmModal.getByLabel("Administrator Password")).not.toBeVisible(); // disable auth mode
await confirmModal.getByRole("button", { name: "Reset & restart" }).click();
await expectModalHidden(confirmModal);
await expectModalHidden(modal);
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "false");
// manual restart
await restart(undefined, undefined, true);
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "true");
// verify sessions deleted
await page.goto("/#/sessions?year=2023&month=5");
await expect(page.getByTestId("sessions-entry")).toHaveCount(0);
await stop();
});
test("reset settings", async ({ page }) => {
const title = "Home base";
await start(undefined, "sessions.sql");
// create grid meter and title via UI
await page.goto("/#/config");
await page.getByTestId("add-grid").click();
const meterModal = page.getByTestId("meter-modal");
await expectModalVisible(meterModal);
await meterModal.getByLabel("Manufacturer").selectOption("Demo meter");
await meterModal.getByLabel("Power").fill("2000");
await meterModal.getByRole("button", { name: "Save" }).click();
await expectModalHidden(meterModal);
await page.getByTestId("generalconfig-title").getByRole("button", { name: "edit" }).click();
const titleModal = page.getByTestId("title-modal");
await expectModalVisible(titleModal);
await titleModal.getByLabel("Title").fill(title);
await titleModal.getByRole("button", { name: "Save" }).click();
await expectModalHidden(titleModal);
// restart to apply
await restart();
// verify changes are present
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "true");
await expect(page.getByTestId("grid")).toBeVisible();
await expect(page.getByTestId("generalconfig-title")).toContainText(title);
// reset settings only
await page.getByRole("button", { name: "Backup & Restore" }).click();
const modal = page.getByTestId("backup-restore-modal");
await expectModalVisible(modal);
await expect(modal.getByRole("button", { name: "Reset..." })).toBeDisabled();
await modal.getByRole("checkbox", { name: "Settings" }).check();
await modal.getByRole("button", { name: "Reset..." }).click();
const confirmModal = page.getByTestId("backup-restore-confirm-modal");
await expectModalVisible(confirmModal);
await expect(confirmModal.getByLabel("Administrator Password")).not.toBeVisible(); // disable auth mode
await confirmModal.getByRole("button", { name: "Reset & restart" }).click();
await expectModalHidden(confirmModal);
await expectModalHidden(modal);
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "false");
await restart(undefined, undefined, true);
// verify welcome message
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "true");
await expect(page.getByRole("heading", { name: "Hello aboard!" })).toBeVisible();
// verify sessions
await page.goto("/#/sessions?year=2023&month=5");
await expect(page.getByTestId("sessions-entry")).toHaveCount(4);
// verify deleted config and settings
await page.goto("/#/config");
await expect(page.getByTestId("grid")).not.toBeVisible();
await expect(page.getByTestId("generalconfig-title")).not.toContainText(title);
await stop();
});
});
test.describe("backup and restore", async () => {
test("download backup and restore from file", async ({ page }) => {
const initialTitle = "My Home Base";
const changedTitle = "Changed Title";
await start();
await page.goto("/#/config");
// set initial title
await page.getByTestId("generalconfig-title").getByRole("button", { name: "edit" }).click();
const titleModal = page.getByTestId("title-modal");
await expectModalVisible(titleModal);
await titleModal.getByLabel("Title").fill(initialTitle);
await titleModal.getByRole("button", { name: "Save" }).click();
await expectModalHidden(titleModal);
// create grid meter
await page.getByTestId("add-grid").click();
const meterModal = page.getByTestId("meter-modal");
await expectModalVisible(meterModal);
await meterModal.getByLabel("Manufacturer").selectOption("Demo meter");
await meterModal.getByLabel("Power").fill("2000");
await meterModal.getByRole("button", { name: "Save" }).click();
await expectModalHidden(meterModal);
// verify initial state
await expect(page.getByTestId("grid")).toBeVisible();
await expect(page.getByTestId("generalconfig-title")).toContainText(initialTitle);
// open backup & restore modal
await page.getByRole("button", { name: "Backup & Restore" }).click();
const backupModal = page.getByTestId("backup-restore-modal");
await expectModalVisible(backupModal);
// download backup
const downloadPromise = page.waitForEvent("download");
await backupModal.getByRole("button", { name: "Download backup..." }).click();
// download backup confirm
const backupConfirmModal = page.getByTestId("backup-restore-confirm-modal");
await expectModalVisible(backupConfirmModal);
await backupConfirmModal.getByRole("button", { name: "Download backup" }).click();
await expectModalHidden(backupConfirmModal);
const download = await downloadPromise;
await expectModalVisible(backupModal);
await backupModal.locator(".btn-close").click();
await expectModalHidden(backupModal);
// change title and delete meter
await page.getByTestId("generalconfig-title").getByRole("button", { name: "edit" }).click();
await expectModalVisible(titleModal);
await titleModal.getByLabel("Title").fill(changedTitle);
await titleModal.getByRole("button", { name: "Save" }).click();
await expectModalHidden(titleModal);
await page.getByTestId("grid").getByRole("button", { name: "edit" }).click();
const gridModal = page.getByTestId("meter-modal");
await expectModalVisible(gridModal);
await gridModal.getByRole("button", { name: "Delete" }).click();
await expectModalHidden(gridModal);
// verify changes
await expect(page.getByTestId("grid")).not.toBeVisible();
await expect(page.getByTestId("generalconfig-title")).toContainText(changedTitle);
// restore
await page.getByRole("button", { name: "Backup & Restore" }).click();
await expectModalVisible(backupModal);
// prepare backup file
const downloadPath = await download.path();
const dbPath = path.join(path.dirname(downloadPath), "backup.db");
fs.copyFileSync(downloadPath, dbPath);
const fileChooserPromise = page.waitForEvent("filechooser");
await backupModal.getByText("Browse").click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(dbPath);
const restoreButton = backupModal.getByRole("button", { name: "Restore..." });
await expect(restoreButton).toBeEnabled({ timeout: 3000 });
await restoreButton.click();
// confirm restore
const restoreConfirmModal = page.getByTestId("backup-restore-confirm-modal");
await expectModalVisible(restoreConfirmModal);
await restoreConfirmModal.getByRole("button", { name: "Restore & restart" }).click();
await expectModalHidden(restoreConfirmModal);
// restart after restore
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "false");
await restart(undefined, undefined, true);
await expect(page.getByTestId("offline-indicator")).toHaveAttribute("aria-hidden", "true");
// redirect to main ui with initial title
await expect(
page.getByTestId("header").getByRole("heading", { name: initialTitle })
).toBeVisible();
// verify initial state in config ui
await page.goto("/#/config");
await expect(page.getByTestId("grid")).toBeVisible();
await expect(page.getByTestId("generalconfig-title")).toContainText(initialTitle);
await stop();
});
test("backup with authentication", async ({ page }) => {
// start with auth enabled
await start(undefined, "password.sql", "");
await page.goto("/#/config");
// login to access config
const loginModal = page.getByTestId("login-modal");
await expectModalVisible(loginModal);
await loginModal.getByLabel("Password").fill("secret");
await loginModal.getByRole("button", { name: "Login" }).click();
await expectModalHidden(loginModal);
// open backup & restore modal
await page.getByRole("button", { name: "Backup & Restore" }).click();
const backupModal = page.getByTestId("backup-restore-modal");
await expectModalVisible(backupModal);
// try wrong password
await backupModal.getByRole("button", { name: "Download backup..." }).click();
const backupConfirmModal = page.getByTestId("backup-restore-confirm-modal");
await expectModalVisible(backupConfirmModal);
const passwordField = backupConfirmModal.getByLabel("Administrator Password");
await expect(passwordField).toBeVisible();
await passwordField.fill("wrongpassword");
await backupConfirmModal.getByRole("button", { name: "Download backup" }).click();
await expect(backupConfirmModal.getByText("Password is invalid.")).toBeVisible();
await passwordField.clear();
await passwordField.fill("secret");
const downloadPromise = page.waitForEvent("download");
await backupConfirmModal.getByRole("button", { name: "Download backup" }).click();
await expectModalHidden(backupConfirmModal);
// verify backup was downloaded successfully
const download = await downloadPromise;
await expect(download.suggestedFilename()).toContain("evcc-backup");
await stop();
});
});