В библиотеке Vest набор валидации (suite) представляет собой структурированную единицу, в которой объединяются правила проверки данных. Такой подход позволяет описывать валидацию декларативно, группировать проверки по смыслу и переиспользовать их в разных частях приложения. Основная идея заключается в том, что suite ведёт себя как тестовый набор: каждая проверка выполняется независимо, но результаты агрегируются в единый объект состояния.
Создание набора начинается с функции suite, внутри
которой описываются правила через test. Каждый тест
содержит условие и сообщение об ошибке, которое возвращается при провале
проверки.
import { suite, test, enforce } from 'vest';
const userSuite = suite('user', (data = {}) => {
test('username', 'Имя пользователя обязательно', () => {
enforce(data.username).isNotEmpty();
});
test('username', 'Имя пользователя должно быть не короче 3 символов', () => {
enforce(data.username).longerThanOrEquals(3);
});
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
});
Каждый test привязан к конкретному полю. Первый аргумент
определяет ключ поля, второй — текст ошибки, третий — функция
проверки.
Vest позволяет группировать проверки по полям без создания отдельных структур. Это особенно важно для форм, где одно поле может иметь несколько независимых правил.
test('password', 'Пароль обязателен', () => {
enforce(data.password).isNotEmpty();
});
test('password', 'Пароль должен содержать минимум 8 символов', () => {
enforce(data.password).longerThanOrEquals(8);
});
test('password', 'Пароль должен содержать цифру', () => {
enforce(data.password).matches(/\d/);
});
Такой подход обеспечивает детализированную обратную связь, где каждое правило возвращает отдельную ошибку.
Набор валидации принимает входные данные как аргумент функции, что позволяет динамически валидировать любые структуры.
const result = userSuite({
username: 'alex',
email: 'alex@mail.com'
});
Результат выполнения содержит информацию о валидности и ошибки по каждому полю.
Каждый вызов suite пересчитывает состояние заново. Это важно для реактивных интерфейсов, где данные изменяются часто.
const result1 = userSuite({ username: 'a', email: '' });
const result2 = userSuite({ username: 'alex', email: 'alex@mail.com' });
Такой подход исключает необходимость ручного сброса состояния.
Vest поддерживает условные проверки, которые позволяют включать или исключать правила в зависимости от входных данных.
test('phone', 'Телефон обязателен', () => {
if (data.contactMethod === 'phone') {
enforce(data.phone).isNotEmpty();
}
});
Условная логика внутри теста даёт гибкость без усложнения структуры suite.
Наборы можно строить модульно, разделяя проверки на логические блоки. Это позволяет собирать сложные схемы из простых частей.
const emailTests = (data) => {
test('email', 'Email обязателен', () => {
enforce(data.email).isNotEmpty();
});
test('email', 'Email некорректен', () => {
enforce(data.email).matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
};
const userSuite = suite('user', (data) => {
emailTests(data);
test('username', 'Имя пользователя обязательно', () => {
enforce(data.username).isNotEmpty();
});
});
Такой стиль позволяет масштабировать валидацию без дублирования кода.
Vest поддерживает асинхронные тесты, что важно для проверки уникальности данных через API или обращения к серверу.
test('username', 'Имя уже существует', async () => {
const exists = await checkUsernameExists(data.username);
enforce(exists).isFalsy();
});
Асинхронные проверки выполняются так же, как и синхронные, но возвращают Promise.
Асинхронные тесты внутри suite могут выполняться параллельно, что снижает время валидации при множественных запросах.
const result = await userSuite({
username: 'alex',
email: 'alex@mail.com'
});
Vest агрегирует результаты после завершения всех тестов.
Набор может строиться динамически, что позволяет создавать валидацию на основе конфигурации.
const createUserSuite = (rules = {}) =>
suite('user', (data) => {
if (rules.usernameRequired) {
test('username', 'Обязательное поле', () => {
enforce(data.username).isNotEmpty();
});
}
if (rules.emailCheck) {
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
}
});
Такой подход используется в системах с настраиваемыми формами.
Результат suite представляет собой структурированный объект, содержащий ошибки по ключам полей. Это упрощает интеграцию с UI.
const result = userSuite(data);
if (result.hasErrors()) {
console.log(result.getErrors('email'));
}
Ошибки можно извлекать как по отдельным полям, так и в агрегированном виде.
Несколько suite можно комбинировать для построения более сложной логики проверки.
const authSuite = suite('auth', (data) => {
test('password', 'Пароль обязателен', () => {
enforce(data.password).isNotEmpty();
});
});
const profileSuite = suite('profile', (data) => {
authSuite(data);
test('nickname', 'Никнейм обязателен', () => {
enforce(data.nickname).isNotEmpty();
});
});
Композиция позволяет разделять ответственность между модулями.
Каждый test изолирован и не влияет на другие проверки.
Это упрощает отладку и делает поведение валидации предсказуемым даже при
большом количестве правил.
test('field', 'Ошибка A', () => {
enforce(data.field).equals('A');
});
test('field', 'Ошибка B', () => {
enforce(data.field).equals('B');
});
Каждое правило выполняется независимо, и результат агрегируется без побочных эффектов.
enforce выступает промежуточным слоем между данными и
логикой проверки. Он позволяет писать проверки в цепочечном стиле.
enforce(data.age).greaterThan(18);
enforce(data.email).matches(/@/);
Такой стиль повышает читаемость и делает код ближе к декларативному описанию требований.