Files
SnapOtter/tests/unit/web/admin-security-settings-save-error.test.tsx
T
SnapOtterandGitHub 846044a463 fix(settings): let admins relax the minimum password length to 1 (#543)
The password policy toggles (uppercase, lowercase, digit, special) can all be switched off in Settings -> Security, but the minimum-length input clamped at 4, so homelab admins couldn't deliberately allow short passwords. The API never enforced a floor; only the UI did. Lower the input floor to 1 and pin it with a test.

Closes #136
2026-07-17 00:22:45 +08:00

56 lines
1.9 KiB
TypeScript

// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
const apiGet = vi.hoisted(() => vi.fn().mockResolvedValue({ settings: {} }));
const apiPut = vi.hoisted(() => vi.fn());
vi.mock("@/lib/api", async (importOriginal) => {
const actual: Record<string, unknown> = await importOriginal();
return { ...actual, apiGet, apiPut };
});
import { AdminSecuritySettings } from "@/components/settings/settings-dialog";
afterEach(() => {
cleanup();
apiGet.mockClear();
apiPut.mockReset();
});
describe("AdminSecuritySettings save errors", () => {
it("shows the server's specific error message when a save is rejected", async () => {
apiPut.mockRejectedValue(new Error("MFA requires an enterprise license"));
render(<AdminSecuritySettings />);
await waitFor(() => expect(apiGet).toHaveBeenCalled());
fireEvent.click(await screen.findByRole("button", { name: /save/i }));
const message = await screen.findByText("MFA requires an enterprise license");
expect(message).toHaveClass("text-destructive");
});
it("lets admins relax the minimum password length down to 1", async () => {
render(<AdminSecuritySettings />);
await waitFor(() => expect(apiGet).toHaveBeenCalled());
const input = screen.getByLabelText("Minimum Password Length");
expect(input).toHaveAttribute("min", "1");
});
it("falls back to a generic message when the save rejects with a non-Error value", async () => {
apiPut.mockRejectedValue("network exploded");
render(<AdminSecuritySettings />);
await waitFor(() => expect(apiGet).toHaveBeenCalled());
fireEvent.click(await screen.findByRole("button", { name: /save/i }));
const message = await screen.findByText("Failed to save security settings");
expect(message).toHaveClass("text-destructive");
});
});