Skip to content

Instantly share code, notes, and snippets.

@voskresla
Created November 6, 2020 11:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voskresla/751830f53a1bdb53caf8f33011230f59 to your computer and use it in GitHub Desktop.
Save voskresla/751830f53a1bdb53caf8f33011230f59 to your computer and use it in GitHub Desktop.
import { Wrapper, mount, VueClass } from '@vue/test-utils'
import { Component, VueComponent } from '@lms/types'
import { Field } from '@lms-ui/field'
import { DatePicker, Calendar, DatePickerProps } from '..'
/**
* TODO: написать в документацию по тестированию а том, что тестировать clickoutside через document.dispatchEvent приводит к false positive,
* потому что смаунченый компонент может просто бытьне прикреплен к document (даже attachTo: document.body в двух подряд тестах не решает этой проблемы в 1.0.0-beta-33)
*
*/
let wrapper: Wrapper<DatePicker | VueComponent>
const waitRAF = () => new Promise(resolve => requestAnimationFrame(resolve))
enum Selectors {
error = '[aria-label="Сообщение об ошибке"]',
clickOutside = '[aria-label="clickOutside"]'
}
const findBySelector = (selector: keyof typeof Selectors) => wrapper.find(Selectors[selector])
const findInput = () => wrapper.find('input')
const findInputElement = () => findInput().element as HTMLInputElement
const findCalendar = () => wrapper.findComponent(Calendar)
const findAllCalendar = () => wrapper.findAllComponents(Calendar)
const findAllDatePicker = () => wrapper.findAllComponents(DatePicker)
const hasError = () => findBySelector('error').exists()
/**
* REVIEW: мы не можем навесить в своем тестовом компоненте aria-label и искать по селектору.
* - Во-первых в datePicker криво вешаются this.$attrs (не на рутовый элемент, и надо как-то эторазрулить)
* - Во-вторых, даже если мы повесим переданый aria-label на рут, то findBySelector вернет нам Element и от него уже нельзя взять findComponent(...)
*/
const findFirstDatePicker = () => findAllDatePicker().at(0)
const findSecondDatePicker = () => findAllDatePicker().at(1)
@Component({
inheritAttrs: false,
})
class OneDatePickerTestComponent extends VueComponent {
render() {
return (
<div
aria-label="clickOutside"
>
{/* REVIEW: TS ругается на то что нет attrs в типах.. */}
{/* @ts-ignore */}
<DatePicker
{...{
attrs: this.$attrs,
}}
/>
</div>
)
}
}
@Component
class TwoDatePickerTestComponent extends VueComponent {
render() {
return (
<div
aria-label="clickOutside"
>
<DatePicker/>
<DatePicker/>
</div>
)
}
}
const commonBeforeEach = async (component: VueClass<VueComponent>, propsData?: Partial<DatePickerProps>) => {
wrapper = mount(component, {
attachTo: document.body,
propsData,
})
/**
* Директива clickoutside использует requestAnimationFrame, чтобы установить или удалить обработчики.
*/
await waitRAF()
// Откроем календарь чтобы дальше не было false positive теста на то что он закрывается.
await wrapper.findComponent(Field).trigger('focus')
expect(findCalendar().exists()).toBe(true)
}
const commonAfterEach = async () => {
wrapper.destroy()
// Удалим все обработчики
await waitRAF()
jest.clearAllMocks()
}
// Один календарь. Период.
describe('Если календарь один', () => {
beforeEach(async () => commonBeforeEach(OneDatePickerTestComponent))
afterEach(async () => commonAfterEach())
it('закрывает календарь если кликнули вне его', async () => {
await findBySelector('clickOutside').trigger('mousedown')
expect(findCalendar().exists()).toBe(false)
})
})
// Один календарь. Период. Ошибки.
describe('Если календарь один и ПЕРИОД НЕ ПРЕДЗАПОЛНЕН, правильно устаналивает ошибку', () => {
beforeEach(async () => commonBeforeEach(OneDatePickerTestComponent))
afterEach(async () => commonAfterEach())
it('когда открыли и закрыли календарь через clickoutside => нет ошибки', async () => {
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
it('когда открыли и ввели не полную дату и закрыли календарь через clickoutside => нет ошибки', async () => {
findInputElement().value = '29.05.2020'
await findInput().trigger('accept')
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(true)
})
})
describe('Если календарь один и ПЕРИОД ПРЕДЗАПОЛНЕН, правильно устаналивает ошибку', () => {
beforeEach(async () => commonBeforeEach(OneDatePickerTestComponent, {
value: {
start: new Date(),
end: new Date(),
},
}))
afterEach(async () => commonAfterEach())
it('когда открыли и закрыли календарь через clickoutside => нет ошибки', async () => {
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
it('когда открыли и очистили дату и закрыли календарь через clickoutside => нет ошибки', async () => {
findInputElement().value = ''
await findInput().trigger('accept')
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
})
// Один календарь. НЕ период. Ошибки.
describe('Если календарь один и ДАТА НЕ ПРЕДЗАПОЛНЕНА, правильно устаналивает ошибку', () => {
beforeEach(async () => commonBeforeEach(OneDatePickerTestComponent, {
range: false,
}))
afterEach(async () => commonAfterEach())
it('когда открыли и закрыли календарь через clickoutside => нет ошибки', async () => {
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
it('когда открыли и ввели не полную дату и закрыли календарь через clickoutside => есть ошибка', async () => {
findInputElement().value = '29.05.2'
await findInput().trigger('accept')
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(true)
})
})
describe('Если календарь один и ДАТА ПРЕДЗАПОЛНЕНА, правильно устаналивает ошибку', () => {
beforeEach(async () => commonBeforeEach(OneDatePickerTestComponent, {
range: false,
value: {
start: new Date(),
},
}))
afterEach(async () => commonAfterEach())
it('когда открыли и закрыли календарь через clickoutside => нет ошибки', async () => {
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
it('когда открыли и очистили дату и закрыли календарь через clickoutside => нет ошибки', async () => {
findInputElement().value = ''
await findInput().trigger('accept')
await findBySelector('clickOutside').trigger('mousedown')
expect(hasError()).toBe(false)
})
})
// Два календаря. Период.
describe('Если календарей несколько', () => {
beforeEach(async () => {
wrapper = mount(TwoDatePickerTestComponent, {
attachTo: document.body,
})
await waitRAF()
// Исключаем false positive в будущем
expect(findAllCalendar().length).toBe(0)
// Открываем первый календарь
await findFirstDatePicker().findComponent(Field).trigger('focus')
expect(findAllCalendar().length).toBe(1)
expect(findFirstDatePicker().findComponent(Calendar).exists()).toBe(true)
expect(findSecondDatePicker().findComponent(Calendar).exists()).toBe(false)
})
afterEach(async () => commonAfterEach())
it('закрывает открытый календарь если кликнули по другому календарю', async () => {
// Кликаем по второму календарю
await findSecondDatePicker().trigger('mousedown')
expect(findFirstDatePicker().findComponent(Calendar).exists()).toBe(false)
})
it('закрывает открытый календарь если кликнули по document', async () => {
// Кликаем по document
document.dispatchEvent(new Event('mousedown'))
await wrapper.vm.$nextTick()
expect(findFirstDatePicker().findComponent(Calendar).exists()).toBe(false)
})
it('закрывает открытый календарь если кликнули по aria-label=[clickOutside]', async () => {
// Кликаем по aria-label=[clickOutside]
await findBySelector('clickOutside').trigger('mousedown')
expect(findFirstDatePicker().findComponent(Calendar).exists()).toBe(false)
})
})
// Два календаря. Период. Ошибки.
/**
* Тест для фикса из https://gitlab.ozon.ru/lms/frontend/components/-/merge_requests/226
*
* BUG: При добавлении директивы устаналивалась ошибка на календаре при любом клике по document
*
* RESOLVE: Изменили логику метода handleClickOutside, добавили проверку на isShowCalendar === true
*/
describe('Если календарей несколько и ПЕРИОД НЕ ПРЕДЗАПОЛНЕН, правильно устанавливаются ошибки', () => {
afterEach(async () => commonAfterEach())
it('когда открыли и закрыли один календарь через clickoutside => нет ошибок', async () => {
wrapper = mount(TwoDatePickerTestComponent, {
attachTo: document.body,
})
await waitRAF()
// Кликаем по document
document.dispatchEvent(new Event('mousedown'))
await wrapper.vm.$nextTick()
expect(hasError()).toBe(false)
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment