mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(terminal): clear stale search highlights (#9519)
Co-authored-by: kaynan <kaynan.camargo@terceiro-sky.com.br>
This commit is contained in:
co-authored by
kaynan
parent
a90ec540f2
commit
b5ae776c31
@@ -0,0 +1,86 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import type { SearchAddon } from '@xterm/addon-search'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import TerminalSearch from './TerminalSearch'
|
||||
|
||||
vi.mock('@/i18n/i18n', () => ({
|
||||
translate: (_key: string, fallback: string) => fallback
|
||||
}))
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function createSearchAddon(): SearchAddon {
|
||||
return {
|
||||
findNext: vi.fn(() => true),
|
||||
findPrevious: vi.fn(() => true),
|
||||
clearDecorations: vi.fn()
|
||||
} as unknown as SearchAddon
|
||||
}
|
||||
|
||||
function renderSearch(searchAddon: SearchAddon): ReturnType<typeof render> {
|
||||
return render(
|
||||
<TerminalSearch
|
||||
isOpen
|
||||
onClose={vi.fn()}
|
||||
searchAddon={searchAddon}
|
||||
searchStateRef={{ current: { query: '', caseSensitive: false, regex: false } }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
describe('TerminalSearch cleanup', () => {
|
||||
it('clears the current addon when the query is erased', async () => {
|
||||
const addon = createSearchAddon()
|
||||
const view = renderSearch(addon)
|
||||
|
||||
fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } })
|
||||
await waitFor(() => expect(addon.findNext).toHaveBeenCalled())
|
||||
vi.mocked(addon.clearDecorations).mockClear()
|
||||
vi.mocked(addon.findNext).mockClear()
|
||||
|
||||
fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: '' } })
|
||||
|
||||
await waitFor(() => expect(addon.clearDecorations).toHaveBeenCalledTimes(1))
|
||||
expect(addon.findNext).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
it('clears the previous addon when the search moves to another pane', async () => {
|
||||
const previousAddon = createSearchAddon()
|
||||
const nextAddon = createSearchAddon()
|
||||
const view = renderSearch(previousAddon)
|
||||
|
||||
fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } })
|
||||
await waitFor(() => expect(previousAddon.findNext).toHaveBeenCalled())
|
||||
vi.mocked(previousAddon.clearDecorations).mockClear()
|
||||
vi.mocked(previousAddon.findNext).mockClear()
|
||||
|
||||
view.rerender(
|
||||
<TerminalSearch
|
||||
isOpen
|
||||
onClose={vi.fn()}
|
||||
searchAddon={nextAddon}
|
||||
searchStateRef={{ current: { query: '', caseSensitive: false, regex: false } }}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(previousAddon.clearDecorations).toHaveBeenCalledTimes(1)
|
||||
expect(previousAddon.findNext).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
it('clears the addon when the search portal unmounts', async () => {
|
||||
const addon = createSearchAddon()
|
||||
const view = renderSearch(addon)
|
||||
|
||||
fireEvent.change(view.getByPlaceholderText('Search...'), { target: { value: 'needle' } })
|
||||
await waitFor(() => expect(addon.findNext).toHaveBeenCalled())
|
||||
vi.mocked(addon.clearDecorations).mockClear()
|
||||
vi.mocked(addon.findNext).mockClear()
|
||||
|
||||
view.unmount()
|
||||
|
||||
expect(addon.clearDecorations).toHaveBeenCalledTimes(1)
|
||||
expect(addon.findNext).toHaveBeenCalledWith('')
|
||||
})
|
||||
})
|
||||
@@ -14,6 +14,15 @@ type TerminalSearchProps = {
|
||||
searchStateRef: React.RefObject<SearchState>
|
||||
}
|
||||
|
||||
function clearTerminalSearch(searchAddon: SearchAddon | null): void {
|
||||
if (!searchAddon) {
|
||||
return
|
||||
}
|
||||
searchAddon.clearDecorations()
|
||||
// Why: xterm keeps the active match selected after decorations are cleared.
|
||||
searchAddon.findNext('')
|
||||
}
|
||||
|
||||
export default function TerminalSearch({
|
||||
isOpen,
|
||||
onClose,
|
||||
@@ -72,17 +81,24 @@ export default function TerminalSearch({
|
||||
input?.focus()
|
||||
}, [])
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
clearTerminalSearch(searchAddon)
|
||||
},
|
||||
[searchAddon]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// Keep the ref in sync so the keyboard handler (Cmd+G / Cmd+Shift+G)
|
||||
// can read the current search state without lifting it to parent state.
|
||||
searchStateRef.current = { query: requestQuery ?? '', caseSensitive, regex }
|
||||
|
||||
if (!isOpen) {
|
||||
searchAddon?.clearDecorations()
|
||||
clearTerminalSearch(searchAddon)
|
||||
return
|
||||
}
|
||||
if (!requestQuery) {
|
||||
searchAddon?.clearDecorations()
|
||||
clearTerminalSearch(searchAddon)
|
||||
return
|
||||
}
|
||||
if (searchAddon) {
|
||||
|
||||
Reference in New Issue
Block a user