import { describe, expect, it, vi } from 'vitest'; import { filterHistoryByProfileAndDate, paginateHistory, parseDateEnd, parseDateStart, sortHistoryByTimestamp, toTimestampMs } from './HistoryTab'; import { buildClonedEntryPayload, filterEntriesByTab } from './EntriesTab'; import { buildConfigUpsertPayload, cloneConfigItems, hasConfigChanges } from './ConfigTab'; import { mapProfileToFormData, parseNumericSettings } from './SettingsTab'; vi.mock('../lib/supabaseClient', () => ({ supabase: { from: vi.fn(() => ({ select: vi.fn(), order: vi.fn(), eq: vi.fn(), insert: vi.fn(), update: vi.fn(), upsert: vi.fn(), delete: vi.fn() })) } })); vi.mock('../components/AuthContext', () => ({ useAuth: () => ({ user: null, profile: { role: 'admin' }, refreshProfile: vi.fn() }) })); describe('tab helper coverage', () => { it('parses and normalizes history timestamps and date bounds', () => { expect(toTimestampMs(1700000000000)).toBe(1700000000000); expect(toTimestampMs('2026-02-15T10:00:00.000Z')).toBeGreaterThan(0); expect(toTimestampMs(undefined, '2026-02-15T10:00:00.000Z')).toBeGreaterThan(0); expect(toTimestampMs('invalid')).toBe(0); expect(parseDateStart('')).toBeNull(); expect(parseDateEnd('')).toBeNull(); expect(parseDateStart('2026-02-15')).toBeGreaterThan(0); expect(parseDateEnd('2026-02-15')).toBeGreaterThan(0); }); it('filters, sorts and paginates history rows deterministically', () => { const records = [ { id: 'b', profile_id: 'p1', timestamp: 20, created_at: '', symbol: 'BTC', side: 'BUY', entry_price: 1, exit_price: 2, pnl: 1, pnl_percent: 1, reason: 'r' }, { id: 'a', profile_id: 'p1', timestamp: 20, created_at: '', symbol: 'BTC', side: 'BUY', entry_price: 1, exit_price: 2, pnl: 1, pnl_percent: 1, reason: 'r' }, { id: 'c', profile_id: 'p2', timestamp: 10, created_at: '', symbol: 'ETH', side: 'SELL', entry_price: 2, exit_price: 1, pnl: -1, pnl_percent: -1, reason: 'r' } ] as any; const filtered = filterHistoryByProfileAndDate(records, 'p1', { from: 0, to: 25 }); expect(filtered).toHaveLength(2); const desc = sortHistoryByTimestamp(filtered, 'desc'); const asc = sortHistoryByTimestamp(filtered, 'asc'); expect(desc[0].id).toBe('b'); expect(asc[0].id).toBe('a'); const paged = paginateHistory(desc, 1, 1); expect(paged).toHaveLength(1); expect(paged[0].id).toBe('b'); }); it('filters entries by tab and builds clone payload safely', () => { const entries = [ { stock_instance_id: '1', is_real_trade: false, active: true, status: 'active' }, { stock_instance_id: '2', is_real_trade: false, active: false, status: 'sellCompleted' }, { stock_instance_id: '3', is_real_trade: true, active: true, status: 'active' } ] as any; expect(filterEntriesByTab(entries, 'paperActive')).toHaveLength(1); expect(filterEntriesByTab(entries, 'paperCompleted')).toHaveLength(1); expect(filterEntriesByTab(entries, 'realActive')).toHaveLength(1); expect(filterEntriesByTab(entries, 'unknown')).toHaveLength(0); const cloned = buildClonedEntryPayload({ stock_instance_id: 'old', id: 'legacy', created_at: 't1', updated_at: 't2', symbol: 'BTC/USDT', active: true, user_id: 'u1', status: 'active', is_crypto: true, is_real_trade: false } as any, 'new-id'); expect(cloned.stock_instance_id).toBe('new-id'); expect(cloned.active).toBe(false); expect(cloned.status).toBe('active'); expect((cloned as any).id).toBeUndefined(); }); it('clones, diffs and maps config upsert payloads', () => { const configs = [{ key: 'A', value: '1', description: 'd' }]; const cloned = cloneConfigItems(configs as any); cloned[0].value = '2'; expect(configs[0].value).toBe('1'); expect(hasConfigChanges(configs as any, cloned as any)).toBe(true); expect(hasConfigChanges(configs as any, configs as any)).toBe(false); const payload = buildConfigUpsertPayload(configs as any, '2026-02-16T00:00:00.000Z'); expect(payload[0]).toEqual({ key: 'A', value: '1', updated_at: '2026-02-16T00:00:00.000Z' }); }); it('maps settings form data and validates numeric fields', () => { const mapped = mapProfileToFormData({ first_name: 'Sam', last_name: 'Trader', trade_enable: true, drop_threshold_for_buy: 0.05, gain_threshold_for_sell: 0.02, market_poll_interval_in_seconds: 30000 }); expect(mapped.first_name).toBe('Sam'); expect(mapped.trade_enable).toBe(true); expect(mapped.drop_threshold_for_buy).toBe('0.05'); const parsed = parseNumericSettings(mapped); expect(parsed.buyThreshold).toBe(0.05); expect(parsed.sellThreshold).toBe(0.02); expect(parsed.interval).toBe(30000); expect(() => parseNumericSettings({ ...mapped, drop_threshold_for_buy: 'x' })).toThrow('Please enter valid numbers for thresholds and interval.'); }); });