vueless
Version:
Vue Styleless UI Component Library, powered by Tailwind CSS.
450 lines (362 loc) • 12.5 kB
text/typescript
import { mount } from "@vue/test-utils";
import { describe, it, expect } from "vitest";
import UDropdownButton from "../UDropdownButton.vue";
import UButton from "../../ui.button/UButton.vue";
import UIcon from "../../ui.image-icon/UIcon.vue";
import UListbox from "../../ui.form-listbox/UListbox.vue";
import type { Props } from "../types.ts";
describe("UDropdownButton.vue", () => {
const defaultOptions = [
{ id: 1, label: "Option 1" },
{ id: 2, label: "Option 2" },
{ id: 3, label: "Option 3" },
];
// Props tests
describe("Props", () => {
// Label prop
it("renders the correct label text", () => {
const label = "Dropdown Button";
const component = mount(UDropdownButton, {
props: {
label,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("label")).toBe(label);
});
// ModelValue prop
it("selects the correct option based on modelValue", async () => {
const modelValue = 2;
const component = mount(UDropdownButton, {
props: {
modelValue,
options: defaultOptions,
},
});
// Find the selected option's label
const selectedOption = defaultOptions.find((option) => option.id === modelValue);
expect(component.findComponent(UButton).props("label")).toBe(selectedOption?.label);
});
// Multiple prop with modelValue
it("handles multiple selections correctly", async () => {
const modelValue = [1, 3];
const component = mount(UDropdownButton, {
props: {
modelValue,
multiple: true,
options: defaultOptions,
},
});
// Find the selected options' labels
const selectedOptions = defaultOptions.filter((option) => modelValue.includes(option.id));
const expectedLabel = selectedOptions.map((option) => option.label).join(", ");
expect(component.findComponent(UButton).props("label")).toBe(expectedLabel);
});
// LabelDisplayCount prop
it("limits displayed labels based on labelDisplayCount", async () => {
const modelValue = [1, 2, 3];
const labelDisplayCount = 1;
// Should show the first label + count of remaining
const expectedLabel = "Option 1, +2";
const component = mount(UDropdownButton, {
props: {
modelValue,
multiple: true,
labelDisplayCount,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("label")).toBe(expectedLabel);
});
// LabelDisplayCount prop with single value
it("correctly displays label when labelDisplayCount is 1 and only one value is selected", async () => {
const modelValue = [1];
const labelDisplayCount = 1;
// Should show only the selected label without +X suffix
const expectedLabel = "Option 1";
const component = mount(UDropdownButton, {
props: {
modelValue,
multiple: true,
labelDisplayCount,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("label")).toBe(expectedLabel);
});
// Variant prop
it("applies the correct variant class", async () => {
const variants = ["solid", "outlined", "subtle", "soft", "ghost"];
variants.forEach((variant) => {
const component = mount(UDropdownButton, {
props: {
variant: variant as Props["variant"],
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("variant")).toBe(variant);
});
});
// Color prop
it("applies the correct color class", async () => {
const colors = [
"primary",
"secondary",
"error",
"warning",
"success",
"info",
"notice",
"neutral",
"grayscale",
];
colors.forEach((color) => {
const component = mount(UDropdownButton, {
props: {
color: color as Props["color"],
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("color")).toBe(color);
});
});
// Size prop
it("applies the correct size class", async () => {
const sizes = ["2xs", "xs", "sm", "md", "lg", "xl"];
sizes.forEach((size) => {
const component = mount(UDropdownButton, {
props: {
size: size as Props["size"],
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("size")).toBe(size);
});
});
// Round prop
it("applies round class when round prop is true", () => {
const round = true;
const component = mount(UDropdownButton, {
props: {
round,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("round")).toBe(round);
});
// Square prop
it("applies square class when square prop is true", () => {
const square = true;
const component = mount(UDropdownButton, {
props: {
square,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("square")).toBe(square);
});
// Disabled prop
it("applies disabled attribute when disabled prop is true", () => {
const disabled = true;
const component = mount(UDropdownButton, {
props: {
disabled,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("disabled")).toBe(disabled);
});
// ToggleIcon prop (boolean: true)
it("shows default toggle icon when toggleIcon is true", () => {
const toggleIcon = true;
const component = mount(UDropdownButton, {
props: {
toggleIcon,
options: defaultOptions,
},
});
const iconComponent = component.findComponent(UIcon);
expect(iconComponent.exists()).toBe(true);
expect(iconComponent.props("name")).toBe("keyboard_arrow_down");
});
// ToggleIcon prop (boolean: false)
it("shows default toggle icon when toggleIcon is true", () => {
const toggleIcon = false;
const component = mount(UDropdownButton, {
props: {
toggleIcon,
options: defaultOptions,
},
});
const iconComponent = component.findComponent(UIcon);
expect(iconComponent.exists()).toBe(false);
});
// ToggleIcon prop (string)
it("shows custom toggle icon when toggleIcon is a string", () => {
const toggleIcon = "custom_icon";
const component = mount(UDropdownButton, {
props: {
toggleIcon,
options: defaultOptions,
},
});
const iconComponent = component.findComponent(UIcon);
expect(iconComponent.exists()).toBe(true);
expect(iconComponent.props("name")).toBe(toggleIcon);
});
// ID prop
it("applies the correct id attribute", () => {
const id = "test-dropdown-id";
const component = mount(UDropdownButton, {
props: {
id,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).props("id")).toBe(id);
});
// DataTest prop
it("applies the correct data-test attribute", () => {
const dataTest = "test-dropdown";
const component = mount(UDropdownButton, {
props: {
dataTest,
options: defaultOptions,
},
});
expect(component.findComponent(UButton).attributes("data-test")).toBe(dataTest);
});
});
// Slots tests
describe("Slots", () => {
// Default slot
it("renders content from default slot", () => {
const slotContent = "Custom Content";
const label = "Dropdown Button";
const component = mount(UDropdownButton, {
props: {
label,
options: defaultOptions,
},
slots: {
default: slotContent,
},
});
expect(component.text()).toContain(slotContent);
});
// Left slot
it("renders content from left slot", () => {
const label = "Dropdown Button";
const slotText = "Left";
const slotClass = "left-content";
const component = mount(UDropdownButton, {
props: {
label,
options: defaultOptions,
},
slots: {
left: `<span class='${slotClass}'>${slotText}</span>`,
},
});
expect(component.find(`.${slotClass}`).exists()).toBe(true);
expect(component.find(`.${slotClass}`).text()).toBe(slotText);
});
// Toggle slot
it("renders content from toggle slot", () => {
const label = "Dropdown Button";
const slotText = "Toggle";
const slotClass = "toggle-content";
const component = mount(UDropdownButton, {
props: {
label,
options: defaultOptions,
},
slots: {
toggle: `<span class='${slotClass}'>${slotText}</span>`,
},
});
expect(component.find(`.${slotClass}`).exists()).toBe(true);
expect(component.find(`.${slotClass}`).text()).toBe(slotText);
});
});
// Events tests
describe("Events", () => {
// Click event to open dropdown
it("opens dropdown when button is clicked", async () => {
const component = mount(UDropdownButton, {
props: {
options: defaultOptions,
},
});
// Initially, dropdown should be closed
expect(component.findComponent(UListbox).exists()).toBe(false);
// Click the button
await component.findComponent(UButton).trigger("click");
// Dropdown should be open
expect(component.findComponent(UListbox).exists()).toBe(true);
});
// update:modelValue event
it("emits update:modelValue event when an option is selected", async () => {
const component = mount(UDropdownButton, {
props: {
options: defaultOptions,
},
});
// Open the dropdown
await component.findComponent(UButton).trigger("click");
// Find the listbox component
const listbox = component.findComponent(UListbox);
// Simulate selecting an option by emitting update:modelValue from the listbox
listbox.vm.$emit("update:modelValue", 2);
// Check if the event was emitted with the correct value
expect(component.emitted("update:modelValue")).toBeTruthy();
expect(component.emitted("update:modelValue")?.[0]).toEqual([2]);
});
// clickOption event
it("emits clickOption event when an option is clicked", async () => {
const component = mount(UDropdownButton, {
props: {
options: defaultOptions,
},
});
// Open the dropdown
await component.findComponent(UButton).trigger("click");
// Find the listbox component
const listbox = component.findComponent(UListbox);
// Simulate clicking an option by emitting clickOption from the listbox
listbox.vm.$emit("clickOption", defaultOptions[1]);
// Check if the event was emitted with the correct value
expect(component.emitted("clickOption")).toBeTruthy();
expect(component.emitted("clickOption")?.[0]).toEqual([defaultOptions[1]]);
});
// Close dropdown when clicking outside
it("closes dropdown when clicking outside", async () => {
const component = mount(UDropdownButton, {
props: {
options: defaultOptions,
},
});
// Open the dropdown
await component.findComponent(UButton).trigger("click");
expect(component.findComponent(UListbox).exists()).toBe(true);
// Directly call the hideOptions function
// This is equivalent to what happens when clicking outside
component.vm.hideOptions();
await component.vm.$nextTick();
// Dropdown should be closed
expect(component.findComponent(UListbox).exists()).toBe(false);
});
});
// Exposed refs tests
describe("Exposed refs", () => {
// wrapperRef
it("exposes wrapperRef", () => {
const component = mount(UDropdownButton, {
props: {
options: defaultOptions,
},
});
expect(component.vm.wrapperRef).toBeDefined();
});
});
});