/** * useSearch 金额区间筛选(P4)。 * 仓库无 testing-library,且 node_modules 中 react-dom(19.2.7) 与 react(19.1.0) * 版本不一致无法 SSR,因此 vi.mock('react') 提供最小 hook 实现,将 useSearch * 当纯函数直接调用(useState 恒返回初值 → useDebounce 直通,无需 timer)。 */ import { vi, describe, expect, it } from 'vitest'; vi.mock('react', () => ({ useMemo: (fn: () => unknown) => fn(), useState: (init: unknown) => [init, () => {}], useEffect: () => {}, useCallback: (fn: unknown) => fn, useRef: (v: unknown) => ({ current: v }), })); import { useSearch, type SearchFilters } from '../src/hooks/useSearch'; import type { Transaction } from '../src/domain/types'; function tx(id: string, postings: { account: string; amount?: string }[]): Transaction { return { id, date: '2026-07-21', flag: '*', narration: id, postings: postings.map(p => ({ account: p.account, amount: p.amount, currency: 'CNY' })), tags: [], links: [], source: 'test', raw: '', }; } describe('金额区间筛选', () => { const txs = [ tx('expense35', [{ account: 'Assets:招行', amount: '-35' }, { account: 'Expenses:餐饮', amount: '35' }]), tx('expense200', [{ account: 'Assets:招行', amount: '-200' }, { account: 'Expenses:购物', amount: '200' }]), tx('income5000', [{ account: 'Assets:招行', amount: '5000' }, { account: 'Income:工资', amount: '-5000' }]), ]; it('amountMin/amountMax 按主金额绝对值过滤', () => { // 三笔:支出 35、支出 200、收入 5000(收入主金额为负 → 绝对值 5000) const range = useSearch(txs, { amountMin: '30', amountMax: '300' } as SearchFilters); expect(range.map(t => t.id)).toEqual(['expense35', 'expense200']); const minOnly = useSearch(txs, { amountMin: '1000' } as SearchFilters); expect(minOnly.map(t => t.id)).toEqual(['income5000']); }); it('无金额 posting 的交易在设了下限时被排除', () => { const withNoAmount = [ ...txs, tx('noAmount', [{ account: 'Assets:招行' }, { account: 'Expenses:其他' }]), ]; const result = useSearch(withNoAmount, { amountMin: '1' } as SearchFilters); expect(result.map(t => t.id)).not.toContain('noAmount'); expect(result).toHaveLength(3); }); it('不设区间时不过滤(回归兜底)', () => { const result = useSearch(txs, {}); expect(result).toHaveLength(3); }); });