35% front tests
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import Footer from '../../src/components/Footer'
|
||||
|
||||
describe('Footer', () => {
|
||||
const renderFooter = (props: Partial<React.ComponentProps<typeof Footer>> = {}) => {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<Footer {...props} />
|
||||
</MemoryRouter>
|
||||
)
|
||||
}
|
||||
|
||||
it('должен рендериться с логотипом', () => {
|
||||
renderFooter()
|
||||
const logo = screen.getByAltText('Logo')
|
||||
expect(logo).toBeInTheDocument()
|
||||
expect(logo).toHaveAttribute('src', '/img/logo.png')
|
||||
})
|
||||
|
||||
it('должен отображать ссылку на документацию', () => {
|
||||
renderFooter()
|
||||
const docLink = screen.getByText('Документация')
|
||||
expect(docLink).toBeInTheDocument()
|
||||
expect(docLink).toHaveAttribute('href', 'https://3dp-manager.com/docs/intro')
|
||||
expect(docLink).toHaveAttribute('target', '_blank')
|
||||
expect(docLink).toHaveAttribute('rel', 'noopener')
|
||||
})
|
||||
|
||||
it('должен отображать иконку GitHub', () => {
|
||||
renderFooter()
|
||||
const githubButton = screen.getByLabelText('GitHub')
|
||||
expect(githubButton).toBeInTheDocument()
|
||||
expect(githubButton.closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'https://github.com/denpiligrim/3dp-manager'
|
||||
)
|
||||
})
|
||||
|
||||
it('должен отображать иконку YouTube', () => {
|
||||
renderFooter()
|
||||
const youtubeButton = screen.getByLabelText('YouTube')
|
||||
expect(youtubeButton).toBeInTheDocument()
|
||||
expect(youtubeButton.closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'https://youtube.com/@denpiligrim'
|
||||
)
|
||||
})
|
||||
|
||||
it('должен отображать иконку Telegram', () => {
|
||||
renderFooter()
|
||||
const telegramButton = screen.getByLabelText('Telegram')
|
||||
expect(telegramButton).toBeInTheDocument()
|
||||
expect(telegramButton.closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'https://t.me/denpiligrim_web'
|
||||
)
|
||||
})
|
||||
|
||||
it('должен принимать prop isMobile', () => {
|
||||
renderFooter({ isMobile: true })
|
||||
expect(screen.getByAltText('Logo')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен иметь правильный семантический тег footer', () => {
|
||||
renderFooter()
|
||||
expect(screen.getByRole('contentinfo')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен иметь правильный layout с Grid', () => {
|
||||
renderFooter()
|
||||
// Проверяем, что все три колонки присутствуют
|
||||
expect(screen.getByText('Документация')).toBeInTheDocument()
|
||||
expect(screen.getByLabelText('GitHub')).toBeInTheDocument()
|
||||
expect(screen.getByAltText('Logo')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,213 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import Header from '../../src/components/Header'
|
||||
import { useThemeContext } from '../../src/ThemeContext'
|
||||
import { useAuth } from '../../src/auth/AuthContext'
|
||||
|
||||
vi.mock('../../src/ThemeContext', () => ({
|
||||
useThemeContext: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../../src/auth/AuthContext', () => ({
|
||||
useAuth: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('Header', () => {
|
||||
const mockToggleColorMode = vi.fn()
|
||||
const mockLogout = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.mocked(useThemeContext).mockReturnValue({
|
||||
mode: 'light',
|
||||
toggleColorMode: mockToggleColorMode,
|
||||
})
|
||||
vi.mocked(useAuth).mockReturnValue({
|
||||
isAuthenticated: true,
|
||||
login: vi.fn(),
|
||||
logout: mockLogout,
|
||||
})
|
||||
})
|
||||
|
||||
const renderHeader = (props: Partial<React.ComponentProps<typeof Header>> = {}) => {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<Header {...props} />
|
||||
</MemoryRouter>
|
||||
)
|
||||
}
|
||||
|
||||
it('должен рендериться с логотипом и названием', () => {
|
||||
renderHeader()
|
||||
expect(screen.getByText('3DP-MANAGER')).toBeInTheDocument()
|
||||
const logo = screen.getByAltText('Logo')
|
||||
expect(logo).toBeInTheDocument()
|
||||
expect(logo).toHaveAttribute('src', '/img/logo.png')
|
||||
})
|
||||
|
||||
it('должен отображать иконку справки', () => {
|
||||
renderHeader()
|
||||
const helpButton = screen.getByLabelText('Справка о программе')
|
||||
expect(helpButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен открывать диалог справки при клике на иконку справки', async () => {
|
||||
renderHeader()
|
||||
const helpButton = screen.getByLabelText('Справка о программе')
|
||||
fireEvent.click(helpButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Об утилите 3DP-MANAGER')).toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByText(/Утилита для автогенерации инбаундов/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен закрывать диалог справки при клике на кнопку "Понятно"', async () => {
|
||||
renderHeader()
|
||||
const helpButton = screen.getByLabelText('Справка о программе')
|
||||
fireEvent.click(helpButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Об утилите 3DP-MANAGER')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const closeButton = screen.getByText('Понятно')
|
||||
fireEvent.click(closeButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Об утилите 3DP-MANAGER')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('должен отображать иконку темы', () => {
|
||||
renderHeader()
|
||||
const themeButton = screen.getByLabelText('Режим: Светлая тема')
|
||||
expect(themeButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен вызывать toggleColorMode при клике на иконку темы', () => {
|
||||
renderHeader()
|
||||
const themeButton = screen.getByLabelText('Режим: Светлая тема')
|
||||
fireEvent.click(themeButton)
|
||||
expect(mockToggleColorMode).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('должен отображать правильную иконку для light mode', () => {
|
||||
vi.mocked(useThemeContext).mockReturnValue({
|
||||
mode: 'light',
|
||||
toggleColorMode: mockToggleColorMode,
|
||||
})
|
||||
renderHeader()
|
||||
expect(screen.getByLabelText('Режим: Светлая тема')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать правильную иконку для dark mode', () => {
|
||||
vi.mocked(useThemeContext).mockReturnValue({
|
||||
mode: 'dark',
|
||||
toggleColorMode: mockToggleColorMode,
|
||||
})
|
||||
renderHeader()
|
||||
expect(screen.getByLabelText('Режим: Темная тема')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать правильную иконку для system mode', () => {
|
||||
vi.mocked(useThemeContext).mockReturnValue({
|
||||
mode: 'system',
|
||||
toggleColorMode: mockToggleColorMode,
|
||||
})
|
||||
renderHeader()
|
||||
expect(screen.getByLabelText('Режим: Системная тема')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать иконку выхода', () => {
|
||||
renderHeader()
|
||||
const logoutButton = screen.getByLabelText('Выйти из системы')
|
||||
expect(logoutButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен открывать диалог подтверждения при клике на выход', async () => {
|
||||
renderHeader()
|
||||
const logoutButton = screen.getByLabelText('Выйти из системы')
|
||||
fireEvent.click(logoutButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Вы действительно хотите выйти?')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('должен закрывать диалог подтверждения при клике на "Отмена"', async () => {
|
||||
renderHeader()
|
||||
const logoutButton = screen.getByLabelText('Выйти из системы')
|
||||
fireEvent.click(logoutButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Вы действительно хотите выйти?')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const cancelButton = screen.getByText('Отмена')
|
||||
fireEvent.click(cancelButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Вы действительно хотите выйти?')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('должен вызывать logout при подтверждении выхода', async () => {
|
||||
renderHeader()
|
||||
const logoutButton = screen.getByLabelText('Выйти из системы')
|
||||
fireEvent.click(logoutButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Вы действительно хотите выйти?')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const confirmButton = screen.getByText('Выйти')
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
expect(mockLogout).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('должен принимать prop isMobile', () => {
|
||||
renderHeader({ isMobile: true })
|
||||
expect(screen.getByText('3DP-MANAGER')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать кнопку меню в мобильном режиме', () => {
|
||||
const onMenuClick = vi.fn()
|
||||
renderHeader({ isMobile: true, onMenuClick })
|
||||
|
||||
const menuButton = screen.getByTestId('icon-Menu').closest('button')
|
||||
expect(menuButton).toBeInTheDocument()
|
||||
|
||||
if (menuButton) {
|
||||
fireEvent.click(menuButton)
|
||||
expect(onMenuClick).toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
|
||||
it('должен отображать версию в диалоге справки', async () => {
|
||||
renderHeader()
|
||||
const helpButton = screen.getByLabelText('Справка о программе')
|
||||
fireEvent.click(helpButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Разработчик:/)).toBeInTheDocument()
|
||||
})
|
||||
// Версия отображается отдельным текстом с br переносом
|
||||
expect(screen.getByText(/\d+\.\d+\.\d+/)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать список возможностей в диалоге справки', async () => {
|
||||
renderHeader()
|
||||
const helpButton = screen.getByLabelText('Справка о программе')
|
||||
fireEvent.click(helpButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Автоматическая генерация')).toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByText('Управление подписками')).toBeInTheDocument()
|
||||
expect(screen.getByText('Белый список доменов')).toBeInTheDocument()
|
||||
expect(screen.getByText('Перенаправление')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,136 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom'
|
||||
import Layout from '../../src/components/Layout'
|
||||
import { useThemeContext } from '../../src/ThemeContext'
|
||||
import { useAuth } from '../../src/auth/AuthContext'
|
||||
|
||||
vi.mock('../../src/ThemeContext', () => ({
|
||||
useThemeContext: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../../src/auth/AuthContext', () => ({
|
||||
useAuth: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('Layout', () => {
|
||||
const mockToggleColorMode = vi.fn()
|
||||
const mockLogout = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.mocked(useThemeContext).mockReturnValue({
|
||||
mode: 'light',
|
||||
toggleColorMode: mockToggleColorMode,
|
||||
})
|
||||
vi.mocked(useAuth).mockReturnValue({
|
||||
isAuthenticated: true,
|
||||
login: vi.fn(),
|
||||
logout: mockLogout,
|
||||
})
|
||||
})
|
||||
|
||||
const renderLayout = (initialPath = '/') => {
|
||||
return render(
|
||||
<MemoryRouter initialEntries={[initialPath]}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
<Route index element={<div data-testid="outlet">SubscriptionsPage</div>} />
|
||||
<Route path="domains" element={<div data-testid="outlet">DomainsPage</div>} />
|
||||
<Route path="tunnels" element={<div data-testid="outlet">TunnelsPage</div>} />
|
||||
<Route path="settings" element={<div data-testid="outlet">SettingsPage</div>} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
)
|
||||
}
|
||||
|
||||
it('должен рендериться без ошибок', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByTestId('outlet')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать Header', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByText('3DP-MANAGER')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать навигационное меню', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByText('Подписки')).toBeInTheDocument()
|
||||
expect(screen.getByText('Домены')).toBeInTheDocument()
|
||||
expect(screen.getByText('Перенаправление')).toBeInTheDocument()
|
||||
expect(screen.getByText('Настройки')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен выделять активный пункт меню для главной страницы', () => {
|
||||
renderLayout('/')
|
||||
const subscriptionsItem = screen.getByText('Подписки').closest('.Mui-selected')
|
||||
expect(subscriptionsItem).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен выделять активный пункт меню для страницы доменов', () => {
|
||||
renderLayout('/domains')
|
||||
const domainsItem = screen.getByText('Домены').closest('.Mui-selected')
|
||||
expect(domainsItem).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен выделять активный пункт меню для страницы туннелей', () => {
|
||||
renderLayout('/tunnels')
|
||||
const tunnelsItem = screen.getByText('Перенаправление').closest('.Mui-selected')
|
||||
expect(tunnelsItem).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен выделять активный пункт меню для страницы настроек', () => {
|
||||
renderLayout('/settings')
|
||||
const settingsItem = screen.getByText('Настройки').closest('.Mui-selected')
|
||||
expect(settingsItem).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен переходить на главную при клике на "Подписки"', () => {
|
||||
renderLayout('/domains')
|
||||
const subscriptionsLink = screen.getByText('Подписки')
|
||||
fireEvent.click(subscriptionsLink)
|
||||
expect(screen.getByText('SubscriptionsPage')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен переходить на страницу доменов при клике на "Домены"', () => {
|
||||
renderLayout('/')
|
||||
const domainsLink = screen.getByText('Домены')
|
||||
fireEvent.click(domainsLink)
|
||||
expect(screen.getByText('DomainsPage')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен переходить на страницу туннелей при клике на "Перенаправление"', () => {
|
||||
renderLayout('/')
|
||||
const tunnelsLink = screen.getByText('Перенаправление')
|
||||
fireEvent.click(tunnelsLink)
|
||||
expect(screen.getByText('TunnelsPage')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен переходить на страницу настроек при клике на "Настройки"', () => {
|
||||
renderLayout('/')
|
||||
const settingsLink = screen.getByText('Настройки')
|
||||
fireEvent.click(settingsLink)
|
||||
expect(screen.getByText('SettingsPage')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен отображать Footer', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByRole('contentinfo')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен иметь правильную структуру с main и Toolbar', () => {
|
||||
renderLayout()
|
||||
const main = screen.getByTestId('outlet').closest('main')
|
||||
expect(main).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('должен иметь правильные иконки для пунктов меню', () => {
|
||||
renderLayout()
|
||||
expect(screen.getByTestId('icon-People')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('icon-Dns')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('icon-SwapHoriz')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('icon-Settings')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user