Валидационная модель Vest строится вокруг декларативного описания правил и привязки результатов проверки к читаемым сообщениям. Сообщения в этой системе не являются второстепенной деталью: они формируют внешний контракт между логикой проверки и пользовательским интерфейсом, а также определяют, насколько точно можно локализовать и интерпретировать ошибки.
Каждое правило валидации в Vest может сопровождаться текстовым
описанием результата, которое возвращается при провале проверки. Этот
механизм позволяет связывать низкоуровневые условия
(enforce) с высокоуровневыми диагностическими сообщениями
без необходимости ручного управления состоянием ошибок.
В простейшем случае сообщение задаётся непосредственно в момент объявления теста:
test('username', 'Имя пользователя обязательно', () => {
enforce(value).isNotEmpty();
});
Здесь вторым аргументом выступает строка сообщения. Она возвращается системой, если тест не проходит. Такая форма является базовой и используется в большинстве сценариев, где достаточно одного статического текста.
Ключевые свойства базового сообщения:
Vest рассматривает тест как атомарную единицу валидации, где сообщение является частью его описания. Это означает, что тест всегда должен отвечать на два вопроса:
test('password', 'Пароль слишком короткий', () => {
enforce(value).longerThanOrEquals(8);
});
В данном случае сообщение не описывает техническое условие
(>= 8), а формирует пользовательскую интерпретацию
результата. Такой подход отделяет бизнес-логику от внутренней реализации
проверок.
Сообщения могут зависеть от входных данных. Для этого используется функция вместо строки:
test('age', ({ value }) => {
if (value < 18) {
return 'Возраст должен быть не менее 18 лет';
}
return;
}, () => {
enforce(value).greaterThanOrEquals(18);
});
Динамическое сообщение позволяет:
Часто используется интерполяция значений:
test('username', ({ value }) => {
return `Имя пользователя "${value}" недопустимо`;
}, () => {
enforce(value).matches(/^[a-z0-9_]+$/i);
});
В Vest результат теста формируется как часть структуры
result после выполнения validate. Каждое
сообщение связывается с конкретным полем и тестом.
Пример структуры результата:
{
errors: {
username: ['Имя пользователя обязательно'],
password: ['Пароль слишком короткий']
}
}
Если к одному полю применяется несколько тестов, каждое сообщение добавляется в массив ошибок этого поля. Это важно для последующей агрегации и отображения на интерфейсе.
При наличии нескольких проваленных проверок порядок сообщений определяется порядком объявления тестов:
test('password', 'Пароль обязателен', () => {
enforce(value).isNotEmpty();
});
test('password', 'Пароль слишком короткий', () => {
enforce(value).longerThan(8);
});
Если значение пустое, первое сообщение будет иметь приоритет, так как второй тест может быть логически зависим от первого. Такой подход позволяет строить каскадную систему проверок, где каждое последующее правило предполагает выполнение предыдущего.
Vest позволяет формировать сообщения в зависимости от контекста
выполнения теста через доступ к context:
test('email', ({ field }) => {
if (field === 'email') {
return 'Некорректный формат электронной почты';
}
return;
}, () => {
enforce(value).matches(/.+@.+\..+/);
});
Контекст может включать:
В сложных схемах валидации сообщения часто группируются по областям
ответственности. Vest поддерживает структурирование через
group, где сообщения остаются локальными внутри своей
группы, но сохраняют общую структуру результата.
group('auth', () => {
test('email', 'Некорректный email', () => {
enforce(value).isEmail();
});
test('password', 'Пароль обязателен', () => {
enforce(value).isNotEmpty();
});
});
В результате ошибки могут быть агрегированы по логическим блокам, сохраняя читаемость и предсказуемость структуры сообщений.
Валидационные функции часто переиспользуются, и сообщения могут быть отделены от логики проверки:
const isValidUsername = () => {
return enforce(value).matches(/^[a-z0-9_]{3,}$/i);
};
test('username', 'Недопустимое имя пользователя', isValidUsername);
Такой подход позволяет:
Одним из ключевых аспектов работы с сообщениями является поддержка нескольких языков. Валидационная логика остаётся неизменной, а текст сообщений может заменяться через внешние словари.
const messages = {
required: 'Поле обязательно для заполнения',
email: 'Некорректный формат email'
};
test('email', messages.email, () => {
enforce(value).isEmail();
});
Более сложные системы локализации используют ключи вместо строк:
test('email', 'validation.email.invalid', () => {
enforce(value).isEmail();
});
Интерпретация ключей выполняется на уровне UI или слоя отображения ошибок.
Vest различает сообщения, привязанные к конкретному полю, и глобальные ошибки формы. Хотя базовый механизм одинаков, семантика отличается.
Полевая ошибка:
test('username', 'Имя пользователя занято', () => {
enforce(value).isAvailable();
});
Глобальная ошибка:
test('form', 'Форма содержит ошибки', () => {
enforce(values).satisfies(allValid);
});
Глобальные сообщения часто используются для отображения состояния формы в целом, например при серверной валидации.
Асинхронные проверки сохраняют ту же модель сообщений, но добавляют задержку получения результата:
test('username', 'Имя уже занято', async () => {
const available = await api.checkUsername(value);
enforce(available).isTruthy();
});
Сообщение фиксируется в момент отклонения промиса или возврата отрицательного результата проверки. Это позволяет сохранять единообразие между синхронными и асинхронными правилами.
Если сообщение не указано, Vest не формирует текст ошибки автоматически. В таком случае результат теста остаётся технически зафиксированным, но не пригодным для отображения без дополнительного слоя обработки.
test('username', () => {
enforce(value).isNotEmpty();
});
Такие тесты применяются в сценариях, где:
При множественных провалах одного и того же теста система предотвращает дублирование идентичных сообщений в пределах одного поля. Это важно при сложных наборах правил, где несколько условий могут возвращать одинаковый результат.
Вся система сообщений в Vest подчиняется общей декларативной логике: результат проверки описывается вместе с самим правилом, без необходимости императивного управления состоянием ошибок. Это позволяет рассматривать сообщения не как побочный эффект, а как структурную часть схемы валидации.
Каждое сообщение: