116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
|
|
import { LocalAuthLogin } from "./LocalAuthLogin";
|
|
|
|
const setLocalAuthTokenMock = vi.hoisted(() => vi.fn());
|
|
const fetchMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("@/auth/localAuth", async () => {
|
|
const actual = await vi.importActual<typeof import("@/auth/localAuth")>(
|
|
"@/auth/localAuth",
|
|
);
|
|
return {
|
|
...actual,
|
|
setLocalAuthToken: setLocalAuthTokenMock,
|
|
};
|
|
});
|
|
|
|
describe("LocalAuthLogin", () => {
|
|
beforeEach(() => {
|
|
fetchMock.mockReset();
|
|
setLocalAuthTokenMock.mockReset();
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
vi.stubEnv("NEXT_PUBLIC_API_URL", "http://localhost:8000/");
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
vi.unstubAllEnvs();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("requires a non-empty token", async () => {
|
|
const user = userEvent.setup();
|
|
render(<LocalAuthLogin />);
|
|
|
|
await user.click(screen.getByRole("button", { name: "Continue" }));
|
|
|
|
expect(screen.getByText("Bearer token is required.")).toBeInTheDocument();
|
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
expect(setLocalAuthTokenMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("requires token length of at least 50 characters", async () => {
|
|
const user = userEvent.setup();
|
|
render(<LocalAuthLogin />);
|
|
|
|
await user.type(screen.getByPlaceholderText("Paste token"), "x".repeat(49));
|
|
await user.click(screen.getByRole("button", { name: "Continue" }));
|
|
|
|
expect(
|
|
screen.getByText("Bearer token must be at least 50 characters."),
|
|
).toBeInTheDocument();
|
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
expect(setLocalAuthTokenMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("rejects invalid token values", async () => {
|
|
const onAuthenticatedMock = vi.fn();
|
|
fetchMock.mockResolvedValueOnce(new Response(null, { status: 401 }));
|
|
const user = userEvent.setup();
|
|
render(<LocalAuthLogin onAuthenticated={onAuthenticatedMock} />);
|
|
|
|
await user.type(screen.getByPlaceholderText("Paste token"), "x".repeat(50));
|
|
await user.click(screen.getByRole("button", { name: "Continue" }));
|
|
|
|
await waitFor(() =>
|
|
expect(screen.getByText("Token is invalid.")).toBeInTheDocument(),
|
|
);
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
"http://localhost:8000/api/v1/users/me",
|
|
expect.objectContaining({
|
|
method: "GET",
|
|
headers: { Authorization: `Bearer ${"x".repeat(50)}` },
|
|
}),
|
|
);
|
|
expect(setLocalAuthTokenMock).not.toHaveBeenCalled();
|
|
expect(onAuthenticatedMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("saves token only after successful backend validation", async () => {
|
|
const onAuthenticatedMock = vi.fn();
|
|
fetchMock.mockResolvedValueOnce(new Response(null, { status: 200 }));
|
|
const user = userEvent.setup();
|
|
render(<LocalAuthLogin onAuthenticated={onAuthenticatedMock} />);
|
|
|
|
const token = ` ${"g".repeat(50)} `;
|
|
await user.type(screen.getByPlaceholderText("Paste token"), token);
|
|
await user.click(screen.getByRole("button", { name: "Continue" }));
|
|
|
|
await waitFor(() =>
|
|
expect(setLocalAuthTokenMock).toHaveBeenCalledWith("g".repeat(50)),
|
|
);
|
|
expect(onAuthenticatedMock).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("shows a clear error when backend is unreachable", async () => {
|
|
const onAuthenticatedMock = vi.fn();
|
|
fetchMock.mockRejectedValueOnce(new TypeError("network error"));
|
|
const user = userEvent.setup();
|
|
render(<LocalAuthLogin onAuthenticated={onAuthenticatedMock} />);
|
|
|
|
await user.type(screen.getByPlaceholderText("Paste token"), "t".repeat(50));
|
|
await user.click(screen.getByRole("button", { name: "Continue" }));
|
|
|
|
await waitFor(() =>
|
|
expect(
|
|
screen.getByText("Unable to reach backend to validate token."),
|
|
).toBeInTheDocument(),
|
|
);
|
|
expect(setLocalAuthTokenMock).not.toHaveBeenCalled();
|
|
expect(onAuthenticatedMock).not.toHaveBeenCalled();
|
|
});
|
|
});
|