test: подключить vitest и покрыть утилиты юнит-тестами

This commit is contained in:
kewldan
2026-07-13 01:09:33 +03:00
parent 758a645dff
commit 42788e842d
9 changed files with 527 additions and 3 deletions

View File

@@ -0,0 +1,23 @@
import { describe, expect, it, vi } from 'vitest';
import { formatTraffic } from './formatTraffic';
vi.mock('../i18n', () => ({
default: { t: (_key: string, fallback: string) => fallback },
}));
describe('formatTraffic', () => {
it('formats terabytes from 1000 GB up', () => {
expect(formatTraffic(1000)).toBe('1.0 TB');
expect(formatTraffic(2500)).toBe('2.5 TB');
});
it('formats gigabytes from 1 GB up', () => {
expect(formatTraffic(1)).toBe('1.0 GB');
expect(formatTraffic(999.94)).toBe('999.9 GB');
});
it('formats sub-gigabyte values as megabytes', () => {
expect(formatTraffic(0.5)).toBe('512 MB');
expect(formatTraffic(0)).toBe('0 MB');
});
});

29
src/utils/period.test.ts Normal file
View File

@@ -0,0 +1,29 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { getMonthToDateRange, isMonthToDate } from './period';
describe('period', () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it('builds a month-to-date range with zero-padded local dates', () => {
vi.setSystemTime(new Date(2026, 2, 5));
expect(getMonthToDateRange()).toEqual({ startDate: '2026-03-01', endDate: '2026-03-05' });
});
it('recognises the current month-to-date period', () => {
vi.setSystemTime(new Date(2026, 6, 13));
expect(isMonthToDate({ startDate: '2026-07-01', endDate: '2026-07-13' })).toBe(true);
});
it('rejects day-based and mismatched periods', () => {
vi.setSystemTime(new Date(2026, 6, 13));
expect(isMonthToDate({ days: 30 })).toBe(false);
expect(isMonthToDate({ startDate: '2026-07-01', endDate: '2026-07-12' })).toBe(false);
expect(isMonthToDate({})).toBe(false);
});
});

View File

@@ -0,0 +1,36 @@
import { describe, expect, it } from 'vitest';
import { getSafeRedirectPath } from './safeRedirect';
describe('getSafeRedirectPath', () => {
it('returns / for empty values', () => {
expect(getSafeRedirectPath(null)).toBe('/');
expect(getSafeRedirectPath(undefined)).toBe('/');
expect(getSafeRedirectPath('')).toBe('/');
});
it('keeps plain absolute in-app paths', () => {
expect(getSafeRedirectPath('/')).toBe('/');
expect(getSafeRedirectPath('/dashboard')).toBe('/dashboard');
expect(getSafeRedirectPath('/sub/page?tab=devices&x=1')).toBe('/sub/page?tab=devices&x=1');
});
it('rejects protocol-relative and absolute URLs', () => {
expect(getSafeRedirectPath('//evil.com')).toBe('/');
expect(getSafeRedirectPath('https://evil.com/path')).toBe('/');
expect(getSafeRedirectPath('http://evil.com')).toBe('/');
});
it('rejects non-path schemes', () => {
expect(getSafeRedirectPath('javascript:alert(1)')).toBe('/');
expect(getSafeRedirectPath('data:text/html,x')).toBe('/');
});
it('rejects URL-encoded host smuggling', () => {
expect(getSafeRedirectPath('%2F%2Fevil.com')).toBe('/');
expect(getSafeRedirectPath('/redirect%3A%2F%2Fevil.com')).toBe('/');
});
it('rejects malformed percent-encoding instead of throwing', () => {
expect(getSafeRedirectPath('/%E0%A4%A')).toBe('/');
});
});

View File

@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import { sanitizeSurrogates } from './sanitizeSurrogates';
describe('sanitizeSurrogates', () => {
it('returns surrogate-free strings unchanged', () => {
expect(sanitizeSurrogates('')).toBe('');
expect(sanitizeSurrogates('https://host/sub/uuid#remark')).toBe('https://host/sub/uuid#remark');
});
it('keeps valid surrogate pairs intact', () => {
expect(sanitizeSurrogates('server 😀 name')).toBe('server 😀 name');
});
it('replaces a lone high surrogate', () => {
expect(sanitizeSurrogates('abc\uD83D')).toBe('abc<62>');
expect(sanitizeSurrogates('\uD83Dxyz')).toBe('<27>xyz');
});
it('replaces a lone low surrogate', () => {
expect(sanitizeSurrogates('abc\uDE00def')).toBe('abc<62>def');
});
it('makes a truncated-emoji string safe for encodeURI', () => {
const broken = 'remark \uD83D truncated';
expect(() => encodeURI(broken)).toThrow();
expect(() => encodeURI(sanitizeSurrogates(broken))).not.toThrow();
});
});

View File

@@ -0,0 +1,17 @@
import { describe, expect, it } from 'vitest';
import { isValidEmail } from './validation';
describe('isValidEmail', () => {
it('accepts common addresses', () => {
expect(isValidEmail('user@example.com')).toBe(true);
expect(isValidEmail('a.b+tag@sub.domain.io')).toBe(true);
});
it('rejects malformed addresses', () => {
expect(isValidEmail('')).toBe(false);
expect(isValidEmail('plain')).toBe(false);
expect(isValidEmail('user@host')).toBe(false);
expect(isValidEmail('user @example.com')).toBe(false);
expect(isValidEmail('user@exa mple.com')).toBe(false);
});
});