Навигация по дереву компонентов

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

Основные методы для навигации

Enzyme поддерживает несколько методов для поиска и навигации по дереву компонентов. Эти методы позволяют искать как компоненты, так и DOM-элементы, взаимодействовать с ними и проверять их состояние.

1. .find(selector)

Метод .find() является основным инструментом для поиска элементов в компоненте или в его дереве. Он принимает селектор в качестве аргумента и возвращает все найденные элементы, соответствующие этому селектору.

Пример:

const wrapper = shallow(<MyComponent />);
const button = wrapper.find('button');
expect(button.length).toBe(1);

В этом примере метод .find('button') находит все элементы <button> внутри компонента MyComponent. Если таких элементов несколько, возвращается коллекция с ними.

Можно использовать как стандартные CSS-селекторы, так и кастомные компоненты:

const wrapper = shallow(<MyComponent />);
const customButton = wrapper.find(Button);
expect(customButton.exists()).toBe(true);
2. .children()

Метод .children() используется для нахождения всех дочерних элементов компонента. В отличие от .find(), который ищет элементы по всему дереву компонентов, .children() ограничивает поиск только прямыми потомками компонента.

Пример:

const wrapper = shallow(
  <div>
    <p>Test</p>
    <span>Another test</span>
  </div>
);
const children = wrapper.children();
expect(children.length).toBe(2);

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

3. .parent()

Метод .parent() используется для нахождения родительского компонента или элемента. Он возвращает первый родительский элемент, относительно которого был вызван метод.

Пример:

const wrapper = shallow(
  <div>
    <span>
      <button>Click me</button>
    </span>
  </div>
);
const button = wrapper.find('button');
const parent = button.parent();
expect(parent.is('span')).toBe(true);

В этом примере метод .parent() находит родительский элемент <span> для кнопки.

4. .parents()

Метод .parents() возвращает всех родителей элемента, начиная с самого ближайшего и заканчивая корневым элементом. Этот метод полезен, когда необходимо проверить несколько уровней родительских компонентов.

Пример:

const wrapper = shallow(
  <div>
    <section>
      <article>
        <p>Test</p>
      </article>
    </section>
  </div>
);
const paragraph = wrapper.find('p');
const parents = paragraph.parents();
expect(parents.length).toBe(3);

Здесь метод .parents() возвращает все элементы от ближайшего родителя <article> до самого внешнего контейнера <div>.

5. .closest(selector)

Метод .closest() ищет ближайший родительский элемент, который соответствует переданному селектору. Этот метод полезен, если необходимо найти не просто первого родителя, а того, кто соответствует конкретному условию.

Пример:

const wrapper = shallow(
  <div>
    <span>
      <button className="my-button">Click me</button>
    </span>
  </div>
);
const button = wrapper.find('button');
const closestDiv = button.closest('div');
expect(closestDiv.exists()).toBe(true);

В этом примере метод .closest() находит ближайший родительский элемент <div> для кнопки.

Работа с состоянием и пропсами

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

1. .state()

Метод .state() позволяет получить состояние компонента. Это особенно полезно, если компонент изменяет свое состояние в ответ на действия пользователя или изменения внешних данных.

Пример:

class MyComponent extends React.Component {
  state = { count: 0 };

  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return <button onCl ick={this.increment}>{this.state.count}</button>;
  }
}

const wrapper = shallow(<MyComponent />);
wrapper.find('button').simulate('click');
expect(wrapper.state().count).toBe(1);

Метод .state() позволяет проверить текущее состояние компонента после симуляции события.

2. .setProps()

Метод .setProps() позволяет изменять пропсы компонента во время теста, что помогает имитировать разные состояния компонента.

Пример:

const wrapper = shallow(<MyComponent title="Initial Title" />);
expect(wrapper.prop('title')).toBe('Initial Title');
wrapper.setProps({ title: 'Updated Title' });
expect(wrapper.prop('title')).toBe('Updated Title');

В этом примере пропс title изменяется с помощью .setProps(), и результат проверяется.

Сложная навигация и фильтрация

Enzyme предоставляет способы фильтрации и комбинирования методов поиска для более сложных случаев. Например, можно использовать .find() совместно с .filter() для выбора элементов, соответствующих определенным условиям.

1. .filter(selector)

Метод .filter() позволяет дополнительно фильтровать элементы, которые были найдены с помощью .find(). Это полезно, когда нужно сузить круг поиска, применив дополнительные условия.

Пример:

const wrapper = shallow(
  <div>
    <button className="primary">Primary</button>
    <button className="secondary">Secondary</button>
  </div>
);
const primaryButton = wrapper.find('button').filter('.primary');
expect(primaryButton.text()).toBe('Primary');

Здесь сначала с помощью .find() находим все кнопки, а затем фильтруем их по классу с помощью .filter().

2. Комбинирование методов

Можно комбинировать несколько методов навигации для более гибкого поиска элементов в дереве.

Пример:

const wrapper = shallow(
  <div>
    <section>
      <p className="first">First paragraph</p>
      <p className="second">Second paragraph</p>
    </section>
  </div>
);
const paragraph = wrapper.find('section').children().filter('.second');
expect(paragraph.text()).toBe('Second paragraph');

В этом примере сначала с помощью .find('section') находим контейнер, затем извлекаем все дочерние элементы с помощью .children() и фильтруем их по классу с помощью .filter().

Заключение

Навигация по дереву компонентов с помощью Enzyme дает широкие возможности для тестирования компонентов и их взаимодействий. Методы, такие как .find(), .children(), .parent(), и .state(), позволяют точно и эффективно проверять компоненты на всех уровнях их структуры. Использование этих методов в сочетании с фильтрацией и симуляцией событий помогает создавать детализированные и устойчивые тесты, которые повышают качество и надежность кода.