mirror of https://github.com/langgenius/dify.git
189 lines
5.3 KiB
TypeScript
189 lines
5.3 KiB
TypeScript
import type { ScopeDescriptor } from './actions/scope-registry'
|
|
import type { SearchResult } from './actions/types'
|
|
import { act, render, screen } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import * as React from 'react'
|
|
import GotoAnything from './index'
|
|
|
|
const routerPush = vi.fn()
|
|
vi.mock('next/navigation', () => ({
|
|
useRouter: () => ({
|
|
push: routerPush,
|
|
}),
|
|
usePathname: () => '/',
|
|
}))
|
|
|
|
const keyPressHandlers: Record<string, (event: any) => void> = {}
|
|
vi.mock('ahooks', () => ({
|
|
useDebounce: (value: any) => value,
|
|
useKeyPress: (keys: string | string[], handler: (event: any) => void) => {
|
|
const keyList = Array.isArray(keys) ? keys : [keys]
|
|
keyList.forEach((key) => {
|
|
keyPressHandlers[key] = handler
|
|
})
|
|
},
|
|
}))
|
|
|
|
const triggerKeyPress = (combo: string) => {
|
|
const handler = keyPressHandlers[combo]
|
|
if (handler) {
|
|
act(() => {
|
|
handler({ preventDefault: vi.fn(), target: document.body })
|
|
})
|
|
}
|
|
}
|
|
|
|
let mockQueryResult = { data: [] as SearchResult[], isLoading: false, isError: false, error: null as Error | null }
|
|
vi.mock('@tanstack/react-query', () => ({
|
|
useQuery: () => mockQueryResult,
|
|
}))
|
|
|
|
vi.mock('@/context/i18n', () => ({
|
|
useGetLanguage: () => 'en_US',
|
|
}))
|
|
|
|
const contextValue = { isWorkflowPage: false, isRagPipelinePage: false }
|
|
vi.mock('./context', () => ({
|
|
useGotoAnythingContext: () => contextValue,
|
|
GotoAnythingProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
}))
|
|
|
|
type MatchAction = typeof import('./actions').matchAction
|
|
type SearchAnything = typeof import('./actions').searchAnything
|
|
|
|
const mockState = vi.hoisted(() => {
|
|
const state = {
|
|
scopes: [] as ScopeDescriptor[],
|
|
useGotoAnythingScopesMock: vi.fn(() => state.scopes),
|
|
matchActionMock: vi.fn<MatchAction>(() => undefined),
|
|
searchAnythingMock: vi.fn<SearchAnything>(async () => []),
|
|
}
|
|
|
|
return state
|
|
})
|
|
|
|
vi.mock('./actions', () => ({
|
|
__esModule: true,
|
|
matchAction: (...args: Parameters<MatchAction>) => mockState.matchActionMock(...args),
|
|
searchAnything: (...args: Parameters<SearchAnything>) => mockState.searchAnythingMock(...args),
|
|
useGotoAnythingScopes: () => mockState.useGotoAnythingScopesMock(),
|
|
}))
|
|
|
|
vi.mock('./actions/commands', () => ({
|
|
SlashCommandProvider: () => null,
|
|
executeCommand: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('./actions/commands/registry', () => ({
|
|
slashCommandRegistry: {
|
|
findCommand: () => null,
|
|
getAvailableCommands: () => [],
|
|
getAllCommands: () => [],
|
|
},
|
|
}))
|
|
|
|
const createScope = (id: ScopeDescriptor['id'], shortcut: string): ScopeDescriptor => ({
|
|
id,
|
|
shortcut,
|
|
title: `${id} title`,
|
|
description: `${id} desc`,
|
|
search: vi.fn(),
|
|
})
|
|
|
|
const scopesMock = [
|
|
createScope('slash', '/'),
|
|
createScope('app', '@app'),
|
|
createScope('plugin', '@plugin'),
|
|
]
|
|
|
|
vi.mock('@/app/components/workflow/utils/common', () => ({
|
|
getKeyboardKeyCodeBySystem: () => 'ctrl',
|
|
isEventTargetInputArea: () => false,
|
|
isMac: () => false,
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/utils/node-navigation', () => ({
|
|
selectWorkflowNode: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../plugins/install-plugin/install-from-marketplace', () => ({
|
|
default: (props: { manifest?: { name?: string }, onClose: () => void }) => (
|
|
<div data-testid="install-modal">
|
|
<span>{props.manifest?.name}</span>
|
|
<button onClick={props.onClose}>close</button>
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
describe('GotoAnything', () => {
|
|
beforeEach(() => {
|
|
routerPush.mockClear()
|
|
Object.keys(keyPressHandlers).forEach(key => delete keyPressHandlers[key])
|
|
mockQueryResult = { data: [], isLoading: false, isError: false, error: null }
|
|
mockState.scopes = scopesMock
|
|
mockState.matchActionMock.mockReset()
|
|
mockState.searchAnythingMock.mockClear()
|
|
mockState.searchAnythingMock.mockImplementation(async () => mockQueryResult.data)
|
|
})
|
|
|
|
it('should open modal via shortcut and navigate to selected result', async () => {
|
|
mockQueryResult = {
|
|
data: [{
|
|
id: 'app-1',
|
|
type: 'app',
|
|
title: 'Sample App',
|
|
description: 'desc',
|
|
path: '/apps/1',
|
|
icon: <div data-testid="icon">🧩</div>,
|
|
data: {},
|
|
} as any],
|
|
isLoading: false,
|
|
isError: false,
|
|
error: null,
|
|
}
|
|
|
|
render(<GotoAnything />)
|
|
|
|
triggerKeyPress('ctrl.k')
|
|
|
|
const input = await screen.findByPlaceholderText('app.gotoAnything.searchPlaceholder')
|
|
await userEvent.type(input, 'app')
|
|
|
|
const result = await screen.findByText('Sample App')
|
|
await userEvent.click(result)
|
|
|
|
expect(routerPush).toHaveBeenCalledWith('/apps/1')
|
|
})
|
|
|
|
it('should open plugin installer when selecting plugin result', async () => {
|
|
mockQueryResult = {
|
|
data: [{
|
|
id: 'plugin-1',
|
|
type: 'plugin',
|
|
title: 'Plugin Item',
|
|
description: 'desc',
|
|
path: '',
|
|
icon: <div />,
|
|
data: {
|
|
name: 'Plugin Item',
|
|
latest_package_identifier: 'pkg',
|
|
},
|
|
} as any],
|
|
isLoading: false,
|
|
isError: false,
|
|
error: null,
|
|
}
|
|
|
|
render(<GotoAnything />)
|
|
|
|
triggerKeyPress('ctrl.k')
|
|
const input = await screen.findByPlaceholderText('app.gotoAnything.searchPlaceholder')
|
|
await userEvent.type(input, 'plugin')
|
|
|
|
const pluginItem = await screen.findByText('Plugin Item')
|
|
await userEvent.click(pluginItem)
|
|
|
|
expect(await screen.findByTestId('install-modal')).toHaveTextContent('Plugin Item')
|
|
})
|
|
})
|