evcc-io/tests/issue.spec.ts

193 lines
8.3 KiB
TypeScript

import { test, expect } from "@playwright/test";
import { start, stop, restart, baseUrl } from "./evcc";
import { startSimulator, stopSimulator, simulatorHost } from "./simulator";
import { enableExperimental, expectModalVisible, expectModalHidden } from "./utils";
test.use({ baseURL: baseUrl() });
test.beforeAll(async () => {
await startSimulator();
});
test.afterAll(async () => {
await stopSimulator();
});
test.afterEach(async () => {
await stop();
});
const REDACT_CONFIG = "sponsor.evcc.yaml";
const CONFIG = "issue.evcc.yaml";
test.describe("issue creation", () => {
test("verify evcc.yaml redaction", async ({ page }) => {
await start(REDACT_CONFIG);
await page.goto("/#/issue");
// get configuration (yaml)
await page
.getByTestId("issueYamlConfig-additional-item")
.getByRole("button", { name: "show details" })
.click();
const modal = page.getByTestId("issueYamlConfig-modal");
await expectModalVisible(modal);
const configContent = await modal.getByRole("textbox").inputValue();
// check for redation
expect(configContent).toContain("sponsortoken: *****");
expect(configContent).toContain("user: *****");
expect(configContent).toContain("password: *****");
// ensure redacted values are not present
expect(configContent).not.toContain("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9");
expect(configContent).not.toContain("test@example.org");
expect(configContent).not.toContain("none");
// verify other poarts
expect(configContent).toContain("site:");
expect(configContent).toContain("loadpoints:");
});
test("create issue via ui", async ({ page }) => {
await start(CONFIG);
await page.goto("/#/config");
// Enable experimental features
await enableExperimental(page, false);
// Create a Shelly meter with username (to test private data redaction)
await page.getByRole("button", { name: "Add grid meter" }).click();
const meterModal = page.getByTestId("meter-modal");
await expectModalVisible(meterModal);
await meterModal.getByLabel("Manufacturer").selectOption("Shelly 1PM");
await meterModal.getByLabel("IP address or hostname").fill(simulatorHost());
await meterModal.getByLabel("Username").fill("testuser@example.com");
await meterModal.getByLabel("Password").fill("secretpass");
await meterModal.getByRole("button", { name: "Validate & save" }).click();
await expectModalHidden(meterModal);
await expect(page.getByTestId("grid")).toBeVisible();
// Restart to apply changes
await restart(CONFIG);
await page.reload();
// Navigate to issue creation from config page
await page.getByRole("link", { name: "Report a problem" }).click();
await expect(page.getByRole("heading", { name: "Report a problem" })).toBeVisible();
await expect(page.getByRole("button", { name: "Start GitHub Discussion..." })).toBeVisible();
await page.getByRole("radio", { name: "Found a bug" }).click();
await expect(page.getByRole("button", { name: "Create GitHub Issue..." })).toBeVisible();
// Fill out the issue form
await page.getByLabel("Title").fill("Kaboom");
await page
.getByLabel("Description")
.fill("This is a test issue created from the config page workflow");
await page
.getByLabel("Steps to reproduce")
.fill("1. Go to config\n2. Enable experimental\n3. Add meter\n4. Report issue");
// check yaml data
const yamlItem = page.getByTestId("issueYamlConfig-additional-item");
await yamlItem.getByRole("button", { name: "show details" }).click();
const yamlModal = page.getByTestId("issueYamlConfig-modal");
await expectModalVisible(yamlModal);
await expect(yamlModal.getByRole("textbox")).toHaveValue(/carport_pv/);
await yamlModal.getByRole("button", { name: "Close" }).first().click();
await expectModalHidden(yamlModal);
// check ui data and verify private data redaction
const uiItem = page.getByTestId("issueUiConfig-additional-item");
await uiItem.getByRole("button", { name: "show details" }).click();
const uiModal = page.getByTestId("issueUiConfig-modal");
await expectModalVisible(uiModal);
const uiContent = await uiModal.getByRole("textbox").inputValue();
// Verify meter is present but private data is redacted
expect(uiContent).toContain("shelly"); // meter type should be visible
expect(uiContent).not.toContain("testuser@example.com"); // user should be redacted
expect(uiContent).not.toContain("secretpass"); // password should be redacted
expect(uiContent).toContain("***"); // redaction marker should be present
await uiModal.getByRole("button", { name: "Close" }).first().click();
await expectModalHidden(uiModal);
// check log data
const logsItem = page.getByTestId("issueLogs-additional-item");
await logsItem.getByRole("button", { name: "show details" }).click();
const logsModal = page.getByTestId("issueLogs-modal");
await expectModalVisible(logsModal);
await expect(logsModal.getByRole("textbox")).toHaveValue(/DEBUG/);
await logsModal.getByRole("button", { name: "Close" }).first().click();
await expectModalHidden(logsModal);
// check state
const stateItem = page.getByTestId("issueState-additional-item");
await stateItem.getByRole("button", { name: "show details" }).click();
const stateModal = page.getByTestId("issueState-modal");
await expectModalVisible(stateModal);
await expect(stateModal.getByRole("textbox")).toHaveValue(/"telemetry":/);
await stateModal.getByRole("button", { name: "Close" }).first().click();
await expectModalHidden(stateModal);
const stateSwitch = stateItem.getByRole("switch", { name: "include" });
await stateSwitch.check();
await expect(stateSwitch).toBeChecked();
await page.getByRole("button", { name: "Create GitHub Issue..." }).click();
// 2-step process
const summaryModal = page.getByTestId("issue-summary-modal");
await expectModalVisible(summaryModal);
await expect(summaryModal.getByRole("heading", { name: /GitHub Problem/ })).toBeVisible();
await expect(summaryModal.getByRole("heading", { name: /Step 1:/ })).toBeVisible();
await expect(summaryModal.getByRole("heading", { name: /Step 2:/ })).toBeVisible();
// check info in textarea
const textarea = summaryModal.getByTestId("issue-summary-modal-textarea");
await expect(textarea).toBeVisible();
const textareaContent = await textarea.inputValue();
expect(textareaContent).toContain("carport_pv"); // from evcc.yaml
expect(textareaContent).toContain("shelly"); // from ui config
expect(textareaContent).toContain("DEBUG"); // from logs
expect(textareaContent).toContain('"telemetry":'); // from state
// check only basics in github link
let href = await summaryModal
.getByRole("link", { name: "Create GitHub Issue" })
.getAttribute("href");
expect(href).toContain("https://github.com/evcc-io/evcc/issues/new?title=Kaboom&body=");
expect(href).not.toContain("TestShelly"); // from ui config
expect(href).not.toContain("carport_pv"); // from evcc.yaml
// close modal
await summaryModal.getByRole("button", { name: "Close" }).click();
await expectModalHidden(summaryModal);
// replace long state with short custom message
await stateItem.getByRole("button", { name: "show details" }).click();
await expectModalVisible(stateModal);
await stateModal.getByRole("textbox").fill("MyFancyState");
await stateModal.getByRole("button", { name: "Apply & close" }).first().click();
await expectModalHidden(stateModal);
// single-step process
await page.getByRole("button", { name: "Create GitHub Issue..." }).click();
await expectModalVisible(summaryModal);
await expect(summaryModal.getByRole("heading", { name: /Step 1:/ })).not.toBeVisible();
await expect(summaryModal.getByRole("heading", { name: /Step 2:/ })).not.toBeVisible();
// verify contents in github link
href = await summaryModal
.getByRole("link", { name: "Create GitHub Issue" })
.getAttribute("href");
expect(href).toContain("https://github.com/evcc-io/evcc/issues/new?title=Kaboom&body=");
expect(href).toContain("shelly"); // from ui config
expect(href).toContain("carport_pv"); // from evcc.yaml
expect(href).toContain("DEBUG"); // from logs
expect(href).toContain("MyFancyState"); // from state
});
});