UNPKG

vueless

Version:

Vue Styleless UI Component Library, powered by Tailwind CSS.

411 lines (323 loc) 12.3 kB
import { mount } from "@vue/test-utils"; import { describe, it, expect } from "vitest"; import UTabs from "../UTabs.vue"; import UTab from "../../ui.navigation-tab/UTab.vue"; import UButton from "../../ui.button/UButton.vue"; import type { Props, UTabsOption } from "../types"; describe("UTabs.vue", () => { // Global options definition const options: UTabsOption[] = [ { value: "tab1", label: "Tab 1" }, { value: "tab2", label: "Tab 2" }, { value: "tab3", label: "Tab 3" }, ]; describe("Props", () => { it("ModelValue – correctly sets the selected tab", () => { const modelValue = "tab2"; const expectedActiveClass = "border-primary"; const component = mount(UTabs, { props: { options, modelValue, }, }); // Find all UTab components const tabs = component.findAllComponents(UTab); // Check that the correct tab is active expect(tabs.length).toBe(options.length); // The second tab should be active const activeTab = tabs[1]; expect(activeTab.classes()).toContain(expectedActiveClass); }); it("Options – renders the correct number of tabs from options", () => { const component = mount(UTabs, { props: { options, }, }); // Find all UTab components const tabs = component.findAllComponents(UTab); // Check that the correct number of tabs is rendered expect(tabs.length).toBe(options.length); // Check that the tabs have the correct labels options.forEach((option, index) => { expect(tabs[index].text()).toBe(option.label); }); }); it("Size – applies the correct size to tabs", () => { const sizes = ["2xs", "xs", "sm", "md", "lg", "xl"]; sizes.forEach((size) => { const component = mount(UTabs, { props: { options: [{ value: "tab1", label: "Tab 1" }], size: size as Props["size"], }, }); // Find the UTab component const tab = component.findComponent(UTab); // Find the UButton inside the UTab const button = tab.findComponent(UButton); // Check that the button has the correct size expect(button.props("size")).toBe(size); }); }); it("Scrollable – applies scrollable class when scrollable prop is true", () => { const scrollable = true; const component = mount(UTabs, { props: { options, scrollable, }, }); // Find the tabs container const tabsContainer = component.find(`[vl-key="tabs"]`); // Check that the container has the scrollable class expect(tabsContainer.classes()).toContain("scroll-smooth"); }); it("Scroll – shows scroll buttons when scrollable and content overflows", async () => { const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({ value: `tab${i}`, label: `Tab ${i}`, })); const component = mount(UTabs, { props: { options: manyOptions, scrollable: true, }, }); // Mock the scroll position to show both arrows const scrollContainer = component.find(`[vl-key="tabs"]`).element; Object.defineProperty(scrollContainer, "scrollLeft", { value: 10 }); Object.defineProperty(scrollContainer, "scrollWidth", { value: 1000 }); Object.defineProperty(scrollContainer, "clientWidth", { value: 500 }); // Trigger scroll event await component.find("[vl-key='tabs']").trigger("scroll"); // Both arrows should be visible const buttons = component.findAllComponents(UButton); // Check that at least two buttons are rendered expect(buttons.length).toBeGreaterThanOrEqual(2); // Find the buttons with the correct icons const prevButton = buttons.find((button) => button.props("icon") === "chevron_left"); const nextButton = buttons.find((button) => button.props("icon") === "chevron_right"); expect(prevButton).toBeDefined(); expect(nextButton).toBeDefined(); }); it("Block – provides block value to tabs", () => { const block = true; const component = mount(UTabs, { props: { options: [{ value: "tab1", label: "Tab 1" }], block, }, }); // Find the UTab component const tab = component.findComponent(UTab); // Find the UButton inside the UTab const button = tab.findComponent(UButton); // Check that the button has the block prop expect(button.props("block")).toBe(block); }); it("Square – provides square value to tabs", () => { const square = true; const component = mount(UTabs, { props: { options: [{ value: "tab1", label: "Tab 1" }], square, }, }); // Find the UTab component const tab = component.findComponent(UTab); // Find the UButton inside the UTab const button = tab.findComponent(UButton); // Check that the button has the square prop expect(button.props("square")).toBe(square); }); it("DataTest – applies the correct data-test attribute", () => { const dataTest = "test-tabs"; const singleOption = [options[0]]; const component = mount(UTabs, { props: { options: singleOption, dataTest, }, }); // Find the tabs container const tabsContainer = component.find(`[data-test="${dataTest}"]`); // Check that the container has the data-test attribute expect(tabsContainer.exists()).toBe(true); // Check that the tab has the correct data-test attribute const tab = component.find(`[data-test="${dataTest}-item-0"]`); expect(tab.exists()).toBe(true); }); }); describe("Slots", () => { it("Default – renders content from default slot", () => { const slotContent = "Custom Tabs"; const slotClass = "custom-tabs"; const component = mount(UTabs, { slots: { default: `<div class="${slotClass}">${slotContent}</div>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.find(`.${slotClass}`).text()).toBe(slotContent); }); it("Prev – renders content from prev slot when scrollable", async () => { const slotContent = "Prev"; const slotClass = "prev-content"; const dataTest = "test-tabs"; const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({ value: `tab${i}`, label: `Tab ${i}`, })); const component = mount(UTabs, { props: { options: manyOptions, scrollable: true, dataTest, }, slots: { prev: `<div class="${slotClass}">${slotContent}</div>`, }, }); // Mock the scroll position to show left arrow const scrollContainer = component.find(`[vl-key="tabs"]`).element; Object.defineProperty(scrollContainer, "scrollLeft", { value: 10 }); Object.defineProperty(scrollContainer, "scrollWidth", { value: 1000 }); Object.defineProperty(scrollContainer, "clientWidth", { value: 500 }); // Trigger scroll event await component.find("[vl-key='tabs']").trigger("scroll"); // Now the left arrow should be visible expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.find(`.${slotClass}`).text()).toBe(slotContent); }); it("Next – renders content from next slot when scrollable", async () => { const slotContent = "Next"; const slotClass = "next-content"; const dataTest = "test-tabs"; const manyOptions: UTabsOption[] = Array.from({ length: 10 }, (_, i) => ({ value: `tab${i}`, label: `Tab ${i}`, })); const component = mount(UTabs, { props: { options: manyOptions, scrollable: true, dataTest, }, slots: { next: `<div class="${slotClass}">${slotContent}</div>`, }, }); // Mock the scroll position to show right arrow const scrollContainer = component.find(`[vl-key="tabs"]`).element; Object.defineProperty(scrollContainer, "scrollLeft", { value: 0 }); Object.defineProperty(scrollContainer, "scrollWidth", { value: 1000 }); Object.defineProperty(scrollContainer, "clientWidth", { value: 500 }); // Trigger scroll event await component.find("[vl-key='tabs']").trigger("scroll"); // Now the right arrow should be visible expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.find(`.${slotClass}`).text()).toBe(slotContent); }); it("Left – renders content from left slot for each tab", () => { const slotText = "Left"; const slotClass = "left-content"; const component = mount(UTabs, { props: { options, }, slots: { left: `<span class="${slotClass}">${slotText}</span>`, }, }); const leftContents = component.findAll(`.${slotClass}`); expect(leftContents.length).toBe(options.length); leftContents.forEach((left) => { expect(left.text()).toBe(slotText); }); }); it("Label – renders content from label slot instead of default label", () => { const testOptions: UTabsOption[] = [{ value: "tab1", label: "Tab 1" }]; const slotText = "Custom Label"; const slotClass = "label-content"; const component = mount(UTabs, { props: { options: testOptions, }, slots: { label: `<span class="${slotClass}">${slotText}</span>`, }, }); expect(component.find(`.${slotClass}`).exists()).toBe(true); expect(component.find(`.${slotClass}`).text()).toBe(slotText); expect(component.text()).not.toContain(testOptions[0].label); }); it("Right – renders content from right slot", () => { const slotText = "Right"; const slotClass = "right-content"; const component = mount(UTabs, { props: { options, }, slots: { right: `<span class="${slotClass}">${slotText}</span>`, }, }); expect(component.findAll(`.${slotClass}`).length).toBe(options.length); expect(component.find(`.${slotClass}`).text()).toBe(slotText); }); it("Slot – passes item, index and active state to slots", () => { const modelValue = "tab2"; const component = mount(UTabs, { props: { options, modelValue, }, slots: { left: ` <div :data-value="params.item.value" :data-index="params.index" :data-active="params.active" /> `, }, }); options.forEach((option, index) => { const el = component.find(`[data-value="${option.value}"][data-index="${index}"]`); expect(el.exists()).toBe(true); }); const activeEl = component.find('[data-active="true"]'); expect(activeEl.exists()).toBe(true); expect(activeEl.attributes("data-value")).toBe(modelValue); }); }); describe("Events", () => { it("Update:modelValue – emits update:modelValue event when tab is clicked", async () => { const component = mount(UTabs, { props: { options, modelValue: "tab1", }, }); // Find the second tab and click it const secondTab = component.findAllComponents(UTab)[1]; await secondTab.trigger("click"); // Check that the update:modelValue event was emitted with the correct value expect(component.emitted("update:modelValue")).toBeTruthy(); expect(component.emitted("update:modelValue")?.[0]).toEqual(["tab2"]); }); }); describe("Exposed refs", () => { it("wrapperRef – exposes wrapperRef", () => { const singleOption = [options[0]]; const component = mount(UTabs, { props: { options: singleOption, }, }); expect(component.vm.wrapperRef).toBeDefined(); }); }); });