// @vitest-environment jsdom import { act, render, screen } from '@testing-library/react'; import { useMemo, useReducer, useRef } from 'react'; import { MemoryRouter, useSearchParams } from 'react-router-dom'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { DEFAULT_TRADE_PLANS_UI_STATE, reduceTradePlansUiState } from './tradePlansState'; import { useTradePlansNavigationState } from './useTradePlansNavigationState'; function NavigationHarness() { const [uiState, dispatch] = useReducer(reduceTradePlansUiState, DEFAULT_TRADE_PLANS_UI_STATE); const [searchParams, setSearchParams] = useSearchParams(); const setupCardRefs = useRef>({}); const availableSellHoldings = useMemo(() => ([ { symbol: 'AAPL', size: 5, entryPrice: 180, profileId: 'p1', tradeId: 'TRD-AAPL', }, { symbol: 'MSFT', size: 2, entryPrice: 410, profileId: 'p1', tradeId: 'TRD-MSFT', }, ]), []); const savedSetups = useMemo(() => ([ { stock_instance_id: 'setup-aapl', symbol: 'AAPL', workflow_type: 'simple', simple_side: 'buy', status: 'simple_bought', active: true, is_crypto: false, is_real_trade: false, }, { stock_instance_id: 'setup-msft', symbol: 'MSFT', workflow_type: 'simple', simple_side: 'buy', status: 'simple_bought', active: true, is_crypto: false, is_real_trade: false, }, ] as any[]), []); const applyHoldingToDraft = (holding: { symbol: string; size: number; entryPrice: number; profileId?: string; tradeId?: string }) => { dispatch({ type: 'apply-holding', tradeId: holding.tradeId || null, symbol: holding.symbol, quantity: String(holding.size), }); }; useTradePlansNavigationState({ searchParams, setSearchParams, savedSetups, availableSellHoldings, applyHoldingToDraft, dispatch, setupCardRefs, }); return (
{uiState.draft.symbol}
{uiState.selectedHoldingTradeId || ''}
{uiState.focusedSetupId || ''}
{uiState.message || ''}
); } describe('useTradePlansNavigationState', () => { beforeEach(() => { vi.useFakeTimers(); Object.defineProperty(window, 'requestAnimationFrame', { writable: true, value: (cb: FrameRequestCallback) => { cb(0); return 0; }, }); Object.defineProperty(Element.prototype, 'scrollIntoView', { writable: true, value: vi.fn(), }); }); afterEach(() => { vi.useRealTimers(); }); it('applies sell prefill and responds to repeated same-page setup focus changes', () => { render( , ); expect(screen.getByTestId('symbol').textContent).toBe('AAPL'); expect(screen.getByTestId('holding').textContent).toBe('TRD-AAPL'); expect(screen.getByTestId('message').textContent).toContain('Loaded AAPL from Portfolio'); act(() => { screen.getByRole('button', { name: 'focus msft' }).click(); }); expect(screen.getByTestId('focused').textContent).toBe('setup-msft'); expect(screen.getByTestId('message').textContent).toContain('Focused saved plan for MSFT'); act(() => { vi.advanceTimersByTime(2200); }); expect(screen.getByTestId('focused').textContent).toBe(''); }); });