// @vitest-environment jsdom import { beforeEach, describe, expect, it, vi } from 'vitest'; import { render, screen, waitFor, fireEvent, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { EntriesTab } from './EntriesTab'; const { authState, confirmMock, fetchManualEntriesMock, deleteManualEntryMock, createManualEntryMock } = vi.hoisted(() => ({ authState: { user: { id: 'user-1', email: 'test@demo.test' } as any, profile: { role: 'user' } as any }, confirmMock: vi.fn(), fetchManualEntriesMock: vi.fn(), deleteManualEntryMock: vi.fn(), createManualEntryMock: vi.fn() })); vi.mock('../components/AuthContext', () => ({ useAuth: () => authState })); vi.mock('../lib/manualEntriesApi', () => ({ fetchManualEntries: fetchManualEntriesMock, deleteManualEntry: deleteManualEntryMock, createManualEntry: createManualEntryMock })); vi.mock('../components/EntryForm', () => ({ EntryForm: ({ onSuccess, initialData }: any) => (
{initialData ? initialData.symbol : 'none'}
) })); describe('EntriesTab master suite', () => { const mockEntry = { stock_instance_id: 'entry-1', symbol: 'BTC/USDT', active: true, user_id: 'user-1', buy_price: '50000', sell_price: '55000', quantity: '1', status: 'active', is_crypto: true, is_real_trade: false, label: 'Test Entry' }; const mockBotState: any = { orders: [], symbols: {}, alerts: [], positions: [], history: [], settings: { executionMode: 'Paper' } }; beforeEach(() => { vi.clearAllMocks(); authState.user = { id: 'user-1', email: 'test@demo.test' }; authState.profile = { role: 'user' }; vi.stubGlobal('confirm', confirmMock); vi.stubGlobal('crypto', { randomUUID: () => 'uuid-123' }); fetchManualEntriesMock.mockResolvedValue([mockEntry]); deleteManualEntryMock.mockResolvedValue(undefined); createManualEntryMock.mockResolvedValue({}); }); it('handles entry drawer interactions', async () => { const user = userEvent.setup(); render(); await waitFor(() => expect(screen.getByText('BTC/USDT')).toBeInTheDocument()); await user.click(screen.getByText(/Manual Entry Injection/i)); expect(screen.getByText(/New Opportunity Initialization/i)).toBeInTheDocument(); await user.click(screen.getByText('SubmitEntry')); expect(screen.queryByText(/New Opportunity Initialization/i)).not.toBeInTheDocument(); }); it('derives diverse entry states (LOCKED, BLOCKED, SUBMITTED, CONFIRMED, ORPHAN)', async () => { const user = userEvent.setup(); const entries = [ { stock_instance_id: 's1', symbol: 'S1', active: true, status: 'active', is_real_trade: false }, { stock_instance_id: 's2', symbol: 'S2', active: false, status: 'cooldown', is_real_trade: false }, // Use 'cooldown' instead of 'blocked' because 'blocked' contains 'lock' { stock_instance_id: 's3', symbol: 'S3', active: false, status: 'submitted', is_real_trade: false }, { stock_instance_id: 's4', symbol: 'S4', active: false, status: 'confirmed', is_real_trade: false }, { stock_instance_id: 's5', symbol: 'S5', active: false, status: 'orphan', is_real_trade: false }, ]; fetchManualEntriesMock.mockResolvedValue(entries); render(); await waitFor(() => expect(screen.getByText('S1')).toBeInTheDocument()); expect(screen.getByText('LOCKED')).toBeInTheDocument(); const suspendedTab = screen.getByRole('button', { name: /Suspended/i }); await user.click(suspendedTab); await waitFor(() => expect(screen.getByText('S2')).toBeInTheDocument(), { timeout: 3000 }); expect(screen.getByText('BLOCKED')).toBeInTheDocument(); expect(screen.getByText('SUBMITTED')).toBeInTheDocument(); expect(screen.getByText('CONFIRMED')).toBeInTheDocument(); expect(screen.getByText('ORPHAN')).toBeInTheDocument(); }); it('covers clone and delete triggers', async () => { render(); await waitFor(() => expect(screen.getByText('BTC/USDT')).toBeInTheDocument()); const card = screen.getByText('BTC/USDT').closest('.group'); const buttons = within(card as HTMLElement).getAllByRole('button'); await fireEvent.click(buttons[1]); expect(createManualEntryMock).toHaveBeenCalled(); confirmMock.mockReturnValue(true); await fireEvent.click(buttons[2]); expect(deleteManualEntryMock).toHaveBeenCalledWith('entry-1'); }); });