UNPKG

@oruga-ui/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

427 lines (359 loc) 15.8 kB
import { describe, test, expect, afterEach } from "vitest"; import { enableAutoUnmount, mount } from "@vue/test-utils"; import { nextTick, defineComponent, type PropType, h } from "vue"; import type { OptionsProp } from "@/composables"; import type { StepItemProps, StepsProps, StepsOptions } from "../props"; import OSteps from "@/components/steps/Steps.vue"; import OStepItem from "@/components/steps/StepItem.vue"; import OButton from "@/components/button/Button.vue"; describe("OSteps tests", () => { enableAutoUnmount(afterEach); test("render correctly", () => { const wrapper = mount(OSteps); expect(!!wrapper.vm).toBeTruthy(); expect(wrapper.exists()).toBeTruthy(); expect(wrapper.attributes("data-oruga")).toBe("steps"); expect(wrapper.html()).toMatchSnapshot(); }); test("render accordingly when has size prop", () => { const size = "large"; const wrapper = mount(OSteps, { props: { size } }); expect(wrapper.classes("o-steps--" + size)).toBeTruthy(); }); test("render accordingly when has vertical prop", () => { const vertical = true; const wrapper = mount(OSteps, { props: { vertical } }); expect(wrapper.classes("o-steps--vertical")).toBeTruthy(); }); describe("integration tests", () => { const Testcomponent = defineComponent({ components: { OSteps, OStepItem, }, props: { stepProps: { type: Object as PropType<StepsProps<unknown>>, default: () => {}, }, itemProps: { type: Object as PropType<StepItemProps<unknown, any>>, default: () => {}, }, }, template: `<OSteps v-bind="stepProps"> <OStepItem v-bind="itemProps" /> </OSteps>`, }); test("test render correctly", () => { const wrapper = mount(Testcomponent, { props: { itemProps: { value: "1", step: "1", label: "Account", content: "ABC", }, }, }); expect(!!wrapper.vm).toBeTruthy(); expect(wrapper.exists()).toBeTruthy(); expect(wrapper.html()).toMatchSnapshot(); expect(wrapper.attributes("data-oruga")).toBe("steps"); const step = wrapper.find('[data-oruga="steps-item"]'); expect(step.exists()).toBeTruthy(); expect(step.text()).toBe("ABC"); }); test("render item with component prop correctly", async () => { const wrapper = mount(Testcomponent, { props: { itemProps: { value: "1", step: "1", component: h(OButton), props: { label: "MyButton" }, }, }, }); await nextTick(); // await component rendering const step = wrapper.find('[data-oruga="steps-item"]'); expect(step.exists()).toBeTruthy(); const button = step.find('[data-oruga="button"]'); expect(button.exists()).toBeTruthy(); expect(button.text()).toBe("MyButton"); }); test("render accordingly when has variant prop", () => { const variant = "success"; const wrapper = mount(OSteps, { props: { variant } }); const steps = wrapper.findAll('[data-oruga="steps-item"]'); steps.forEach((step) => { expect(step.classes("o-steps--" + variant)).toBeTruthy(); }); }); describe("handle navigation correctly", () => { const options: StepsOptions<string> = [ { label: "Flint", value: "flint", clickable: true }, { label: "Silver", value: "silver", clickable: true, }, { label: "Vane", value: "vane", clickable: true }, { label: "Billy", value: "billy", clickable: true }, { label: "Jack", value: "jack", clickable: true }, ]; test("render clickable state correctly", async () => { const options: StepsOptions<string> = [ { label: "Flint", value: "flint", clickable: true, }, { label: "Vane", value: "vane", }, { label: "Billy", value: "billy", clickable: false, }, { label: "Jack", value: "jack", }, ]; const wrapper = mount(OSteps, { props: { options, modelValue: options[3].value }, }); await nextTick(); // await child items got rendered const stepElements = wrapper.findAll(".o-steps__step"); expect(stepElements).toHaveLength(options.length); stepElements.forEach((el, idx) => { expect(el.classes("o-steps__step--clickable")).toBe( options[idx].clickable ?? idx !== 3, ); }); }); test("render accordingly when item is clicked", async () => { let currentIndex = 2; const wrapper = mount(OSteps, { props: { options, modelValue: options[currentIndex].value }, }); await nextTick(); // await child items got rendered const stepElements = wrapper.findAll(".o-steps__step"); expect(stepElements).toHaveLength(options.length); stepElements.forEach((el, idx) => { expect(el.classes("o-steps__step--previous")).toBe( idx < currentIndex, ); expect(el.classes("o-steps__step--active")).toBe( idx == currentIndex, ); expect(el.classes("o-steps__step--next")).toBe( idx > currentIndex, ); }); currentIndex = 4; expect(stepElements[currentIndex].attributes("role")).toBe( "tab", ); await stepElements[currentIndex].trigger("click"); stepElements.forEach((el, idx) => { expect(el.classes("o-steps__step--previous")).toBe( idx < currentIndex, ); expect(el.classes("o-steps__step--active")).toBe( idx == currentIndex, ); expect(el.classes("o-steps__step--next")).toBe( idx > currentIndex, ); }); currentIndex = 0; expect(stepElements[currentIndex].attributes("role")).toBe( "tab", ); await stepElements[currentIndex].trigger("click"); stepElements.forEach((el, idx) => { expect(el.classes("o-steps__step--previous")).toBe( idx < currentIndex, ); expect(el.classes("o-steps__step--active")).toBe( idx == currentIndex, ); expect(el.classes("o-steps__step--next")).toBe( idx > currentIndex, ); }); expect(wrapper.emitted("update:modelValue")).toStrictEqual([ [options[4].value], [options[0].value], ]); expect(wrapper.emitted("change")).toStrictEqual([ [options[4].value, options[2].value], [options[0].value, options[4].value], ]); }); test("render accordingly when prev is clicked", async () => { const currentIndex = 2; const wrapper = mount(OSteps, { props: { options, modelValue: options[currentIndex].value }, }); await nextTick(); // await child items got rendered const navigation = wrapper.find(".o-steps__navigation"); expect(navigation.exists()).toBeTruthy(); const buttons = navigation.findAll("button"); expect(buttons).toHaveLength(2); // should be undefined because current index is 2 expect(buttons[0].attributes("disabled")).toBeUndefined(); expect(buttons[1].attributes("disabled")).toBeUndefined(); // trigger prev await buttons[0].trigger("click"); // trigger prev await buttons[0].trigger("click"); expect(wrapper.emitted("update:modelValue")).toStrictEqual([ [options[currentIndex - 1].value], [options[currentIndex - 2].value], ]); expect(wrapper.emitted("change")).toStrictEqual([ [ options[currentIndex - 1].value, options[currentIndex].value, ], [ options[currentIndex - 2].value, options[currentIndex - 1].value, ], ]); }); test("render accordingly when next is clicked", async () => { const currentIndex = 0; const wrapper = mount(OSteps, { props: { options, modelValue: options[currentIndex].value }, }); await nextTick(); // await child items got rendered const navigation = wrapper.find(".o-steps__navigation"); expect(navigation.exists()).toBeTruthy(); const buttons = navigation.findAll("button"); expect(buttons).toHaveLength(2); // should be undefined because current index is 2 expect(buttons[0].attributes("disabled")).toBe(""); expect(buttons[1].attributes("disabled")).toBeUndefined(); // trigger prev await buttons[1].trigger("click"); // trigger prev await buttons[1].trigger("click"); expect(wrapper.emitted("update:modelValue")).toStrictEqual([ [options[currentIndex + 1].value], [options[currentIndex + 2].value], ]); expect(wrapper.emitted("change")).toStrictEqual([ [ options[currentIndex + 1].value, options[currentIndex].value, ], [ options[currentIndex + 2].value, options[currentIndex + 1].value, ], ]); }); }); describe("handle options props correctly", () => { test("handle options as primitves correctly", async () => { const options: OptionsProp = [ "Flint", "Silver", "Vane", 0, 1, 2, ]; const wrapper = mount(OSteps, { props: { options } }); await nextTick(); const navElements = wrapper.findAll(".o-steps__step"); expect(navElements).toHaveLength(options.length); const itemElements = wrapper.findAll( '[data-oruga="steps-item"]', ); expect(itemElements).toHaveLength(options.length); navElements.forEach((el, idx) => { expect(el.text()).toBe(String(options[idx])); }); }); test("handle options as object correctly", async () => { const options: OptionsProp = { flint: "Flint", silver: "Silver", vane: "Vane", 0: "Zero", 1: "One", 2: "Two", }; const wrapper = mount(OSteps, { props: { options } }); await nextTick(); const navElements = wrapper.findAll(".o-steps__step"); expect(navElements).toHaveLength(Object.keys(options).length); const optionElements = wrapper.findAll( '[data-oruga="steps-item"]', ); expect(optionElements).toHaveLength( Object.keys(options).length, ); navElements.forEach((el, idx) => { expect(el.text()).toBe(Object.entries(options)[idx][1]); }); }); test("handle options as options array correctly", async () => { const options: StepsOptions<string | number> = [ { label: "Flint", value: "flint", content: "flint", }, { label: "Silver", value: "silver", clickable: true, content: "silver", }, { label: "Vane", value: "vane", content: "abc" }, { label: "Zero", value: 0, content: "x" }, { label: "One", value: 1, content: "y", disabled: true, }, { label: "Two", value: 2, clickable: true, content: "test", }, ]; const wrapper = mount(OSteps, { props: { options } }); await nextTick(); const navElements = wrapper.findAll(".o-steps__step"); expect(navElements).toHaveLength(Object.keys(options).length); const optionElements = wrapper.findAll( '[data-oruga="steps-item"]', ); expect(optionElements).toHaveLength(options.length); navElements.forEach((el, idx) => { expect(el.text()).toBe(options[idx].label); expect(el.classes("o-steps__step--clickable")).toBe( options[idx].clickable || false, ); expect(el.classes("o-steps__step--disabled")).toBe( options[idx].disabled || false, ); }); optionElements.forEach((el, idx) => { expect(el.text()).toBe(options[idx].content); }); }); }); }); });