From d7c769c6556a659b52d7c8acd7918ae245ea7e59 Mon Sep 17 00:00:00 2001 From: shats Date: Wed, 24 Dec 2025 16:22:57 +0700 Subject: [PATCH] chore: Remove unused testing dependencies and configuration files to streamline the project and reduce clutter in the codebase. --- frontend/package.json | 11 +- frontend/src/hooks/useApiQuery.test.ts | 141 ------------------------- frontend/src/lib/datetime.test.ts | 122 --------------------- frontend/src/test/setup.ts | 49 --------- frontend/src/utils/ipUtils.test.ts | 110 ------------------- frontend/src/utils/serverUtils.test.ts | 129 ---------------------- frontend/vitest.config.ts | 34 ------ 7 files changed, 2 insertions(+), 594 deletions(-) delete mode 100644 frontend/src/hooks/useApiQuery.test.ts delete mode 100644 frontend/src/lib/datetime.test.ts delete mode 100644 frontend/src/test/setup.ts delete mode 100644 frontend/src/utils/ipUtils.test.ts delete mode 100644 frontend/src/utils/serverUtils.test.ts delete mode 100644 frontend/vitest.config.ts diff --git a/frontend/package.json b/frontend/package.json index 4a5377c..2b22468 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,10 +9,7 @@ "build:check": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "typecheck": "tsc --noEmit", - "test": "vitest", - "test:run": "vitest run", - "test:coverage": "vitest run --coverage" + "typecheck": "tsc --noEmit" }, "dependencies": { "@tabler/core": "^1.3.2", @@ -27,8 +24,6 @@ }, "devDependencies": { "@eslint/js": "^9.29.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "^4.5.2", @@ -36,10 +31,8 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^16.2.0", - "jsdom": "^26.0.0", "typescript": "^5.7.2", - "vite": "^7.0.0", - "vitest": "^2.1.8" + "vite": "^7.0.0" }, "optionalDependencies": { "@rollup/rollup-linux-x64-musl": "4.40.0" diff --git a/frontend/src/hooks/useApiQuery.test.ts b/frontend/src/hooks/useApiQuery.test.ts deleted file mode 100644 index 436e905..0000000 --- a/frontend/src/hooks/useApiQuery.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { renderHook, waitFor } from '@testing-library/react'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import React from 'react'; -import { - useDomains, - useIpRanges, - useAsns, - useServers, - useCommunities, - queryKeys, -} from './useApiQuery'; - -// Мокаем api -vi.mock('../lib/api', () => ({ - default: { - get: vi.fn(), - post: vi.fn(), - }, -})); - -import api from '../lib/api'; - -const createWrapper = () => { - const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - gcTime: 0, - }, - }, - }); - - return ({ children }: { children: React.ReactNode }) => ( - React.createElement(QueryClientProvider, { client: queryClient }, children) - ); -}; - -describe('useApiQuery hooks', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - describe('useDomains', () => { - it('загружает домены успешно', async () => { - const mockDomains = [ - { domain: 'example.com', type: '120' }, - { domain: 'test.com', type: '130' }, - ]; - - (api.get as ReturnType).mockResolvedValueOnce({ - data: { items: mockDomains }, - headers: { etag: 'abc123', 'last-modified': '2024-01-01' }, - }); - - const { result } = renderHook(() => useDomains(), { - wrapper: createWrapper(), - }); - - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - }); - - expect(result.current.data?.items).toEqual(mockDomains); - expect(result.current.data?.etag).toBe('abc123'); - }); - - it('обрабатывает пустой ответ', async () => { - (api.get as ReturnType).mockResolvedValueOnce({ - data: { items: null }, - headers: {}, - }); - - const { result } = renderHook(() => useDomains(), { - wrapper: createWrapper(), - }); - - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - }); - - expect(result.current.data?.items).toEqual([]); - }); - }); - - describe('useServers', () => { - it('загружает серверы успешно', async () => { - const mockServers = [ - { id: '1', ip: '192.168.1.1', dns: 'srv1.example.com' }, - { id: '2', ip: '192.168.1.2', dns: 'srv2.example.com' }, - ]; - - (api.get as ReturnType).mockResolvedValueOnce({ - data: mockServers, - headers: { etag: 'xyz789' }, - }); - - const { result } = renderHook(() => useServers(), { - wrapper: createWrapper(), - }); - - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - }); - - expect(result.current.data?.items).toEqual(mockServers); - }); - }); - - describe('useCommunities', () => { - it('загружает справочник communities', async () => { - const mockCommunities = [ - { value: '120', name: 'Cloudflare' }, - { value: '130', name: 'Telegram' }, - ]; - - (api.get as ReturnType).mockResolvedValueOnce({ - data: mockCommunities, - }); - - const { result } = renderHook(() => useCommunities(), { - wrapper: createWrapper(), - }); - - await waitFor(() => { - expect(result.current.isSuccess).toBe(true); - }); - - expect(result.current.data).toEqual(mockCommunities); - }); - }); - - describe('queryKeys', () => { - it('генерирует правильные ключи', () => { - expect(queryKeys.domains).toEqual(['domains']); - expect(queryKeys.servers).toEqual(['servers']); - expect(queryKeys.serverFilters('srv-1')).toEqual(['serverFilters', 'srv-1']); - }); - }); -}); - diff --git a/frontend/src/lib/datetime.test.ts b/frontend/src/lib/datetime.test.ts deleted file mode 100644 index f410607..0000000 --- a/frontend/src/lib/datetime.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { - formatDateTime, - formatDateTimeShort, - formatTime, - formatRelative, - formatDateTimeWithRelative, - now, -} from './datetime'; - -describe('datetime utilities', () => { - beforeEach(() => { - // Фиксируем время для предсказуемых тестов - vi.useFakeTimers(); - vi.setSystemTime(new Date('2024-12-24T15:30:45.000Z')); - }); - - afterEach(() => { - vi.useRealTimers(); - }); - - describe('formatDateTime', () => { - it('форматирует Date объект', () => { - const date = new Date('2024-12-24T15:30:45.000Z'); - // Результат зависит от часового пояса, проверяем формат - const result = formatDateTime(date); - expect(result).toMatch(/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}$/); - }); - - it('форматирует строку даты', () => { - const result = formatDateTime('2024-12-24T15:30:45.000Z'); - expect(result).toMatch(/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}$/); - }); - - it('возвращает пустую строку для null/undefined', () => { - expect(formatDateTime(null)).toBe(''); - expect(formatDateTime(undefined)).toBe(''); - expect(formatDateTime('')).toBe(''); - }); - - it('возвращает пустую строку для невалидной даты', () => { - expect(formatDateTime('invalid-date')).toBe(''); - }); - }); - - describe('formatDateTimeShort', () => { - it('форматирует без секунд', () => { - const date = new Date('2024-12-24T15:30:45.000Z'); - const result = formatDateTimeShort(date); - expect(result).toMatch(/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}$/); - expect(result).not.toContain(':45'); - }); - - it('возвращает пустую строку для null', () => { - expect(formatDateTimeShort(null)).toBe(''); - }); - }); - - describe('formatTime', () => { - it('возвращает только время', () => { - const date = new Date('2024-12-24T15:30:45.000Z'); - const result = formatTime(date); - expect(result).toMatch(/^\d{2}:\d{2}:\d{2}$/); - }); - - it('возвращает пустую строку для null', () => { - expect(formatTime(null)).toBe(''); - }); - }); - - describe('formatRelative', () => { - it('возвращает "только что" для недавних дат', () => { - const now = new Date(); - expect(formatRelative(now)).toBe('только что'); - }); - - it('возвращает секунды', () => { - const date = new Date(Date.now() - 30 * 1000); // 30 секунд назад - expect(formatRelative(date)).toBe('30 сек назад'); - }); - - it('возвращает минуты', () => { - const date = new Date(Date.now() - 5 * 60 * 1000); // 5 минут назад - expect(formatRelative(date)).toBe('5 мин назад'); - }); - - it('возвращает часы', () => { - const date = new Date(Date.now() - 3 * 60 * 60 * 1000); // 3 часа назад - expect(formatRelative(date)).toBe('3 ч назад'); - }); - - it('возвращает дни', () => { - const date = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000); // 2 дня назад - expect(formatRelative(date)).toBe('2 дн назад'); - }); - - it('возвращает пустую строку для null', () => { - expect(formatRelative(null)).toBe(''); - }); - }); - - describe('formatDateTimeWithRelative', () => { - it('комбинирует абсолютное и относительное время', () => { - const date = new Date(Date.now() - 5 * 60 * 1000); // 5 минут назад - const result = formatDateTimeWithRelative(date); - expect(result).toContain('(5 мин назад)'); - expect(result).toMatch(/^\d{2}\.\d{2}\.\d{4}/); - }); - - it('возвращает пустую строку для null', () => { - expect(formatDateTimeWithRelative(null)).toBe(''); - }); - }); - - describe('now', () => { - it('возвращает текущую дату в правильном формате', () => { - const result = now(); - expect(result).toMatch(/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}$/); - }); - }); -}); - diff --git a/frontend/src/test/setup.ts b/frontend/src/test/setup.ts deleted file mode 100644 index 06a650a..0000000 --- a/frontend/src/test/setup.ts +++ /dev/null @@ -1,49 +0,0 @@ -import '@testing-library/jest-dom'; -import { afterEach, vi } from 'vitest'; -import { cleanup } from '@testing-library/react'; - -// Очистка после каждого теста -afterEach(() => { - cleanup(); -}); - -// Мок для localStorage -const localStorageMock = { - getItem: vi.fn(), - setItem: vi.fn(), - removeItem: vi.fn(), - clear: vi.fn(), -}; -Object.defineProperty(window, 'localStorage', { value: localStorageMock }); - -// Мок для matchMedia -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: vi.fn().mockImplementation((query: string) => ({ - matches: false, - media: query, - onchange: null, - addListener: vi.fn(), - removeListener: vi.fn(), - addEventListener: vi.fn(), - removeEventListener: vi.fn(), - dispatchEvent: vi.fn(), - })), -}); - -// Мок для ResizeObserver -class ResizeObserverMock { - observe = vi.fn(); - unobserve = vi.fn(); - disconnect = vi.fn(); -} -Object.defineProperty(window, 'ResizeObserver', { value: ResizeObserverMock }); - -// Мок для clipboard -Object.defineProperty(navigator, 'clipboard', { - value: { - writeText: vi.fn().mockResolvedValue(undefined), - readText: vi.fn().mockResolvedValue(''), - }, -}); - diff --git a/frontend/src/utils/ipUtils.test.ts b/frontend/src/utils/ipUtils.test.ts deleted file mode 100644 index 22a5876..0000000 --- a/frontend/src/utils/ipUtils.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - isValidIPv4, - isValidIPv6, - isValidIP, - isValidCIDR, - isValidIPRange, - normalizeIP, - ipToNumber, - compareIPs, -} from './ipUtils'; - -describe('ipUtils', () => { - describe('isValidIPv4', () => { - it('валидирует корректные IPv4 адреса', () => { - expect(isValidIPv4('192.168.1.1')).toBe(true); - expect(isValidIPv4('10.0.0.0')).toBe(true); - expect(isValidIPv4('255.255.255.255')).toBe(true); - expect(isValidIPv4('0.0.0.0')).toBe(true); - }); - - it('отклоняет некорректные IPv4 адреса', () => { - expect(isValidIPv4('256.1.1.1')).toBe(false); - expect(isValidIPv4('1.1.1')).toBe(false); - expect(isValidIPv4('1.1.1.1.1')).toBe(false); - expect(isValidIPv4('abc.def.ghi.jkl')).toBe(false); - expect(isValidIPv4('')).toBe(false); - }); - }); - - describe('isValidIPv6', () => { - it('валидирует корректные IPv6 адреса', () => { - expect(isValidIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(true); - expect(isValidIPv6('2001:db8:85a3::8a2e:370:7334')).toBe(true); - expect(isValidIPv6('::1')).toBe(true); - expect(isValidIPv6('::')).toBe(true); - expect(isValidIPv6('fe80::1')).toBe(true); - }); - - it('отклоняет некорректные IPv6 адреса', () => { - expect(isValidIPv6('gggg::1')).toBe(false); - expect(isValidIPv6('2001:db8')).toBe(false); - expect(isValidIPv6('')).toBe(false); - }); - }); - - describe('isValidIP', () => { - it('валидирует и IPv4 и IPv6', () => { - expect(isValidIP('192.168.1.1')).toBe(true); - expect(isValidIP('::1')).toBe(true); - expect(isValidIP('invalid')).toBe(false); - }); - }); - - describe('isValidCIDR', () => { - it('валидирует корректные CIDR нотации', () => { - expect(isValidCIDR('192.168.1.0/24')).toBe(true); - expect(isValidCIDR('10.0.0.0/8')).toBe(true); - expect(isValidCIDR('2001:db8::/32')).toBe(true); - }); - - it('отклоняет некорректные CIDR нотации', () => { - expect(isValidCIDR('192.168.1.0/33')).toBe(false); - expect(isValidCIDR('192.168.1.0')).toBe(false); - expect(isValidCIDR('invalid/24')).toBe(false); - }); - }); - - describe('isValidIPRange', () => { - it('валидирует IP адреса и CIDR', () => { - expect(isValidIPRange('192.168.1.1')).toBe(true); - expect(isValidIPRange('192.168.1.0/24')).toBe(true); - }); - - it('отклоняет некорректный ввод', () => { - expect(isValidIPRange('invalid')).toBe(false); - }); - }); - - describe('normalizeIP', () => { - it('нормализует IPv4', () => { - expect(normalizeIP('192.168.001.001')).toBe('192.168.1.1'); - }); - - it('возвращает как есть при ошибке', () => { - expect(normalizeIP('invalid')).toBe('invalid'); - }); - }); - - describe('ipToNumber', () => { - it('конвертирует IPv4 в число', () => { - expect(ipToNumber('0.0.0.1')).toBe(1); - expect(ipToNumber('0.0.1.0')).toBe(256); - expect(ipToNumber('192.168.1.1')).toBe(3232235777); - }); - - it('возвращает 0 для невалидного IP', () => { - expect(ipToNumber('invalid')).toBe(0); - }); - }); - - describe('compareIPs', () => { - it('сравнивает IP адреса', () => { - expect(compareIPs('192.168.1.1', '192.168.1.2')).toBeLessThan(0); - expect(compareIPs('192.168.1.2', '192.168.1.1')).toBeGreaterThan(0); - expect(compareIPs('192.168.1.1', '192.168.1.1')).toBe(0); - }); - }); -}); - diff --git a/frontend/src/utils/serverUtils.test.ts b/frontend/src/utils/serverUtils.test.ts deleted file mode 100644 index 5b450dd..0000000 --- a/frontend/src/utils/serverUtils.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - makeGateway, - normalizeGateways, - countryToFlag, - needsGateways, - SERVER_TYPE_OPTIONS, - normalizeCommunityValue, - getShortCommunityValue, -} from './serverUtils'; - -describe('serverUtils', () => { - describe('makeGateway', () => { - it('создаёт gateway с дефолтными значениями', () => { - const gw = makeGateway(); - expect(gw).toHaveProperty('id'); - expect(gw.name).toBe(''); - expect(gw.ip).toBe(''); - expect(gw.comment).toBe(''); - expect(gw.primary).toBe(false); - }); - - it('создаёт gateway с переопределёнными значениями', () => { - const gw = makeGateway({ name: 'Test', primary: true }); - expect(gw.name).toBe('Test'); - expect(gw.primary).toBe(true); - }); - - it('генерирует уникальные id', () => { - const gw1 = makeGateway(); - const gw2 = makeGateway(); - expect(gw1.id).not.toBe(gw2.id); - }); - }); - - describe('normalizeGateways', () => { - it('возвращает пустой массив для undefined', () => { - expect(normalizeGateways(undefined, '')).toEqual([]); - }); - - it('возвращает массив как есть, если валидный', () => { - const gateways = [{ id: '1', name: 'GW1', ip: '10.0.0.1', primary: true }]; - const result = normalizeGateways(gateways, ''); - expect(result).toHaveLength(1); - expect(result[0].name).toBe('GW1'); - }); - - it('добавляет fallbackName если name пустое', () => { - const gateways = [{ id: '1', name: '', ip: '10.0.0.1', primary: true }]; - const result = normalizeGateways(gateways, 'Fallback'); - expect(result[0].name).toBe('Fallback'); - }); - }); - - describe('countryToFlag', () => { - it('конвертирует код страны в эмодзи флаг', () => { - expect(countryToFlag('RU')).toBe('🇷🇺'); - expect(countryToFlag('US')).toBe('🇺🇸'); - expect(countryToFlag('DE')).toBe('🇩🇪'); - }); - - it('обрабатывает нижний регистр', () => { - expect(countryToFlag('ru')).toBe('🇷🇺'); - }); - - it('возвращает пустую строку для пустого ввода', () => { - expect(countryToFlag('')).toBe(''); - expect(countryToFlag(null as any)).toBe(''); - }); - }); - - describe('needsGateways', () => { - it('возвращает true для jumphost', () => { - expect(needsGateways('jumphost')).toBe(true); - }); - - it('возвращает true для exit', () => { - expect(needsGateways('exit')).toBe(true); - }); - - it('возвращает false для других типов', () => { - expect(needsGateways('unknown')).toBe(false); - expect(needsGateways('')).toBe(false); - }); - - it('работает независимо от регистра', () => { - expect(needsGateways('JUMPHOST')).toBe(true); - expect(needsGateways('Exit')).toBe(true); - }); - }); - - describe('SERVER_TYPE_OPTIONS', () => { - it('содержит jumphost и exit', () => { - expect(SERVER_TYPE_OPTIONS).toContainEqual({ value: 'jumphost', label: 'Jumphost' }); - expect(SERVER_TYPE_OPTIONS).toContainEqual({ value: 'exit', label: 'Выходная нода' }); - }); - }); - - describe('normalizeCommunityValue', () => { - it('добавляет baseAS если нет двоеточия', () => { - expect(normalizeCommunityValue('120', '65001')).toBe('65001:120'); - }); - - it('оставляет как есть если уже есть двоеточие', () => { - expect(normalizeCommunityValue('65000:120', '65001')).toBe('65000:120'); - }); - - it('возвращает пустую строку для пустого ввода', () => { - expect(normalizeCommunityValue('', '65001')).toBe(''); - expect(normalizeCommunityValue(' ', '65001')).toBe(''); - }); - }); - - describe('getShortCommunityValue', () => { - it('извлекает часть после двоеточия', () => { - expect(getShortCommunityValue('65001:120')).toBe('120'); - }); - - it('возвращает как есть если нет двоеточия', () => { - expect(getShortCommunityValue('120')).toBe('120'); - }); - - it('возвращает пустую строку для пустого ввода', () => { - expect(getShortCommunityValue('')).toBe(''); - expect(getShortCommunityValue(' ')).toBe(''); - }); - }); -}); - diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts deleted file mode 100644 index c6663cd..0000000 --- a/frontend/vitest.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { defineConfig } from 'vitest/config'; -import react from '@vitejs/plugin-react'; -import { resolve } from 'path'; - -export default defineConfig({ - plugins: [react()], - test: { - globals: true, - environment: 'jsdom', - setupFiles: ['./src/test/setup.ts'], - include: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}'], - coverage: { - provider: 'v8', - reporter: ['text', 'json', 'html'], - include: ['src/**/*.{js,jsx,ts,tsx}'], - exclude: [ - 'src/test/**', - 'src/**/*.d.ts', - 'src/main.jsx', - 'src/types/**', - ], - }, - }, - resolve: { - alias: { - '@': resolve(__dirname, './src'), - '@components': resolve(__dirname, './src/components'), - '@hooks': resolve(__dirname, './src/hooks'), - '@lib': resolve(__dirname, './src/lib'), - '@utils': resolve(__dirname, './src/utils'), - }, - }, -}); -