diff --git a/src/shared/ui/Button/Button.test.tsx b/src/shared/ui/Button/Button.test.tsx
new file mode 100644
index 0000000..bb0b329
--- /dev/null
+++ b/src/shared/ui/Button/Button.test.tsx
@@ -0,0 +1,34 @@
+import { fireEvent, render, screen } from '@testing-library/react'
+
+import { Button } from './Button'
+
+describe('Button', () => {
+ // Тест на рендеринг кнопки
+ it('должна рендериться корректно', () => {
+ render()
+ expect(screen.getByText('Test Button')).toBeInTheDocument()
+ })
+
+ // Тест на применение класса варианта
+ it('должна применять класс варианта', () => {
+ render()
+ const button = screen.getByText('Regular Button')
+ // Проверяем наличие класса, который генерируется CSS модулями (частичное совпадение)
+ expect(button.className).toMatch(/regular/)
+ })
+
+ // Тест на обработку клика
+ it('должна вызывать обработчик onClick при клике', () => {
+ const handleClick = jest.fn()
+ render()
+
+ fireEvent.click(screen.getByText('Click Me'))
+ expect(handleClick).toHaveBeenCalledTimes(1)
+ })
+
+ // Тест на отключенное состояние
+ it('должна быть отключена при передаче пропса disabled', () => {
+ render()
+ expect(screen.getByText('Disabled Button')).toBeDisabled()
+ })
+})
diff --git a/src/shared/ui/Card/Card.test.tsx b/src/shared/ui/Card/Card.test.tsx
new file mode 100644
index 0000000..e823dab
--- /dev/null
+++ b/src/shared/ui/Card/Card.test.tsx
@@ -0,0 +1,18 @@
+import { render, screen } from '@testing-library/react'
+
+import { Card } from './Card'
+
+describe('Card', () => {
+ // Тест на рендеринг заголовка и описания
+ it('должна рендерить заголовок и описание', () => {
+ render()
+ expect(screen.getByText('1992')).toBeInTheDocument()
+ expect(screen.getByText('Test Description')).toBeInTheDocument()
+ })
+
+ // Тест на рендеринг числового заголовка
+ it('должна корректно рендерить числовой заголовок', () => {
+ render()
+ expect(screen.getByText('2023')).toBeInTheDocument()
+ })
+})
diff --git a/src/widgets/TimeFrameSlider/ui/CircleTimeline/CircleTimeline.test.tsx b/src/widgets/TimeFrameSlider/ui/CircleTimeline/CircleTimeline.test.tsx
new file mode 100644
index 0000000..60da310
--- /dev/null
+++ b/src/widgets/TimeFrameSlider/ui/CircleTimeline/CircleTimeline.test.tsx
@@ -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()
+ const points = screen.getAllByRole('button')
+ expect(points).toHaveLength(HISTORICAL_PERIODS.length)
+ })
+
+ // Тест на активную точку
+ it('должна корректно отображать активную точку', () => {
+ render()
+ const points = screen.getAllByRole('button')
+
+ // Проверяем aria-current для доступности
+ expect(points[1]).toHaveAttribute('aria-current', 'true')
+ expect(points[0]).toHaveAttribute('aria-current', 'false')
+ })
+
+ // Тест на клик по точке
+ it('должна вызывать onPeriodChange при клике по точке', () => {
+ render()
+ const points = screen.getAllByRole('button')
+
+ fireEvent.click(points[2])
+ expect(mockOnPeriodChange).toHaveBeenCalledWith(2)
+ })
+
+ // Тест на вызов GSAP анимации
+ it('должна вызывать GSAP анимацию при изменении rotation', () => {
+ const { rerender } = render()
+
+ rerender()
+
+ // Проверяем, что gsap.to был вызван
+ expect(gsap.to).toHaveBeenCalled()
+ })
+})
diff --git a/src/widgets/TimeFrameSlider/ui/EventsCarousel/EventsCarousel.test.tsx b/src/widgets/TimeFrameSlider/ui/EventsCarousel/EventsCarousel.test.tsx
new file mode 100644
index 0000000..f32eebd
--- /dev/null
+++ b/src/widgets/TimeFrameSlider/ui/EventsCarousel/EventsCarousel.test.tsx
@@ -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) =>
{children}
,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ SwiperSlide: ({ children }: any) => (
+ {children}
+ ),
+}))
+
+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()
+
+ // Проверяем, что слайды отрендерились
+ 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()
+ // В реальном компоненте класс применяется к контейнеру Swiper или обертке
+ // Здесь мы проверяем наличие контента, так как opacity управляется через CSS/GSAP
+ expect(screen.getByTestId('swiper')).toBeInTheDocument()
+ })
+})
diff --git a/src/widgets/TimeFrameSlider/ui/TimeFrameSlider/TimeFrameSlider.test.tsx b/src/widgets/TimeFrameSlider/ui/TimeFrameSlider/TimeFrameSlider.test.tsx
new file mode 100644
index 0000000..ac91efb
--- /dev/null
+++ b/src/widgets/TimeFrameSlider/ui/TimeFrameSlider/TimeFrameSlider.test.tsx
@@ -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) => (
+
+
+ Active: {activeIndex}
+
+ ),
+}))
+
+jest.mock('../EventsCarousel/EventsCarousel', () => ({
+ EventsCarousel: () => Carousel
,
+}))
+
+describe('TimeFrameSlider', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ // Тест на рендеринг заголовка
+ it('должен рендерить заголовок', () => {
+ render()
+ expect(screen.getByText('Исторические даты')).toBeInTheDocument()
+ })
+
+ // Тест на отображение начального периода
+ it('должен отображать начальный период (первый в списке)', () => {
+ render()
+ const firstPeriod = HISTORICAL_PERIODS[0]
+ expect(screen.getByText(firstPeriod.yearFrom)).toBeInTheDocument()
+ expect(screen.getByText(firstPeriod.yearTo)).toBeInTheDocument()
+ })
+
+ // Тест на переключение вперед
+ it('должен переключать период вперед при клике на кнопку "Следующий"', () => {
+ render()
+
+ const nextButton = screen.getByLabelText('Следующий период')
+ fireEvent.click(nextButton)
+
+ // Проверяем, что отображается второй период
+ const secondPeriod = HISTORICAL_PERIODS[1]
+ expect(screen.getByText(secondPeriod.yearFrom)).toBeInTheDocument()
+ })
+
+ // Тест на переключение назад
+ it('должен переключать период назад при клике на кнопку "Предыдущий"', () => {
+ render()
+
+ // Сначала переключаем вперед, чтобы не быть на первом элементе (хотя логика циклична)
+ 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()
+
+ const dots = screen.getAllByLabelText(/Перейти к периоду/)
+ fireEvent.click(dots[2]) // Клик по 3-й точке
+
+ const thirdPeriod = HISTORICAL_PERIODS[2]
+ expect(screen.getByText(thirdPeriod.yearFrom)).toBeInTheDocument()
+ })
+})