Files
homarr/e2e/custom-widgets-custom-jsx.spec.ts
T
Thomas Camlong 926b7e2a5e test(e2e): wait for login form hydration before filling
The customJsx E2E test occasionally failed at login with the form
showing 'This field is required' on the username, because the submit
button was clicked while the form was still hydrating (it is disabled
until mounted) and the field values were reset by the re-mount. Wait
for the submit button to become enabled before filling, and assert the
username value stuck before submitting.
2026-08-07 15:49:21 +02:00

88 lines
3.9 KiB
TypeScript

import { chromium, expect } from "@playwright/test";
import { describe, test } from "vitest";
import { createHomarrContainer } from "./shared/create-homarr-container";
import { createSqliteDbFileAsync } from "./shared/e2e-db";
import { startMockApiServerAsync } from "./shared/mock-api-server";
import { seedAdminUserAsync } from "./shared/seed-admin-user";
const adminCredentials = {
username: "admin",
password: "Comp(exP4sswOrd",
};
const initialTemplate =
'<Stack gap="xs"><Title order={4}>{data.title}</Title><Badge color="blue">{data.status}</Badge></Stack>';
const updatedTemplate = '<Stack gap="xs"><Text>{data.title}</Text><Badge color="green">{data.value}</Badge></Stack>';
describe("Custom JSX custom widgets", () => {
test("creates, previews, and edits a customJsx widget", async () => {
const mockApi = await startMockApiServerAsync({
title: "E2E Widget",
status: "online",
value: 42,
});
const { db, localMountPath } = await createSqliteDbFileAsync();
await seedAdminUserAsync(db, adminCredentials);
const homarrContainer = await createHomarrContainer({
environment: {
AUTH_PROVIDERS: "credentials",
},
mounts: {
"/appdata": localMountPath,
},
}).start();
const baseUrl = `http://${homarrContainer.getHost()}:${homarrContainer.getMappedPort(7575)}`;
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
try {
await page.goto(`${baseUrl}/auth/login`);
// The submit button is disabled until the form finishes hydrating, so wait for it
// before filling to avoid a hydration re-mount resetting the field values.
const submitButton = page.locator("css=button[type='submit']");
await expect(submitButton).toBeEnabled({ timeout: 30_000 });
await page.getByLabel("Username").fill(adminCredentials.username);
await page.locator("#password").fill(adminCredentials.password);
await expect(page.getByLabel("Username")).toHaveValue(adminCredentials.username);
await submitButton.click();
await expect(page).not.toHaveURL(/\/auth\/login/, { timeout: 30_000 });
await page.goto(`${baseUrl}/manage/custom-widgets/new`);
await page.waitForURL("**/manage/custom-widgets/new", { timeout: 15_000 });
await page.getByRole("textbox", { name: "Name" }).fill("E2E Custom JSX");
await page.getByRole("textbox", { name: "URL", exact: true }).fill(`${mockApi.url}/status`);
await page.getByRole("combobox", { name: "Display Type" }).click();
await page.getByRole("option", { name: "Custom JSX" }).click();
await page.getByLabel("JSX Template").fill(initialTemplate);
await page.getByRole("button", { name: "Test" }).first().click();
const previewPanel = page.locator(".mantine-Card-root").filter({ hasText: "Preview" });
await expect(previewPanel.getByRole("heading", { name: "E2E Widget" }).first()).toBeVisible({
timeout: 15_000,
});
await page.getByRole("button", { name: "Create" }).first().click();
await expect(page.getByText('Widget "E2E Custom JSX" created successfully.')).toBeVisible({ timeout: 15_000 });
await page.waitForURL("**/manage/custom-widgets/edit/**", { timeout: 15_000 });
await expect(page.getByLabel("JSX Template")).toHaveValue(initialTemplate);
await page.getByLabel("JSX Template").fill(updatedTemplate);
await expect(page.getByLabel("JSX Template")).toHaveValue(updatedTemplate);
await page.getByRole("button", { name: "Save" }).first().click();
await expect(page.getByText('Widget "E2E Custom JSX" updated successfully.')).toBeVisible({ timeout: 15_000 });
await expect(page.getByLabel("JSX Template")).toHaveValue(updatedTemplate);
} finally {
await browser.close();
await homarrContainer.stop();
await mockApi.close();
}
}, 120_000);
});