UNPKG

@maxpike/vue

Version:

Vue VariantJS: Fully configurable Vue 3 components styled with TailwindCSS

279 lines (215 loc) 8.01 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { NormalizedOptions } from '@variantjs/core'; import { shallowMount } from '@vue/test-utils'; import { ref } from 'vue'; import RichSelectOptionsList from '../../../components/TRichSelect/RichSelectOptionsList.vue'; import { TSelectOptions } from '../../../types'; describe('RichSelectOptionsList', () => { const options: NormalizedOptions = [ { value: 'A', text: 'Option A', }, { value: 'B', text: 'Option B', }, ]; const props = { options, deep: 0, }; const global = { provide: { configuration: {} as TSelectOptions, fetchingMoreOptions: ref(false), shown: ref(false), dropdownBottomReachedHandler: jest.fn(), }, }; it('should render without errors', () => { const wrapper = shallowMount(RichSelectOptionsList, { props, global }); expect(wrapper.vm.$el.tagName).toBe('UL'); }); it('doesnt have `style` attribute by default', () => { const wrapper = shallowMount(RichSelectOptionsList, { props, global }); expect(wrapper.vm.$el.getAttribute('style')).toBeNull(); }); it('adds the max-height style if defined on the settings', () => { const configuration: TSelectOptions = { maxHeight: 100, }; const wrapper = shallowMount(RichSelectOptionsList, { props, global: { provide: { ...global.provide, configuration, }, }, }); expect(wrapper.vm.$el.getAttribute('style')).toBe('max-height: 100px; overflow-x: auto;'); }); it('doesnt add the max-height style if deep > 0', () => { const configuration: TSelectOptions = { maxHeight: 100, }; const wrapper = shallowMount(RichSelectOptionsList, { props: { options, deep: 1, }, global: { provide: { ...global.provide, configuration, }, }, }); expect(wrapper.vm.$el.getAttribute('style')).toBeNull(); }); it('adds every option', () => { const wrapper = shallowMount(RichSelectOptionsList, { props, global, }); expect(wrapper.findAll('rich-select-option-stub').length).toBe(options.length); }); it('scroll the list to the "fetchingMoreOptions" element after loaded more options ends', async () => { const fetchingMoreOptions = ref(false); const scrollElementMock = jest.fn(); window.HTMLLIElement.prototype.scrollIntoView = scrollElementMock; const wrapper = shallowMount(RichSelectOptionsList, { props, global: { provide: { ...global.provide, fetchingMoreOptions, }, }, }); expect(scrollElementMock).not.toHaveBeenCalled(); fetchingMoreOptions.value = true; await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); expect(scrollElementMock).toHaveBeenCalledTimes(1); // Should not call again when `fetchingMoreOptions` is false again fetchingMoreOptions.value = false; await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick(); expect(scrollElementMock).toHaveBeenCalledTimes(1); }); describe('dropdownBottomReachedHandler', () => { it('adds a scroll listener attached to the bottomReachedObserver when component is mounted', () => { const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener'); const wrapper = shallowMount(RichSelectOptionsList, { props, global, }); expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver); addEventListenerSpy.mockRestore(); }); it('doesnt adds a scroll listener if no options list', () => { const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener'); shallowMount(RichSelectOptionsList, { props: { ...props, options: [], }, global, }); expect(addEventListenerSpy).not.toHaveBeenCalled(); addEventListenerSpy.mockRestore(); }); it('adds a scroll listener once it have options list and is rendered', async () => { const addEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'addEventListener'); const wrapper = shallowMount(RichSelectOptionsList, { props: { ...props, options: [], }, global, }); expect(addEventListenerSpy).not.toHaveBeenCalled(); await wrapper.setProps({ options: [{ value: 'a', text: 'A' }], }); // Not called yet since the element is not in the DOM yet expect(addEventListenerSpy).not.toHaveBeenCalled(); await wrapper.vm.$nextTick(); expect(addEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver); addEventListenerSpy.mockRestore(); }); it('removes the scroll listener when no longer have options list', async () => { const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener'); const wrapper = shallowMount(RichSelectOptionsList, { props, global, }); await wrapper.setProps({ options: [], }); expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver); removeEventListenerSpy.mockRestore(); }); it('removes the scroll listener when component is unmounted', () => { const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener'); const wrapper = shallowMount(RichSelectOptionsList, { props, global, }); wrapper.unmount(); expect(removeEventListenerSpy).toHaveBeenCalledWith('scroll', (wrapper.vm.$ as any).setupState.bottomReachedObserver); removeEventListenerSpy.mockRestore(); }); it('doesnt removes the scroll listener when component is unmounted if no options', () => { const removeEventListenerSpy = jest.spyOn(window.HTMLUListElement.prototype, 'removeEventListener'); const wrapper = shallowMount(RichSelectOptionsList, { props: { ...props, options: [], }, global, }); wrapper.unmount(); expect(removeEventListenerSpy).not.toHaveBeenCalled(); removeEventListenerSpy.mockRestore(); }); it('calls the dropdownBottomReachedHandler when bottom reached', () => { const dropdownBottomReachedHandlerMock = jest.fn(); jest.useFakeTimers(); const wrapper = shallowMount(RichSelectOptionsList, { props, global: { provide: { ...global.provide, dropdownBottomReachedHandler: dropdownBottomReachedHandlerMock, }, }, }); const container = wrapper.vm.$el; jest.spyOn(container, 'clientHeight', 'get').mockReturnValue(100); // 150 - 49 != 100 (which is the not height of the container) meaning it // not reached the bottom yet jest.spyOn(container, 'scrollHeight', 'get').mockReturnValue(150); jest.spyOn(container, 'scrollTop', 'get').mockReturnValue(49); container.dispatchEvent(new Event('scroll', { target: container, } as any)); // (is debounced 200ms) jest.advanceTimersByTime(200); expect(dropdownBottomReachedHandlerMock).not.toHaveBeenCalled(); // 150 - 50 === 100 (meaning reached the bottom) jest.spyOn(container, 'scrollTop', 'get').mockReturnValue(50); container.dispatchEvent(new Event('scroll', { target: container, } as any)); // (is debounced 200ms) jest.advanceTimersByTime(199); expect(dropdownBottomReachedHandlerMock).not.toHaveBeenCalled(); jest.advanceTimersByTime(1); expect(dropdownBottomReachedHandlerMock).toHaveBeenCalled(); jest.useRealTimers(); }); }); });