Haunted — это библиотека для создания веб-компонентов с реактивным рендерингом, основанная на Hooks в стиле React. При работе с Haunted тестирование интеграции играет ключевую роль, поскольку компоненты часто взаимодействуют друг с другом через состояние, события и DOM.
Для тестирования интеграции в Haunted чаще всего используют
комбинацию Jest и Testing Library
(например, @testing-library/dom или
@testing-library/web-component). Основные шаги:
npm install --save-dev jest @testing-library/dom @testing-library/jest-dom
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
};
import { render } from 'haunted';
import { html } from 'lit-html';
export function renderComponent(Component, props = {}, container = document.body) {
const el = document.createElement('div');
container.appendChild(el);
render(html`<${Component} ...=${props}></${Component}>`, el);
return el;
}
Тестирование интеграции отличается от юнит-тестирования тем, что проверяется не изолированный компонент, а взаимодействие нескольких компонентов или компонентов с реальными DOM-элементами.
Haunted-компоненты часто обмениваются данными через кастомные события.
import { html, component, useState } from 'haunted';
function Child({ onNotify }) {
return html`<button @click=${() => onNotify('hello')}>Notify</button>`;
}
function Parent() {
const [message, setMessage] = useState('');
return html`
<child-component @notify=${e => setMessage(e.detail)}></child-component>
<span data-testid="msg">${message}</span>
`;
}
customElements.define('child-component', component(Child));
customElements.define('parent-component', component(Parent));
Тестирование:
import { screen, fireEvent } from '@testing-library/dom';
import { renderComponent } from './test-utils';
import './parent-component';
test('Parent получает событие от Child', () => {
renderComponent('parent-component');
const button = screen.getByText('Notify');
fireEvent.click(button);
const message = screen.getByTestId('msg');
expect(message.textContent).toBe('hello');
});
Haunted использует Hooks, поэтому интеграционное тестирование должно учитывать динамическое обновление состояния:
function Counter() {
const [count, setCount] = useState(0);
return html`
<span data-testid="count">${count}</span>
<button @click=${() => setCount(count + 1)}>Increment</button>
`;
}
customElements.define('counter-component', component(Counter));
Тестирование:
test('Счётчик корректно увеличивает значение', () => {
renderComponent('counter-component');
const button = screen.getByText('Increment');
const count = screen.getByTestId('count');
expect(count.textContent).toBe('0');
fireEvent.click(button);
expect(count.textContent).toBe('1');
});
Компоненты Haunted часто делают асинхронные обновления, например
через fetch или таймеры. Для интеграции важно корректно
обрабатывать асинхронность.
function AsyncComponent() {
const [data, setData] = useState('');
useEffect(() => {
setTimeout(() => setData('loaded'), 100);
}, []);
return html`<div data-testid="data">${data}</div>`;
}
customElements.define('async-component', component(AsyncComponent));
Тестирование с использованием waitFor:
import { waitFor } from '@testing-library/dom';
test('Асинхронное обновление данных', async () => {
renderComponent('async-component');
await waitFor(() => {
expect(screen.getByTestId('data').textContent).toBe('loaded');
});
});
Haunted-компоненты могут взаимодействовать с библиотеками вроде Redux, RxJS или сторонними API. В интеграционном тестировании важно:
Пример с RxJS:
import { BehaviorSubject } from 'rxjs';
const store$ = new BehaviorSubject(0);
function Display() {
const [value, setValue] = useState(0);
useEffect(() => {
const sub = store$.subscribe(setValue);
return () => sub.unsubscribe();
}, []);
return html`<span data-testid="value">${value}</span>`;
}
customElements.define('display-component', component(Display));
Тест:
test('Компонент корректно отображает данные из RxJS', () => {
renderComponent('display-component');
store$.next(42);
expect(screen.getByTestId('value').textContent).toBe('42');
});
Haunted использует Shadow DOM для изоляции стилей и структуры. Для интеграционных тестов важно:
shadowRoot.querySelector для
доступа к внутренним элементам.@testing-library/dom с within
для работы внутри Shadow DOM:import { within } from '@testing-library/dom';
const host = document.querySelector('parent-component');
const shadow = within(host.shadowRoot);
const button = shadow.getByText('Notify');
afterEach(() => document.body.innerHTML = '')).Эти практики позволяют создавать устойчивые и точные интеграционные тесты для Haunted-компонентов, обеспечивая корректную работу как отдельных элементов, так и их взаимодействий.