import { render, waitFor } from "@testing-library/react"; import React from "react"; import { vi, describe, it, expect, beforeEach, afterEach } from "vitest"; vi.mock("next-auth/react", () => ({ signOut: vi.fn(), })); vi.mock("next/navigation", () => ({ usePathname: () => "/settings", })); vi.mock("@calcom/lib/hooks/useLocale", () => ({ useLocale: () => ({ t: (key: string) => key, }), })); vi.mock("@calcom/lib/hooks/useUserAgentData", () => ({ useUserAgentData: () => ({ os: "linux", browser: "chrome", isMobile: false, }), })); const mockUseMeQuery = vi.fn(); vi.mock("@calcom/trpc/react/hooks/useMeQuery", () => ({ default: () => mockUseMeQuery(), })); vi.mock("@calcom/web/components/settings/platform/hooks/useGetUserAttributes", () => ({ useGetUserAttributes: () => ({ isPlatformUser: false, }), })); vi.mock("@calcom/web/modules/ee/support/lib/freshchat/FreshChatProvider", () => ({ default: ({ children }: { children: React.ReactNode }) =>
{children}
, })); vi.mock("@calcom/ui/components/avatar", () => ({ Avatar: () =>
Avatar
, })); vi.mock("@calcom/ui/components/icon", () => ({ Icon: ({ name }: { name: string }) => {name}, })); vi.mock("@coss/ui/components/menu", () => ({ Menu: ({ children }: { children: React.ReactNode }) =>
{children}
, MenuTrigger: ({ children, render }: { children: React.ReactNode; render?: React.ReactElement }) => { if (render) { return React.cloneElement(render, {}, children); } return
{children}
; }, MenuPopup: ({ children }: { children: React.ReactNode }) =>
{children}
, MenuItem: ({ children }: { children: React.ReactNode }) =>
{children}
, MenuSeparator: () =>
, MenuSub: ({ children }: { children: React.ReactNode }) =>
{children}
, MenuSubTrigger: ({ children }: { children: React.ReactNode }) =>
{children}
, MenuSubPopup: ({ children }: { children: React.ReactNode }) =>
{children}
, })); vi.mock("@calcom/ui/classNames", () => ({ default: (...args: string[]) => args.filter(Boolean).join(" "), })); describe("UserDropdown", () => { let mockBeacon: ReturnType; let mockSupportOpen: ReturnType; beforeEach(() => { vi.clearAllMocks(); mockBeacon = vi.fn(); mockSupportOpen = vi.fn(); Object.defineProperty(window, "Beacon", { value: mockBeacon, writable: true, configurable: true, }); Object.defineProperty(window, "Support", { value: { open: mockSupportOpen, shouldShowTriggerButton: vi.fn() }, writable: true, configurable: true, }); Object.defineProperty(window, "screen", { value: { width: 1920, height: 1080 }, writable: true, configurable: true, }); }); afterEach(() => { delete window.Beacon; delete window.Support; }); describe("Beacon session-data functionality", () => { it("should call Beacon with session-data when Beacon is available and user has username", async () => { mockUseMeQuery.mockReturnValue({ data: { username: "testuser", name: "Test User", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); render(); await waitFor(() => { expect(mockBeacon).toHaveBeenCalledWith("session-data", { username: "testuser", screenResolution: "1920x1080", }); }); }); it("should call Beacon with 'Unknown' username when user has no username", async () => { mockUseMeQuery.mockReturnValue({ data: { username: null, name: "Test User", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); render(); await waitFor(() => { expect(mockBeacon).toHaveBeenCalledWith("session-data", { username: "Unknown", screenResolution: "1920x1080", }); }); }); it("should not throw error when Beacon is undefined", async () => { delete window.Beacon; mockUseMeQuery.mockReturnValue({ data: { username: "testuser", name: "Test User", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); // Should not throw expect(() => render()).not.toThrow(); }); it("should call Beacon when it loads lazily after mount", async () => { // Start with Beacon undefined (simulating lazy load) delete window.Beacon; mockUseMeQuery.mockReturnValue({ data: { username: "testuser", name: "Test User", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); render(); // Beacon should not have been called yet expect(mockBeacon).not.toHaveBeenCalled(); // Simulate Beacon loading after mount Object.defineProperty(window, "Beacon", { value: mockBeacon, writable: true, configurable: true, }); // Wait for the polling interval to detect Beacon await waitFor( () => { expect(mockBeacon).toHaveBeenCalledWith("session-data", { username: "testuser", screenResolution: "1920x1080", }); }, { timeout: 2000 } ); }); it("should update Beacon session-data when username changes", async () => { const { rerender } = render(
); // First render with initial username mockUseMeQuery.mockReturnValue({ data: { username: "user1", name: "User One", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); const { rerender: rerenderComponent } = render(); await waitFor(() => { expect(mockBeacon).toHaveBeenCalledWith("session-data", { username: "user1", screenResolution: "1920x1080", }); }); // Clear mock to track new calls mockBeacon.mockClear(); // Update username mockUseMeQuery.mockReturnValue({ data: { username: "user2", name: "User Two", avatarUrl: null, avatar: null }, isPending: false, }); rerenderComponent(); await waitFor(() => { expect(mockBeacon).toHaveBeenCalledWith("session-data", { username: "user2", screenResolution: "1920x1080", }); }); }); }); describe("Component rendering", () => { it("should return null when user is not available and not pending", async () => { mockUseMeQuery.mockReturnValue({ data: null, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); const { container } = render(); // Component should return null expect(container.firstChild).toBeNull(); }); it("should render dropdown when user is available", async () => { mockUseMeQuery.mockReturnValue({ data: { username: "testuser", name: "Test User", avatarUrl: null, avatar: null }, isPending: false, }); const { UserDropdown } = await import("./UserDropdown"); const { getByTestId } = render(); expect(getByTestId("menu")).toBeInTheDocument(); }); it("should render dropdown when isPending is true (loading state)", async () => { mockUseMeQuery.mockReturnValue({ data: null, isPending: true, }); const { UserDropdown } = await import("./UserDropdown"); const { getByTestId } = render(); expect(getByTestId("menu")).toBeInTheDocument(); }); }); });