import { mount, shallowMount } from "@vue/test-utils"; import DRadio from "@/components/atoms/d-radio"; import config from "@/components/atoms/d-radio/config"; import { DButtonAsync as DButton } from "@/components/atoms/d-button/async"; import { TYPE } from "@/components/atoms/d-radio/constants"; import { COLOR_SCHEME } from "@darwin-studio/ui-codegen/dist/constants/color-scheme"; import { PADDING } from "@darwin-studio/ui-codegen/dist/constants/padding"; import { SIZE } from "@darwin-studio/ui-codegen/dist/constants/size"; import colorSchemeStyles from "@darwin-studio/ui-codegen/dist/styles/color-scheme.css"; // TODO: shorter path, default export ??? TODO: make it module ??? import { baseClassCase, borderClassCase, colorSchemeClassCase, controlIdAbsenceCase, controlIdAutogeneratedCase, controlIdPresenceCase, defaultCheckMarkCase, propVNodeCase, propStringCase, iconSlotCase, inputAttrsCase, inputClassCase, elementValueAttrCase, labelClassCase, labelFontCase, minControlWidthCase, outlineClassCase, paddingEqualClassesCase, roundingClassCase, sizeClassCase, transitionClassCase, tagCase, slotCase, disabledClassCase, disabledAttrCase, } from "@/utils/test-case-factories"; import sleep from "@/utils/sleep"; import prepareCssClassName from "@darwin-studio/ui-codegen/src/utils/prepareCssClassName"; import codegenConfig from "@darwin-studio/ui-codegen/config.json"; import DCaption from "@/components/atoms/d-caption"; describe("DRadio", () => { const name = "some name"; const value = "some value"; const wrapper = mount(DRadio, { props: { name, value, checked: true, caption: "Not empty" }, }); baseClassCase(wrapper, config.class); it("Should render input element with radio type, name and value", () => { const inputEl = wrapper.find("input"); expect(inputEl.exists()).toBeTruthy(); expect(inputEl.attributes().name).toBe(name); expect(inputEl.attributes().value).toBe(value); }); elementValueAttrCase(wrapper); inputClassCase(wrapper); inputAttrsCase(wrapper); minControlWidthCase(wrapper); defaultCheckMarkCase(wrapper, config); iconSlotCase(DRadio, `.${config.iconContainerClass}`, { name, value }); // TODO: make check target class factory ??? it("Icon container classes should contain props.iconContainerClass if passed", async () => { const iconContainerClass = "iconContainerCustomClass"; await wrapper.setProps({ iconContainerClass }); const iconContainerEl = wrapper.find(`.${config.iconContainerClass}`); expect(iconContainerEl.classes()).toContain(iconContainerClass); }); // TODO: make check target class factory ??? it("Icon container classes should contain colorSchemeStyles.__disabled if props.disabled passed", async () => { await wrapper.setProps({ disabled: true }); const iconContainerEl = wrapper.find(`.${config.iconContainerClass}`); expect(iconContainerEl.classes()).toContain(colorSchemeStyles.__disabled); }); it("Should render DButton instead of icon container if props.type === 'button'", async () => { const label = "some label"; const wrapper = shallowMount(DRadio, { props: { type: TYPE.BUTTON, name, value, label }, }); const button = wrapper.findComponent(DButton); expect(button).toBeTruthy(); expect(button.attributes().content).toBe(label); }); it("Should change input state on DButton click", async () => { const checked = false; const wrapper = mount(DRadio, { props: { type: TYPE.BUTTON, name, checked, value }, }); await sleep(0); // async component should be imported const buttonComponent = wrapper.findComponent(DButton); await buttonComponent.trigger("click"); const inputEl = wrapper.find("input"); expect(inputEl.element.checked).toBeTruthy(); }); it("Shouldn't change input state on DButton click if props.disabled", async () => { const checked = false; const wrapper = mount(DRadio, { props: { type: TYPE.BUTTON, name, checked, value, disabled: true }, }); await sleep(0); // async component should be imported const buttonComponent = wrapper.findComponent(DButton); await buttonComponent.trigger("click"); const inputEl = wrapper.find("input"); expect(inputEl.element.checked).toBeFalsy(); }); labelClassCase(wrapper); labelFontCase(wrapper); propStringCase(wrapper, `.${config.labelInnerClass}`, "label"); propVNodeCase(wrapper, `.${config.labelInnerClass}`, "label"); slotCase(DRadio, `.${config.labelInnerClass}`, "label", { name, value, }); disabledClassCase(wrapper, `.${config.labelClass}`); disabledAttrCase(wrapper, `.${config.inputClass}`); controlIdPresenceCase(wrapper); controlIdAbsenceCase(wrapper); controlIdAutogeneratedCase(wrapper); borderClassCase( wrapper, `.${config.iconContainerClass}`, COLOR_SCHEME.SECONDARY ); colorSchemeClassCase( wrapper, `.${config.iconContainerClass}`, COLOR_SCHEME.DANGER ); outlineClassCase(wrapper, "input", COLOR_SCHEME.SECONDARY, SIZE.LARGE); paddingEqualClassesCase(wrapper, `.${config.iconContainerClass}`); it("Should pass props.padding to DButton if props.type === 'button'", async () => { const padding = PADDING.NONE; const wrapper = shallowMount(DRadio, { props: { type: TYPE.BUTTON, name, value, padding }, }); const button = wrapper.findComponent(DButton); expect(button).toBeTruthy(); expect(button.attributes().padding).toBe(padding); }); roundingClassCase(wrapper, `.${config.iconContainerClass}`); sizeClassCase(wrapper, "input"); sizeClassCase(wrapper, `.${config.iconContainerClass}`); sizeClassCase(wrapper, `.${config.iconContainerBackdropClass}`); transitionClassCase(wrapper, `.${config.iconContainerClass}`); // TODO: combine all Caption cases in one factory or just test composition??? it("Shouldn render props.size into props.font of the DCaption", async () => { const size = SIZE.HUGE; const wrapper = await mount(DRadio, { props: { size, caption: "Not empty", value }, }); await sleep(0); // Should wait next event loop step for asyncComponent to be imported const captionEl = wrapper.find(`.${config.captionOptions.class}`); const className = prepareCssClassName( codegenConfig.TOKENS.FONT.CSS_CLASS_PREFIX, size ); expect(captionEl.classes()).toContain(className); }); it("Shouldn't render caption element if props.caption isn't passed", async () => { const wrapper = await mount(DRadio, { props: { value } }); await sleep(0); // Should wait next event loop step for asyncComponent to be imported const captionEl = wrapper.find(`.${config.captionOptions.class}`); expect(captionEl.exists()).toBeFalsy(); }); it("Should render caption element with props.caption content if passed", async () => { const captionContent = "some caption"; const caption =
{captionContent}
; const wrapper = await mount(DRadio, { props: { caption, value } }); await sleep(0); // Should wait next event loop step for asyncComponent to be imported const captionEl = wrapper.find(`.${config.captionOptions.class}`); expect(captionEl.exists()).toBeTruthy(); expect(captionEl.text()).toBe(captionContent); }); slotCase(DRadio, `.${config.captionOptions.class}`, "caption", { value }); it("Should render props.captionOffset to the caption style as '--offset: props.captionOffset'", async () => { const captionOffset = 33; await wrapper.setProps({ caption: "Caption string", captionOffset, }); const captionEl = wrapper.find(`.${config.captionOptions.class}`); expect(captionEl.attributes("style")).toContain( `--offset: ${captionOffset}` ); }); transitionClassCase(wrapper, `.${config.captionOptions.class}`); it("Should merge props from props.caption and config.captionOptions to the caption element attrs", async () => { const externalClass = "some-external-class"; const wrapper = mount(DRadio, { props: { caption: "not empty", captionOptions: { class: externalClass, }, value, }, }); const caption = wrapper.findComponent(DCaption); expect(caption.classes()).toContain(externalClass); }); it("Should emit onChange event with checked and value payload", async () => { const value = "some value"; const checked = false; const wrapper = shallowMount(DRadio, { props: { name, value, checked } }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("change"); expect(wrapper.emitted("change")?.[0]).toStrictEqual([ !checked, !checked ? value : undefined, ]); expect(wrapper.emitted("update:checked")?.[0]).toStrictEqual([!checked]); expect(wrapper.emitted("update:value")?.[0]).toStrictEqual([ !checked ? value : undefined, ]); }); it("Shouldn't emit onChange if props.disabled is passed", async () => { const value = "some value"; const checked = false; const disabled = true; const wrapper = shallowMount(DRadio, { props: { name, value, checked, disabled }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("change"); expect(wrapper.emitted("change")?.[0]).toBeFalsy(); expect(wrapper.emitted("update:checked")?.[0]).toBeFalsy(); expect(wrapper.emitted("update:value")?.[0]).toBeFalsy(); }); it("Should call passed props.whenChange", async () => { const value = "some value"; const checked = false; const whenChange = jest.fn(); const wrapper = shallowMount(DRadio, { props: { name, value, checked, whenChange }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("change"); expect(whenChange).toHaveBeenCalledWith( !checked, !checked ? value : undefined ); }); it("Shouldn't call passed props.whenChange if props.disabled passed", async () => { const value = "some value"; const checked = false; const disabled = true; const whenChange = jest.fn(); const wrapper = shallowMount(DRadio, { props: { name, value, checked, disabled, whenChange }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("change"); expect(whenChange).toHaveBeenCalledTimes(0); }); it("Should emit onInput event with value payload", async () => { const value = "some value"; const checked = false; const disabled = false; const wrapper = shallowMount(DRadio, { props: { name, value, checked, disabled }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("input"); expect(wrapper.emitted("input")?.[0]).toStrictEqual([ !checked ? value : undefined, ]); }); it("Shouldn't emit onInput if props.disabled is passed", async () => { const value = "some value"; const checked = false; const disabled = true; const wrapper = shallowMount(DRadio, { props: { name, value, checked, disabled }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("input"); expect(wrapper.emitted("input")?.[0]).toBeFalsy(); expect(wrapper.emitted("update:value")?.[0]).toBeFalsy(); }); it("Should call passed props.whenInput", async () => { const value = "some value"; const checked = false; const whenInput = jest.fn(); const wrapper = shallowMount(DRadio, { props: { name, value, checked, whenInput }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("input"); expect(whenInput).toHaveBeenCalledWith(!checked ? value : undefined); }); it("Shouldn't call passed props.whenInput if props.disabled passed", async () => { const value = "some value"; const checked = false; const disabled = true; const whenInput = jest.fn(); const wrapper = shallowMount(DRadio, { props: { name, value, checked, disabled, whenInput }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); await inputEl.trigger("input"); expect(whenInput).toHaveBeenCalledTimes(0); }); it("Shouldn't call any props.when... or emit events if already is checked", async () => { const checked = true; const whenInput = jest.fn(); const whenChange = jest.fn(); const wrapper = shallowMount(DRadio, { props: { name, value, checked, whenChange, whenInput }, }); const inputEl = wrapper.find("input"); await inputEl.trigger("click"); expect(wrapper.emitted("change")?.[0]).toBeFalsy(); expect(wrapper.emitted("update:checked")?.[0]).toBeFalsy(); expect(wrapper.emitted("update:value")?.[0]).toBeFalsy(); expect(whenChange).toHaveBeenCalledTimes(0); expect(whenInput).toHaveBeenCalledTimes(0); }); tagCase(wrapper); it("Should change innerChecked on props.checked change", async () => { const checked = true; await wrapper.setProps({ checked }); expect(wrapper.props().checked).toBe(checked); await wrapper.setProps({ checked: !checked }); expect(wrapper.props().checked).toBe(!checked); }); // TODO: props. ...Options cases });