Снэпшот-тестирование

Снэпшот-тестирование позволяет зафиксировать текущее состояние компонента и автоматически проверять, не изменился ли его вывод при последующих изменениях кода. В контексте Haunted это особенно важно, поскольку библиотека построена на Web Components и реактивных хуках, а визуальное представление компонента часто зависит от состояния и пропсов.


Настройка окружения

Для снэпшот-тестирования компонентов Haunted чаще всего используют комбинацию:

  • Mocha или Jest для организации тестов.
  • @open-wc/testing для облегчения работы с веб-компонентами.
  • chai для утверждений.

Пример установки необходимых пакетов:

npm install --save-dev @open-wc/testing mocha chai

Для Jest потребуется также настроить поддержку Web Components через jsdom.


Подготовка компонента к тестированию

Haunted-компонент определяется функцией и регистрируется как кастомный элемент:

import { html, component } from 'haunted';

function MyButton({ label }) {
  return html`<button>${label}</button>`;
}

customElements.define('my-button', component(MyButton));

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


Снэпшот-тест с использованием @open-wc/testing

import { fixture, html } from '@open-wc/testing';
import { expect } from 'chai';
import './my-button.js';

describe('MyButton', () => {
  it('должен совпадать со снэпшотом', async () => {
    const el = await fixture(html`<my-button label="Нажми"></my-button>`);
    expect(el).shadowDom.to.equalSnapshot();
  });
});

Ключевые моменты:

  • fixture создаёт изолированную среду для компонента.
  • shadowDom.to.equalSnapshot() сохраняет и сравнивает текущее состояние Shadow DOM с предыдущим снэпшотом.
  • Снэпшоты хранятся автоматически, при изменении визуального представления тест будет падать, сигнализируя о непреднамеренных изменениях.

Тестирование компонентов с состоянием

Haunted использует хуки, такие как useState и useEffect. Для тестирования состояния важно правильно обновлять пропсы и имитировать взаимодействие:

import { useState } from 'haunted';

function Counter() {
  const [count, setCount] = useState(0);
  return html`
    <button @click=${() => setCount(count + 1)}>${count}</button>
  `;
}

customElements.define('my-counter', component(Counter));

Тестирование снэпшота с изменением состояния:

it('снэпшот после клика', async () => {
  const el = await fixture(html`<my-counter></my-counter>`);
  el.shadowRoot.querySelector('button').click();
  await el.updateComplete; // ожидание обновления компонента
  expect(el).shadowDom.to.equalSnapshot();
});

Особенности Haunted:

  • updateComplete не является встроенным API, но можно использовать промис на requestUpdate или Promise.resolve() для ожидания следующего рендера.
  • Каждый вызов setState инициирует повторный рендер компонента, который должен быть зафиксирован в снэпшоте.

Моки и внешние зависимости

Для компонентов, взаимодействующих с внешними API, рекомендуется:

  • Подменять fetch или XMLHttpRequest.
  • Использовать фиктивные данные.
  • Изолировать компонент от глобальных состояний и браузерных API, чтобы снэпшоты оставались стабильными.

Пример подмены данных:

function UserCard({ user }) {
  return html`<div>${user.name}</div>`;
}

const mockUser = { name: 'Иван' };
const el = await fixture(html`<user-card .user=${mockUser}></user-card>`);
expect(el).shadowDom.to.equalSnapshot();

Практические советы

  • Снэпшоты должны хранить только разметку, а не динамические данные вроде временных меток.
  • Для крупных компонентов можно использовать фрагменты снэпшотов, чтобы изолировать тестируемые части.
  • Регулярно пересматривать снэпшоты, чтобы не сохранять устаревшие изменения.
  • Комбинировать с юнит-тестами функций и хуков — снэпшоты показывают визуальные изменения, но не проверяют логику.

Снэпшот-тестирование в Haunted позволяет обеспечить стабильность UI, отлавливать непреднамеренные изменения в Shadow DOM и работать с реактивными хуками без сложной мок-среды. Оно становится эффективным инструментом при разработке сложных компонентов с состоянием и взаимодействием.