import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; import { EntityManagerPage } from '../desktop/src/renderer/pages/entity-manager-page'; /** * Mock window.electron with entity IPC methods */ const mockIpcRenderer = { invoke: jest.fn(), on: jest.fn(), off: jest.fn(), send: jest.fn(), }; Object.defineProperty(window, 'electron', { value: { ipcRenderer: mockIpcRenderer, }, writable: true, }); /** * Test data for entity associations */ const mockEntities = [ { id: 1, entityId: '0.0.12345', entityName: 'Test Topic', entityType: 'topic', transactionId: '0.0.12345-123456789-000000000', sessionId: 'session-1', createdAt: new Date('2023-01-01T10:00:00Z'), updatedAt: new Date('2023-01-01T10:00:00Z'), isActive: true, metadata: null, }, { id: 2, entityId: '0.0.67890', entityName: 'Test Token', entityType: 'token', transactionId: '0.0.67890-123456789-000000001', sessionId: 'session-1', createdAt: new Date('2023-01-02T10:00:00Z'), updatedAt: new Date('2023-01-02T10:00:00Z'), isActive: true, metadata: '{"symbol":"TTK","decimals":2}', }, ]; const renderWithRouter = (component: React.ReactElement) => { return render( {component} ); }; describe('EntityManagerPage', () => { beforeEach(() => { jest.clearAllMocks(); mockIpcRenderer.invoke.mockImplementation((channel: string) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: mockEntities }); } return Promise.resolve({ success: true, data: null }); }); }); afterEach(() => { jest.restoreAllMocks(); }); it('should render page header and title', async () => { renderWithRouter(); await waitFor(() => { expect(screen.getByText('Entity Manager')).toBeInTheDocument(); }); }); it('should load entities on mount', async () => { renderWithRouter(); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:getAll', undefined); }); }); it('should display loading state initially', () => { renderWithRouter(); expect(screen.getByText('Loading entities...')).toBeInTheDocument(); }); it('should display entities after loading', async () => { renderWithRouter(); await waitFor(() => { expect(screen.getByText('Test Topic')).toBeInTheDocument(); expect(screen.getByText('Test Token')).toBeInTheDocument(); }); }); it('should display search filter component', async () => { renderWithRouter(); await waitFor(() => { expect(screen.getByPlaceholderText('Search entities...')).toBeInTheDocument(); }); }); it('should display entity type filter', async () => { renderWithRouter(); await waitFor(() => { expect(screen.getByLabelText('Entity Type')).toBeInTheDocument(); }); }); it('should handle entity deletion', async () => { const user = userEvent.setup(); mockIpcRenderer.invoke.mockImplementation((channel: string, ..._args: unknown[]) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: mockEntities }); } if (channel === 'entity:delete') { return Promise.resolve({ success: true }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Test Topic')).toBeInTheDocument(); }); const deleteButton = screen.getAllByLabelText('Delete entity')[0]; await user.click(deleteButton); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:delete', '0.0.12345'); }); }); it('should handle search filtering', async () => { const user = userEvent.setup(); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Test Topic')).toBeInTheDocument(); }); const searchInput = screen.getByPlaceholderText('Search entities...'); await user.type(searchInput, 'Topic'); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:getAll', { search: 'Topic' }); }); }); it('should handle entity type filtering', async () => { const user = userEvent.setup(); renderWithRouter(); await waitFor(() => { expect(screen.getByLabelText('Entity Type')).toBeInTheDocument(); }); const typeFilter = screen.getByLabelText('Entity Type'); await user.selectOptions(typeFilter, 'topic'); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:getAll', { entityType: 'topic' }); }); }); it('should display error message when loading fails', async () => { mockIpcRenderer.invoke.mockImplementation((channel: string) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: false, error: 'Database connection failed' }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Error loading entities: Database connection failed')).toBeInTheDocument(); }); }); it('should display bulk actions when entities are selected', async () => { const user = userEvent.setup(); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Test Topic')).toBeInTheDocument(); }); const checkbox = screen.getAllByRole('checkbox')[0]; await user.click(checkbox); await waitFor(() => { expect(screen.getByText('1 entity selected')).toBeInTheDocument(); expect(screen.getByText('Delete Selected')).toBeInTheDocument(); }); }); it('should handle bulk delete operation', async () => { const user = userEvent.setup(); mockIpcRenderer.invoke.mockImplementation((channel: string, ..._args: unknown[]) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: mockEntities }); } if (channel === 'entity:bulkDelete') { return Promise.resolve({ success: true }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Test Topic')).toBeInTheDocument(); }); const checkbox = screen.getAllByRole('checkbox')[0]; await user.click(checkbox); const deleteSelectedButton = await screen.findByText('Delete Selected'); await user.click(deleteSelectedButton); const confirmButton = await screen.findByText('Confirm Delete'); await user.click(confirmButton); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:bulkDelete', ['0.0.12345']); }); }); it('should display empty state when no entities exist', async () => { mockIpcRenderer.invoke.mockImplementation((channel: string) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: [] }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('No entities found')).toBeInTheDocument(); expect(screen.getByText('Create entities using the available tools to see them here.')).toBeInTheDocument(); }); }); it('should support virtual scrolling for large datasets', async () => { const largeDataset = Array.from({ length: 1000 }, (_, i) => ({ id: i + 1, entityId: `0.0.${12345 + i}`, entityName: `Entity ${i + 1}`, entityType: 'topic', transactionId: `0.0.${12345 + i}-123456789-00000000${i}`, sessionId: 'session-1', createdAt: new Date(`2023-01-01T${String(10 + (i % 12)).padStart(2, '0')}:00:00Z`), updatedAt: new Date(`2023-01-01T${String(10 + (i % 12)).padStart(2, '0')}:00:00Z`), isActive: true, metadata: null, })); mockIpcRenderer.invoke.mockImplementation((channel: string) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: largeDataset }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Entity 1')).toBeInTheDocument(); }); expect(screen.queryByText('Entity 500')).not.toBeInTheDocument(); }); it('should handle real-time entity updates', async () => { renderWithRouter(); expect(mockIpcRenderer.on).toHaveBeenCalledWith('entity:created', expect.any(Function)); expect(mockIpcRenderer.on).toHaveBeenCalledWith('entity:deleted', expect.any(Function)); }); it('should support export functionality', async () => { const user = userEvent.setup(); mockIpcRenderer.invoke.mockImplementation((channel: string, ..._args: unknown[]) => { if (channel === 'entity:getAll') { return Promise.resolve({ success: true, data: mockEntities }); } if (channel === 'entity:export') { return Promise.resolve({ success: true, data: 'exported-file-path' }); } return Promise.resolve({ success: true }); }); renderWithRouter(); await waitFor(() => { expect(screen.getByText('Export')).toBeInTheDocument(); }); const exportButton = screen.getByText('Export'); await user.click(exportButton); const exportJsonButton = await screen.findByText('Export as JSON'); await user.click(exportJsonButton); await waitFor(() => { expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('entity:export', undefined, 'json'); }); }); });