Skip to content

Commit 8aba92f

Browse files
committed
feat: implement actual sign out flow
1 parent 8f12190 commit 8aba92f

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

src/app/layout.test.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { render, screen } from "@testing-library/react";
2-
import { describe, expect, it } from "vitest";
2+
import { describe, expect, it, vi } from "vitest";
33
import { UserMenu } from "@/components/user-menu";
4+
import { signOut } from "@/lib/auth/auth-client";
45

56
describe("UserMenu", () => {
67
it.each([
@@ -36,4 +37,26 @@ describe("UserMenu", () => {
3637
const signOutItem = screen.getByText("Sign out");
3738
expect(signOutItem).toBeTruthy();
3839
});
40+
41+
it("calls signOut when sign out menu item is clicked", async () => {
42+
const userEvent = (await import("@testing-library/user-event")).default;
43+
render(<UserMenu userName="Jane Smith" />);
44+
const user = userEvent.setup();
45+
46+
// Open dropdown - use getAllByRole and find the one in this render
47+
const buttons = screen.getAllByRole("button");
48+
const trigger = buttons.find((btn) =>
49+
btn.textContent?.includes("Jane Smith"),
50+
);
51+
expect(trigger).toBeTruthy();
52+
if (!trigger) return;
53+
await user.click(trigger);
54+
55+
// Find and click the sign out menu item (it's a div with role="menuitem")
56+
const signOutItem = screen.getByRole("menuitem", { name: /sign out/i });
57+
await user.click(signOutItem);
58+
59+
// Verify signOut was called
60+
expect(signOut).toHaveBeenCalledOnce();
61+
});
3962
});

src/components/user-menu.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
DropdownMenuItem,
99
DropdownMenuTrigger,
1010
} from "@/components/ui/dropdown-menu";
11+
import { signOut } from "@/lib/auth/auth-client";
1112

1213
interface UserMenuProps {
1314
userName: string;
@@ -24,6 +25,10 @@ function getInitials(name: string): string {
2425
export function UserMenu({ userName }: UserMenuProps) {
2526
const initials = getInitials(userName);
2627

28+
const handleSignOut = async () => {
29+
await signOut();
30+
};
31+
2732
return (
2833
<DropdownMenu>
2934
<DropdownMenuTrigger asChild>
@@ -35,7 +40,7 @@ export function UserMenu({ userName }: UserMenuProps) {
3540
</Button>
3641
</DropdownMenuTrigger>
3742
<DropdownMenuContent>
38-
<DropdownMenuItem>Sign out</DropdownMenuItem>
43+
<DropdownMenuItem onSelect={handleSignOut}>Sign out</DropdownMenuItem>
3944
</DropdownMenuContent>
4045
</DropdownMenu>
4146
);

vitest.setup.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,20 @@ vi.mock("@/lib/auth/auth-client", () => ({
6060
},
6161
signOut: vi.fn(),
6262
},
63+
signIn: {
64+
oauth2: vi.fn(),
65+
},
66+
signOut: vi.fn(),
67+
useSession: vi.fn(),
6368
}));
6469

70+
import { cleanup } from "@testing-library/react";
6571
// Reset mocks between test cases globally
6672
import { afterEach } from "vitest";
6773

6874
afterEach(() => {
6975
// Clear calls/instances, but keep hoisted module mock implementations intact
7076
vi.clearAllMocks();
77+
// Clean up DOM after each test
78+
cleanup();
7179
});

0 commit comments

Comments
 (0)