feat: Добавлены тесты React компонентов. Тесты базовой логики, Граничных тестов, стилей
This commit is contained in:
34
src/shared/ui/Button/Button.test.tsx
Normal file
34
src/shared/ui/Button/Button.test.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
|
||||
import { Button } from './Button'
|
||||
|
||||
describe('Button', () => {
|
||||
// Тест на рендеринг кнопки
|
||||
it('должна рендериться корректно', () => {
|
||||
render(<Button>Test Button</Button>)
|
||||
expect(screen.getByText('Test Button')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на применение класса варианта
|
||||
it('должна применять класс варианта', () => {
|
||||
render(<Button variant='regular'>Regular Button</Button>)
|
||||
const button = screen.getByText('Regular Button')
|
||||
// Проверяем наличие класса, который генерируется CSS модулями (частичное совпадение)
|
||||
expect(button.className).toMatch(/regular/)
|
||||
})
|
||||
|
||||
// Тест на обработку клика
|
||||
it('должна вызывать обработчик onClick при клике', () => {
|
||||
const handleClick = jest.fn()
|
||||
render(<Button onClick={handleClick}>Click Me</Button>)
|
||||
|
||||
fireEvent.click(screen.getByText('Click Me'))
|
||||
expect(handleClick).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
// Тест на отключенное состояние
|
||||
it('должна быть отключена при передаче пропса disabled', () => {
|
||||
render(<Button disabled>Disabled Button</Button>)
|
||||
expect(screen.getByText('Disabled Button')).toBeDisabled()
|
||||
})
|
||||
})
|
||||
18
src/shared/ui/Card/Card.test.tsx
Normal file
18
src/shared/ui/Card/Card.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
|
||||
import { Card } from './Card'
|
||||
|
||||
describe('Card', () => {
|
||||
// Тест на рендеринг заголовка и описания
|
||||
it('должна рендерить заголовок и описание', () => {
|
||||
render(<Card title='1992' description='Test Description' />)
|
||||
expect(screen.getByText('1992')).toBeInTheDocument()
|
||||
expect(screen.getByText('Test Description')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на рендеринг числового заголовка
|
||||
it('должна корректно рендерить числовой заголовок', () => {
|
||||
render(<Card title={2023} description='Year Description' />)
|
||||
expect(screen.getByText('2023')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,56 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import gsap from 'gsap'
|
||||
|
||||
import { HISTORICAL_PERIODS } from '@/entities/TimePeriod'
|
||||
|
||||
import { CircleTimeline } from './CircleTimeline'
|
||||
|
||||
describe('CircleTimeline', () => {
|
||||
const mockOnPeriodChange = jest.fn()
|
||||
const defaultProps = {
|
||||
periods: HISTORICAL_PERIODS,
|
||||
activeIndex: 0,
|
||||
onPeriodChange: mockOnPeriodChange,
|
||||
rotation: 0,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
// Тест на рендеринг правильного количества точек
|
||||
it('должна рендерить правильное количество точек', () => {
|
||||
render(<CircleTimeline {...defaultProps} />)
|
||||
const points = screen.getAllByRole('button')
|
||||
expect(points).toHaveLength(HISTORICAL_PERIODS.length)
|
||||
})
|
||||
|
||||
// Тест на активную точку
|
||||
it('должна корректно отображать активную точку', () => {
|
||||
render(<CircleTimeline {...defaultProps} activeIndex={1} />)
|
||||
const points = screen.getAllByRole('button')
|
||||
|
||||
// Проверяем aria-current для доступности
|
||||
expect(points[1]).toHaveAttribute('aria-current', 'true')
|
||||
expect(points[0]).toHaveAttribute('aria-current', 'false')
|
||||
})
|
||||
|
||||
// Тест на клик по точке
|
||||
it('должна вызывать onPeriodChange при клике по точке', () => {
|
||||
render(<CircleTimeline {...defaultProps} />)
|
||||
const points = screen.getAllByRole('button')
|
||||
|
||||
fireEvent.click(points[2])
|
||||
expect(mockOnPeriodChange).toHaveBeenCalledWith(2)
|
||||
})
|
||||
|
||||
// Тест на вызов GSAP анимации
|
||||
it('должна вызывать GSAP анимацию при изменении rotation', () => {
|
||||
const { rerender } = render(<CircleTimeline {...defaultProps} />)
|
||||
|
||||
rerender(<CircleTimeline {...defaultProps} rotation={60} />)
|
||||
|
||||
// Проверяем, что gsap.to был вызван
|
||||
expect(gsap.to).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
|
||||
import { EventsCarousel } from './EventsCarousel'
|
||||
|
||||
// Мокаем Swiper
|
||||
jest.mock('swiper/react', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Swiper: ({ children }: any) => <div data-testid='swiper'>{children}</div>,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
SwiperSlide: ({ children }: any) => (
|
||||
<div data-testid='swiper-slide'>{children}</div>
|
||||
),
|
||||
}))
|
||||
|
||||
jest.mock('swiper/modules', () => ({
|
||||
Navigation: jest.fn(),
|
||||
Pagination: jest.fn(),
|
||||
FreeMode: jest.fn(),
|
||||
}))
|
||||
|
||||
// Мокаем стили Swiper
|
||||
jest.mock('swiper/css', () => ({}))
|
||||
jest.mock('swiper/css/navigation', () => ({}))
|
||||
jest.mock('swiper/css/pagination', () => ({}))
|
||||
|
||||
describe('EventsCarousel', () => {
|
||||
const mockEvents = [
|
||||
{ id: 1, year: 1990, description: 'Event 1' },
|
||||
{ id: 2, year: 1991, description: 'Event 2' },
|
||||
]
|
||||
|
||||
// Тест на рендеринг событий
|
||||
it('должен рендерить переданные события', () => {
|
||||
render(<EventsCarousel events={mockEvents} visible={true} />)
|
||||
|
||||
// Проверяем, что слайды отрендерились
|
||||
const slides = screen.getAllByTestId('swiper-slide')
|
||||
expect(slides).toHaveLength(mockEvents.length)
|
||||
|
||||
// Проверяем контент внутри слайдов (Card компонент)
|
||||
expect(screen.getByText('1990')).toBeInTheDocument()
|
||||
expect(screen.getByText('Event 1')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на видимость
|
||||
it('должен применять класс visible, когда visible=true', () => {
|
||||
render(<EventsCarousel events={mockEvents} visible={true} />)
|
||||
// В реальном компоненте класс применяется к контейнеру Swiper или обертке
|
||||
// Здесь мы проверяем наличие контента, так как opacity управляется через CSS/GSAP
|
||||
expect(screen.getByTestId('swiper')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,82 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
|
||||
import { HISTORICAL_PERIODS } from '@/entities/TimePeriod'
|
||||
|
||||
import { TimeFrameSlider } from './TimeFrameSlider'
|
||||
|
||||
// Мокаем дочерние компоненты, чтобы тестировать изолированно
|
||||
jest.mock('../CircleTimeline/CircleTimeline', () => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
CircleTimeline: ({ activeIndex, onPeriodChange }: any) => (
|
||||
<div data-testid='circle-timeline'>
|
||||
<button onClick={() => onPeriodChange(activeIndex + 1)}>
|
||||
Next Period
|
||||
</button>
|
||||
<span>Active: {activeIndex}</span>
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
jest.mock('../EventsCarousel/EventsCarousel', () => ({
|
||||
EventsCarousel: () => <div data-testid='events-carousel'>Carousel</div>,
|
||||
}))
|
||||
|
||||
describe('TimeFrameSlider', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
// Тест на рендеринг заголовка
|
||||
it('должен рендерить заголовок', () => {
|
||||
render(<TimeFrameSlider />)
|
||||
expect(screen.getByText('Исторические даты')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на отображение начального периода
|
||||
it('должен отображать начальный период (первый в списке)', () => {
|
||||
render(<TimeFrameSlider />)
|
||||
const firstPeriod = HISTORICAL_PERIODS[0]
|
||||
expect(screen.getByText(firstPeriod.yearFrom)).toBeInTheDocument()
|
||||
expect(screen.getByText(firstPeriod.yearTo)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на переключение вперед
|
||||
it('должен переключать период вперед при клике на кнопку "Следующий"', () => {
|
||||
render(<TimeFrameSlider />)
|
||||
|
||||
const nextButton = screen.getByLabelText('Следующий период')
|
||||
fireEvent.click(nextButton)
|
||||
|
||||
// Проверяем, что отображается второй период
|
||||
const secondPeriod = HISTORICAL_PERIODS[1]
|
||||
expect(screen.getByText(secondPeriod.yearFrom)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на переключение назад
|
||||
it('должен переключать период назад при клике на кнопку "Предыдущий"', () => {
|
||||
render(<TimeFrameSlider />)
|
||||
|
||||
// Сначала переключаем вперед, чтобы не быть на первом элементе (хотя логика циклична)
|
||||
const nextButton = screen.getByLabelText('Следующий период')
|
||||
fireEvent.click(nextButton)
|
||||
|
||||
// Теперь назад
|
||||
const prevButton = screen.getByLabelText('Предыдущий период')
|
||||
fireEvent.click(prevButton)
|
||||
|
||||
// Должны вернуться к первому периоду
|
||||
const firstPeriod = HISTORICAL_PERIODS[0]
|
||||
expect(screen.getByText(firstPeriod.yearFrom)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Тест на пагинацию (точки)
|
||||
it('должен переключать период при клике на точку пагинации', () => {
|
||||
render(<TimeFrameSlider />)
|
||||
|
||||
const dots = screen.getAllByLabelText(/Перейти к периоду/)
|
||||
fireEvent.click(dots[2]) // Клик по 3-й точке
|
||||
|
||||
const thirdPeriod = HISTORICAL_PERIODS[2]
|
||||
expect(screen.getByText(thirdPeriod.yearFrom)).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user