Skip to content

Commit dcae623

Browse files
test(secrets): add search and filter integration tests for list page
Add tests for search input and faceted filter functionality on the Secret Store list page to prevent regressions from React Compiler memoization issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e0211b4 commit dcae623

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

frontend/src/components/pages/secrets-store/secrets-store-list-page.test.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ vi.mock('components/redpanda-ui/components/data-table', async (importOriginal) =
4646
};
4747
});
4848

49+
Element.prototype.scrollIntoView = vi.fn();
50+
4951
import { SecretsStoreListPage } from './secrets-store-list-page';
5052

5153
const 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: /scope/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: /mcp server/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

Comments
 (0)