Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
249 lines
7.6 KiB
TypeScript
249 lines
7.6 KiB
TypeScript
import React from 'react'
|
|
import { render, screen, waitFor } from '@testing-library/react'
|
|
import userEvent from '@testing-library/user-event'
|
|
import SettingBuiltInTool from './setting-built-in-tool'
|
|
import I18n from '@/context/i18n'
|
|
import { CollectionType, type Tool, type ToolParameter } from '@/app/components/tools/types'
|
|
|
|
const fetchModelToolList = jest.fn()
|
|
const fetchBuiltInToolList = jest.fn()
|
|
const fetchCustomToolList = jest.fn()
|
|
const fetchWorkflowToolList = jest.fn()
|
|
jest.mock('@/service/tools', () => ({
|
|
fetchModelToolList: (collectionName: string) => fetchModelToolList(collectionName),
|
|
fetchBuiltInToolList: (collectionName: string) => fetchBuiltInToolList(collectionName),
|
|
fetchCustomToolList: (collectionName: string) => fetchCustomToolList(collectionName),
|
|
fetchWorkflowToolList: (appId: string) => fetchWorkflowToolList(appId),
|
|
}))
|
|
|
|
type MockFormProps = {
|
|
value: Record<string, any>
|
|
onChange: (val: Record<string, any>) => void
|
|
}
|
|
let nextFormValue: Record<string, any> = {}
|
|
const FormMock = ({ value, onChange }: MockFormProps) => {
|
|
return (
|
|
<div data-testid="mock-form">
|
|
<div data-testid="form-value">{JSON.stringify(value)}</div>
|
|
<button
|
|
type="button"
|
|
onClick={() => onChange({ ...value, ...nextFormValue })}
|
|
>
|
|
update-form
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|
|
jest.mock('@/app/components/header/account-setting/model-provider-page/model-modal/Form', () => ({
|
|
__esModule: true,
|
|
default: (props: MockFormProps) => <FormMock {...props} />,
|
|
}))
|
|
|
|
let pluginAuthClickValue = 'credential-from-plugin'
|
|
jest.mock('@/app/components/plugins/plugin-auth', () => ({
|
|
AuthCategory: { tool: 'tool' },
|
|
PluginAuthInAgent: (props: { onAuthorizationItemClick?: (id: string) => void }) => (
|
|
<div data-testid="plugin-auth">
|
|
<button type="button" onClick={() => props.onAuthorizationItemClick?.(pluginAuthClickValue)}>
|
|
choose-plugin-credential
|
|
</button>
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
jest.mock('@/app/components/plugins/readme-panel/entrance', () => ({
|
|
ReadmeEntrance: ({ className }: { className?: string }) => <div className={className}>readme</div>,
|
|
}))
|
|
|
|
const createParameter = (overrides?: Partial<ToolParameter>): ToolParameter => ({
|
|
name: 'settingParam',
|
|
label: {
|
|
en_US: 'Setting Param',
|
|
zh_Hans: 'Setting Param',
|
|
},
|
|
human_description: {
|
|
en_US: 'desc',
|
|
zh_Hans: 'desc',
|
|
},
|
|
type: 'string',
|
|
form: 'config',
|
|
llm_description: '',
|
|
required: true,
|
|
multiple: false,
|
|
default: '',
|
|
...overrides,
|
|
})
|
|
|
|
const createTool = (overrides?: Partial<Tool>): Tool => ({
|
|
name: 'search',
|
|
author: 'tester',
|
|
label: {
|
|
en_US: 'Search Tool',
|
|
zh_Hans: 'Search Tool',
|
|
},
|
|
description: {
|
|
en_US: 'tool description',
|
|
zh_Hans: 'tool description',
|
|
},
|
|
parameters: [
|
|
createParameter({
|
|
name: 'infoParam',
|
|
label: {
|
|
en_US: 'Info Param',
|
|
zh_Hans: 'Info Param',
|
|
},
|
|
form: 'llm',
|
|
required: false,
|
|
}),
|
|
createParameter(),
|
|
],
|
|
labels: [],
|
|
output_schema: {},
|
|
...overrides,
|
|
})
|
|
|
|
const baseCollection = {
|
|
id: 'provider-1',
|
|
name: 'vendor/provider-1',
|
|
author: 'tester',
|
|
description: {
|
|
en_US: 'desc',
|
|
zh_Hans: 'desc',
|
|
},
|
|
icon: 'https://example.com/icon.png',
|
|
label: {
|
|
en_US: 'Provider Label',
|
|
zh_Hans: 'Provider Label',
|
|
},
|
|
type: CollectionType.builtIn,
|
|
team_credentials: {},
|
|
is_team_authorization: true,
|
|
allow_delete: true,
|
|
labels: [],
|
|
tools: [createTool()],
|
|
}
|
|
|
|
const renderComponent = (props?: Partial<React.ComponentProps<typeof SettingBuiltInTool>>) => {
|
|
const onHide = jest.fn()
|
|
const onSave = jest.fn()
|
|
const onAuthorizationItemClick = jest.fn()
|
|
const utils = render(
|
|
<I18n.Provider value={{ locale: 'en-US', i18n: {}, setLocaleOnClient: jest.fn() as any }}>
|
|
<SettingBuiltInTool
|
|
collection={baseCollection as any}
|
|
toolName="search"
|
|
isModel
|
|
setting={{ settingParam: 'value' }}
|
|
onHide={onHide}
|
|
onSave={onSave}
|
|
onAuthorizationItemClick={onAuthorizationItemClick}
|
|
{...props}
|
|
/>
|
|
</I18n.Provider>,
|
|
)
|
|
return {
|
|
...utils,
|
|
onHide,
|
|
onSave,
|
|
onAuthorizationItemClick,
|
|
}
|
|
}
|
|
|
|
describe('SettingBuiltInTool', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
nextFormValue = {}
|
|
pluginAuthClickValue = 'credential-from-plugin'
|
|
})
|
|
|
|
test('should fetch tool list when collection has no tools', async () => {
|
|
fetchModelToolList.mockResolvedValueOnce([createTool()])
|
|
renderComponent({
|
|
collection: {
|
|
...baseCollection,
|
|
tools: [],
|
|
},
|
|
})
|
|
|
|
await waitFor(() => {
|
|
expect(fetchModelToolList).toHaveBeenCalledTimes(1)
|
|
expect(fetchModelToolList).toHaveBeenCalledWith('vendor/provider-1')
|
|
})
|
|
expect(await screen.findByText('Search Tool')).toBeInTheDocument()
|
|
})
|
|
|
|
test('should switch between info and setting tabs', async () => {
|
|
renderComponent()
|
|
await waitFor(() => {
|
|
expect(screen.getByTestId('mock-form')).toBeInTheDocument()
|
|
})
|
|
|
|
await userEvent.click(screen.getByText('tools.setBuiltInTools.parameters'))
|
|
expect(screen.getByText('Info Param')).toBeInTheDocument()
|
|
await userEvent.click(screen.getByText('tools.setBuiltInTools.setting'))
|
|
expect(screen.getByTestId('mock-form')).toBeInTheDocument()
|
|
})
|
|
|
|
test('should call onSave with updated values when save button clicked', async () => {
|
|
const { onSave } = renderComponent()
|
|
await waitFor(() => expect(screen.getByTestId('mock-form')).toBeInTheDocument())
|
|
nextFormValue = { settingParam: 'updated' }
|
|
await userEvent.click(screen.getByRole('button', { name: 'update-form' }))
|
|
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
|
expect(onSave).toHaveBeenCalledWith(expect.objectContaining({ settingParam: 'updated' }))
|
|
})
|
|
|
|
test('should keep save disabled until required field provided', async () => {
|
|
renderComponent({
|
|
setting: {},
|
|
})
|
|
await waitFor(() => expect(screen.getByTestId('mock-form')).toBeInTheDocument())
|
|
const saveButton = screen.getByRole('button', { name: 'common.operation.save' })
|
|
expect(saveButton).toBeDisabled()
|
|
nextFormValue = { settingParam: 'filled' }
|
|
await userEvent.click(screen.getByRole('button', { name: 'update-form' }))
|
|
expect(saveButton).not.toBeDisabled()
|
|
})
|
|
|
|
test('should call onHide when cancel button is pressed', async () => {
|
|
const { onHide } = renderComponent()
|
|
await waitFor(() => expect(screen.getByTestId('mock-form')).toBeInTheDocument())
|
|
await userEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
|
|
expect(onHide).toHaveBeenCalled()
|
|
})
|
|
|
|
test('should trigger authorization callback from plugin auth section', async () => {
|
|
const { onAuthorizationItemClick } = renderComponent()
|
|
await userEvent.click(screen.getByRole('button', { name: 'choose-plugin-credential' }))
|
|
expect(onAuthorizationItemClick).toHaveBeenCalledWith('credential-from-plugin')
|
|
})
|
|
|
|
test('should call onHide when back button is clicked', async () => {
|
|
const { onHide } = renderComponent({
|
|
showBackButton: true,
|
|
})
|
|
await userEvent.click(screen.getByText('plugin.detailPanel.operation.back'))
|
|
expect(onHide).toHaveBeenCalled()
|
|
})
|
|
|
|
test('should load workflow tools when workflow collection is provided', async () => {
|
|
fetchWorkflowToolList.mockResolvedValueOnce([createTool({
|
|
name: 'workflow-tool',
|
|
})])
|
|
renderComponent({
|
|
collection: {
|
|
...baseCollection,
|
|
type: CollectionType.workflow,
|
|
tools: [],
|
|
id: 'workflow-1',
|
|
} as any,
|
|
isBuiltIn: false,
|
|
isModel: false,
|
|
})
|
|
|
|
await waitFor(() => {
|
|
expect(fetchWorkflowToolList).toHaveBeenCalledWith('workflow-1')
|
|
})
|
|
})
|
|
})
|