refactor(custom-widgets): extract domain package

This commit is contained in:
Thomas Camlong committed 2026-07-14 16:59:06 +02:00
1 parent c5703e7e53
commit 4c6ffcd0fa
167 files changed
+12282 -10033

No files matched your search

+11 -10
View File
@@ -3,7 +3,7 @@ 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 { startMockApiContainerAsync } from "./shared/mock-api-container";
import { seedAdminUserAsync } from "./shared/seed-admin-user";
const adminCredentials = {
@@ -18,7 +18,7 @@ const updatedTemplate = '<Stack gap="xs"><Text>{data.title}</Text><Badge color="
describe("Custom JSX custom widgets", () => {
test("creates, previews, and edits a customJsx widget", async () => {
const mockApi = await startMockApiServerAsync({
const mockApi = await startMockApiContainerAsync({
title: "E2E Widget",
status: "online",
value: 42,
@@ -52,12 +52,13 @@ describe("Custom JSX custom widgets", () => {
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.getByRole("button", { name: /Custom JSX/u }).click();
await page.getByRole("combobox", { name: "Network scope" }).click();
await page.getByRole("option", { name: "Private networks" }).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" });
const previewPanel = page.getByRole("complementary", { name: "Preview" });
await previewPanel.getByRole("button", { name: "Test" }).click();
await expect(previewPanel.getByRole("heading", { name: "E2E Widget" }).first()).toBeVisible({
timeout: 15_000,
});
@@ -65,18 +66,18 @@ describe("Custom JSX custom widgets", () => {
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 expect(page.getByLabel("JSX Template")).toHaveText(initialTemplate);
await page.getByLabel("JSX Template").fill(updatedTemplate);
await expect(page.getByLabel("JSX Template")).toHaveValue(updatedTemplate);
await expect(page.getByLabel("JSX Template")).toHaveText(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);
await expect(page.getByLabel("JSX Template")).toHaveText(updatedTemplate);
} finally {
await browser.close();
await homarrContainer.stop();
await mockApi.close();
await mockApi.stop();
}
}, 120_000);
});
+27
View File
@@ -0,0 +1,27 @@
import { GenericContainer, Wait } from "testcontainers";
import type { StartedTestContainer } from "testcontainers";
export interface MockApiContainer {
url: string;
stop: () => Promise<void>;
}
export async function startMockApiContainerAsync(responseBody: unknown): Promise<MockApiContainer> {
const body = JSON.stringify(responseBody);
const script = [
'const { createServer } = require("node:http");',
`const body = ${JSON.stringify(body)};`,
'createServer((_request, response) => { response.writeHead(200, { "content-type": "application/json" }); response.end(body); }).listen(8080, "0.0.0.0");',
].join("\n");
const container: StartedTestContainer = await new GenericContainer("node:24-alpine")
.withCommand(["node", "-e", script])
.withExposedPorts(8080)
.withWaitStrategy(Wait.forHttp("/status", 8080))
.start();
return {
url: `http://${container.getIpAddress("bridge")}:8080`,
stop: async () => void (await container.stop()),
};
}
-35
View File
@@ -1,35 +0,0 @@
import { createServer } from "node:http";
import type { Server } from "node:http";
export interface MockApiServer {
url: string;
port: number;
close: () => Promise<void>;
}
export const startMockApiServerAsync = async (responseBody: unknown): Promise<MockApiServer> => {
const body = JSON.stringify(responseBody);
const server: Server = await new Promise((resolve) => {
const s = createServer((_req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(body);
});
s.listen(0, "0.0.0.0", () => resolve(s));
});
const address = server.address();
if (!address || typeof address === "string") {
throw new Error("Failed to start mock API server");
}
return {
port: address.port,
url: `http://host.docker.internal:${address.port}`,
close: () =>
new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
}),
};
};