From 4251b888086d8fd9e7ddc756ae2ae72ddec29dfc Mon Sep 17 00:00:00 2001 From: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com> Date: Thu, 29 Oct 2026 09:02:48 +0530 Subject: [PATCH 1/3] FIX: restart finding evidence pagination on each opening --- .../Chat/FindingEvidenceDialog.test.tsx | 22 +++++++++++++++++++ .../components/Chat/FindingEvidenceDialog.tsx | 6 +++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx index b30efff3dd..2844641742 100644 --- a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx +++ b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx @@ -365,6 +365,28 @@ it('does not keep a cancelled selection when the picker reopens', async () => { expect(screen.getByRole('button', { name: 'Attach attack' })).toBeDisabled() }) +it('restarts pagination and search when the picker reopens', async () => { + const user = userEvent.setup() + ;(operationsApi.listFindings as jest.Mock).mockResolvedValue({ + items: [FINDING], has_more: true, next_offset: 20, + }) + renderDialog() + await waitFor(() => { expect(screen.getByRole('button', { name: 'Link to finding' })).toBeEnabled() }) + await user.click(screen.getByRole('button', { name: 'Link to finding' })) + await screen.findByRole('radio') + await user.click(screen.getByRole('button', { name: 'Next findings' })) + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 20, title: '' }) + }) + + await user.click(screen.getByRole('button', { name: 'Cancel' })) + await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) + await user.click(screen.getByRole('button', { name: 'Link to finding' })) + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 0, title: '' }) + }) +}) + it('drops the selection when the viewed attack changes while the picker is open', async () => { const user = userEvent.setup() const { rerender } = render( diff --git a/frontend/src/components/Chat/FindingEvidenceDialog.tsx b/frontend/src/components/Chat/FindingEvidenceDialog.tsx index a9ab5adcd3..dbb4b423df 100644 --- a/frontend/src/components/Chat/FindingEvidenceDialog.tsx +++ b/frontend/src/components/Chat/FindingEvidenceDialog.tsx @@ -60,10 +60,12 @@ export default function FindingEvidenceDialog({ const newFindingRef = useRef(null) const wasOpen = useRef(false) const wasCreating = useRef(false) - // Each opening starts without a previous selection or result. + // Each opening starts without a previous selection or result, and back at the + // first page of an unfiltered finding list, so a later opening never resumes + // the previous opening's pagination or search. if (open !== shownOpen) { setShownOpen(open) - if (open) { setPicked(null); setNotice(null); setAttachError('') } + if (open) { setPicked(null); setNotice(null); setAttachError(''); setOffset(0); setTitle('') } } // Evidence can come from any attack; the user picks whose findings to link, defaulting to the attack's own Operation. const operation = chosen?.key === viewKey ? chosen.operation From 90c354fdeac34db579a0f9479acd682b629783ef Mon Sep 17 00:00:00 2001 From: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com> Date: Sun, 1 Nov 2026 11:11:10 +0530 Subject: [PATCH 2/3] FIX: cover the search reset in the evidence dialog reopen test The regression never typed a search, so it passed with the title reset removed from the reopening effect. The test now enters a search, cancels, reopens, and asserts both the empty input and the unfiltered request, and it fails if either half of the reset is removed. Signed-off-by: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com> --- .../src/components/Chat/FindingEvidenceDialog.test.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx index 2844641742..ac288aacbc 100644 --- a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx +++ b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx @@ -379,9 +379,16 @@ it('restarts pagination and search when the picker reopens', async () => { expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 20, title: '' }) }) + await user.type(screen.getByRole('textbox'), 'Proof') + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 0, title: 'Proof' }) + }) + await user.click(screen.getByRole('button', { name: 'Cancel' })) await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) await user.click(screen.getByRole('button', { name: 'Link to finding' })) + await screen.findByRole('radio') + expect(screen.getByRole('textbox')).toHaveValue('') await waitFor(() => { expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 0, title: '' }) }) From 278e7ce44c066a45c9a183bc1a91fa9bd8be05e1 Mon Sep 17 00:00:00 2001 From: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com> Date: Mon, 2 Nov 2026 07:52:30 +0530 Subject: [PATCH 3/3] FIX: page forward again after the search in the evidence dialog reopen test The search handler already resets the offset, so the previous shape had no later page left at reopen. The combined test now pages forward after the Proof search settles, checks offset 20 with the title, and reopens to offset 0 with an empty title; a separate pagination only reopen test protects the original bug. Signed-off-by: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com> --- .../Chat/FindingEvidenceDialog.test.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx index ac288aacbc..8e6de34f32 100644 --- a/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx +++ b/frontend/src/components/Chat/FindingEvidenceDialog.test.tsx @@ -383,6 +383,10 @@ it('restarts pagination and search when the picker reopens', async () => { await waitFor(() => { expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 0, title: 'Proof' }) }) + await user.click(screen.getByRole('button', { name: 'Next findings' })) + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 20, title: 'Proof' }) + }) await user.click(screen.getByRole('button', { name: 'Cancel' })) await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) @@ -394,6 +398,29 @@ it('restarts pagination and search when the picker reopens', async () => { }) }) +it('restarts pagination when the picker reopens without a search', async () => { + const user = userEvent.setup() + ;(operationsApi.listFindings as jest.Mock).mockResolvedValue({ + items: [FINDING], has_more: true, next_offset: 20, + }) + renderDialog() + await waitFor(() => { expect(screen.getByRole('button', { name: 'Link to finding' })).toBeEnabled() }) + await user.click(screen.getByRole('button', { name: 'Link to finding' })) + await screen.findByRole('radio') + await user.click(screen.getByRole('button', { name: 'Next findings' })) + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 20, title: '' }) + }) + + await user.click(screen.getByRole('button', { name: 'Cancel' })) + await waitFor(() => { expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) + await user.click(screen.getByRole('button', { name: 'Link to finding' })) + await screen.findByRole('radio') + await waitFor(() => { + expect(operationsApi.listFindings).toHaveBeenLastCalledWith('saved', { limit: 20, offset: 0, title: '' }) + }) +}) + it('drops the selection when the viewed attack changes while the picker is open', async () => { const user = userEvent.setup() const { rerender } = render(