Валидационные правила в Vest строятся как набор тестов, которые выполняются над состоянием формы. При этом ключевая особенность библиотеки — возможность свободно использовать обычную JavaScript-логику внутри тестов, что делает реализацию условных правил естественной частью кода, а не отдельным декларативным слоем.
Условная валидация возникает в момент, когда корректность одного поля зависит от значения другого. Такие сценарии встречаются в формах повсеместно: переключение режимов, адресные данные, платежные реквизиты, подтверждения, динамические обязательные поля.
В Vest нет отдельного DSL для условных правил. Вместо этого используется обычная логика JavaScript внутри тестов:
ifОсновная идея заключается в том, что тест выполняется только тогда, когда он актуален.
import { create, test, enforce } from 'vest';
const suite = create((data = {}) => {
test('email', 'Email обязателен', () => {
enforce(data.email).isNotEmpty();
});
test('phone', 'Телефон обязателен при выборе SMS', () => {
if (data.contactMethod === 'sms') {
enforce(data.phone).isNotEmpty();
}
});
});
Здесь второе правило зависит от значения contactMethod.
Если выбран email-канал, тест фактически становится неактивным.
Все условные зависимости в Vest строятся вокруг объекта данных, передаваемого в suite. Он играет роль единого источника состояния формы.
Типичная структура:
data.fieldName — текущее значение поляtest('state', 'Штат обязателен для США', () => {
if (data.country === 'US') {
enforce(data.state).isNotEmpty();
}
});
Такой подход позволяет не размазывать логику по UI-слою: правила остаются централизованными.
Условные проверки в Vest не обязаны выполняться всегда. Логика может полностью исключить тест из фактической валидации.
test('companyName', 'Название компании обязательно для бизнеса', () => {
if (data.accountType !== 'business') return;
enforce(data.companyName).isNotEmpty();
});
Если условие не выполняется, тест завершится без ошибок и не повлияет на результат suite.
Это важный механизм: он позволяет избегать лишних сообщений об ошибках и снижает шум в интерфейсе.
Частый сценарий — взаимозависимость нескольких полей одновременно. Например, пароль и подтверждение пароля.
test('passwordConfirmation', 'Пароли должны совпадать', () => {
if (!data.password || !data.passwordConfirmation) return;
enforce(data.passwordConfirmation).equals(data.password);
});
Здесь проверка активируется только при наличии обоих значений. Это снижает количество ложных ошибок при частичном вводе.
Одна из наиболее распространённых форм условной логики — динамическая обязательность.
test('vatNumber', 'Номер НДС обязателен для компаний в ЕС', () => {
const isEUCountry = ['DE', 'FR', 'IT', 'ES'].includes(data.country);
if (isEUCountry && data.accountType === 'business') {
enforce(data.vatNumber).isNotEmpty();
}
});
Здесь сразу две зависимости:
Такая композиция легко расширяется без изменения архитектуры suite.
Вместо вложенных условий часто применяется ранний выход. Это делает код теста более читаемым.
test('zipCode', 'Почтовый индекс обязателен', () => {
if (data.country !== 'US') return;
enforce(data.zipCode).isNotEmpty();
});
При большом количестве условий такой стиль уменьшает вложенность и упрощает поддержку.
В более сложных формах поля могут зависеть от комбинаций значений.
test('deliveryAddress', 'Адрес доставки обязателен', () => {
const needsDeliveryAddress =
data.deliveryType === 'courier' ||
(data.deliveryType === 'pickup' && data.insurance === true);
if (!needsDeliveryAddress) return;
enforce(data.deliveryAddress).isNotEmpty();
});
Здесь логика выходит за рамки одного поля и становится бизнес-правилом, описанным прямо внутри теста.
При усложнении логики часто выделяют промежуточные переменные, чтобы не перегружать тесты условиями.
test('discountCode', 'Промокод доступен только для премиум', () => {
const isPremium = data.plan === 'premium';
const isPromoRequired = data.campaign === 'summer_sale';
if (!(isPremium && isPromoRequired)) return;
enforce(data.discountCode).isNotEmpty();
});
Такой подход позволяет описывать правила декларативно, сохраняя при этом JavaScript-выразительность.
Хотя Vest чаще используется для синхронной валидации, условные правила могут учитывать состояние, полученное асинхронно (например, загруженные настройки формы). Важно, что решение о выполнении теста принимается на момент запуска suite.
test('username', 'Имя пользователя занято', async () => {
if (!data.username) return;
const isTaken = await api.checkUsername(data.username);
enforce(isTaken).equals(false);
});
Здесь условие не отключает тест, но защищает от лишнего запроса при пустом значении.
Формы часто имеют режимы: создание, редактирование, просмотр. Условные правила позволяют адаптировать поведение без дублирования suite.
test('email', 'Email нельзя изменить после создания', () => {
if (data.mode !== 'edit') return;
if (!data.originalEmail) return;
enforce(data.email).equals(data.originalEmail);
});
Таким образом, одна и та же форма обслуживает разные бизнес-сценарии.
При росте сложности удобно группировать условия по доменам, чтобы не смешивать разные аспекты формы.
const isBillingRequired = data.hasSubscription;
const isShippingRequired = data.isPhysicalProduct;
test('billingAddress', 'Требуется адрес оплаты', () => {
if (!isBillingRequired) return;
enforce(data.billingAddress).isNotEmpty();
});
test('shippingAddress', 'Требуется адрес доставки', () => {
if (!isShippingRequired) return;
enforce(data.shippingAddress).isNotEmpty();
});
Такой подход делает правила более читаемыми и управляемыми.
При построении условных правил важно избегать модификации состояния внутри тестов. Vest предполагает, что тесты являются чистыми функциями, зависящими только от входных данных.
Корректный подход:
Некорректный подход:
При повторяющихся паттернах условную логику выносят в функции.
const isBusinessAccount = (data) => data.accountType === 'business';
test('companyId', 'Требуется идентификатор компании', () => {
if (!isBusinessAccount(data)) return;
enforce(data.companyId).isNotEmpty();
});
Это снижает дублирование и упрощает масштабирование правил.
Условные проверки позволяют одной suite адаптироваться к множеству контекстов без разветвления архитектуры. Это особенно важно для сложных форм, где бизнес-логика часто меняется.
test('promoCode', 'Промокод недоступен в текущем регионе', () => {
const isPromoAllowed = data.region === 'EU' && data.platform !== 'mobile';
if (!isPromoAllowed) return;
enforce(data.promoCode).isNotEmpty();
});
Такая модель делает правила ближе к предметной области, чем к UI-реализации.
При грамотной организации suite условные тесты начинают выполнять роль бизнес-логики формы. Они описывают не только “валидно/невалидно”, но и “когда применимо”.
Это позволяет:
При этом сохраняется простота: всё выражается через обычный JavaScript и тестовые конструкции Vest.