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