UNPKG

vueless

Version:

Vue Styleless UI Component Library, powered by Tailwind CSS.

725 lines (569 loc) 20.7 kB
import { mount } from "@vue/test-utils"; import { describe, it, expect, vi } from "vitest"; import UDrawer from "../UDrawer.vue"; import UHeader from "../../ui.text-header/UHeader.vue"; import type { Props } from "../types"; describe("UDrawer", () => { const modelValue = true; // Wait for an async component to load function sleep(ms: number = 0) { return new Promise((resolve) => setTimeout(resolve, ms)); } // Props tests describe("Props", () => { it("ModelValue – renders when true", () => { const component = mount(UDrawer, { props: { modelValue, }, }); expect(component.isVisible()).toBe(modelValue); }); it("ModelValue – does not render when false", () => { const modelValue = false; const component = mount(UDrawer, { props: { modelValue, }, }); expect(component.find("[vl-key='overlay']").exists()).toBe(modelValue); }); it("Title – renders with title prop", () => { const title = "Drawer Title"; const component = mount(UDrawer, { props: { modelValue, title, }, }); const header = component.findComponent(UHeader); expect(header.exists()).toBe(true); expect(header.props("label")).toBe(title); }); it("Description – renders with description prop", () => { const title = "Drawer Title"; const description = "Drawer Description"; const component = mount(UDrawer, { props: { modelValue, title, description, }, }); expect(component.text()).toContain(description); }); it("Position – applies correct position classes", () => { const positions = { top: ["top-0", "w-full", "h-auto"], bottom: ["bottom-0", "w-full", "h-auto"], left: ["left-0", "w-max", "h-full"], right: ["right-0", "w-max", "h-full"], }; Object.entries(positions).forEach(([position, expectedClasses]) => { const component = mount(UDrawer, { props: { modelValue, position: position as Props["position"], }, }); const drawerClasses = component.find("[vl-key='drawerWrapper']").attributes("class"); expectedClasses.forEach((expectedClass) => { expect(drawerClasses).toContain(expectedClass); }); }); }); it("Variant – applies correct variant classes", () => { const variants = { solid: "bg-default", outlined: "bg-default", subtle: "bg-muted", soft: "bg-muted", }; Object.entries(variants).forEach(([variant, expectedClasses]) => { const component = mount(UDrawer, { props: { modelValue, variant: variant as Props["variant"], }, }); expect(component.find("[vl-key='drawerWrapper']").attributes("class")).toContain( expectedClasses, ); }); }); it("Handle – renders handle when prop is true", () => { const handle = [true, false]; handle.forEach((value) => { const component = mount(UDrawer, { props: { modelValue, handle: value, }, }); const handleWrapper = component.find("[vl-key='handleWrapper']"); const handleElement = component.find("[vl-key='handle']"); expect(handleWrapper.exists()).toBe(value); expect(handleElement.exists()).toBe(value); }); }); it("Inset – applies inset class when prop is true", () => { const inset = true; const expectedClass = "m-4"; const component = mount(UDrawer, { props: { modelValue, inset, }, }); const innerWrapper = component.find("[vl-key='innerWrapper']"); expect(innerWrapper.attributes("class")).toContain(expectedClass); }); it("CloseOnOverlay – closes drawer when overlay is clicked", () => { const closeOnOverlay = [true, false]; closeOnOverlay.forEach(async (value) => { const component = mount(UDrawer, { props: { modelValue, closeOnOverlay: value, }, }); const innerWrapper = component.find('[vl-key="innerWrapper"]'); expect(innerWrapper.exists()).toBe(true); await innerWrapper.trigger("mousedown"); await innerWrapper.trigger("click"); await sleep(1000); const drawer = component.find('[vl-key="drawer"]'); expect(drawer.exists()).toBe(!value); }); }); it("CloseOnEsc – closes drawer when escape key is pressed", () => { const closeOnEsc = [true, false]; closeOnEsc.forEach(async (value) => { const component = mount(UDrawer, { props: { modelValue, closeOnEsc: value, }, }); const wrapper = component.find("[vl-key='wrapper']"); await wrapper.trigger("keydown", { key: "Escape" }); await sleep(1000); const drawer = component.find('[vl-key="drawer"]'); expect(drawer.exists()).toBe(!value); }); }); it("DataTest – applies the correct data-test attribute", () => { const dataTest = "drawer-test"; const component = mount(UDrawer, { props: { modelValue, dataTest, }, }); const drawerWrapper = component.find("[vl-key='wrapper']"); expect(drawerWrapper.attributes("data-test")).toBe(dataTest); }); it("CloseOnCross – shows cross by default and closes on click when true", async () => { const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", closeOnCross: true, }, }); const closeButton = component.find('[vl-key="closeButton"]'); expect(closeButton.exists()).toBe(true); await closeButton.trigger("click"); expect(component.emitted("update:modelValue")).toBeTruthy(); expect(component.emitted("update:modelValue")?.[0]).toEqual([false]); expect(component.emitted("close")).toBeTruthy(); }); it("CloseOnCross – hides cross when false", () => { const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", closeOnCross: false, }, }); const closeButton = component.find('[vl-key="closeButton"]'); expect(closeButton.exists()).toBe(false); }); }); // Slots tests describe("Slots", () => { it("Default – renders content in default slot", () => { const slotClass = "default-content"; const slotContent = "Default Content"; const component = mount(UDrawer, { props: { modelValue: true }, slots: { default: `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); }); it("Before Title – renders content in slot and shows header", () => { const slotClass = "before-title"; const slotContent = "Before Title"; const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", }, slots: { "before-title": `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that header is shown when before-title slot is provided const header = component.find("[vl-key='header']"); expect(header.exists()).toBe(true); expect(header.text()).toContain(slotContent); }); it("Title – renders custom content in slot and shows header", () => { const slotClass = "custom-title"; const slotContent = "Custom Title"; const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", }, slots: { title: `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); expect(component.findComponent(UHeader).exists()).toBe(false); // Check that header is shown when title slot is provided const header = component.find("[vl-key='header']"); expect(header.exists()).toBe(true); expect(header.text()).toContain(slotContent); }); it("After Title – renders content in slot and shows header", () => { const slotClass = "after-title"; const slotContent = "After Title"; const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", }, slots: { "after-title": `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that header is shown when after-title slot is provided const header = component.find("[vl-key='header']"); expect(header.exists()).toBe(true); expect(header.text()).toContain(slotContent); }); it("Actions – renders custom content in slot and shows header", () => { const slotClass = "actions"; const slotContent = "Actions"; const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", }, slots: { actions: `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that header is shown when actions slot is provided const header = component.find("[vl-key='header']"); expect(header.exists()).toBe(true); expect(header.text()).toContain(slotContent); }); it("Header – does not show when no title or slots are provided", () => { const component = mount(UDrawer, { props: { modelValue }, }); const header = component.find("[vl-key='header']"); expect(header.exists()).toBe(false); }); it("Actions – provides close binding to slot", () => { const component = mount(UDrawer, { props: { modelValue: true, title: "Drawer Title", }, slots: { actions: ` <template #default="{ close }"> <button class="custom-close" @click="close">Close</button> </template> `, }, }); const closeButton = component.find(".custom-close"); expect(closeButton.exists()).toBe(true); // Click the close button closeButton.trigger("click"); // Check if the drawer emitted the update:modelValue event with false expect(component.emitted("update:modelValue")).toBeTruthy(); expect(component.emitted("update:modelValue")?.[0]).toEqual([false]); }); it("Handle – renders custom content in slot", () => { const slotClass = "custom-handle"; const slotContent = "Custom Handle"; const component = mount(UDrawer, { props: { modelValue: true, handle: true, }, slots: { handle: `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that default handle element is not rendered when slot is used const defaultHandle = component.find("[vl-key='handle']"); expect(defaultHandle.exists()).toBe(false); }); it("Footer Left – renders content in slot and shows footer", () => { const slotClass = "footer-left"; const slotContent = "Footer Left"; const component = mount(UDrawer, { props: { modelValue: true }, slots: { "footer-left": `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that footer is shown when footer-left slot is provided const footer = component.find("[vl-key='footer']"); expect(footer.exists()).toBe(true); expect(footer.text()).toContain(slotContent); }); it("Footer Right – renders content in slot and shows footer", () => { const slotClass = "footer-right"; const slotContent = "Footer Right"; const component = mount(UDrawer, { props: { modelValue: true }, slots: { "footer-right": `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.text()).toContain(slotContent); // Check that footer is shown when footer-right slot is provided const footer = component.find("[vl-key='footer']"); expect(footer.exists()).toBe(true); expect(footer.text()).toContain(slotContent); }); it("Footer – does not show when no footer slots are provided", () => { const component = mount(UDrawer, { props: { modelValue }, }); const footer = component.find("[vl-key='footer']"); expect(footer.exists()).toBe(false); }); }); // Events tests describe("Events", () => { it("Update:modelValue – emits event when drawer is closed", async () => { const title = "Drawer Title"; const component = mount(UDrawer, { props: { modelValue, title, }, slots: { actions: ` <template #default="{ close }"> <button class="close-btn" @click="close">Close</button> </template> `, }, }); const closeButton = component.find(".close-btn"); await closeButton.trigger("click"); expect(component.emitted("update:modelValue")).toBeTruthy(); expect(component.emitted("update:modelValue")?.[0]).toEqual([false]); }); it("Close – emits event when drawer is closed", async () => { const title = "Drawer Title"; const component = mount(UDrawer, { props: { modelValue, title, }, slots: { actions: ` <template #default="{ close }"> <button class="close-btn" @click="close">Close</button> </template> `, }, }); const closeButton = component.find(".close-btn"); await closeButton.trigger("click"); expect(component.emitted("close")).toBeTruthy(); }); it("CloseOnOverlay – emits events when overlay is clicked based on prop", async () => { const closeOnOverlay = [true, false]; for (const value of closeOnOverlay) { const component = mount(UDrawer, { props: { modelValue, closeOnOverlay: value, }, }); const innerWrapper = component.find("[vl-key='innerWrapper']"); await innerWrapper.trigger("mousedown"); await innerWrapper.trigger("click"); expect(component.emitted("update:modelValue")).toEqual(value ? [[false]] : undefined); expect(Boolean(component.emitted("close")?.length)).toBe(value); } }); it("CloseOnOverlay – does not close when mousedown on drawer and mouseup on overlay", async () => { const component = mount(UDrawer, { props: { modelValue: true, closeOnOverlay: true, }, }); const drawer = component.find("[vl-key='drawer']"); const innerWrapper = component.find("[vl-key='innerWrapper']"); // Mousedown on drawer content await drawer.trigger("mousedown"); // Click (mouseup) on overlay await innerWrapper.trigger("click"); // Drawer should NOT close expect(component.emitted("update:modelValue")).toBeFalsy(); expect(component.emitted("close")).toBeFalsy(); }); it("CloseOnEsc – emits events when escape key is pressed based on prop", async () => { const closeOnEsc = [true, false]; for (const value of closeOnEsc) { const component = mount(UDrawer, { props: { modelValue, closeOnEsc: value, }, }); const wrapper = component.find("[vl-key='wrapper']"); await wrapper.trigger("keydown", { key: "Escape" }); expect(component.emitted("update:modelValue")).toEqual(value ? [[false]] : undefined); expect(Boolean(component.emitted("close")?.length)).toBe(value); } }); }); // Drag functionality tests describe("Drag Functionality", () => { it("Cursor – applies drag cursor classes when handle is enabled", () => { const component = mount(UDrawer, { props: { modelValue: true, handle: true, }, }); const handleWrapper = component.find("[vl-key='handleWrapper']"); expect(handleWrapper.attributes("class")).toContain("cursor-grab"); }); it("Mouse Drag – handles drag start from handle", async () => { const component = mount(UDrawer, { props: { modelValue: true, handle: true, }, }); const drawer = component.find("[vl-key='drawerWrapper']"); const handleWrapper = component.find("[vl-key='handleWrapper']"); // Mock getBoundingClientRect const mockRect = { width: 300, height: 400, top: 0, left: 0, right: 300, bottom: 400, }; vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect); await handleWrapper.trigger("mousedown", { clientX: 100, clientY: 100, }); // Check if handle has drag cursor class expect(handleWrapper.attributes("class")).toContain("cursor-grab"); }); it("Touch Drag – handles drag start from handle", async () => { const component = mount(UDrawer, { props: { modelValue: true, handle: true, }, }); const drawer = component.find("[vl-key='drawerWrapper']"); const handleWrapper = component.find("[vl-key='handleWrapper']"); // Mock getBoundingClientRect const mockRect = { width: 300, height: 400, top: 0, left: 0, right: 300, bottom: 400, }; vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect); await handleWrapper.trigger("touchstart", { touches: [{ clientX: 100, clientY: 100 }], }); // Check if handle has drag cursor class expect(handleWrapper.attributes("class")).toContain("cursor-grab"); }); it("Transform – applies drag transform styles during drag", async () => { const component = mount(UDrawer, { props: { modelValue: true, position: "left", }, }); const drawer = component.find("[vl-key='drawerWrapper']"); // Mock getBoundingClientRect const mockRect = { width: 300, height: 400, top: 0, left: 0, right: 300, bottom: 400, }; vi.spyOn(drawer.element, "getBoundingClientRect").mockReturnValue(mockRect as DOMRect); // Start drag await drawer.trigger("mousedown", { clientX: 100, clientY: 100, }); // Simulate drag movement const mouseMoveEvent = new MouseEvent("mousemove", { clientX: 150, // 50px movement clientY: 100, }); document.dispatchEvent(mouseMoveEvent); // Check if transform is applied const drawerElement = component.find("[vl-key='drawerWrapper']"); const style = drawerElement.attributes("style"); expect(style ? style.includes("transform") : drawerElement.exists()).toBe(true); }); }); // Exposed refs tests describe("Exposed refs", () => { it("WrapperRef – exposes wrapper element ref", () => { const component = mount(UDrawer, { props: { modelValue }, }); expect(component.vm.wrapperRef).toBeDefined(); expect(component.vm.wrapperRef instanceof HTMLDivElement).toBe(true); }); }); });