import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import type { ActionableItem, ActionableItemSource, TimeGroup } from '../../types/intelligence'; import IntelligenceMemoryTab from './IntelligenceMemoryTab'; function makeItem(overrides: Partial = {}): ActionableItem { return { id: 'i1', title: 'Reply to Alice', source: 'email', priority: 'normal', status: 'active', createdAt: new Date(), updatedAt: new Date(), actionable: true, ...overrides, }; } interface Overrides { isRunning?: boolean; items?: ActionableItem[]; itemsLoading?: boolean; searchFilter?: string; sourceFilter?: ActionableItemSource | 'all'; timeGroups?: TimeGroup[]; usingMemoryData?: boolean; } function renderTab(overrides: Overrides = {}) { const handleAnalyzeNow = vi.fn().mockResolvedValue(undefined); const handleComplete = vi.fn().mockResolvedValue(undefined); const handleDismiss = vi.fn(); const handleSnooze = vi.fn().mockResolvedValue(undefined); const setSearchFilter = vi.fn(); const setSourceFilter = vi.fn(); render( ); return { handleAnalyzeNow, handleComplete, handleDismiss, handleSnooze, setSearchFilter, setSourceFilter, }; } describe('', () => { it('renders the search input and source filter select', () => { renderTab(); expect(screen.getByRole('textbox')).toBeInTheDocument(); expect(screen.getByRole('combobox')).toBeInTheDocument(); }); it('dispatches setSearchFilter on input change', () => { const { setSearchFilter } = renderTab(); fireEvent.change(screen.getByRole('textbox'), { target: { value: 'foo' } }); expect(setSearchFilter).toHaveBeenCalledWith('foo'); }); it('dispatches setSourceFilter on select change', () => { const { setSourceFilter } = renderTab(); fireEvent.change(screen.getByRole('combobox'), { target: { value: 'email' } }); expect(setSourceFilter).toHaveBeenCalledWith('email'); }); it('renders the loading state when itemsLoading and no memory data yet', () => { renderTab({ itemsLoading: true }); expect(screen.getByRole('heading', { level: 2, name: /Loading Memory/i })).toBeInTheDocument(); }); it('renders the analyzing state when isRunning and items are empty', () => { renderTab({ isRunning: true }); expect( screen.getByRole('heading', { level: 2, name: /Analyzing Memory/i }) ).toBeInTheDocument(); }); it('invokes handleAnalyzeNow when the Analyze Now button is clicked', () => { const { handleAnalyzeNow } = renderTab({ usingMemoryData: false }); fireEvent.click(screen.getByRole('button', { name: /Analyze Now/i })); expect(handleAnalyzeNow).toHaveBeenCalledTimes(1); }); it('renders the no-matches state when filters are active and groups are empty', () => { renderTab({ searchFilter: 'foo', sourceFilter: 'email' }); expect( screen.getByRole('heading', { level: 2, name: /No Matches Found/i }) ).toBeInTheDocument(); }); it('renders the all-caught-up state when usingMemoryData and no groups', () => { renderTab({ usingMemoryData: true }); expect(screen.getByRole('heading', { level: 2, name: /All Caught Up/i })).toBeInTheDocument(); }); it('renders time-group sections with their items', () => { const items = [makeItem({ id: 'A', title: 'First task' })]; const timeGroups: TimeGroup[] = [{ label: 'Today', items, count: items.length }]; renderTab({ items, timeGroups }); expect(screen.getByText('Today')).toBeInTheDocument(); expect(screen.getByText('First task')).toBeInTheDocument(); // Group count badge. expect(screen.getByText('1')).toBeInTheDocument(); }); });