feat(auth): /login page
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, fireEvent, waitFor, cleanup } from "@testing-library/react";
|
||||
import { MemoryRouter, Routes, Route } from "react-router-dom";
|
||||
import { Login } from "./Login";
|
||||
|
||||
vi.mock("@/auth/useAuth", () => ({ useAuth: vi.fn() }));
|
||||
import { useAuth } from "@/auth/useAuth";
|
||||
|
||||
function setup() {
|
||||
const login = vi.fn();
|
||||
(useAuth as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
||||
user: null,
|
||||
status: "unauthenticated",
|
||||
login,
|
||||
logout: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
});
|
||||
return { login };
|
||||
}
|
||||
|
||||
describe("Login page", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it("submits username + password", async () => {
|
||||
const { login } = setup();
|
||||
login.mockResolvedValue(undefined);
|
||||
const { getByLabelText, getByRole } = render(
|
||||
<MemoryRouter initialEntries={["/login"]}>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/" element={<div>HOME</div>} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
fireEvent.change(getByLabelText(/username/i), {
|
||||
target: { value: "alice" },
|
||||
});
|
||||
fireEvent.change(getByLabelText(/password/i), {
|
||||
target: { value: "hunter2hunter2" },
|
||||
});
|
||||
fireEvent.click(getByRole("button", { name: /sign in/i }));
|
||||
await waitFor(() =>
|
||||
expect(login).toHaveBeenCalledWith("alice", "hunter2hunter2")
|
||||
);
|
||||
});
|
||||
|
||||
it("shows error on failed login", async () => {
|
||||
const { login } = setup();
|
||||
login.mockRejectedValue(
|
||||
Object.assign(new Error("401"), { code: "invalid_credentials" })
|
||||
);
|
||||
const { getByLabelText, getByRole, getByText } = render(
|
||||
<MemoryRouter initialEntries={["/login"]}>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
fireEvent.change(getByLabelText(/username/i), {
|
||||
target: { value: "alice" },
|
||||
});
|
||||
fireEvent.change(getByLabelText(/password/i), {
|
||||
target: { value: "wrong" },
|
||||
});
|
||||
fireEvent.click(getByRole("button", { name: /sign in/i }));
|
||||
await waitFor(() =>
|
||||
expect(getByText(/username or password is incorrect/i)).toBeTruthy()
|
||||
);
|
||||
});
|
||||
|
||||
it("redirects to `next` query param on success", async () => {
|
||||
const { login } = setup();
|
||||
login.mockResolvedValue(undefined);
|
||||
const { getByLabelText, getByRole, getByText } = render(
|
||||
<MemoryRouter initialEntries={["/login?next=/claims"]}>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/claims" element={<div>CLAIMS</div>} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
fireEvent.change(getByLabelText(/username/i), {
|
||||
target: { value: "alice" },
|
||||
});
|
||||
fireEvent.change(getByLabelText(/password/i), {
|
||||
target: { value: "hunter2hunter2" },
|
||||
});
|
||||
fireEvent.click(getByRole("button", { name: /sign in/i }));
|
||||
await waitFor(() => expect(getByText("CLAIMS")).toBeTruthy());
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user