Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions hooks/useRecentSearches.accessibility.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { renderHook, act } from '@testing-library/react';
import { describe, expect, it, beforeEach, vi } from 'vitest';
import { useRecentSearches, STORAGE_KEY, MAX_SEARCHES } from './useRecentSearches';

describe('useRecentSearches - State Persistence & Array Ordering Operations', () => {
beforeEach(() => {
window.localStorage.clear();
vi.clearAllMocks();
});

// Test Case 1: Verifies loadFromStorage() integration on mount
it('hydrates state correctly from localStorage during the component initialization phase', () => {
const preExistingSearches = ['remix', 'astro'];
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(preExistingSearches));

const { result } = renderHook(() => useRecentSearches());

// Confirms the mounting useEffect successfully reads and populates historical storage data
expect(result.current.searches).toEqual(preExistingSearches);
});

// Test Case 2: Verifies writeStorage() lifecycle updates
it('persists newly appended searches to localStorage on state changes', () => {
const { result } = renderHook(() => useRecentSearches());

act(() => {
result.current.addSearch('vue');
});

expect(result.current.searches).toEqual(['vue']);
expect(JSON.parse(window.localStorage.getItem(STORAGE_KEY) || '[]')).toEqual(['vue']);
});

// Test Case 3: Verifies exact deduplication array shuffling behavior
it('moves duplicate search entries to the absolute front of the stack rather than stacking them', () => {
const { result } = renderHook(() => useRecentSearches());

act(() => {
result.current.addSearch('typescript');
result.current.addSearch('javascript');
});
expect(result.current.searches).toEqual(['javascript', 'typescript']);

act(() => {
// Re-adding 'typescript' should slide it to index 0, not append it
result.current.addSearch('typescript');
});
expect(result.current.searches).toEqual(['typescript', 'javascript']);
});

// Test Case 4: Verifies empty string rejection constraints
it('completely ignores empty strings or whitespace-only queries without modifying active state', () => {
const { result } = renderHook(() => useRecentSearches());

act(() => {
result.current.addSearch('');
result.current.addSearch(' ');
});

expect(result.current.searches).toEqual([]);
});

// Test Case 5: Verifies targeted item eviction and state clearing boundaries
it('evicts only the specified single item when calling removeSearch and purges storage when cleared', () => {
const { result } = renderHook(() => useRecentSearches());

act(() => {
result.current.addSearch('tailwindcss');
result.current.addSearch('bootstrap');
result.current.addSearch('shadcn');
});

act(() => {
result.current.removeSearch('bootstrap');
});
expect(result.current.searches).toEqual(['shadcn', 'tailwindcss']);

act(() => {
result.current.clearSearches();
});
expect(result.current.searches).toEqual([]);
expect(window.localStorage.getItem(STORAGE_KEY)).toBeNull();
});
});
Loading