import { ResourceSearch, Resource } from '@/components/Attendees/ResourceSearch' import { searchUsers } from '@/features/User/userAPI' import { act, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { renderWithProviders } from '../utils/Renderwithproviders' jest.mock('@/features/User/userAPI') const mockedSearchUsers = searchUsers as jest.MockedFunction describe('ResourceSearch', () => { const baseResource: Resource = { displayName: 'Projector Room' } function setup( selectedResources: Resource[] = [], props?: Partial> ) { const onChange = jest.fn() renderWithProviders( ) return { onChange } } beforeEach(() => { jest.useFakeTimers() mockedSearchUsers.mockReset() }) afterEach(() => { jest.useRealTimers() }) it('calls searchUsers after debounce when typing', async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) }) await waitFor(() => { expect(mockedSearchUsers).toHaveBeenCalledWith('Room', ['resource']) }) }) it('renders search results and allows selection', async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) const { onChange } = setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) }) const option = await screen.findByText('Projector Room') await userEvent.click(option) await waitFor(() => { expect(onChange).toHaveBeenCalled() }) }) it('does not show already selected resources in options', async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) setup([baseResource]) const input = screen.getByRole('combobox') await userEvent.type(input, 'Projector') await act(async () => { jest.advanceTimersByTime(300) }) await waitFor(() => { // It shouldn't be in the dropdown options anymore const options = screen.queryAllByRole('option') expect( options.find(opt => opt.textContent === 'Projector Room') ).toBeUndefined() }) }) it('respects disabled state', () => { setup([], { disabled: true }) expect(screen.getByRole('combobox')).toBeDisabled() }) it("no options doesn't show dropdown when input is empty", async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) }) await waitFor(() => { expect(screen.getByRole('listbox')).toBeInTheDocument() }) await userEvent.clear(input) await waitFor(() => { expect(screen.queryByRole('listbox')).not.toBeInTheDocument() }) }) it("shows 'No results' when search succeeds but returns empty array", async () => { mockedSearchUsers.mockResolvedValueOnce([]) setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) await Promise.resolve() await Promise.resolve() }) const noResults = await screen.findByText( 'resourceSearch.noResults', {}, { timeout: 5000 } ) expect(noResults).toBeInTheDocument() }) it('clears options when search fails and shows error snackbar', async () => { mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) await Promise.resolve() }) await waitFor(() => { expect(screen.getByText('Projector Room')).toBeInTheDocument() }) mockedSearchUsers.mockRejectedValueOnce(new Error('Network error')) await userEvent.clear(input) await userEvent.type(input, 'Error') await act(async () => { jest.advanceTimersByTime(300) await Promise.resolve() }) const errorMessage = await screen.findByText('resourceSearch.searchError') expect(errorMessage).toBeInTheDocument() expect(screen.queryByText('Projector Room')).not.toBeInTheDocument() mockedSearchUsers.mockResolvedValueOnce([ baseResource ] as unknown as Awaited>) await userEvent.clear(input) await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) await Promise.resolve() }) await waitFor(() => { expect(screen.getByText('Projector Room')).toBeInTheDocument() }) }) it('shows loading text when searching', async () => { let resolveSearch: (value: Resource[]) => void const searchPromise = new Promise(resolve => { resolveSearch = resolve }) mockedSearchUsers.mockReturnValueOnce( searchPromise as unknown as ReturnType ) setup() const input = screen.getByRole('combobox') await userEvent.type(input, 'Room') await act(async () => { jest.advanceTimersByTime(300) }) const loadingText = await screen.findByText( 'resourceSearch.loading', {}, { timeout: 5000 } ) expect(loadingText).toBeInTheDocument() await act(async () => { resolveSearch!([baseResource]) await searchPromise }) }) })