All checks were successful
Production PR / QA Tests (pull_request) Successful in 44s
336 lines
9.3 KiB
TypeScript
336 lines
9.3 KiB
TypeScript
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<typeof SidebarUI>();
|
|
const { ref } = await import("vue");
|
|
const MockComponent = {
|
|
template: "<div><slot /></div>",
|
|
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: "<div><slot /></div>" };
|
|
return {
|
|
Avatar: MockComponent,
|
|
AvatarFallback: MockComponent,
|
|
AvatarImage: MockComponent,
|
|
};
|
|
});
|
|
|
|
vi.mock("~/components/ui/dropdown-menu", () => {
|
|
const MockComponent = { template: "<div><slot /></div>" };
|
|
const MockContent = {
|
|
template: '<div data-testid="dropdown-content"><slot /></div>',
|
|
name: "DropdownMenuContent",
|
|
};
|
|
const DropdownMenuItem = {
|
|
name: "DropdownMenuItem",
|
|
template: '<div data-testid="dropdown-item" @click="$emit(\'click\')"><slot /></div>',
|
|
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: '<svg class="lucide-mock" />' };
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(wrapper.text()).toContain("Log in");
|
|
expect(wrapper.text()).not.toContain("Log out");
|
|
});
|
|
|
|
it("calls navigateTo('/auth/logout') when Log out is clicked", async () => {
|
|
const wrapper = mount(SidebarFooterComponent, {
|
|
props: { user },
|
|
global: {
|
|
stubs: {
|
|
ClientOnly: { template: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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("/auth/logout");
|
|
});
|
|
|
|
it("calls navigateTo('/auth/login') when Log in is clicked", async () => {
|
|
const wrapper = mount(SidebarFooterComponent, {
|
|
props: { user: null },
|
|
global: {
|
|
stubs: {
|
|
ClientOnly: { template: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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("/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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
// "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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
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: "<div><slot /></div>" },
|
|
},
|
|
},
|
|
});
|
|
|
|
// Access the component's internal state/computed properties
|
|
expect((wrapper.vm as any).userInititials).toBe("");
|
|
});
|
|
});
|