UNPKG

buefy

Version:

Lightweight UI components for Vue.js (v3) based on Bulma

483 lines (423 loc) 17.3 kB
import { beforeEach, describe, expect, it, vi } from 'vitest' import { shallowMount } from '@vue/test-utils' import type { VueWrapper } from '@vue/test-utils' import BDatepicker from '@components/datepicker/Datepicker.vue' import config, { setOptions } from '@utils/config' let wrapper: VueWrapper<InstanceType<typeof BDatepicker>> const newDate = (y: number, m: number, d: number) => { const date = new Date(Date.UTC(y, m, d)) date.getDate = vi.fn(() => date.getUTCDate()) date.getMonth = vi.fn(() => date.getUTCMonth()) return date } const defaultMonthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] const defaultDayNames = ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'S'] const defaultFirstDayOfWeek = 0 const defaultProps = () => ({ dayNames: config.defaultDayNames, monthNames: config.defaultMonthNames, focusedDate: newDate(2018, 7, 15) }) describe('BDatepicker', () => { describe('with invalid value from config config', () => { beforeEach(() => { setOptions(Object.assign(config, { defaultMonthNames: 'A string!', defaultDayNames: 'A string!', defaultFirstDayOfWeek: 'A string!' })) wrapper = shallowMount(BDatepicker, { global: { stubs: { transition: true } } }) }) it('should have valid default values', () => { expect(wrapper.vm.firstDayOfWeek).toBe(0) expect(wrapper.vm.newMonthNames.length).toBe(defaultMonthNames.length) expect(wrapper.vm.newDayNames.length).toBe(defaultDayNames.length) }) it('manage props validator', () => { const type = wrapper.vm.$options.props.type expect(type.type).toBe(String) expect(type.validator && type.validator('day')).toBeFalsy() expect(type.validator && type.validator('month')).toBeTruthy() }) }) beforeEach(() => { setOptions(Object.assign(config, { defaultMonthNames, defaultDayNames, defaultFirstDayOfWeek })) wrapper = shallowMount(BDatepicker, { props: { ...defaultProps() }, global: { stubs: { transition: true } } }) wrapper.vm.updateInternalState = vi.fn(() => wrapper.vm.updateInternalState) wrapper.vm.togglePicker = vi.fn(() => wrapper.vm.togglePicker) }) it('is called', () => { expect(wrapper.vm).toBeTruthy() expect(wrapper.vm.$options.name).toBe('BDatepicker') }) it('render correctly', () => { wrapper = shallowMount(BDatepicker, { props: { ...defaultProps() }, global: { stubs: { transition: true, // reproduces a snapshot closer to the legacy one that // depends on default slots rendered even if stubbed bDropdown: false, bDropdownItem: false, bField: false, bSelect: false } } }) wrapper.setProps({ dateCreator: () => newDate(2024, 8, 21) }) expect(wrapper.html()).toMatchSnapshot() }) it('should have valid default values', () => { expect(wrapper.vm.firstDayOfWeek).toBe(0) expect(wrapper.vm.monthNames).toEqual(defaultMonthNames) expect(wrapper.vm.dayNames).toEqual(defaultDayNames) }) it('react accordingly when setting computedValue', () => { const date = new Date() wrapper.vm.computedValue = date expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(date) expect(wrapper.vm.togglePicker).toHaveBeenCalled() expect(wrapper.emitted()['update:modelValue']).toBeTruthy() }) it('react accordingly when handling native picker', () => { const date = new Date(2020, 0, 1) wrapper.vm.onChangeNativePicker({ target: { value: '2020-01-01' } }) expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(date) expect(wrapper.vm.togglePicker).toHaveBeenCalled() expect(wrapper.emitted()['update:modelValue']).toBeTruthy() }) it('react accordingly when handling native picker clear', () => { wrapper.vm.onChangeNativePicker({ target: { value: '' } }) expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(null) expect(wrapper.vm.togglePicker).toHaveBeenCalled() expect(wrapper.emitted()['update:modelValue']).toEqual([[null]]) }) it('react accordingly when changing v-model', async () => { const date = new Date() await wrapper.setProps({ modelValue: date }) expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(date) expect(wrapper.vm.togglePicker).toHaveBeenCalled() }) it('set focusedDateData when changing focused date', async () => { const date = newDate(2019, 8, 26) await wrapper.setProps({ focusedDate: date }) expect(wrapper.vm.focusedDateData).toEqual({ day: date.getDate(), month: date.getMonth(), year: date.getFullYear() }) }) it('react accordingly when calling onChange', async () => { const date = new Date() await wrapper.setProps({ dateParser: vi.fn() }) wrapper.vm.onChange(date + '') expect(wrapper.vm.dateParser).toHaveBeenCalled() }) it('react accordingly when calling prev', async () => { let date = newDate(2019, 8, 26) await wrapper.setProps({ focusedDate: date }) wrapper.vm.prev() expect(wrapper.vm.focusedDateData.month).toBe(7) date = newDate(2019, 0, 26) await wrapper.setProps({ focusedDate: date }) wrapper.vm.prev() expect(wrapper.vm.focusedDateData.year).toBe(2018) expect(wrapper.vm.focusedDateData.month).toBe(11) date = newDate(2021, 1, 1) await wrapper.setProps({ focusedDate: date, type: 'month' }) wrapper.vm.prev() expect(wrapper.vm.focusedDateData.year).toBe(2020) date = newDate(2021, 1, 1) await wrapper.setProps({ focusedDate: date, type: 'month', disabled: true }) wrapper.vm.prev() expect(wrapper.vm.focusedDateData.year).toBe(2021) }) it('react accordingly when calling next', async () => { let date = newDate(2019, 8, 26) await wrapper.setProps({ focusedDate: date }) wrapper.vm.next() expect(wrapper.vm.focusedDateData.month).toBe(9) date = newDate(2019, 11, 26) await wrapper.setProps({ focusedDate: date }) wrapper.vm.next() expect(wrapper.vm.focusedDateData.year).toBe(2020) expect(wrapper.vm.focusedDateData.month).toBe(0) date = newDate(2021, 1, 1) await wrapper.setProps({ focusedDate: date, type: 'month' }) wrapper.vm.next() expect(wrapper.vm.focusedDateData.year).toBe(2022) date = newDate(2021, 1, 1) await wrapper.setProps({ focusedDate: date, type: 'month', disabled: true }) wrapper.vm.next() expect(wrapper.vm.focusedDateData.year).toBe(2021) }) it('handles accordingly the list of months', async () => { await wrapper.setProps({ focusedDate: newDate(2021, 10, 16), minDate: newDate(2021, 10, 15), maxDate: null }) expect(wrapper.vm.listOfMonths.filter((month) => !month.disabled).map((month) => month.name)).toEqual(['November', 'December']) await wrapper.setProps({ focusedDate: newDate(2021, 2, 1), minDate: null, maxDate: newDate(2021, 2, 15) }) expect(wrapper.vm.listOfMonths.filter((month) => !month.disabled).map((month) => month.name)).toEqual(['January', 'February', 'March']) }) it('handles accordingly the list of years', async () => { await wrapper.setProps({ minDate: newDate(2017, 1, 1), maxDate: null }) const y = [2017] for (let i = 1; i <= 11; i++) y.push(y[i - 1] + 1) expect(wrapper.vm.listOfYears).toEqual(y.reverse()) await wrapper.setProps({ maxDate: newDate(2020, 1, 1) }) expect(wrapper.vm.listOfYears.sort()).toEqual([2020, 2019, 2018, 2017].sort()) }) it('handles accordingly focus', async () => { await wrapper.setProps({ openOnFocus: false }) wrapper.vm.onFocus = vi.fn() wrapper.vm.togglePicker = vi.fn() wrapper.vm.handleOnFocus() expect(wrapper.vm.onFocus).toHaveBeenCalled() expect(wrapper.vm.togglePicker).toHaveBeenCalledTimes(0) await wrapper.setProps({ openOnFocus: true }) wrapper.vm.handleOnFocus() expect(wrapper.vm.onFocus).toHaveBeenCalled() expect(wrapper.vm.togglePicker).toHaveBeenCalledTimes(1) }) describe('#dateFormatter', () => { beforeEach(() => { // uses en-US locale to make outputs predictable wrapper.setProps({ locale: 'en-US' }) }) it('should add one to month since month in dates starts from 0', () => { const dateToFormat = new Date(2019, 3, 1) const formattedDate = wrapper.vm.dateFormatter(dateToFormat, wrapper.vm) expect(formattedDate).toEqual('4/1/2019') }) it('should format based on 2-digit numeric locale date with type === month', async () => { await wrapper.setProps({ type: 'month' }) const dateToFormat = new Date(2019, 3, 15) const formattedDate = wrapper.vm.dateFormatter(dateToFormat, wrapper.vm) expect(formattedDate).toEqual('4/2019') }) it('should format a range of dates passed via array', () => { const dateToFormat = [ new Date(2019, 3, 1), new Date(2019, 3, 3) ] const formattedDate = wrapper.vm.dateFormatter(dateToFormat, wrapper.vm) expect(formattedDate).toEqual('4/1/2019 - 4/3/2019') }) describe('multiple', () => { beforeEach(() => { wrapper.setProps({ inline: true, multiple: true }) wrapper.vm.updateInternalState = vi.fn(() => wrapper.vm.updateInternalState) wrapper.vm.togglePicker = vi.fn(() => wrapper.vm.togglePicker) }) it('should format multiple dates passed via array', () => { const dateToFormat = [ new Date(2019, 3, 1), new Date(2019, 3, 13), new Date(2019, 3, 3) ] const formattedDate = wrapper.vm.dateFormatter(dateToFormat, wrapper.vm) expect(formattedDate).toEqual('4/1/2019, 4/13/2019, 4/3/2019') }) it('react accordingly when setting computedValue', () => { const date = new Date() wrapper.vm.computedValue = date expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(date) expect(wrapper.vm.togglePicker).toHaveBeenCalledTimes(0) expect(wrapper.emitted()['update:modelValue']).toBeTruthy() }) it('react accordingly when changing v-model', async () => { const date = new Date() await wrapper.setProps({ modelValue: date }) expect(wrapper.vm.updateInternalState).toHaveBeenCalledWith(date) expect(wrapper.vm.togglePicker).toHaveBeenCalledTimes(0) }) }) }) describe('#formatValue', () => { it('should call dateFormatter, passing the date', async () => { const mockDateFormatter = vi.fn() await wrapper.setProps({ dateFormatter: mockDateFormatter, closeOnClick: false }) const date = new Date() wrapper.vm.formatValue(date) expect(mockDateFormatter.mock.calls[0][0]).toEqual(date) }) it('should not call dateFormatter when value is undefined or NaN', async () => { const mockDateFormatter = vi.fn() await wrapper.setProps({ dateFormatter: mockDateFormatter }) wrapper.vm.formatValue(undefined) expect(mockDateFormatter.mock.calls.length).toEqual(0) wrapper.vm.formatValue('buefy' as unknown as Date) expect(mockDateFormatter.mock.calls.length).toEqual(0) }) it('should not call dateFormatter when value is an array with undefined or NaN elements', async () => { const mockDateFormatter = vi.fn() await wrapper.setProps({ dateFormatter: mockDateFormatter }) wrapper.vm.formatValue([new Date(), undefined] as unknown as Date[]) expect(mockDateFormatter.mock.calls.length).toEqual(0) wrapper.vm.formatValue([new Date(), 'buefy'] as unknown as Date[]) expect(mockDateFormatter.mock.calls.length).toEqual(0) }) }) describe('when horizontalTimePicker is true', () => { beforeEach(() => { wrapper = shallowMount(BDatepicker, { global: { stubs: { transition: true, // tests depend on slots under BDropdown bDropdown: false, bDropdownItem: false } }, slots: { default: ['<div>Custom footer</div>'] }, props: { horizontalTimePicker: true } }) }) it('renders a component with .dropdown-horizontal-timepicker class', () => { const subject = wrapper.find('.dropdown-horizontal-timepicker') expect(subject.exists()).toBeTruthy() }) it('renders a component with .content-horizontal-timepicker class', () => { const subject = wrapper.find('.content-horizontal-timepicker') expect(subject.exists()).toBeTruthy() }) it('renders a component with .footer-horizontal-timepicker class', () => { const subject = wrapper.find('.footer-horizontal-timepicker') expect(subject.exists()).toBeTruthy() }) }) describe('with fallthrough attributes', () => { const attrs = { class: 'fallthrough-class', style: 'font-size: 2rem;', id: 'fallthrough-id' } it('should apply class, style, and id to the root div element, if compatFallthrough is true (default)', () => { const wrapper = shallowMount(BDatepicker, { attrs, global: { stubs: { // b-dropdown must be rendered to access ref=input 'b-dropdown': false } } }) const root = wrapper.find('div.datepicker') expect(root.classes(attrs.class)).toBe(true) expect(root.attributes('style')).toBe(attrs.style) expect(root.attributes('id')).toBe(attrs.id) const input = wrapper.findComponent({ ref: 'input' }) expect(input.classes(attrs.class)).toBe(false) expect(input.attributes('style')).toBeUndefined() expect(input.attributes('id')).toBeUndefined() }) it('should apply class, style, and id to the underlying input, if compatFallthrough is false', () => { const wrapper = shallowMount(BDatepicker, { attrs, props: { compatFallthrough: false }, global: { stubs: { // b-dropdown must be rendered to access ref=input 'b-dropdown': false } } }) const root = wrapper.find('div.datepicker') expect(root.classes(attrs.class)).toBe(false) expect(root.attributes('style')).toBeUndefined() expect(root.attributes('id')).toBeUndefined() const input = wrapper.findComponent({ ref: 'input' }) expect(input.classes(attrs.class)).toBe(true) expect(input.attributes('style')).toBe(attrs.style) expect(input.attributes('id')).toBe(attrs.id) }) }) })