Задание 1: Тестирование Composition API с ref, computed, watch
<template>
<div>
<input v-model="inputValue" data-testid="input" />
<p data-testid="computed">{{ upperCaseValue }}</p>
<p data-testid="watch-triggered">{{ watchTriggered }}</p>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
const inputValue = ref('')
const upperCaseValue = computed(() => inputValue.value.toUpperCase())
const watchTriggered = ref(false)
watch(inputValue, (newValue) => {
if (newValue.length > 5) {
watchTriggered.value = true
}
})
</script>
Задание:
- Проверить, что
computed свойство upperCaseValue корректно отображает введённое значение в верхнем регистре.
- Проверить, что
watch срабатывает и устанавливает watchTriggered в true, когда длина inputValue становится больше 5 символов.
- Проверить, что
watch не срабатывает, если длина inputValue меньше или равна 5 символам.
Решение:
import { mount } from '@vue/test-utils';
import { it, describe, expect } from 'vitest';
import { nextTick } from 'vue'
import Test1 from '@/components/test1.vue'
describe('test1.vue', () => {
it("computed свойство upperCaseValue", async () => {
const wrapper = mount(Test1);
const input = wrapper.get('[data-testid="input"]');
await input.setValue('test');
expect(wrapper.find('[data-testid="computed"]').text()).toBe('TEST');
});
it("тестируем watch > 5", async () => {
const wrapper = mount(Test1);
const input = wrapper.get('[data-testid="input"]');
await input.setValue('tester>6');
await nextTick();
expect(wrapper.find('[data-testid="watch-triggered"]').text()).toBe('true');
});
it("тестируем watch < 5", async () => {
const wrapper = mount(Test1);
const input = wrapper.get('[data-testid="input"]');
await input.setValue('teste');
await nextTick();
expect(wrapper.find('[data-testid="watch-triggered"]').text()).toBe('false');
});
})
Задание 2: Тестирование async логики и v-model с defineProps / defineEmits
<template>
<div>
<input v-model="localValue" @blur="validate" data-testid="input" />
<p v-if="error" data-testid="error">{{ error }}</p>
<p v-else data-testid="success">OK</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
validateAsync: { type: Function, required: true }
})
const emit = defineEmits(['update:modelValue'])
const localValue = ref('')
const error = ref('')
// Синхронизируем локальный ref с внешним modelValue
watch(
() => props.modelValue,
(newValue) => {
localValue.value = newValue
},
{ immediate: true }
)
// Синхронизируем локальный ref обратно к внешнему modelValue
watch(localValue, (newValue) => {
emit('update:modelValue', newValue)
})
async function validate() {
if (!localValue.value) {
error.value = 'Поле обязательно'
return
}
try {
error.value = await props.validateAsync(localValue.value)
} catch (e) {
error.value = 'Ошибка валидации'
}
}
</script>
Задание:
Что протестировать:
- Проверить, что при потере фокуса (
blur) вызывается переданная функция validateAsync с параметром из input.
- Проверить, что при
validateAsync, возвращающей ошибку, она отображается в элементе с data-testid="error".
- Проверить, что при изменении
localValue эмитится событие update:modelValue.
Решение:
import { mount } from '@vue/test-utils';
import { it, describe, expect, vi } from 'vitest';
import { nextTick } from 'vue'
import Test2 from '@/components/test2.vue'
describe('test2.vue', () => {
it("вызывает validateAsync при blur", async () => {
const validateAsyncMock = vi.fn(()=> Promise.resolve('') )
const wrapper = mount(Test2,{
props:{
validateAsync:validateAsyncMock
}
});
await wrapper.get(['[data-testid="input"]']).setValue('test');
await wrapper.get(['[data-testid="input"]']).trigger('blur');
expect(validateAsyncMock).toHaveBeenCalledWith('test');
});
it("Мок ошибки", async () => {
const validateAsyncMock = vi.fn(()=>Promise.resolve('Ошибка валидации'));
const wrapper = mount(Test2,{
props:{
validateAsync:validateAsyncMock
}
});
await wrapper.get(['[data-testid="input"]']).setValue('test');
await wrapper.get(['[data-testid="input"]']).trigger('blur');
await nextTick();
expect(wrapper.find('[data-testid="error"]').text()).toBe('Ошибка валидации')
});
})