Haunted — это библиотека для создания веб-компонентов с использованием реактивного подхода через хуки, похожие на React. Тестирование хуков в Haunted требует понимания их жизненного цикла, взаимодействия с компонентами и особенностей асинхронного обновления состояния.
@open-wc/testing для HauntedДля тестирования Haunted-компонентов чаще всего применяются
инструменты из экосистемы Web Components, такие как
@open-wc/testing. Она предоставляет удобные функции:
fixture(html``) — создание тестового экземпляра
компонента в DOM.expect() — assertion-библиотека с chai-подобным
синтаксисом.aTimeout(ms) — ожидание асинхронных обновлений
DOM.Пример базового теста компонента с хуком:
import { fixture, expect, html, aTimeout } from '@open-wc/testing';
import './my-component.js';
it('инициализирует состояние с useState', async () => {
const el = await fixture(html`<my-component></my-component>`);
// Ожидание обновления DOM после инициализации
await aTimeout(0);
const content = el.shadowRoot.textContent;
expect(content).to.include('Состояние: 0');
});
Ключевой момент: хуки Haunted обновляют DOM
асинхронно, поэтому важно использовать задержку
(aTimeout) или updateComplete, чтобы
гарантировать актуальное состояние перед проверкой.
useState возвращает текущее состояние и функцию для его
изменения. Проверка работы useState обычно строится на:
Пример:
import { html } from 'haunted';
import { useState } from 'haunted';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div>Счёт: ${count}</div>
<button @click=${() => setCount(count + 1)}>Увеличить</button>
`;
}
customElements.define('x-counter', Counter);
Тест:
it('увеличивает счет после клика', async () => {
const el = await fixture(html`<x-counter></x-counter>`);
const button = el.shadowRoot.querySelector('button');
button.click();
await aTimeout(0);
expect(el.shadowRoot.textContent).to.include('Счёт: 1');
});
useEffect в Haunted выполняется после рендера, useLayoutEffect — синхронно перед отображением. При тестировании это важно для контроля обновлений и побочных эффектов.
Пример теста для useEffect:
import { useEffect, useState } from 'haunted';
function FetchComponent() {
const [data, setData] = useState(null);
useEffect(() => {
setTimeout(() => setData('Данные загружены'), 50);
}, []);
return html`<div>${data || 'Загрузка...'}</div>`;
}
customElements.define('x-fetch', FetchComponent);
Тестирование:
it('загружает данные после эффекта', async () => {
const el = await fixture(html`<x-fetch></x-fetch>`);
expect(el.shadowRoot.textContent).to.equal('Загрузка...');
await aTimeout(60);
expect(el.shadowRoot.textContent).to.equal('Данные загружены');
});
Для хуков, выполняющих HTTP-запросы или сложные побочные эффекты,
рекомендуется использовать моки и
spy-функции. Например, можно подменять
fetch или использовать sinon для отслеживания
вызовов.
import sinon from 'sinon';
it('вызывает fetch один раз', async () => {
const fetchStub = sinon.stub(window, 'fetch').resolves({ json: () => ({}) });
const el = await fixture(html`<x-fetch></x-fetch>`);
await aTimeout(0);
sinon.assert.calledOnce(fetchStub);
fetchStub.restore();
});
Haunted позволяет создавать собственные хуки. При тестировании кастомного хука важно проверять:
Пример простого кастомного хука:
import { useState, useEffect } from 'haunted';
function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
const increment = () => setCount(c => c + 1);
useEffect(() => {
console.log('Счётчик изменён:', count);
}, [count]);
return [count, increment];
}
Тест:
it('кастомный хук изменяет значение', async () => {
const el = await fixture(html`<x-counter></x-counter>`);
const [count, increment] = el.counterHook;
expect(count).to.equal(0);
increment();
await aTimeout(0);
expect(el.counterHook[0]).to.equal(1);
});
aTimeout или updateComplete
для ожидания обновлений.@testing-library с HauntedМожно интегрировать @testing-library/dom для более
«реалистичного» взаимодействия:
import { screen, fireEvent } from '@testing-library/dom';
import '@testing-library/jest-dom';
import { fixture, html } from '@open-wc/testing';
it('увеличивает счет при клике (Testing Library)', async () => {
await fixture(html`<x-counter></x-counter>`);
const button = screen.getByText('Увеличить');
fireEvent.click(button);
await aTimeout(0);
expect(screen.getByText('Счёт: 1')).toBeInTheDocument();
});
Такой подход упрощает тесты интерактивных компонентов и делает их ближе к реальному пользовательскому поведению.
Тестирование хуков в Haunted сочетает методы unit- и интеграционных тестов для веб-компонентов. Контроль асинхронности, правильное ожидание рендера и изоляция побочных эффектов обеспечивают стабильность тестов и точное покрытие поведения хуков.