@maxpike/vue
Version:
Vue VariantJS: Fully configurable Vue 3 components styled with TailwindCSS
595 lines (483 loc) • 14.3 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { NormalizedOption } from '@variantjs/core';
import { mount, shallowMount } from '@vue/test-utils';
import { ref } from 'vue';
import RichSelectOption from '../../../components/TRichSelect/RichSelectOption.vue';
describe('RichSelectOption', () => {
const scrollIntoViewMock = jest.fn();
window.HTMLLIElement.prototype.scrollIntoView = scrollIntoViewMock;
const toggleOption = jest.fn();
const setActiveOption = jest.fn();
const optionIsSelected = jest.fn();
const optionIsActive = jest.fn();
const shown = ref(true);
const option: NormalizedOption = {
value: 'a',
text: 'Option A',
};
const deep = 0;
const global = {
provide: {
toggleOption,
setActiveOption,
optionIsSelected,
optionIsActive,
shown,
},
stubs: {
RichSelectOptionsList: {
template: '<div />',
},
},
};
afterEach(() => {
jest.clearAllMocks();
optionIsActive.mockReturnValue(false);
scrollIntoViewMock.mockReset();
});
it('renders the component', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$el.tagName).toBe('LI');
expect(wrapper.vm.$el.textContent).toBe('Option A');
});
it('determines if option has children', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.hasChildren).toBe(false);
});
it('determines if option has children when empty', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
...option,
children: [],
},
deep,
},
global,
});
expect(wrapper.vm.hasChildren).toBe(false);
});
it('determines if option has children when not empty', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
...option,
children: [
{ value: 1, text: 1 },
],
},
deep,
},
global,
});
expect(wrapper.vm.hasChildren).toBe(true);
});
it('determines if option is selected', () => {
optionIsSelected.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.isSelected).toBe(true);
});
it('determines if option is selected when false', () => {
optionIsSelected.mockReturnValue(false);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.isSelected).toBe(false);
});
it('determines if option is active', () => {
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
jest.spyOn(wrapper.vm, 'scrollIntoViewIfNeccesary').mockImplementation(() => {});
expect(wrapper.vm.isActive).toBe(true);
});
it('determines if option is active when false', () => {
optionIsActive.mockReturnValue(false);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.isActive).toBe(false);
});
it('shows the checkmark icon if option is selected', () => {
optionIsSelected.mockReturnValue(true);
const wrapper = mount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$refs.checkIcon).toBeDefined();
});
it('hides the checkmark icon if option is not selected', () => {
optionIsSelected.mockReturnValue(false);
const wrapper = mount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$refs.checkIcon).toBeUndefined();
});
it('will scroll into the view if shown and is active', async () => {
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;
await wrapper.vm.$nextTick();
expect(scrollIntoViewMock).toHaveBeenCalled();
});
it('will scroll into the view if active changes state', async () => {
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
await wrapper.vm.$nextTick();
// First time when created
expect(scrollIntoViewMock).toHaveBeenCalledTimes(1);
(wrapper.vm.$options.watch!.isActive as any).call(wrapper.vm);
// Second time when state changed
expect(scrollIntoViewMock).toHaveBeenCalledTimes(2);
});
it('will not scroll into the view if shown but is not active', async () => {
optionIsActive.mockReturnValue(false);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;
await wrapper.vm.$nextTick();
expect(scrollIntoViewMock).not.toHaveBeenCalled();
});
it('will not scroll into the view if is active but is not shown', async () => {
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global: {
provide: {
...global.provide,
shown: ref(false),
},
stubs: global.stubs,
},
});
wrapper.vm.$el.scrollIntoView = scrollIntoViewMock;
await wrapper.vm.$nextTick();
expect(scrollIntoViewMock).not.toHaveBeenCalled();
});
describe('event handlers', () => {
it('calls the `mousemoveHandler` when option mousemove', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
const mousemoveHandlerSpy = jest.spyOn(wrapper.vm, 'mousemoveHandler');
wrapper.vm.$el.dispatchEvent(new MouseEvent('mousemove'));
expect(mousemoveHandlerSpy).toHaveBeenCalled();
});
it('calls setActiveOption method when `mousemoveHandler` called', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
wrapper.vm.mousemoveHandler();
expect(setActiveOption).toHaveBeenCalledWith(option);
});
it('doesnt call setActiveOption method when `mousemoveHandler` called and option is disabled', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
...option,
disabled: true,
},
deep,
},
global,
});
wrapper.vm.mousemoveHandler();
expect(setActiveOption).not.toHaveBeenCalled();
});
it('calls the `mousewheelHandler` when mousewheel event', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
const mousewheelHandlerSpy = jest.spyOn(wrapper.vm, 'mousewheelHandler');
wrapper.vm.$el.dispatchEvent(new MouseEvent('mousewheel'));
expect(mousewheelHandlerSpy).toHaveBeenCalled();
});
it('call setActiveOption method when `mousewheelHandler` is called ', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
wrapper.vm.mousewheelHandler();
expect(setActiveOption).toHaveBeenCalledWith(option);
});
it('doesnt call setActiveOption method when `mousewheelHandler` is called and option is disabled', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
...option,
disabled: true,
},
deep,
},
global,
});
wrapper.vm.mousewheelHandler();
expect(setActiveOption).not.toHaveBeenCalled();
});
it('calls the `clickHandler` when option clicked', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
const clickHandlerSpy = jest.spyOn(wrapper.vm, 'clickHandler');
wrapper.vm.$el.dispatchEvent(new MouseEvent('click'));
expect(clickHandlerSpy).toHaveBeenCalled();
});
it('the `clickHandler` toggles the option', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
wrapper.vm.clickHandler();
expect(toggleOption).toHaveBeenCalledWith(option);
});
it('doesnt call option toggle method when `clickHandler` is called and option is disabled', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
...option,
disabled: true,
},
deep,
},
global,
});
wrapper.vm.clickHandler();
expect(toggleOption).not.toHaveBeenCalled();
});
});
describe('regular option attributes', () => {
it('has the correct `aria-selected` attribute when is selected', () => {
optionIsSelected.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('aria-selected')).toBe('true');
});
it('has the correct `aria-selected` attribute when is not selected', () => {
optionIsSelected.mockReturnValue(false);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('aria-selected')).toBe('false');
});
it('has the role=option attribute', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('role')).toBe('option');
});
it('has the tabindex=-1 attribute', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('tabindex')).toBe('-1');
});
it.each([1, 'foo', undefined, NaN])('adds a value attribute for regular values with %s', (value) => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
value,
text: 'Foo',
},
deep,
},
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('value')).toBe(String(value));
});
it.each([{ foo: 'bar' }, [1, 2], null])('adds a value attribute for objects %s', (value) => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
value,
text: 'Foo',
},
deep,
} as any,
global,
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('value')).toBe(JSON.stringify(value));
});
});
describe('option classes', () => {
const classesList = {
option: 'option',
selectedHighlightedOption: 'selected-highlighted-option',
selectedOption: 'selected-option',
highlightedOption: 'highlighted-option',
};
const configuration = { classesList };
it('adds the selectedHighlightedOption if option is selected an active', () => {
optionIsSelected.mockReturnValue(true);
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global: {
...global,
provide: {
...global.provide,
configuration,
},
},
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option selected-highlighted-option');
});
it('adds the selectedOption if option is selected but is not active', () => {
optionIsSelected.mockReturnValue(true);
optionIsActive.mockReturnValue(false);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global: {
...global,
provide: {
...global.provide,
configuration,
},
},
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option selected-option');
});
it('adds the highlightedOption if option is active but is not selected', () => {
optionIsSelected.mockReturnValue(false);
optionIsActive.mockReturnValue(true);
const wrapper = shallowMount(RichSelectOption, {
props: {
option,
deep,
},
global: {
...global,
provide: {
...global.provide,
configuration,
},
},
});
expect(wrapper.vm.$el.querySelector('button').getAttribute('class')).toBe('option highlighted-option');
});
});
describe('option has children', () => {
const wrapper = shallowMount(RichSelectOption, {
props: {
option: {
value: 'foo',
text: 'Foo',
children: [{ value: 1, text: 1 }],
},
deep: 0,
},
global,
});
it('has the role=optgroup attribute', () => {
expect(wrapper.vm.$el.getAttribute('role')).toBe('optgroup');
});
it('has the option text', () => {
expect(wrapper.vm.$el.textContent).toBe('Foo');
});
it('shows the childrenOptions component', () => {
expect(wrapper.vm.$refs.childrenOptions).toBeDefined();
});
});
});