Skip to content

Commit 1ad5a8b

Browse files
Jeremias Santosclaude
authored andcommitted
test(pav-5): add frontend tests for OAuth integration
- Add getGoogleAuthUrl tests to authService tests - Add refreshUser tests to AuthContext tests - Create AuthCallback page tests (loading, redirect on success/failure) Brings branch coverage above 80% threshold. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f72e161 commit 1ad5a8b

3 files changed

Lines changed: 190 additions & 1 deletion

File tree

frontend/tests/unit/context/AuthContext.test.tsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ vi.mock("@/services/api", () => ({
1616
import { AuthProvider, useAuth } from "@/context/AuthContext";
1717

1818
function TestConsumer() {
19-
const { user, isLoading, login, logout } = useAuth();
19+
const { user, isLoading, login, logout, refreshUser } = useAuth();
2020
return (
2121
<div>
2222
<span data-testid="loading">{String(isLoading)}</span>
@@ -25,6 +25,7 @@ function TestConsumer() {
2525
login
2626
</button>
2727
<button onClick={() => logout()}>logout</button>
28+
<button onClick={() => refreshUser()}>refresh</button>
2829
</div>
2930
);
3031
}
@@ -146,4 +147,58 @@ describe("AuthContext", () => {
146147
render(<Naked />);
147148
expect(screen.getByTestId("ctx")).toBeInTheDocument();
148149
});
150+
151+
it("refreshUser atualiza o usuário ao chamar /auth/me novamente", async () => {
152+
mockApiGet.mockRejectedValueOnce(new Error("Unauthorized"));
153+
154+
renderWithProvider();
155+
156+
await waitFor(() => {
157+
expect(screen.getByTestId("loading").textContent).toBe("false");
158+
});
159+
160+
expect(screen.getByTestId("user").textContent).toBe("null");
161+
162+
mockApiGet.mockResolvedValueOnce({
163+
data: { user: { id: "2", email: "novo@teste.com", name: "Novo" } },
164+
});
165+
166+
await act(async () => {
167+
screen.getByRole("button", { name: /refresh/i }).click();
168+
});
169+
170+
await waitFor(() => {
171+
expect(screen.getByTestId("loading").textContent).toBe("false");
172+
});
173+
174+
const user = JSON.parse(screen.getByTestId("user").textContent!);
175+
expect(user.id).toBe("2");
176+
expect(user.email).toBe("novo@teste.com");
177+
});
178+
179+
it("refreshUser define user como null quando /auth/me falha", async () => {
180+
mockApiGet.mockResolvedValueOnce({
181+
data: { user: { id: "1", email: "joao@teste.com" } },
182+
});
183+
184+
renderWithProvider();
185+
186+
await waitFor(() => {
187+
expect(screen.getByTestId("loading").textContent).toBe("false");
188+
});
189+
190+
expect(screen.getByTestId("user").textContent).not.toBe("null");
191+
192+
mockApiGet.mockRejectedValueOnce(new Error("Unauthorized"));
193+
194+
await act(async () => {
195+
screen.getByRole("button", { name: /refresh/i }).click();
196+
});
197+
198+
await waitFor(() => {
199+
expect(screen.getByTestId("loading").textContent).toBe("false");
200+
});
201+
202+
expect(screen.getByTestId("user").textContent).toBe("null");
203+
});
149204
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { render, screen, waitFor } from "@testing-library/react";
2+
import { describe, expect, it, vi, beforeEach } from "vitest";
3+
import "@testing-library/jest-dom/vitest";
4+
5+
const mockNavigate = vi.fn();
6+
vi.mock("react-router-dom", () => ({
7+
useNavigate: () => mockNavigate,
8+
}));
9+
10+
const mockUseAuth = vi.fn();
11+
vi.mock("@/context/AuthContext", () => ({
12+
useAuth: () => mockUseAuth(),
13+
}));
14+
15+
vi.mock("@/Loading", () => ({
16+
default: () => <div data-testid="loading">Loading...</div>,
17+
}));
18+
19+
import AuthCallback from "@/pages/auth/AuthCallback";
20+
21+
describe("AuthCallback", () => {
22+
beforeEach(() => {
23+
vi.clearAllMocks();
24+
});
25+
26+
it("mostra loading enquanto isLoading é true", () => {
27+
mockUseAuth.mockReturnValue({
28+
user: null,
29+
isLoading: true,
30+
refreshUser: vi.fn(),
31+
});
32+
33+
render(<AuthCallback />);
34+
35+
expect(screen.getByTestId("loading")).toBeInTheDocument();
36+
});
37+
38+
it("navega para /app quando user existe", async () => {
39+
const refreshUser = vi.fn();
40+
mockUseAuth
41+
.mockReturnValueOnce({ user: null, isLoading: true, refreshUser })
42+
.mockReturnValue({
43+
user: { id: "1", email: "test@test.com" },
44+
isLoading: false,
45+
refreshUser,
46+
});
47+
48+
const { rerender } = render(<AuthCallback />);
49+
50+
expect(refreshUser).toHaveBeenCalled();
51+
52+
rerender(<AuthCallback />);
53+
54+
await waitFor(() => {
55+
expect(mockNavigate).toHaveBeenCalledWith("/app", { replace: true });
56+
});
57+
});
58+
59+
it("navega para /login com erro quando user é null", async () => {
60+
const refreshUser = vi.fn();
61+
mockUseAuth
62+
.mockReturnValueOnce({ user: null, isLoading: true, refreshUser })
63+
.mockReturnValue({ user: null, isLoading: false, refreshUser });
64+
65+
const { rerender } = render(<AuthCallback />);
66+
67+
expect(refreshUser).toHaveBeenCalled();
68+
69+
rerender(<AuthCallback />);
70+
71+
await waitFor(() => {
72+
expect(mockNavigate).toHaveBeenCalledWith("/login?error=oauth_failed", {
73+
replace: true,
74+
});
75+
});
76+
});
77+
78+
it("chama refreshUser ao montar", () => {
79+
const refreshUser = vi.fn();
80+
mockUseAuth.mockReturnValue({
81+
user: null,
82+
isLoading: true,
83+
refreshUser,
84+
});
85+
86+
render(<AuthCallback />);
87+
88+
expect(refreshUser).toHaveBeenCalledTimes(1);
89+
});
90+
});

frontend/tests/unit/services/auth.service.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,48 @@ describe("authService", () => {
252252
);
253253
});
254254
});
255+
256+
describe("getGoogleAuthUrl", () => {
257+
it("should return Google auth URL on success", async () => {
258+
fetchMock.mockResolvedValueOnce(
259+
mockResponse({
260+
jsonData: { url: "https://accounts.google.com/o/oauth2/auth?state=abc" },
261+
})
262+
);
263+
264+
const url = await auth.getGoogleAuthUrl();
265+
266+
expect(url).toBe("https://accounts.google.com/o/oauth2/auth?state=abc");
267+
expect(fetchMock).toHaveBeenCalledWith(
268+
expect.stringContaining("/api/auth/google/url"),
269+
expect.objectContaining({ credentials: "include" })
270+
);
271+
});
272+
273+
it("should throw error on failure with message", async () => {
274+
fetchMock.mockResolvedValueOnce(
275+
mockResponse({
276+
ok: false,
277+
status: 500,
278+
jsonData: { message: "Provider unavailable" },
279+
})
280+
);
281+
282+
await expect(auth.getGoogleAuthUrl()).rejects.toThrow("Provider unavailable");
283+
});
284+
285+
it("should throw error on failure without message", async () => {
286+
fetchMock.mockResolvedValueOnce(
287+
mockResponse({
288+
ok: false,
289+
status: 500,
290+
jsonData: {},
291+
})
292+
);
293+
294+
await expect(auth.getGoogleAuthUrl()).rejects.toThrow(
295+
"Falha ao obter URL de autenticacao Google."
296+
);
297+
});
298+
});
255299
});

0 commit comments

Comments
 (0)