Динамические правила

Динамические правила в Vest представляют собой механизм условного формирования и выполнения валидационных тестов в зависимости от текущего состояния данных, внешних параметров или значений других полей. Такой подход позволяет строить адаптивные схемы проверки, которые изменяются во время выполнения, а не фиксируются заранее.

Контекст выполнения тестов

В основе работы Vest лежит декларативная модель, в которой набор проверок описывается через suite. Каждое правило оформляется как тест, выполняемый в рамках текущего контекста данных.

Контекст включает:

  • текущее состояние проверяемого объекта;
  • значения отдельных полей;
  • внешние параметры, переданные в момент запуска валидации;
  • результаты ранее выполненных тестов.

Динамическое поведение достигается за счёт того, что выполнение тестов может быть условно включено или исключено на основании этого контекста.

Условная активация тестов

Основной механизм динамических правил в Vest основан на условном выполнении тестов. На практике это реализуется через функции условного контроля выполнения, позволяющие пропускать или включать проверки.

Типовой подход:

import { test, skip } from "vest";

test("username", "username is required", () => {
  if (!get().username) {
    skip();
  }

  expect(get().username).toBeTruthy();
});

В этом примере логика проверки зависит от наличия значения. Если поле отсутствует, тест не выполняется как строгая проверка, а его поведение изменяется.

Такой механизм позволяет:

  • избегать лишних ошибок валидации;
  • строить гибкие цепочки проверок;
  • управлять зависимостями между полями.

Использование состояния для динамики

Vest поддерживает внешнее состояние, которое может влиять на правила валидации. Это состояние передаётся в suite и доступно внутри тестов.

import { create, test } from "vest";

const suite = create((data, config) => {
  test("password", "too short", () => {
    if (config.strictMode) {
      expect(data.password).toHaveLengthGreaterThan(10);
    } else {
      expect(data.password).toHaveLengthGreaterThan(6);
    }
  });
});

Здесь поведение проверки зависит от параметра strictMode. Таким образом, одна и та же схема тестов может адаптироваться к различным сценариям использования.

Динамическое состояние используется для:

  • переключения режимов валидации;
  • адаптации под разные формы ввода;
  • реализации бизнес-логики проверки.

Зависимости между полями

Одной из ключевых областей применения динамических правил является обработка зависимых полей. Валидация одного поля может зависеть от значения другого.

test("confirmPassword", "passwords do not match", () => {
  if (get().password) {
    expect(get().confirmPassword).toBe(get().password);
  } else {
    skip();
  }
});

Здесь правило становится активным только при наличии базового значения. Такая модель позволяет:

  • избегать некорректных сравнений;
  • строить каскадные проверки;
  • управлять логикой форм с зависимыми полями.

Динамическое включение и исключение тестов

Vest позволяет реализовывать выборочное выполнение тестов через условные конструкции внутри suite. Это особенно важно при сложных формах, где часть полей появляется только при определённых условиях.

const suite = create((data) => {
  if (data.type === "business") {
    test("companyName", "required", () => {
      expect(data.companyName).toBeTruthy();
    });
  }

  if (data.type === "individual") {
    test("firstName", "required", () => {
      expect(data.firstName).toBeTruthy();
    });
  }
});

Такой подход формирует динамическую структуру валидации, которая изменяется в зависимости от входных данных.

Ключевые особенности:

  • тесты создаются только при выполнении условий;
  • исключённые правила не влияют на результат;
  • структура suite становится контекстно-зависимой.

Использование вычисляемых условий

Динамические правила часто строятся на основе вычисляемых условий, которые могут включать сложные выражения.

const suite = create((data) => {
  const isAdult = data.age >= 18;
  const hasConsent = data.consent === true;

  test("access", "not allowed", () => {
    if (!isAdult && !hasConsent) {
      expect(false).toBe(true);
    }
  });
});

Такая модель позволяет переносить бизнес-логику в слой валидации, сохраняя при этом читаемость структуры.

Комбинирование динамических правил

В реальных сценариях динамические правила редко используются изолированно. Чаще применяется комбинация условий, состояния и зависимостей.

const suite = create((data, config) => {
  const requiresAddress = config.mode === "shipping";

  if (requiresAddress) {
    test("address", "required", () => {
      expect(data.address).toBeTruthy();
    });

    test("postalCode", "invalid", () => {
      expect(data.postalCode).toMatch(/^\d{6}$/);
    });
  }

  if (data.country === "local") {
    test("region", "required", () => {
      expect(data.region).toBeTruthy();
    });
  }
});

Здесь одновременно используются:

  • внешний конфигурационный режим;
  • значения полей данных;
  • условное включение групп тестов.

Паттерны построения динамических правил

Паттерн переключаемых секций

Используется для форм, где разные блоки данных активируются по типу сущности.

if (data.mode === "A") {
  // набор тестов A
}

if (data.mode === "B") {
  // набор тестов B
}

Паттерн зависимых цепочек

Один тест активируется только при прохождении другого условия.

test("discount", "invalid", () => {
  if (data.coupon) {
    expect(data.discount).toBeGreaterThan(0);
  }
});

Паттерн конфигурационной адаптации

Логика валидации изменяется через внешний конфиг.

const strict = config.strict;

test("field", "invalid", () => {
  if (strict) {
    expect(data.field).toMatchStrictRule();
  } else {
    expect(data.field).toMatchLooseRule();
  }
});

Оптимизация динамических правил

При использовании динамических правил важно учитывать стоимость пересоздания suite и количество условных ветвлений.

Основные принципы:

  • минимизация вложенных условий внутри тестов;
  • перенос логики выбора тестов на уровень suite;
  • исключение лишних вычислений при отсутствии необходимости;
  • группировка однотипных условий.

Неэффективный подход:

test("field", () => {
  if (a) {
    if (b) {
      if (c) {
        // проверка
      }
    }
  }
});

Более оптимальный подход:

if (a && b && c) {
  test("field", () => {
    // проверка
  });
}

Сложные сценарии динамической валидации

В сложных формах динамические правила могут строиться как комбинация нескольких уровней условий: глобального режима, состояния формы и локальных зависимостей.

const suite = create((data, config) => {
  const isEditable = config.mode !== "readonly";

  if (!isEditable) return;

  if (data.step >= 2) {
    test("email", "invalid", () => {
      expect(data.email).toContain("@");
    });
  }

  if (data.step >= 3 && data.subscribed) {
    test("preferences", "required", () => {
      expect(data.preferences).toBeDefined();
    });
  }
});

Такая структура позволяет моделировать многоступенчатые формы, где каждая стадия активирует свой набор проверок, а правила изменяются по мере заполнения данных.