Перейти к содержимому

27. Тестирование

Надёжный проект без тестов — это не надёжный проект. Astro хорошо вписывается в современную экосистему тестирования: Vitest для юнит-тестов, Testing Library для React-островов и Playwright для end-to-end сценариев.

Vitest — это быстрый тест-раннер, совместимый с Jest API. Устанавливаем:

Окно терминала
npm install -D vitest @vitest/ui

Настройка в astro.config.mjs:

import { defineConfig } from 'astro/config';
export default defineConfig({
vite: {
test: {
globals: true, // describe, it, expect без импорта
environment: 'node', // или 'happy-dom' для DOM-тестов
},
},
});

Пример юнит-теста для утилитарной функции:

src/utils/slug.test.ts
import { describe, it, expect } from 'vitest';
import { generateSlug, truncate, formatDate } from './helpers';
describe('generateSlug', () => {
it('переводит в нижний регистр', () => {
expect(generateSlug('Hello World')).toBe('hello-world');
});
it('заменяет пробелы на дефисы', () => {
expect(generateSlug('astro web framework')).toBe('astro-web-framework');
});
it('удаляет специальные символы', () => {
expect(generateSlug('TypeScript: глубокое погружение!')).toBe('typescript-glubokoe-pogruzhenie');
});
});
describe('truncate', () => {
it('обрезает текст до указанной длины', () => {
expect(truncate('Long text here', 8)).toBe('Long tex...');
});
it('не обрезает короткий текст', () => {
expect(truncate('Short', 100)).toBe('Short');
});
});

Официальный инструмент для проверки TypeScript в .astro файлах:

Окно терминала
npm install -D @astrojs/check typescript

Добавляем скрипт в package.json:

{
"scripts": {
"check": "astro check",
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage"
}
}

Запуск: npm run check выведет все TypeScript ошибки в .astro файлах.

Окно терминала
npm install -D @testing-library/react @testing-library/jest-dom happy-dom

Конфигурируем среду для DOM-тестов:

vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'happy-dom',
setupFiles: ['./src/test/setup.ts'],
globals: true,
},
});
src/test/setup.ts
import '@testing-library/jest-dom';

Пишем тест для React-компонента:

src/components/Counter.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';
describe('Counter', () => {
it('отображает начальное значение', () => {
render(<Counter initialValue={5} />);
expect(screen.getByText('5')).toBeInTheDocument();
});
it('увеличивает счётчик при клике', () => {
render(<Counter initialValue={0} />);
fireEvent.click(screen.getByRole('button', { name: /увеличить/i }));
expect(screen.getByText('1')).toBeInTheDocument();
});
it('не уходит ниже нуля', () => {
render(<Counter initialValue={0} />);
fireEvent.click(screen.getByRole('button', { name: /уменьшить/i }));
expect(screen.getByText('0')).toBeInTheDocument();
});
});

Playwright тестирует реальный браузер. Устанавливаем:

Окно терминала
npm init playwright@latest

Пример e2e теста:

tests/blog.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Страница блога', () => {
test('показывает список статей', async ({ page }) => {
await page.goto('/blog');
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
const articles = page.getByRole('article');
await expect(articles).toHaveCount(10);
});
test('открывает статью по клику', async ({ page }) => {
await page.goto('/blog');
const firstPost = page.getByRole('article').first();
const title = await firstPost.getByRole('heading').textContent();
await firstPost.getByRole('link').click();
await expect(page.getByRole('heading', { level: 1 })).toHaveText(title!);
});
test('работает поиск', async ({ page }) => {
await page.goto('/blog');
await page.getByRole('searchbox').fill('astro');
await expect(page.getByRole('article')).toHaveCount(3);
});
});
.github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run check # TypeScript
- run: npm run test:coverage # Vitest
- run: npm run build # Сборка
- run: npx playwright install --with-deps
- run: npx playwright test # E2E