Skip to content

Commit 92b1c59

Browse files
committed
feat(ui): show list with available indexes in the manage indexes drawer
re RI-7197
1 parent a0c099a commit 92b1c59

File tree

5 files changed

+223
-2
lines changed

5 files changed

+223
-2
lines changed

redisinsight/ui/src/pages/vector-search/manage-indexes/ManageIndexesDrawer.spec.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ describe('ManageIndexesDrawer', () => {
3838
// const header = screen.getByText('Manage indexes')
3939
// expect(header).toBeInTheDocument()
4040

41-
// TODO: Check the body content, once implemented
4241
const body = screen.getByTestId('manage-indexes-drawer-body')
4342
expect(body).toBeInTheDocument()
43+
44+
const list = screen.getByTestId('manage-indexes-list')
45+
expect(list).toBeInTheDocument()
4446
})
4547
})

redisinsight/ui/src/pages/vector-search/manage-indexes/ManageIndexesDrawer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DrawerBody,
66
DrawerHeader,
77
} from 'uiSrc/components/base/layout/drawer'
8+
import { ManageIndexesList } from './ManageIndexesList'
89

910
export interface ManageIndexesDrawerProps extends DrawerProps {}
1011

@@ -21,7 +22,7 @@ export const ManageIndexesDrawer = ({
2122
>
2223
<DrawerHeader title="Manage indexes" />
2324
<DrawerBody data-testid="manage-indexes-drawer-body">
24-
<p>Manage indexes info will be added later</p>
25+
<ManageIndexesList />
2526
</DrawerBody>
2627
</Drawer>
2728
)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import React from 'react'
2+
import { cleanup, render, screen } from 'uiSrc/utils/test-utils'
3+
import {
4+
redisearchListSelector,
5+
fetchRedisearchListAction,
6+
} from 'uiSrc/slices/browser/redisearch'
7+
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
8+
import { ManageIndexesList } from './ManageIndexesList'
9+
10+
jest.mock('uiSrc/slices/browser/redisearch', () => ({
11+
...jest.requireActual('uiSrc/slices/browser/redisearch'),
12+
redisearchListSelector: jest.fn().mockReturnValue({
13+
data: [],
14+
loading: false,
15+
error: '',
16+
}),
17+
fetchRedisearchListAction: jest
18+
.fn()
19+
.mockReturnValue({ type: 'FETCH_REDISEARCH_LIST' }),
20+
}))
21+
22+
jest.mock('uiSrc/slices/instances/instances', () => ({
23+
...jest.requireActual('uiSrc/slices/instances/instances'),
24+
connectedInstanceSelector: jest.fn().mockReturnValue({
25+
id: 'test-instance-123',
26+
connectionType: 'STANDALONE',
27+
host: 'localhost',
28+
modules: [{ name: 'search' }],
29+
db: 0,
30+
}),
31+
}))
32+
33+
const renderComponent = () => render(<ManageIndexesList />)
34+
35+
describe('ManageIndexesList', () => {
36+
beforeEach(() => {
37+
cleanup()
38+
jest.clearAllMocks()
39+
40+
// Reset mocks to default state before each test
41+
;(redisearchListSelector as jest.Mock).mockReturnValue({
42+
data: [],
43+
loading: false,
44+
error: '',
45+
})
46+
;(connectedInstanceSelector as jest.Mock).mockReturnValue({
47+
id: 'test-instance-123',
48+
connectionType: 'STANDALONE',
49+
host: 'localhost',
50+
modules: [{ name: 'search' }],
51+
db: 0,
52+
})
53+
})
54+
55+
it('should render', () => {
56+
const { container } = renderComponent()
57+
58+
expect(container).toBeTruthy()
59+
60+
const list = screen.getByTestId('manage-indexes-list')
61+
expect(list).toBeInTheDocument()
62+
})
63+
64+
it('should render Loader spinner while fetching data', async () => {
65+
;(redisearchListSelector as jest.Mock).mockReturnValue({
66+
data: [],
67+
loading: true,
68+
error: '',
69+
})
70+
71+
renderComponent()
72+
73+
const loader = await screen.findByTestId('manage-indexes-list--loader')
74+
expect(loader).toBeInTheDocument()
75+
})
76+
77+
it('should render indexes boxes when data is available', () => {
78+
const mockIndexes = [
79+
Buffer.from('test-index-1'),
80+
Buffer.from('test-index-2'),
81+
]
82+
83+
;(redisearchListSelector as jest.Mock).mockReturnValue({
84+
data: mockIndexes,
85+
loading: false,
86+
error: '',
87+
})
88+
89+
renderComponent()
90+
91+
// Make sure the loader is not present
92+
const loader = screen.queryByTestId('manage-indexes-list--loader')
93+
expect(loader).not.toBeInTheDocument()
94+
95+
// Check if each index is rendered
96+
const items = screen.getAllByTestId(/^manage-indexes-list--item--/)
97+
expect(items.length).toBe(mockIndexes.length)
98+
99+
mockIndexes.forEach((index) => {
100+
const indexName = index.toString()
101+
const indexElement = screen.getByTestId(
102+
`manage-indexes-list--item--${indexName}`,
103+
)
104+
105+
expect(indexElement).toBeInTheDocument()
106+
})
107+
})
108+
109+
it('should not render indexes boxes when there is no instanceHost', () => {
110+
// Mock connectedInstanceSelector to return no host
111+
// TODO: Potential candidate for a factory function to create mock instances
112+
;(connectedInstanceSelector as jest.Mock).mockReturnValue({
113+
id: 'test-instance-123',
114+
connectionType: 'STANDALONE',
115+
host: null, // No host means no instanceId
116+
modules: [{ name: 'search' }],
117+
db: 0,
118+
})
119+
120+
renderComponent()
121+
122+
// Verify that fetchRedisearchListAction was not called
123+
expect(fetchRedisearchListAction).not.toHaveBeenCalled()
124+
125+
// Should still render the container but no data/loader
126+
const list = screen.getByTestId('manage-indexes-list')
127+
expect(list).toBeInTheDocument()
128+
129+
const loader = screen.queryByTestId('manage-indexes-list--loader')
130+
expect(loader).not.toBeInTheDocument()
131+
132+
const items = screen.queryAllByTestId(/^manage-indexes-list--item--/)
133+
expect(items.length).toBe(0)
134+
})
135+
136+
it('should not render indexes boxes when redisearch module is not available', () => {
137+
// Mock connectedInstanceSelector to return modules without redisearch
138+
// TODO: Potential candidate for a factory function to create mock instances
139+
;(connectedInstanceSelector as jest.Mock).mockReturnValue({
140+
id: 'test-instance-123',
141+
connectionType: 'STANDALONE',
142+
host: 'localhost',
143+
modules: [{ name: 'timeseries' }, { name: 'graph' }], // No search/redisearch module
144+
db: 0,
145+
})
146+
147+
renderComponent()
148+
149+
// Verify that fetchRedisearchListAction was not called
150+
expect(fetchRedisearchListAction).not.toHaveBeenCalled()
151+
152+
// Should still render the container but no data/loader
153+
const list = screen.getByTestId('manage-indexes-list')
154+
expect(list).toBeInTheDocument()
155+
156+
const loader = screen.queryByTestId('manage-indexes-list--loader')
157+
expect(loader).not.toBeInTheDocument()
158+
159+
const items = screen.queryAllByTestId(/^manage-indexes-list--item--/)
160+
expect(items.length).toBe(0)
161+
})
162+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import styled from 'styled-components'
2+
import { FlexGroup } from 'uiSrc/components/base/layout/flex'
3+
4+
export const StyledManageIndexesListAction = styled(FlexGroup)`
5+
flex-direction: column;
6+
`
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Loader } from '@redis-ui/components'
2+
import React, { useEffect } from 'react'
3+
import { useDispatch, useSelector } from 'react-redux'
4+
import {
5+
fetchRedisearchListAction,
6+
redisearchListSelector,
7+
} from 'uiSrc/slices/browser/redisearch'
8+
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
9+
import {
10+
bufferToString,
11+
formatLongName,
12+
isRedisearchAvailable,
13+
} from 'uiSrc/utils'
14+
import { StyledManageIndexesListAction } from './ManageIndexesList.styles'
15+
16+
export const ManageIndexesList = () => {
17+
const { loading, data } = useSelector(redisearchListSelector)
18+
const { modules, host: instanceHost } = useSelector(connectedInstanceSelector)
19+
20+
const dispatch = useDispatch()
21+
22+
useEffect(() => {
23+
if (!instanceHost) {
24+
return
25+
}
26+
27+
const moduleExists = isRedisearchAvailable(modules)
28+
if (moduleExists) {
29+
dispatch(fetchRedisearchListAction())
30+
}
31+
}, [instanceHost, modules])
32+
33+
return (
34+
<StyledManageIndexesListAction data-testid="manage-indexes-list">
35+
{loading && <Loader data-testid="manage-indexes-list--loader" />}
36+
37+
{data.map((index) => {
38+
const indexName = bufferToString(index)
39+
return (
40+
<div
41+
key={`index-${indexName}`}
42+
data-testid={`manage-indexes-list--item--${indexName}`}
43+
>
44+
{formatLongName(indexName)}
45+
</div>
46+
)
47+
})}
48+
</StyledManageIndexesListAction>
49+
)
50+
}

0 commit comments

Comments
 (0)