vueless
Version:
Vue Styleless UI Component Library, powered by Tailwind CSS.
392 lines (314 loc) • 10.7 kB
text/typescript
import { mount, VueWrapper } from "@vue/test-utils";
import { describe, it, expect } from "vitest";
import UDataList from "../UDataList.vue";
import UEmpty from "../../ui.text-empty/UEmpty.vue";
import UIcon from "../../ui.image-icon/UIcon.vue";
import draggable from "vuedraggable";
import type { Props, DataListItem } from "../types.ts";
describe("UDataList.vue", () => {
const defaultList: DataListItem[] = [
{ id: 1, label: "Item 1" },
{ id: 2, label: "Item 2", crossed: true },
{ id: 3, label: "Item 3", actions: false },
];
const nestedList: DataListItem[] = [
{
id: 1,
label: "Parent 1",
children: [
{ id: 11, label: "Child 1.1" },
{ id: 12, label: "Child 1.2" },
],
},
{ id: 2, label: "Parent 2" },
];
describe("Props", () => {
it("List – renders all list items correctly", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
});
const items = component.findAll('[vl-key="itemWrapper"]');
expect(items).toHaveLength(defaultList.length);
expect(items[0].text()).toContain(defaultList[0].label);
expect(items[1].text()).toContain(defaultList[1].label);
expect(items[2].text()).toContain(defaultList[2].label);
});
it("List – renders empty state when list is empty", () => {
const emptyText = "There is no data in the list.";
const component = mount(UDataList, {
props: {
list: [],
},
});
const emptyComponent = component.findComponent(UEmpty);
expect(emptyComponent.exists()).toBe(true);
expect(emptyComponent.text()).toContain(emptyText);
});
it("List – does not render empty state when hideEmptyStateForNesting is true", () => {
const component = mount(UDataList, {
props: {
list: [],
hideEmptyStateForNesting: true,
},
});
const emptyComponent = component.findComponent(UEmpty);
expect(emptyComponent.exists()).toBe(false);
});
it("Group – sets correct group name for draggable", async () => {
const groupName = "test-group";
const component = mount(UDataList, {
props: {
list: defaultList,
group: groupName,
},
});
const draggableComponent = component.findComponent(draggable);
expect(draggableComponent.vm.$attrs.group).toEqual({ name: groupName });
});
it("Size – applies correct size classes", () => {
const sizeClasses = {
sm: "gap-2.5 py-2.5",
md: "gap-3 py-3",
lg: "gap-3.5 py-3.5",
};
Object.entries(sizeClasses).forEach(([size, classes]) => {
const component = mount(UDataList, {
props: {
list: defaultList,
size: size as Props["size"],
},
});
const itemElement = component.find('[vl-key="item"]');
expect(itemElement.attributes("class")).toContain(classes);
});
});
it("Label Key – uses correct label key for display", () => {
const customList = [
{ id: 1, title: "Custom Title 1" },
{ id: 2, title: "Custom Title 2" },
];
const component = mount(UDataList, {
props: {
list: customList,
labelKey: "title",
},
});
const items = component.findAll('[vl-key="item"]');
expect(items[0].text()).toContain(customList[0].title);
expect(items[1].text()).toContain(customList[1].title);
});
it("Value Key – uses correct value key for item identification", () => {
const customList = [
{ customId: "item-1", label: "Item 1" },
{ customId: "item-2", label: "Item 2" },
];
const valueKey = "customId";
const component = mount(UDataList, {
props: {
list: customList,
valueKey: valueKey,
},
});
const items = component.findAll('[vl-key="itemWrapper"]');
expect(items[0].attributes("id")).toBe(customList[0][valueKey]);
expect(items[1].attributes("id")).toBe(customList[1][valueKey]);
});
it("Animation Duration – sets correct animation duration", () => {
const animationDuration = 300;
const component = mount(UDataList, {
props: {
list: defaultList,
animationDuration,
},
});
const draggableComponent = component.findComponent(draggable);
expect(draggableComponent.vm.$attrs.animation).toBe(animationDuration);
});
it("Nesting – renders nested items when children array is present", async () => {
const component = mount(UDataList, {
props: {
list: nestedList,
},
});
const nestedComponent = component.getComponent("[vl-key='nested']") as VueWrapper<
typeof component.vm
>;
expect(nestedComponent.props("list")).toEqual(nestedList[0].children);
});
it("Data Test – applies correct data-test attribute", () => {
const dataTest = "test-data-list";
const component = mount(UDataList, {
props: {
list: defaultList,
dataTest,
},
});
const draggableComponent = component.findComponent({ name: "draggable" });
expect(draggableComponent.attributes("data-test")).toBe(dataTest);
});
it("Crossed Items – applies crossed styling to crossed items", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
});
const crossedItem = component.findAll("[vl-key='item']")[1].find('[vl-key="labelCrossed"]');
expect(crossedItem.exists()).toBe(true);
});
it("Actions – hides actions when actions is false", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
actions: "<button>Delete</button>",
},
});
const itemWithActions = component.findAll("[vl-key='item']")[0];
const itemWithoutActions = component.findAll("[vl-key='item']")[2];
expect(itemWithActions.text()).toContain("Delete");
expect(itemWithoutActions.text()).not.toContain("Delete");
});
});
describe("Slots", () => {
it("Empty – renders custom empty state content", () => {
const customEmptyContent = "Custom empty message";
const component = mount(UDataList, {
props: {
list: [],
},
slots: {
empty: customEmptyContent,
},
});
expect(component.text()).toContain(customEmptyContent);
expect(component.findComponent(UEmpty).exists()).toBe(false);
});
it("Empty – exposes empty title and description to slot", () => {
const emptyText = "There is no data in the list.";
const emptyTitle = "No Items";
const component = mount(UDataList, {
props: {
list: [],
config: {
i18n: {
emptyTitle: emptyTitle,
emptyDescription: emptyText,
},
},
},
slots: {
empty: "<div>{{ emptyTitle }} - {{ emptyDescription }}</div>",
},
});
expect(component.text()).toContain(emptyTitle);
expect(component.text()).toContain(emptyText);
});
it("Drag – renders custom drag content", () => {
const customDragContent = "Custom Drag";
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
drag: customDragContent,
},
});
expect(component.text()).toContain(customDragContent);
});
it("Drag – exposes item and icon-name to slot", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
drag: "<div>{{ item.label }} - {{ iconName }}</div>",
},
});
expect(component.text()).toContain("Item 1 - drag_indicator");
});
it("Label – renders custom label content", () => {
const customLabelContent = "Custom Label";
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
label: customLabelContent,
},
});
expect(component.text()).toContain(customLabelContent);
});
it("Label – exposes item and crossed to slot", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
label: "<div>{{ item.label }} - Crossed: {{ crossed }}</div>",
},
});
expect(component.text()).toContain("Item 1 - Crossed: false");
expect(component.text()).toContain("Item 2 - Crossed: true");
});
it("Actions – renders custom actions content", () => {
const customActionsContent = "Custom Actions";
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
actions: customActionsContent,
},
});
expect(component.text()).toContain(customActionsContent);
});
it("Actions – exposes item to slot", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
slots: {
actions: "<button>Delete {{ item.label }}</button>",
},
});
expect(component.text()).toContain(defaultList[0].label);
expect(component.text()).toContain(defaultList[1].label);
});
it("Actions – renders default drag icon when no drag slot provided", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
});
const dragIcon = component.findComponent(UIcon);
expect(dragIcon.exists()).toBe(true);
expect(dragIcon.props("name")).toBe("drag_indicator");
});
});
describe("Events", () => {
it("Drag Sort – emits when dragging ends", async () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
});
const draggableComponent = component.findComponent({ name: "draggable" });
await draggableComponent.vm.$emit("end");
expect(component.emitted("dragSort")).toBeTruthy();
expect(component.emitted("dragSort")![0][0]).toHaveLength(3);
});
});
describe("Exposed Properties", () => {
it("Wrapper Ref – exposes wrapper element ref", () => {
const component = mount(UDataList, {
props: {
list: defaultList,
},
});
expect(component.vm.wrapperRef).toBeDefined();
});
});
});