Created
November 6, 2020 11:18
-
-
Save voskresla/751830f53a1bdb53caf8f33011230f59 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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