import { mount } from "@vue/test-utils"; import { describe, expect, it, vi } from "vitest"; import SidebarFooterComponent from "~/layouts/default/SidebarFooter.vue"; import type * as SidebarUI from "~/components/ui/sidebar"; const { useSidebarMock } = vi.hoisted(() => ({ useSidebarMock: vi.fn(), })); // Mock navigateTo const navigateToMock = vi.fn(); vi.stubGlobal("navigateTo", navigateToMock); // Mock UI components vi.mock("~/components/ui/sidebar", async (importOriginal) => { const actual = await importOriginal(); const { ref } = await import("vue"); const MockComponent = { template: "
", inheritAttrs: false, }; useSidebarMock.mockReturnValue({ isMobile: ref(false), // Ensure isMobile is a ref state: ref("expanded"), openMobile: ref(false), setOpenMobile: vi.fn(), }); return { ...actual, SidebarFooter: MockComponent, SidebarMenu: MockComponent, SidebarMenuItem: MockComponent, SidebarMenuButton: MockComponent, useSidebar: useSidebarMock, }; }); vi.mock("~/components/ui/avatar", () => { const MockComponent = { template: "
" }; return { Avatar: MockComponent, AvatarFallback: MockComponent, AvatarImage: MockComponent, }; }); vi.mock("~/components/ui/dropdown-menu", () => { const MockComponent = { template: "
" }; const MockContent = { template: '
', name: "DropdownMenuContent", }; const DropdownMenuItem = { name: "DropdownMenuItem", template: '
', emits: ["click"], }; return { DropdownMenu: MockComponent, DropdownMenuTrigger: MockComponent, DropdownMenuContent: MockContent, DropdownMenuGroup: MockComponent, DropdownMenuItem: DropdownMenuItem, DropdownMenuLabel: MockComponent, DropdownMenuSeparator: MockComponent, }; }); vi.mock("lucide-vue-next", () => { const MockIcon = { template: '' }; return { BadgeCheck: MockIcon, Bell: MockIcon, ChevronsUpDown: MockIcon, CreditCard: MockIcon, LogIn: MockIcon, LogOut: MockIcon, }; }); describe("SidebarFooter.vue", () => { const user = { name: "Liviu Test", email: "test@example.com", image: "avatar.png", id: "123", emailVerified: true, createdAt: new Date(), updatedAt: new Date(), }; it("renders user information correctly when user is provided", () => { const wrapper = mount(SidebarFooterComponent, { props: { user }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Liviu Test"); expect(wrapper.text()).toContain("test@example.com"); // Initials "Liviu Test" -> "LT" expect(wrapper.text()).toContain("LT"); }); it("renders anonymous view correctly when user is not provided (null)", () => { const wrapper = mount(SidebarFooterComponent, { props: { user: null }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Anonymous"); expect(wrapper.text()).toContain("No email"); expect(wrapper.text()).toContain("Anon"); }); it("renders anonymous view correctly when user is undefined", () => { const wrapper = mount(SidebarFooterComponent, { props: { user: undefined }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Anonymous"); expect(wrapper.text()).toContain("No email"); expect(wrapper.text()).toContain("Anon"); }); it("renders 'Log out' option when user is logged in", () => { const wrapper = mount(SidebarFooterComponent, { props: { user }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Log out"); expect(wrapper.text()).not.toContain("Log in"); }); it("renders 'Log in' option when user is anonymous", () => { const wrapper = mount(SidebarFooterComponent, { props: { user: null }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Log in"); expect(wrapper.text()).not.toContain("Log out"); }); it("calls navigateTo('/member/auth/logout') when Log out is clicked", async () => { const wrapper = mount(SidebarFooterComponent, { props: { user }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const logoutItem = wrapper.findAll('[data-testid="dropdown-item"]').find((item) => item.text().includes("Log out")); expect(logoutItem).toBeDefined(); await logoutItem?.trigger("click"); expect(navigateToMock).toHaveBeenCalledWith("/member/auth/logout"); }); it("calls navigateTo('/member/auth/login') when Log in is clicked", async () => { const wrapper = mount(SidebarFooterComponent, { props: { user: null }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const loginItem = wrapper.findAll('[data-testid="dropdown-item"]').find((item) => item.text().includes("Log in")); expect(loginItem).toBeDefined(); await loginItem?.trigger("click"); expect(navigateToMock).toHaveBeenCalledWith("/member/auth/login"); }); it("computes initials correctly for single name", () => { const singleNameUser = { ...user, name: "Liviu" }; const wrapper = mount(SidebarFooterComponent, { props: { user: singleNameUser }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); // "Liviu" -> "L" expect(wrapper.text()).toContain("L"); }); it("renders correctly when user has no image", () => { const userNoImage = { ...user, image: null as unknown as string }; const wrapper = mount(SidebarFooterComponent, { props: { user: userNoImage }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); expect(wrapper.text()).toContain("Liviu Test"); expect(wrapper.text()).toContain("test@example.com"); expect(wrapper.text()).toContain("LT"); }); it("sets side to 'bottom' when isMobile is true", () => { const { ref } = require("vue"); useSidebarMock.mockReturnValue({ isMobile: ref(true), state: ref("expanded"), openMobile: ref(false), setOpenMobile: vi.fn(), }); const wrapper = mount(SidebarFooterComponent, { props: { user }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const dropdownContent = wrapper.findComponent({ name: "DropdownMenuContent" }); expect(dropdownContent.attributes("side")).toBe("bottom"); }); it("sets side to 'right' when isMobile is false", () => { const { ref } = require("vue"); useSidebarMock.mockReturnValue({ isMobile: ref(false), state: ref("expanded"), openMobile: ref(false), setOpenMobile: vi.fn(), }); const wrapper = mount(SidebarFooterComponent, { props: { user }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const dropdownContent = wrapper.findComponent({ name: "DropdownMenuContent" }); expect(dropdownContent.attributes("side")).toBe("right"); }); it("sets side to 'bottom' when isMobile is true and user is anonymous", () => { const { ref } = require("vue"); useSidebarMock.mockReturnValue({ isMobile: ref(true), state: ref("expanded"), openMobile: ref(false), setOpenMobile: vi.fn(), }); const wrapper = mount(SidebarFooterComponent, { props: { user: null }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const dropdownContent = wrapper.findComponent({ name: "DropdownMenuContent" }); expect(dropdownContent.attributes("side")).toBe("bottom"); }); it("sets side to 'right' when isMobile is false and user is anonymous", () => { const { ref } = require("vue"); useSidebarMock.mockReturnValue({ isMobile: ref(false), state: ref("expanded"), openMobile: ref(false), setOpenMobile: vi.fn(), }); const wrapper = mount(SidebarFooterComponent, { props: { user: null }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); const dropdownContent = wrapper.findComponent({ name: "DropdownMenuContent" }); expect(dropdownContent.attributes("side")).toBe("right"); }); it("returns empty string for userInititials when user is undefined (covering line 24)", () => { const wrapper = mount(SidebarFooterComponent, { props: { user: undefined }, global: { stubs: { ClientOnly: { template: "
" }, }, }, }); // Access the component's internal state/computed properties expect((wrapper.vm as any).userInititials).toBe(""); }); });