Stencil Testing Library

Stencil Testing Library представляет собой набор инструментов для тестирования компонентов, разработанных с использованием фреймворка Stencil. Она предоставляет мощные возможности для тестирования веб-компонентов, фокусируясь на их поведении и взаимодействиях, а не на реализации. Эта библиотека использует знакомый API, схожий с популярной Testing Library для React, но адаптирован для работы с веб-компонентами, что позволяет создавать более стабильные и поддерживаемые тесты.

Установка и настройка

Для начала необходимо установить Stencil Testing Library. Она входит в состав большинства шаблонов Stencil, но если вам нужно установить её вручную, это можно сделать через npm:

npm install @stencil/core @stencil/testing

После установки Stencil Testing Library необходимо настроить тестовую среду. Stencil использует Jest для выполнения тестов, поэтому необходимо убедиться, что Jest и необходимые зависимости для работы с компонентами установлены.

npm install --save-dev jest @stencil/core @testing-library/jest-dom

Кроме того, для корректной работы тестов следует настроить jest.config.js:

module.exports = {
  preset: '@stencil/core/testing',
};

После выполнения этих шагов Stencil Testing Library готова к использованию.

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

Stencil Testing Library ориентирован на поведение компонентов. Это означает, что тесты должны фокусироваться на взаимодействиях с компонентами, а не на их внутренней реализации. Тесты обычно пишутся в формате, схожем с тестами для других JavaScript фреймворков, например, React, с использованием библиотеки Jest и API Testing Library.

Написание первого теста

Для начала создадим простейший компонент и напишем для него тест.

Пример компонента:

@Component({
  tag: 'my-button',
  styleUrl: 'my-button.css',
  shadow: true,
})
export class MyButton {
  @Prop() text: string;

  render() {
    return <button>{this.text}</button>;
  }
}

Для тестирования компонента, который рендерит простую кнопку, можно использовать следующий код:

import { newSpecPage } from '@stencil/core/testing';
import { MyButton } from './my-button';

describe('my-button', () => {
  it('renders the correct text', async () => {
    const page = await newSpecPage({
      components: [MyButton],
      html: `<my-button text="Click me"></my-button>`,
    });
    expect(page.root).toEqualHtml(`
      <my-button text="Click me">
        <button>Click me</button>
      </my-button>
    `);
  });
});

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

Мокирование пропсов и событий

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

Мокирование пропса

it('renders the button with the provided text', async () => {
  const page = await newSpecPage({
    components: [MyButton],
    html: `<my-button text="Submit"></my-button>`,
  });
  expect(page.root.textContent).toBe('Submit');
});

В этом тесте мокируется пропс text, и проверяется, что текст кнопки соответствует переданному значению.

Тестирование событий

Stencil поддерживает создание пользовательских событий через механизм EventEmitter. Например, для тестирования события можно сделать следующее:

@Component({
  tag: 'my-button',
  styleUrl: 'my-button.css',
  shadow: true,
})
export class MyButton {
  @Event() buttonClicked: EventEmitter;

  @Method()
  async clickButton() {
    this.buttonClicked.emit();
  }

  render() {
    return <button onCl ick={() => this.clickButton()}>Click me</button>;
  }
}

Тестирование этого компонента с событием можно выполнить следующим образом:

import { newSpecPage } from '@stencil/core/testing';
import { MyButton } from './my-button';

describe('my-button', () => {
  it('emits buttonClicked event when clicked', async () => {
    const page = await newSpecPage({
      components: [MyButton],
      html: `<my-button></my-button>`,
    });
    const button = page.root.shadowRoot.querySelector('button');
    const spy = jest.fn();
    page.root.addEventListener('buttonClicked', spy);

    button.click();
    await page.waitForChanges();

    expect(spy).toHaveBeenCalled();
  });
});

В этом тесте создается шпион (spy) на событие buttonClicked, и проверяется, что оно вызывается при клике на кнопку.

Асинхронные тесты

Stencil Testing Library поддерживает асинхронные тесты с помощью стандартных возможностей Jest. Когда компоненты требуют асинхронного рендеринга (например, при ожидании загрузки данных или асинхронных операций), можно использовать методы await и waitForChanges.

Пример асинхронного теста:

it('loads data asynchronously', async () => {
  const page = await newSpecPage({
    components: [MyButton],
    html: `<my-button></my-button>`,
  });

  page.root.text = 'Loading...';
  await page.waitForChanges();

  page.root.text = 'Data loaded';
  await page.waitForChanges();

  expect(page.root.textContent).toBe('Data loaded');
});

Здесь waitForChanges используется для ожидания завершения асинхронных изменений в компоненте.

Стратегии тестирования стилей и Shadow DOM

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

Пример теста, который проверяет стили компонента:

it('applies styles correctly', async () => {
  const page = await newSpecPage({
    components: [MyButton],
    html: `<my-button></my-button>`,
  });
  const button = page.root.shadowRoot.querySelector('button');
  expect(button).toHaveStyle({ 'background-color': 'blue' });
});

Этот тест проверяет, что кнопка внутри компонента имеет правильный стиль.

Моки и шпионы для внешних зависимостей

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

Пример использования мока для HTTP-запроса:

it('fetches data from API', async () => {
  const mockFetch = jest.fn().mockResolvedValue({
    json: jest.fn().mockResolvedValue({ data: 'test' }),
  });
  global.fetch = mockFetch;

  const page = await newSpecPage({
    components: [MyButton],
    html: `<my-button></my-button>`,
  });

  await page.root.loadData();
  await page.waitForChanges();

  expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data');
});

Здесь fetch мокается с помощью Jest, и проверяется, что запрос был отправлен на правильный URL.

Интеграционные тесты

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

Пример интеграционного теста:

it('interacts between components', async () => {
  const page = await newSpecPage({
    components: [MyButton, MyOtherComponent],
    html: `<my-button></my-button><my-other-component></my-other-component>`,
  });

  const button = page.root.querySelector('my-button');
  const otherComponent = page.root.querySelector('my-other-component');

  button.click();
  await page.waitForChanges();

  expect(otherComponent.someProperty).toBe(true);
});

Здесь проверяется взаимодействие между кнопкой и другим компонентом при выполнении действия.

Заключение

Stencil Testing Library предоставляет мощные инструменты для тестирования компонентов в экосистеме Stencil. С помощью Jest и API, аналогичного Testing Library, можно легко создавать надежные и поддерживаемые тесты для компонентов, обеспечивая проверку их поведения, стилей, событий и взаимодействий с другими компонентами.