@@ -46,6 +46,8 @@ vi.mock('components/redpanda-ui/components/data-table', async (importOriginal) =
4646 } ;
4747} ) ;
4848
49+ Element . prototype . scrollIntoView = vi . fn ( ) ;
50+
4951import { SecretsStoreListPage } from './secrets-store-list-page' ;
5052
5153const createListSecretsTransport = ( listSecretsMock : ReturnType < typeof vi . fn > ) =>
@@ -180,4 +182,126 @@ describe('SecretsStoreListPage', () => {
180182
181183 expect ( screen . getByRole ( 'button' , { name : 'Next Page' } ) ) . toBeDisabled ( ) ;
182184 } ) ;
185+
186+ test ( 'search input updates value on keystrokes' , async ( ) => {
187+ const user = userEvent . setup ( ) ;
188+
189+ const secret1 = create ( SecretSchema , {
190+ id : 'my-secret' ,
191+ labels : { } ,
192+ scopes : [ Scope . AI_GATEWAY ] ,
193+ } ) ;
194+
195+ const listSecretsMock = vi . fn ( ) . mockReturnValue (
196+ create ( ListSecretsResponseSchema , {
197+ response : { secrets : [ secret1 ] , nextPageToken : '' } ,
198+ } )
199+ ) ;
200+ const transport = createListSecretsTransport ( listSecretsMock ) ;
201+
202+ renderWithFileRoutes ( < SecretsStoreListPage /> , { transport } ) ;
203+
204+ await waitFor ( ( ) => {
205+ expect ( screen . getByText ( 'my-secret' ) ) . toBeVisible ( ) ;
206+ } ) ;
207+
208+ const filterInput = screen . getByPlaceholderText ( 'Filter by ID...' ) ;
209+ await user . type ( filterInput , 'hello' ) ;
210+
211+ // Input value must reflect typed text — a React Compiler memoization
212+ // bug would freeze it at the initial empty string.
213+ expect ( filterInput ) . toHaveValue ( 'hello' ) ;
214+ } ) ;
215+
216+ test ( 'filters secrets by ID via search input' , async ( ) => {
217+ const user = userEvent . setup ( ) ;
218+
219+ const secret1 = create ( SecretSchema , {
220+ id : 'alpha-secret' ,
221+ labels : { } ,
222+ scopes : [ Scope . AI_GATEWAY ] ,
223+ } ) ;
224+
225+ const secret2 = create ( SecretSchema , {
226+ id : 'beta-secret' ,
227+ labels : { } ,
228+ scopes : [ Scope . MCP_SERVER ] ,
229+ } ) ;
230+
231+ const listSecretsMock = vi . fn ( ) . mockReturnValue (
232+ create ( ListSecretsResponseSchema , {
233+ response : { secrets : [ secret1 , secret2 ] , nextPageToken : '' } ,
234+ } )
235+ ) ;
236+ const transport = createListSecretsTransport ( listSecretsMock ) ;
237+
238+ renderWithFileRoutes ( < SecretsStoreListPage /> , { transport } ) ;
239+
240+ await waitFor ( ( ) => {
241+ expect ( screen . getByText ( 'alpha-secret' ) ) . toBeVisible ( ) ;
242+ expect ( screen . getByText ( 'beta-secret' ) ) . toBeVisible ( ) ;
243+ } ) ;
244+
245+ const filterInput = screen . getByPlaceholderText ( 'Filter by ID...' ) ;
246+ await user . type ( filterInput , 'beta' ) ;
247+
248+ await waitFor ( ( ) => {
249+ expect ( screen . getByText ( 'beta-secret' ) ) . toBeVisible ( ) ;
250+ expect ( screen . queryByText ( 'alpha-secret' ) ) . not . toBeInTheDocument ( ) ;
251+ } ) ;
252+
253+ // Clear and verify all rows reappear
254+ await user . clear ( filterInput ) ;
255+
256+ await waitFor ( ( ) => {
257+ expect ( screen . getByText ( 'alpha-secret' ) ) . toBeVisible ( ) ;
258+ expect ( screen . getByText ( 'beta-secret' ) ) . toBeVisible ( ) ;
259+ } ) ;
260+ } ) ;
261+
262+ test ( 'scope faceted filter filters results' , async ( ) => {
263+ const user = userEvent . setup ( ) ;
264+
265+ const secret1 = create ( SecretSchema , {
266+ id : 'gateway-secret' ,
267+ labels : { } ,
268+ scopes : [ Scope . AI_GATEWAY ] ,
269+ } ) ;
270+
271+ const secret2 = create ( SecretSchema , {
272+ id : 'mcp-secret' ,
273+ labels : { } ,
274+ scopes : [ Scope . MCP_SERVER ] ,
275+ } ) ;
276+
277+ const listSecretsMock = vi . fn ( ) . mockReturnValue (
278+ create ( ListSecretsResponseSchema , {
279+ response : { secrets : [ secret1 , secret2 ] , nextPageToken : '' } ,
280+ } )
281+ ) ;
282+ const transport = createListSecretsTransport ( listSecretsMock ) ;
283+
284+ renderWithFileRoutes ( < SecretsStoreListPage /> , { transport } ) ;
285+
286+ await waitFor ( ( ) => {
287+ expect ( screen . getByText ( 'gateway-secret' ) ) . toBeVisible ( ) ;
288+ expect ( screen . getByText ( 'mcp-secret' ) ) . toBeVisible ( ) ;
289+ } ) ;
290+
291+ // Click the "Scope" faceted filter button (not the column header one in <thead>)
292+ const scopeFilterButton = screen
293+ . getAllByRole ( 'button' , { name : / s c o p e / i } )
294+ . find ( ( btn ) => ! btn . closest ( 'thead' ) ) ! ;
295+ await user . click ( scopeFilterButton ) ;
296+
297+ // Select the "MCP Server" option from the filter popover
298+ const mcpOption = await screen . findByRole ( 'option' , { name : / m c p s e r v e r / i } ) ;
299+ await user . click ( mcpOption ) ;
300+
301+ // Only the MCP-scoped secret should remain visible
302+ await waitFor ( ( ) => {
303+ expect ( screen . getByText ( 'mcp-secret' ) ) . toBeVisible ( ) ;
304+ expect ( screen . queryByText ( 'gateway-secret' ) ) . not . toBeInTheDocument ( ) ;
305+ } ) ;
306+ } ) ;
183307} ) ;
0 commit comments