feat(auth): AuthProvider context + useAuth hook
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { renderHook, act, waitFor } from "@testing-library/react";
|
||||
|
||||
vi.mock("./api", () => ({
|
||||
authApi: {
|
||||
me: vi.fn(),
|
||||
login: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
import { authApi } from "./api";
|
||||
import { AuthProvider, useAuth } from "./AuthProvider";
|
||||
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
);
|
||||
|
||||
describe("AuthProvider", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("loads user on mount", async () => {
|
||||
(authApi.me as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
id: 1,
|
||||
username: "alice",
|
||||
role: "user",
|
||||
});
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.status).toBe("authenticated"));
|
||||
expect(result.current.user?.username).toBe("alice");
|
||||
});
|
||||
|
||||
it("status is unauthenticated when /me fails", async () => {
|
||||
(authApi.me as unknown as ReturnType<typeof vi.fn>).mockRejectedValue(
|
||||
new Error("401")
|
||||
);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.status).toBe("unauthenticated"));
|
||||
expect(result.current.user).toBeNull();
|
||||
});
|
||||
|
||||
it("login() updates state on success", async () => {
|
||||
(authApi.me as unknown as ReturnType<typeof vi.fn>).mockRejectedValue(
|
||||
new Error("401")
|
||||
);
|
||||
(
|
||||
authApi.login as unknown as ReturnType<typeof vi.fn>
|
||||
).mockResolvedValue({ id: 1, username: "alice", role: "admin" });
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.status).toBe("unauthenticated"));
|
||||
await act(async () => {
|
||||
await result.current.login("alice", "pw");
|
||||
});
|
||||
expect(result.current.user?.username).toBe("alice");
|
||||
expect(result.current.status).toBe("authenticated");
|
||||
});
|
||||
|
||||
it("logout() clears state", async () => {
|
||||
(authApi.me as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
id: 1,
|
||||
username: "alice",
|
||||
role: "user",
|
||||
});
|
||||
(
|
||||
authApi.logout as unknown as ReturnType<typeof vi.fn>
|
||||
).mockResolvedValue(undefined);
|
||||
const { result } = renderHook(() => useAuth(), { wrapper });
|
||||
await waitFor(() => expect(result.current.status).toBe("authenticated"));
|
||||
await act(async () => {
|
||||
await result.current.logout();
|
||||
});
|
||||
expect(result.current.user).toBeNull();
|
||||
expect(result.current.status).toBe("unauthenticated");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user