Функция create — центральный механизм библиотеки Vest,
предназначенный для создания валидационных suites. Она формирует
изолированное пространство проверки, в котором описываются правила,
зависимости, условия выполнения и состояние валидации.
Через create определяется:
Базовая сигнатура:
import { create, test, enforce } from 'vest';
const suite = create((data = {}) => {
test('username', 'Имя пользователя обязательно', () => {
enforce(data.username).isNotBlank();
});
});
После создания suite функция может вызываться многократно:
const result = suite({
username: '',
});
create возвращает функцию-валидатор, сохраняющую
внутреннее состояние между вызовами.
const suite = create((data) => {
// правила
});
Внутри такого suite библиотека хранит:
Это позволяет Vest выполнять оптимизированную повторную валидацию без полного пересчёта всех правил.
Простейший пример:
import { create, test } from 'vest';
const suite = create((data) => {
test('email', 'Email обязателен', () => {
if (!data.email) {
throw new Error();
}
});
});
Вызов:
const result = suite({
email: '',
});
Проверка ошибок:
result.hasErrors('email');
Получение сообщений:
result.getErrors('email');
enforceЧаще всего create применяется совместно с
enforce.
import { create, test, enforce } from 'vest';
const suite = create((data) => {
test('password', 'Минимум 8 символов', () => {
enforce(data.password).longerThanOrEquals(8);
});
});
enforce предоставляет декларативный API проверок:
enforce(value).isString();
enforce(value).isNotEmpty();
enforce(value).matches(/[A-Z]/);
Функция, передаваемая в create, может принимать
параметры.
const suite = create((data, currentField) => {
// ...
});
Обычно содержит данные формы.
suite({
email: 'admin@mail.com',
password: '12345678',
});
Используется для selective validation.
suite(data, 'email');
Пример:
const suite = create((data, currentField) => {
only(currentField);
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/@/);
});
test('password', 'Слишком короткий пароль', () => {
enforce(data.password).longerThan(5);
});
});
Suite создаётся один раз и используется повторно.
const loginSuite = create((data) => {
test('email', 'Введите email', () => {
enforce(data.email).isNotBlank();
});
});
Повторные вызовы:
loginSuite({ email: '' });
loginSuite({ email: 'user@mail.com' });
Состояние обновляется автоматически.
Результат выполнения содержит методы анализа состояния.
const result = suite(data);
result.hasErrors();
result.hasErrors('email');
result.isPending();
result.isPending('email');
result.getErrors();
result.getErrors('email');
result.isValid();
Одна из ключевых возможностей create — частичная
валидация.
suite(data, 'email');
В сочетании с only:
import { create, test, only } from 'vest';
const suite = create((data, currentField) => {
only(currentField);
test('email', 'Email обязателен', () => {
enforce(data.email).isNotBlank();
});
test('password', 'Введите пароль', () => {
enforce(data.password).isNotBlank();
});
});
Если вызывается:
suite(data, 'email');
проверяется только поле email.
createSuite поддерживает обычный JavaScript-код.
const suite = create((data) => {
if (data.accountType === 'company') {
test('companyName', 'Введите название компании', () => {
enforce(data.companyName).isNotBlank();
});
}
});
Это важное отличие Vest от декларативных схемных валидаторов.
const suite = create((data) => {
if (data.isAdmin) {
test('accessLevel', 'Укажите уровень доступа', () => {
enforce(data.accessLevel).isNotBlank();
});
if (data.superAdmin) {
test('secretKey', 'Нужен секретный ключ', () => {
enforce(data.secretKey).isNotBlank();
});
}
}
});
create поддерживает async/await.
const suite = create((data) => {
test(
'email',
'Email уже используется',
async () => {
const exists = await checkEmail(data.email);
enforce(exists).isFalsy();
}
);
});
Во время выполнения:
result.isPending('email');
вернёт true.
Асинхронные проверки внутри suite выполняются независимо.
const suite = create((data) => {
test('email', 'Email занят', async () => {
await verifyEmail(data.email);
});
test('username', 'Имя занято', async () => {
await verifyUsername(data.username);
});
});
Vest управляет состоянием каждого теста отдельно.
create позволяет логически группировать проверки.
const suite = create((data) => {
if (data.step === 1) {
test('name', 'Введите имя', () => {
enforce(data.name).isNotBlank();
});
}
if (data.step === 2) {
test('address', 'Введите адрес', () => {
enforce(data.address).isNotBlank();
});
}
});
Подобная структура особенно полезна в многошаговых формах.
skipНекоторые проверки можно пропускать.
import { create, test, skip } from 'vest';
const suite = create((data) => {
skip(data.disablePasswordValidation, () => {
test('password', 'Пароль обязателен', () => {
enforce(data.password).isNotBlank();
});
});
});
omitWhenomitWhen полностью исключает тесты из выполнения.
import { create, omitWhen } from 'vest';
const suite = create((data) => {
omitWhen(data.isGuest, () => {
test('passport', 'Введите паспортные данные', () => {
enforce(data.passport).isNotBlank();
});
});
});
Разница между skip и omitWhen:
| Механизм | Тест создаётся | Тест выполняется |
|---|---|---|
skip |
Да | Нет |
omitWhen |
Нет | Нет |
warnПомимо ошибок suite поддерживает предупреждения.
import { create, test, warn } from 'vest';
const suite = create((data) => {
warn();
test('password', 'Пароль слишком простой', () => {
enforce(data.password).longerThan(6);
});
});
Проверка предупреждений:
result.hasWarnings();
Поскольку внутри create используется обычный JavaScript,
правила можно создавать динамически.
const fields = ['firstName', 'lastName', 'city'];
const suite = create((data) => {
fields.forEach((field) => {
test(field, `${field} обязательно`, () => {
enforce(data[field]).isNotBlank();
});
});
});
const suite = create((data) => {
for (const phone of data.phones) {
test(`phone_${phone.id}`, 'Некорректный номер', () => {
enforce(phone.number).matches(/^\d+$/);
});
}
});
Крупные suites обычно разделяются на функции.
function validateProfile(data) {
test('firstName', 'Введите имя', () => {
enforce(data.firstName).isNotBlank();
});
test('lastName', 'Введите фамилию', () => {
enforce(data.lastName).isNotBlank();
});
}
function validateSecurity(data) {
test('password', 'Пароль слишком короткий', () => {
enforce(data.password).longerThan(7);
});
}
const suite = create((data) => {
validateProfile(data);
validateSecurity(data);
});
Suite может использовать внешние переменные.
const blockedDomains = ['spam.com'];
const suite = create((data) => {
test('email', 'Домен запрещён', () => {
const domain = data.email.split('@')[1];
enforce(blockedDomains.includes(domain)).isFalsy();
});
});
Если внутри теста выбрасывается ошибка, Vest интерпретирует это как failed validation.
test('email', 'Email обязателен', () => {
if (!data.email) {
throw new Error();
}
});
Однако предпочтительнее использовать enforce.
Каждый вызов create создаёт независимый validation
context.
const loginSuite = create(() => {});
const registerSuite = create(() => {});
Их состояния не пересекаются.
Некоторые сценарии требуют сброса состояния suite.
suite.reset();
После reset:
onlyonly ограничивает набор выполняемых тестов.
const suite = create((data, fieldName) => {
only(fieldName);
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/@/);
});
});
Это критически важно для производительности больших форм.
createVest использует несколько оптимизаций:
Поэтому create подходит для крупных интерфейсов с
большим количеством полей.
Пример полноценного suite:
import {
create,
test,
enforce,
only,
omitWhen,
} from 'vest';
const registrationSuite = create((data, currentField) => {
only(currentField);
test('email', 'Введите email', () => {
enforce(data.email).isNotBlank();
enforce(data.email).matches(/@/);
});
test('password', 'Минимум 8 символов', () => {
enforce(data.password).longerThanOrEquals(8);
});
test('confirmPassword', 'Пароли не совпадают', () => {
enforce(data.confirmPassword).equals(data.password);
});
omitWhen(!data.isCompany, () => {
test('companyName', 'Введите название компании', () => {
enforce(data.companyName).isNotBlank();
});
});
});
Использование:
const result = registrationSuite(formData, 'email');
Проверка состояния:
result.hasErrors('email');
result.getErrors('email');
result.isValid();
Распространённая структура:
validators/
├── authSuite.js
├── profileSuite.js
├── paymentSuite.js
└── shared/
├── emailRules.js
└── passwordRules.js
Пример переиспользуемых правил:
export function emailRules(value) {
enforce(value).isNotBlank();
enforce(value).matches(/@/);
}
Использование:
test('email', 'Некорректный email', () => {
emailRules(data.email);
});
createПлохо:
function Component() {
const suite = create(() => {});
}
Каждый рендер создаёт новый state container.
Правильно:
const suite = create(() => {});
function Component() {
}
Плохо:
suite(data);
на каждое изменение поля.
Лучше:
suite(data, fieldName);
Плохо:
test('price', 'Ошибка', () => {
saveToDatabase();
});
Тесты должны оставаться чистыми и предсказуемыми.
create не зависит от конкретного UI.
Используется с:
Пример для React:
const result = suite(formData, changedField);
if (result.hasErrors('email')) {
setError(result.getErrors('email')[0]);
}
Каждый вызов проходит несколько этапов:
create;Такой подход делает create не просто функцией запуска
тестов, а полноценным orchestration-механизмом системы валидации.