test: add unit tests for useSwipeSidebar and useLocale hooks, exclude tests from build tsconfig
This commit is contained in:
57
src/hooks/__tests__/useLocale.test.ts
Normal file
57
src/hooks/__tests__/useLocale.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @vitest-environment jsdom
|
||||
*/
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useLocale, useT } from '../useLocale';
|
||||
import { setLocale } from '../../lib/i18n';
|
||||
import type { TranslationKey } from '../../lib/i18n';
|
||||
|
||||
describe('useLocale', () => {
|
||||
beforeEach(() => {
|
||||
setLocale('en');
|
||||
});
|
||||
|
||||
it('returns current locale', () => {
|
||||
const { result } = renderHook(() => useLocale());
|
||||
expect(result.current).toBe('en');
|
||||
});
|
||||
|
||||
it('updates when locale changes', () => {
|
||||
const { result } = renderHook(() => useLocale());
|
||||
expect(result.current).toBe('en');
|
||||
|
||||
act(() => { setLocale('fr'); });
|
||||
expect(result.current).toBe('fr');
|
||||
});
|
||||
});
|
||||
|
||||
describe('useT', () => {
|
||||
beforeEach(() => {
|
||||
setLocale('en');
|
||||
});
|
||||
|
||||
it('returns a translation function', () => {
|
||||
const { result } = renderHook(() => useT());
|
||||
expect(typeof result.current).toBe('function');
|
||||
});
|
||||
|
||||
it('translates keys for current locale', () => {
|
||||
const { result } = renderHook(() => useT());
|
||||
// 'send' is a common key that should exist
|
||||
const translated = result.current('chat.send' as TranslationKey);
|
||||
expect(typeof translated).toBe('string');
|
||||
expect(translated.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('re-renders with new translations when locale changes', () => {
|
||||
const { result } = renderHook(() => useT());
|
||||
const enText = result.current('chat.send' as TranslationKey);
|
||||
|
||||
act(() => { setLocale('fr'); });
|
||||
const frText = result.current('chat.send' as TranslationKey);
|
||||
|
||||
// EN='Send', FR='Envoyer'
|
||||
expect(enText).not.toBe(frText);
|
||||
});
|
||||
});
|
||||
130
src/hooks/__tests__/useSwipeSidebar.test.ts
Normal file
130
src/hooks/__tests__/useSwipeSidebar.test.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @vitest-environment jsdom
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useSwipeSidebar } from '../useSwipeSidebar';
|
||||
|
||||
function touch(x: number, y: number) {
|
||||
return { clientX: x, clientY: y } as Touch;
|
||||
}
|
||||
|
||||
function fireTouchStart(x: number, y: number) {
|
||||
const ev = new TouchEvent('touchstart', {
|
||||
touches: [touch(x, y)],
|
||||
bubbles: true,
|
||||
});
|
||||
document.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
function fireTouchMove(x: number, y: number) {
|
||||
const ev = new TouchEvent('touchmove', {
|
||||
touches: [touch(x, y)],
|
||||
bubbles: true,
|
||||
});
|
||||
document.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
function fireTouchEnd(x: number, y: number) {
|
||||
const ev = new TouchEvent('touchend', {
|
||||
changedTouches: [touch(x, y)],
|
||||
bubbles: true,
|
||||
});
|
||||
document.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
describe('useSwipeSidebar', () => {
|
||||
let onOpen: ReturnType<typeof vi.fn>;
|
||||
let onClose: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
onOpen = vi.fn();
|
||||
onClose = vi.fn();
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('opens sidebar on right swipe from left edge when closed', () => {
|
||||
renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
fireTouchStart(10, 200);
|
||||
fireTouchMove(70, 205);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1200);
|
||||
fireTouchEnd(100, 205);
|
||||
|
||||
expect(onOpen).toHaveBeenCalledTimes(1);
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not open when swipe starts outside edge zone', () => {
|
||||
renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
fireTouchStart(50, 200);
|
||||
fireTouchMove(120, 205);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1200);
|
||||
fireTouchEnd(150, 205);
|
||||
|
||||
expect(onOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('closes sidebar on left swipe when open', () => {
|
||||
renderHook(() => useSwipeSidebar(true, onOpen, onClose));
|
||||
|
||||
fireTouchStart(200, 200);
|
||||
fireTouchMove(130, 205);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1200);
|
||||
fireTouchEnd(100, 205);
|
||||
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
expect(onOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores swipe with too much vertical drift', () => {
|
||||
renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
fireTouchStart(10, 200);
|
||||
fireTouchMove(70, 300); // 100px vertical drift > MAX_Y_DRIFT (80)
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1200);
|
||||
fireTouchEnd(100, 300);
|
||||
|
||||
expect(onOpen).not.toHaveBeenCalled();
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores swipe that is too slow (>500ms)', () => {
|
||||
renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
fireTouchStart(10, 200);
|
||||
fireTouchMove(70, 205);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1600); // 600ms elapsed
|
||||
fireTouchEnd(100, 205);
|
||||
|
||||
expect(onOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores swipe that is too short (<50px)', () => {
|
||||
renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
fireTouchStart(10, 200);
|
||||
fireTouchMove(30, 205);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(1200);
|
||||
fireTouchEnd(40, 205);
|
||||
|
||||
expect(onOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cleans up event listeners on unmount', () => {
|
||||
const removeSpy = vi.spyOn(document, 'removeEventListener');
|
||||
const { unmount } = renderHook(() => useSwipeSidebar(false, onOpen, onClose));
|
||||
|
||||
unmount();
|
||||
|
||||
const removedEvents = removeSpy.mock.calls.map(c => c[0]);
|
||||
expect(removedEvents).toContain('touchstart');
|
||||
expect(removedEvents).toContain('touchmove');
|
||||
expect(removedEvents).toContain('touchend');
|
||||
});
|
||||
});
|
||||
@@ -24,5 +24,6 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/__tests__/**", "src/**/*.test.ts", "src/**/*.test.tsx"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user