UNPKG

vueless

Version:

Vue Styleless UI Component Library, powered by Tailwind CSS.

632 lines (516 loc) 17.4 kB
import { mount } from "@vue/test-utils"; import { describe, it, expect } from "vitest"; import { nextTick } from "vue"; import UDropdown from "../UDropdown.vue"; import UListbox from "../../ui.form-listbox/UListbox.vue"; describe("UDropdown.vue", () => { const defaultOptions = [ { value: 1, label: "Option 1" }, { value: 2, label: "Option 2" }, { value: 3, label: "Option 3" }, ]; describe("Props", () => { it("ModelValue – selects the correct option based on modelValue", async () => { const modelValue = 2; const component = mount(UDropdown, { props: { modelValue, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); expect(component.vm.selectedOptions[0]?.value).toBe(modelValue); }); it("Multiple – handles multiple selections correctly", async () => { const modelValue = [1, 3]; const component = mount(UDropdown, { props: { modelValue, multiple: true, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); expect(component.vm.selectedOptions.length).toBe(2); expect(component.vm.selectedOptions[0]?.value).toBe(1); expect(component.vm.selectedOptions[1]?.value).toBe(3); }); it("Disabled – applies disabled state correctly", async () => { const component = mount(UDropdown, { props: { disabled: true, options: defaultOptions, dataTest: "dropdown", }, slots: { default: `<button>Click me</button>`, }, }); const collapsible = component.findComponent({ name: "UCollapsible" }); expect(collapsible.props("disabled")).toBe(true); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).exists()).toBe(false); }); it("Searchable – renders searchable dropdown", async () => { const component = mount(UDropdown, { props: { searchable: true, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("searchable")).toBe(true); }); it("Size – applies the correct size to listbox", async () => { const sizes = ["sm", "md", "lg"] as const; for (const size of sizes) { const component = mount(UDropdown, { props: { size, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("size")).toBe(size); } }); it("Color – applies the correct color to listbox", async () => { const colors = [ "primary", "secondary", "error", "warning", "success", "info", "notice", "neutral", "grayscale", ] as const; for (const color of colors) { const component = mount(UDropdown, { props: { color, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("color")).toBe(color); } }); it("XPosition – passes xPosition prop to collapsible", () => { const component = mount(UDropdown, { props: { xPosition: "right", options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); const collapsible = component.findComponent({ name: "UCollapsible" }); expect(collapsible.props("xPosition")).toBe("right"); }); it("YPosition – passes yPosition prop to collapsible", () => { const component = mount(UDropdown, { props: { yPosition: "top", options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); const collapsible = component.findComponent({ name: "UCollapsible" }); expect(collapsible.props("yPosition")).toBe("top"); }); it("LabelKey – uses custom label key for options", async () => { const customOptions = [ { id: 1, name: "First" }, { id: 2, name: "Second" }, ]; const component = mount(UDropdown, { props: { options: customOptions, labelKey: "name", valueKey: "id", modelValue: 1, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("labelKey")).toBe("name"); }); it("ValueKey – uses custom value key for options", async () => { const customOptions = [ { id: 1, name: "First" }, { id: 2, name: "Second" }, ]; const component = mount(UDropdown, { props: { options: customOptions, labelKey: "name", valueKey: "id", modelValue: 1, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("valueKey")).toBe("id"); }); it("GroupValueKey – passes groupValueKey prop to listbox", async () => { const component = mount(UDropdown, { props: { groupValueKey: "items", options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).props("groupValueKey")).toBe("items"); }); it("CloseOnSelect – closes dropdown when option is selected and closeOnSelect is true", async () => { const component = mount(UDropdown, { props: { closeOnSelect: true, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).exists()).toBe(true); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("click-option", defaultOptions[0]); await nextTick(); // Give time for the hide function to execute await new Promise((resolve) => setTimeout(resolve, 20)); await nextTick(); expect(component.findComponent(UListbox).exists()).toBe(false); }); it("CloseOnSelect – keeps dropdown open when option is selected and closeOnSelect is false", async () => { const component = mount(UDropdown, { props: { closeOnSelect: false, options: defaultOptions, }, slots: { default: `<button>Trigger</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findComponent(UListbox).exists()).toBe(true); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("click-option", defaultOptions[0]); await nextTick(); expect(component.findComponent(UListbox).exists()).toBe(true); }); }); describe("Slots", () => { it("Default – renders default slot with correct bindings", () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: ` <template #default="{ opened }"> <button>{{ opened ? 'Opened' : 'Closed' }}</button> </template> `, }, }); expect(component.find("button").exists()).toBe(true); expect(component.text()).toContain("Closed"); }); it("Empty – renders empty slot when no options are provided", async () => { const component = mount(UDropdown, { props: { options: [], }, slots: { default: `<button>Open</button>`, empty: `<div class="empty-state">No options available</div>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.find(".empty-state").exists()).toBe(true); expect(component.text()).toContain("No options available"); }); it("Before-option – renders before-option slot", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, "before-option": `<span class="before-icon">→</span>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findAll(".before-icon").length).toBeGreaterThan(0); }); it("Option – renders option slot with custom content", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, option: `<template #option="{ option }"><div class="custom-option">{{ option.label }}</div></template>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findAll(".custom-option").length).toBeGreaterThan(0); }); it("After-option – renders after-option slot", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, "after-option": `<span class="after-icon">✓</span>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.findAll(".after-icon").length).toBeGreaterThan(0); }); it("Dropdown – renders custom dropdown content slot", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, dropdown: `<div class="custom-dropdown">Custom dropdown content</div>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.find(".custom-dropdown").exists()).toBe(true); expect(component.text()).toContain("Custom dropdown content"); }); }); describe("Events", () => { it("Update:modelValue – emits update:modelValue when an option is selected", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("update:modelValue", defaultOptions[0].value); expect(component.emitted("update:modelValue")).toBeTruthy(); expect(component.emitted("update:modelValue")![0][0]).toBe(defaultOptions[0].value); }); it("Open – emits open event when dropdown is opened", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); expect(component.emitted("open")).toBeTruthy(); }); it("Close – emits close event when dropdown is closed", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); const hide = component.vm.hide as () => void; hide(); await nextTick(); expect(component.emitted("close")).toBeTruthy(); }); it("ClickOption – emits click-option event when an option is clicked", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("click-option", defaultOptions[0]); expect(component.emitted("click-option")).toBeTruthy(); }); it("SearchChange – emits search-change event when search value changes", async () => { const component = mount(UDropdown, { props: { searchable: true, options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("search-change", "test query"); expect(component.emitted("search-change")).toBeTruthy(); expect(component.emitted("search-change")![0][0]).toBe("test query"); }); it("Update:search – emits update:search event when search value updates", async () => { const component = mount(UDropdown, { props: { searchable: true, options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); await component.find("button").trigger("click"); await nextTick(); const listbox = component.findComponent(UListbox); await listbox.vm.$emit("update:search", "new search"); expect(component.emitted("update:search")).toBeTruthy(); expect(component.emitted("update:search")![0][0]).toBe("new search"); }); }); describe("Exposed methods", () => { it("Toggle – exposes toggle method", () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.toggle).toBeDefined(); expect(typeof component.vm.toggle).toBe("function"); }); it("Hide – exposes hide method", () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.hide).toBeDefined(); expect(typeof component.vm.hide).toBe("function"); }); it("WrapperRef – exposes wrapperRef", () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.wrapperRef).toBeDefined(); }); it("IsOpened – exposes isOpened property", async () => { const component = mount(UDropdown, { props: { options: defaultOptions, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.isOpened).toBe(false); await component.find("button").trigger("click"); await nextTick(); expect(component.vm.isOpened).toBe(true); }); it("SelectedOptions – exposes selectedOptions property", () => { const component = mount(UDropdown, { props: { options: defaultOptions, modelValue: 2, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.selectedOptions).toBeDefined(); expect(component.vm.selectedOptions.length).toBe(1); expect(component.vm.selectedOptions[0].value).toBe(2); }); it("DisplayLabel – exposes displayLabel property", () => { const component = mount(UDropdown, { props: { options: defaultOptions, modelValue: 2, labelDisplayCount: 1, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.displayLabel).toBeDefined(); expect(component.vm.displayLabel).toBe("Option 2"); }); it("FullLabel – exposes fullLabel property", () => { const component = mount(UDropdown, { props: { options: defaultOptions, modelValue: [1, 2], multiple: true, }, slots: { default: `<button>Open</button>`, }, }); expect(component.vm.fullLabel).toBeDefined(); expect(component.vm.fullLabel).toBe("Option 1, Option 2"); }); }); });