// @vitest-environment jsdom import { describe, it, expect, vi, beforeEach } from 'vitest'; import '@testing-library/jest-dom/vitest'; import { render, screen, fireEvent, cleanup } from '@testing-library/react'; import React from 'react'; const { mockSearchNotes, mockClearSearch } = vi.hoisted(() => ({ mockSearchNotes: vi.fn(), mockClearSearch: vi.fn() })); // selectedNotebookId 를 테스트 간 변경할 수 있도록 ref 로 관리. let mockSelectedNotebookId: string | null = 'nb-1'; vi.mock('../../src/renderer/inbox/store.js', () => ({ useInbox: Object.assign( (selector?: (s: { searchQuery: string; selectedNotebookId: string | null }) => unknown) => { const state = { searchQuery: '', selectedNotebookId: mockSelectedNotebookId }; return selector ? selector(state) : state; }, { getState: () => ({ searchNotes: mockSearchNotes, clearSearch: mockClearSearch }) } ) })); import { SearchBox } from '../../src/renderer/inbox/components/SearchBox'; describe('SearchBox', () => { beforeEach(() => { vi.clearAllMocks(); cleanup(); vi.useFakeTimers(); mockSelectedNotebookId = 'nb-1'; }); it('타이핑 → 200ms debounce 후 searchNotes 호출', () => { render(); const input = screen.getByRole('searchbox'); fireEvent.change(input, { target: { value: '회의' } }); expect(mockSearchNotes).not.toHaveBeenCalled(); vi.advanceTimersByTime(200); expect(mockSearchNotes).toHaveBeenCalledWith('회의', { notebookId: 'nb-1' }); }); it('빈 값 → clearSearch 호출', () => { render(); const input = screen.getByRole('searchbox'); fireEvent.change(input, { target: { value: '' } }); vi.advanceTimersByTime(200); expect(mockClearSearch).toHaveBeenCalled(); }); it('기본 scope=current — searchNotes 에 selectedNotebookId 전달', () => { render(); const input = screen.getByRole('searchbox'); fireEvent.change(input, { target: { value: '노트' } }); vi.advanceTimersByTime(200); expect(mockSearchNotes).toHaveBeenCalledWith('노트', { notebookId: 'nb-1' }); }); it('scope=all 변경 시 다음 검색에서 notebookId 미전달 (undefined)', () => { render(); const input = screen.getByRole('searchbox'); const scopeSelect = screen.getByRole('combobox', { name: '검색 범위' }); fireEvent.change(scopeSelect, { target: { value: 'all' } }); fireEvent.change(input, { target: { value: '리뷰' } }); vi.advanceTimersByTime(200); expect(mockSearchNotes).toHaveBeenCalledWith('리뷰', { notebookId: undefined }); }); it('selectedNotebookId=null 이면 scope=current 에서 notebookId=undefined 전달', () => { mockSelectedNotebookId = null; render(); const input = screen.getByRole('searchbox'); fireEvent.change(input, { target: { value: '테스트' } }); vi.advanceTimersByTime(200); expect(mockSearchNotes).toHaveBeenCalledWith('테스트', { notebookId: undefined }); }); });