orca/src/renderer/src/lib/worktree-palette-search.tes...

247 lines
6.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { getWorktreePaletteSearchScope, searchWorktrees } from './worktree-palette-search'
import type { Repo, Worktree } from '../../../shared/types'
function makeWorktree(overrides: Partial<Worktree> = {}): Worktree {
return {
id: 'wt-1',
repoId: 'repo-1',
path: '/tmp/wt-1',
head: 'abc123',
branch: 'refs/heads/feature/worktree-jump',
isBare: false,
isMainWorktree: false,
displayName: 'Jump Palette',
comment: '',
linkedIssue: null,
linkedPR: null,
linkedLinearIssue: null,
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 0,
...overrides
}
}
const repoMap = new Map<string, Repo>([
[
'repo-1',
{
id: 'repo-1',
path: '/repo/orca',
displayName: 'stablyai/orca',
badgeColor: '#22c55e',
addedAt: 0
}
]
])
describe('worktree-palette-search', () => {
it('uses the filtered recent list for empty queries', () => {
const visible = makeWorktree({ id: 'visible' })
const hidden = makeWorktree({ id: 'hidden-by-filter' })
const scope = getWorktreePaletteSearchScope({
hasQuery: false,
allWorktrees: [visible, hidden],
emptyQueryWorktrees: [visible]
})
expect(scope.map((worktree) => worktree.id)).toEqual(['visible'])
})
it('uses all non-archived worktrees for typed queries', () => {
const visible = makeWorktree({ id: 'visible' })
const hiddenByFilter = makeWorktree({ id: 'hidden-by-filter' })
const archived = makeWorktree({ id: 'archived', isArchived: true })
const scope = getWorktreePaletteSearchScope({
hasQuery: true,
allWorktrees: [visible, hiddenByFilter, archived],
emptyQueryWorktrees: [visible]
})
expect(scope.map((worktree) => worktree.id)).toEqual(['visible', 'hidden-by-filter'])
})
it('returns every worktree with no match metadata for an empty query', () => {
const results = searchWorktrees([makeWorktree()], '', repoMap, null, null)
expect(results).toEqual([
{
worktreeId: 'wt-1',
matchedField: null,
displayNameRange: null,
branchRange: null,
repoRange: null,
supportingText: null
}
])
})
it('returns a truncated comment snippet with the highlighted match range', () => {
const results = searchWorktrees(
[
makeWorktree({
comment:
'This worktree carries the quick jump refresh implementation details for the new palette.'
})
],
'implementation',
repoMap,
null,
null
)
expect(results).toHaveLength(1)
expect(results[0].supportingText?.label).toBe('Comment')
expect(results[0].supportingText?.text).toContain('implementation')
expect(
results[0].supportingText?.text.slice(
results[0].supportingText.matchRange!.start,
results[0].supportingText.matchRange!.end
)
).toBe('implementation')
})
it('keeps PR title matches in the search result model instead of inferring them during render', () => {
const results = searchWorktrees(
[makeWorktree({ branch: 'refs/heads/feature/palette-refresh', linkedPR: 426 })],
'quick jump',
repoMap,
{
'/repo/orca::feature/palette-refresh': {
data: {
number: 426,
title: 'Refresh the worktree quick jump palette'
}
}
},
null
)
expect(results).toHaveLength(1)
expect(results[0].supportingText).toEqual({
label: 'PR',
text: 'Refresh the worktree quick jump palette',
matchRange: { start: 21, end: 31 }
})
})
it('preserves input order when query matches a repo name', () => {
const worktrees = [
makeWorktree({
id: 'wt-feature',
branch: 'refs/heads/feature/foo',
displayName: 'foo feature',
isMainWorktree: false
}),
makeWorktree({
id: 'wt-bugfix',
branch: 'refs/heads/bugfix/bar',
displayName: 'bar bugfix',
isMainWorktree: false
}),
makeWorktree({
id: 'wt-main',
branch: 'refs/heads/main',
displayName: 'main',
isMainWorktree: true
})
]
const results = searchWorktrees(worktrees, 'orca', repoMap, null, null)
// All three match on the repo name, order preserved from input
expect(results).toHaveLength(3)
expect(results[0].worktreeId).toBe('wt-feature')
expect(results[1].worktreeId).toBe('wt-bugfix')
expect(results[2].worktreeId).toBe('wt-main')
})
it('supports "repo/worktree" composite queries and highlights both segments', () => {
const worktrees = [
makeWorktree({
id: 'wt-main',
branch: 'refs/heads/main',
displayName: 'main'
}),
makeWorktree({
id: 'wt-feature',
branch: 'refs/heads/feature/foo',
displayName: 'feature foo'
})
]
const results = searchWorktrees(worktrees, 'orca/main', repoMap, null, null)
expect(results).toHaveLength(1)
expect(results[0].worktreeId).toBe('wt-main')
expect(results[0].matchedField).toBe('branch')
expect(results[0].repoRange).toEqual({ start: 9, end: 13 })
expect(results[0].branchRange).toEqual({ start: 0, end: 4 })
})
it('falls back to single-token matching when a composite query has no composite hits', () => {
const results = searchWorktrees(
[makeWorktree({ branch: 'refs/heads/feature/palette-refresh' })],
'feature/palette',
repoMap,
null,
null
)
expect(results).toHaveLength(1)
expect(results[0].matchedField).toBe('branch')
expect(results[0].branchRange).toEqual({ start: 0, end: 'feature/palette'.length })
})
it('matches issue numbers with a leading hash and returns issue render context', () => {
const results = searchWorktrees(
[makeWorktree({ linkedIssue: 304 })],
'#304',
repoMap,
null,
null
)
expect(results).toHaveLength(1)
expect(results[0].supportingText).toEqual({
label: 'Issue',
text: 'Issue #304',
matchRange: { start: 7, end: 10 }
})
})
it('matches workspace ports by port number before issue and PR numbers', () => {
const results = searchWorktrees(
[makeWorktree({ id: 'wt-port', linkedIssue: 3000 })],
'3000',
repoMap,
null,
null,
new Map([
[
'wt-port',
[
{
port: 3000,
processName: 'vite'
}
]
]
])
)
expect(results).toHaveLength(1)
expect(results[0].matchedField).toBe('port')
expect(results[0].supportingText).toEqual({
label: 'Port',
text: '3000 · vite',
matchRange: { start: 0, end: 4 }
})
})
})