@maxpike/vue
Version:
Vue VariantJS: Fully configurable Vue 3 components styled with TailwindCSS
279 lines (215 loc) • 8.01 kB
text/typescript
/* 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();
});
});
});