From 6a1d027b243b0250f6b453779e87d04ef3703da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Urba=C5=84ski?= Date: Sat, 6 Sep 2025 04:04:07 +0200 Subject: [PATCH] #4927 Fix search database on list not filtering out databases that are not connected. Refactor tests for better understanding. --- .../SearchDatabasesList.spec.tsx | 142 +++++++++++++----- .../SearchDatabasesList.tsx | 1 + 2 files changed, 103 insertions(+), 40 deletions(-) diff --git a/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.spec.tsx b/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.spec.tsx index c1bc144a32..a9ba02c1ec 100644 --- a/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.spec.tsx +++ b/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.spec.tsx @@ -11,7 +11,7 @@ import { } from 'uiSrc/utils/test-utils' import { loadInstancesSuccess } from 'uiSrc/slices/instances/instances' import { RootState, store } from 'uiSrc/slices/store' -import { Instance } from 'uiSrc/slices/interfaces' +import { ConnectionType, Instance } from 'uiSrc/slices/interfaces' import SearchDatabasesList from './SearchDatabasesList' jest.mock('react-redux', () => ({ @@ -20,7 +20,7 @@ jest.mock('react-redux', () => ({ })) let storeMock: typeof mockedStore -const instancesMock: Instance[] = [ +const connectedInstancesMock: Instance[] = [ { id: '1', name: 'local', @@ -28,6 +28,7 @@ const instancesMock: Instance[] = [ port: 6379, visible: true, modules: [], + connectionType: ConnectionType.Sentinel, lastConnection: new Date(), tags: [ { @@ -47,19 +48,56 @@ const instancesMock: Instance[] = [ port: 6379, visible: true, modules: [], + connectionType: ConnectionType.Cluster, lastConnection: new Date(), tags: [], version: '', - }, + } ] -beforeEach(() => { - cleanup() - storeMock = cloneDeep(mockedStore) - storeMock.clearActions() - - const state: RootState = store.getState() +const otherInstancesMock: Instance[] = [ + /* + Reasoning behind the mock data: + - The 'not_connected' instance simulates a Redis instance that is not currently connected. + - The 'some_future_unrecognized_connection_type' instance represents a Redis instance with a connection type that is not + recognized by the current application logic (e.g something is added in future and this part of the application is not yet aware of it). + */ + { + id: '3', + name: 'not_connected', + host: 'not_connected', + port: 6379, + visible: true, + modules: [], + connectionType: 'NOT CONNECTED' as any, + tags: [], + version: '', + }, + { + id: '4', + name: 'some_future_unrecognized_connection_type', + host: 'some_future_unrecognized_connection_type', + port: 6379, + visible: true, + modules: [], + connectionType: 'UNRECOGNIZED' as any, + tags: [], + version: '', + }, + { + id: '5', + name: 'undefined_connection_type', + host: 'undefined_connection_type', + port: 6379, + visible: true, + modules: [], + connectionType: undefined, + tags: [], + version: '', + } +] +const mockInitialState = (state: RootState, instances: Instance[], options?: { selectedTags?: Set }) => { ;(useSelector as jest.Mock).mockImplementation( (callback: (arg0: RootState) => RootState) => callback({ @@ -68,63 +106,87 @@ beforeEach(() => { ...state.connections, instances: { ...state.connections.instances, - data: instancesMock, + data: instances, }, tags: { ...state.connections.tags, - selectedTags: new Set(['env:prod']), + selectedTags: options?.selectedTags ?? state.connections.tags?.selectedTags, }, }, }), ) +} + +const simulateUserTypedInSearchBox = async (value: string) => { + await act(() => { + fireEvent.change(screen.getByTestId('search-database-list'), { + target: { value }, + }) + }) +} + +beforeEach(() => { + cleanup() + storeMock = cloneDeep(mockedStore) + storeMock.clearActions() }) describe('SearchDatabasesList', () => { it('should render', () => { + mockInitialState( + store.getState(), + connectedInstancesMock + ) expect(render()).toBeTruthy() }) - it('should call loadInstancesSuccess with after typing', async () => { - const state: RootState = store.getState() - ;(useSelector as jest.Mock).mockImplementation( - (callback: (arg0: RootState) => RootState) => - callback({ - ...state, - connections: { - ...state.connections, - instances: { - ...state.connections.instances, - data: instancesMock, - }, - }, - }), + it.each([ + { + description: 'with connected instances', + instancesMock: connectedInstancesMock, + expectedInstances: [ + { ...connectedInstancesMock[0], visible: false }, + { ...connectedInstancesMock[1], visible: false } + ] + }, + { + description: 'with other than connected connectionType', + instancesMock: otherInstancesMock, + expectedInstances: [ + { ...otherInstancesMock[0], visible: false }, + { ...otherInstancesMock[1], visible: false }, + { ...otherInstancesMock[2], visible: false } + ] + } + ])('should call loadInstancesSuccess with all instances hidden after typing value not matching anything ($description)', async ({ instancesMock, expectedInstances }) => { + mockInitialState( + store.getState(), + instancesMock ) - const newInstancesMock = [...instancesMock] render() - await act(() => { - fireEvent.change(screen.getByTestId('search-database-list'), { - target: { value: 'test' }, - }) - }) - - newInstancesMock[0].visible = false - newInstancesMock[1].visible = false + await simulateUserTypedInSearchBox('value_which_matches_nothing') - const expectedActions = [loadInstancesSuccess(newInstancesMock)] + const expectedActions = [loadInstancesSuccess(expectedInstances)] expect(storeMock.getActions()).toEqual(expectedActions) }) - it('should call loadInstancesSuccess after selected tags state changes', async () => { - const newInstancesMock = [ - { ...instancesMock[0], visible: true }, - { ...instancesMock[1], visible: false }, + it('should call loadInstancesSuccess with not matching instances hidden when selected tags in state are provided', async () => { + mockInitialState( + store.getState(), + connectedInstancesMock, + { selectedTags: new Set(['env:prod']) } + ) + + const expectedInstancesAfterRendering = [ + { ...connectedInstancesMock[0], visible: true }, + { ...connectedInstancesMock[1], visible: false } ] render() - const expectedActions = [loadInstancesSuccess(newInstancesMock)] + const expectedActions = [loadInstancesSuccess(expectedInstancesAfterRendering)] expect(storeMock.getActions()).toEqual(expectedActions) }) }) diff --git a/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.tsx b/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.tsx index bedbf4be3b..526d25df0d 100644 --- a/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.tsx +++ b/redisinsight/ui/src/pages/home/components/search-databases-list/SearchDatabasesList.tsx @@ -45,6 +45,7 @@ const SearchDatabasesList = () => { item.host?.toString()?.indexOf(value) !== -1 || item.port?.toString()?.indexOf(value) !== -1 || (item.connectionType && + CONNECTION_TYPE_DISPLAY[item.connectionType] && CONNECTION_TYPE_DISPLAY[item.connectionType] ?.toLowerCase() ?.indexOf(value) !== -1) ||