mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
refactor(web): migrate tools/MCP/external-knowledge toast usage to UI toast and add i18n (#33797)
This commit is contained in:
@ -1,21 +1,24 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { importSchemaFromURL } from '@/service/tools'
|
||||
import Toast from '../../../base/toast'
|
||||
import examples from '../examples'
|
||||
import GetSchema from '../get-schema'
|
||||
|
||||
vi.mock('@/service/tools', () => ({
|
||||
importSchemaFromURL: vi.fn(),
|
||||
}))
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
},
|
||||
}))
|
||||
const importSchemaFromURLMock = vi.mocked(importSchemaFromURL)
|
||||
|
||||
describe('GetSchema', () => {
|
||||
const notifySpy = vi.spyOn(Toast, 'notify')
|
||||
const mockOnChange = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
notifySpy.mockClear()
|
||||
importSchemaFromURLMock.mockReset()
|
||||
render(<GetSchema onChange={mockOnChange} />)
|
||||
})
|
||||
@ -27,9 +30,9 @@ describe('GetSchema', () => {
|
||||
fireEvent.change(input, { target: { value: 'ftp://invalid' } })
|
||||
fireEvent.click(screen.getByText('common.operation.ok'))
|
||||
|
||||
expect(notifySpy).toHaveBeenCalledWith({
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
message: 'tools.createTool.urlError',
|
||||
title: 'tools.createTool.urlError',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -10,8 +10,8 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { importSchemaFromURL } from '@/service/tools'
|
||||
import Toast from '../../base/toast'
|
||||
import examples from './examples'
|
||||
|
||||
type Props = {
|
||||
@ -27,9 +27,9 @@ const GetSchema: FC<Props> = ({
|
||||
const [isParsing, setIsParsing] = useState(false)
|
||||
const handleImportFromUrl = async () => {
|
||||
if (!importUrl.startsWith('http://') && !importUrl.startsWith('https://')) {
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'error',
|
||||
message: t('createTool.urlError', { ns: 'tools' }),
|
||||
title: t('createTool.urlError', { ns: 'tools' }),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import MCPModal from '../modal'
|
||||
|
||||
// Mock the service API
|
||||
@ -48,7 +48,18 @@ vi.mock('@/service/use-plugins', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
},
|
||||
}))
|
||||
|
||||
describe('MCPModal', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
const createWrapper = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@ -299,6 +310,10 @@ describe('MCPModal', () => {
|
||||
// Wait a bit and verify onConfirm was not called
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
expect(onConfirm).not.toHaveBeenCalled()
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'tools.mcp.modal.invalidServerUrl',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not call onConfirm with invalid server identifier', async () => {
|
||||
@ -320,6 +335,10 @@ describe('MCPModal', () => {
|
||||
// Wait a bit and verify onConfirm was not called
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
expect(onConfirm).not.toHaveBeenCalled()
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'tools.mcp.modal.invalidServerIdentifier',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import { Mcp } from '@/app/components/base/icons/src/vender/other'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import TabSlider from '@/app/components/base/tab-slider'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { MCPAuthMethod } from '@/app/components/tools/types'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { shouldUseMcpIconForAppIcon } from '@/utils/mcp'
|
||||
@ -82,11 +82,11 @@ const MCPModalContent: FC<MCPModalContentProps> = ({
|
||||
|
||||
const submit = async () => {
|
||||
if (!isValidUrl(state.url)) {
|
||||
Toast.notify({ type: 'error', message: 'invalid server url' })
|
||||
toast.add({ type: 'error', title: t('mcp.modal.invalidServerUrl', { ns: 'tools' }) })
|
||||
return
|
||||
}
|
||||
if (!isValidServerID(state.serverIdentifier.trim())) {
|
||||
Toast.notify({ type: 'error', message: 'invalid server identifier' })
|
||||
toast.add({ type: 'error', title: t('mcp.modal.invalidServerIdentifier', { ns: 'tools' }) })
|
||||
return
|
||||
}
|
||||
const formattedHeaders = state.headers.reduce((acc, item) => {
|
||||
|
||||
@ -70,11 +70,11 @@ vi.mock('@/app/components/tools/edit-custom-collection-modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock Toast
|
||||
// Mock toast
|
||||
const mockToastNotify = vi.fn()
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
default: {
|
||||
notify: (options: { type: string, message: string }) => mockToastNotify(options),
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: (options: { type: string, title: string }) => mockToastNotify(options),
|
||||
},
|
||||
}))
|
||||
|
||||
@ -200,7 +200,7 @@ describe('CustomCreateCard', () => {
|
||||
await waitFor(() => {
|
||||
expect(mockToastNotify).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
message: expect.any(String),
|
||||
title: expect.any(String),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -92,8 +92,9 @@ vi.mock('@/app/components/base/confirm', () => ({
|
||||
: null,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/toast', () => ({
|
||||
default: { notify: vi.fn() },
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: { add: mockToastAdd },
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/header/indicator', () => ({
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
} from '@remixicon/react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { createCustomCollection } from '@/service/tools'
|
||||
@ -21,9 +21,9 @@ const Contribute = ({ onRefreshData }: Props) => {
|
||||
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
|
||||
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
|
||||
await createCustomCollection(data)
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
onRefreshData()
|
||||
|
||||
@ -13,7 +13,7 @@ import Confirm from '@/app/components/base/confirm'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import { LinkExternal02, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import Icon from '@/app/components/plugins/card/base/card-icon'
|
||||
@ -122,18 +122,18 @@ const ProviderDetail = ({
|
||||
await getCustomProvider()
|
||||
// Use fresh data from form submission to avoid race condition with collection.labels
|
||||
setCustomCollection(prev => prev ? { ...prev, labels: data.labels } : null)
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
}
|
||||
const doRemoveCustomToolCollection = async () => {
|
||||
await removeCustomCollection(collection?.name as string)
|
||||
onRefreshData()
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
}
|
||||
@ -161,9 +161,9 @@ const ProviderDetail = ({
|
||||
const removeWorkflowToolProvider = async () => {
|
||||
await deleteWorkflowTool(collection.id)
|
||||
onRefreshData()
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
setIsShowEditWorkflowToolModal(false)
|
||||
}
|
||||
@ -175,9 +175,9 @@ const ProviderDetail = ({
|
||||
invalidateAllWorkflowTools()
|
||||
onRefreshData()
|
||||
getWorkflowToolProvider()
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
setIsShowEditWorkflowToolModal(false)
|
||||
}
|
||||
@ -385,18 +385,18 @@ const ProviderDetail = ({
|
||||
onCancel={() => setShowSettingAuth(false)}
|
||||
onSaved={async (value) => {
|
||||
await updateBuiltInToolCredential(collection.name, value)
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
await onRefreshData()
|
||||
setShowSettingAuth(false)
|
||||
}}
|
||||
onRemove={async () => {
|
||||
await removeBuiltInToolCredential(collection.name)
|
||||
Toast.notify({
|
||||
toast.add({
|
||||
type: 'success',
|
||||
message: t('api.actionSuccess', { ns: 'common' }),
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
await onRefreshData()
|
||||
setShowSettingAuth(false)
|
||||
|
||||
Reference in New Issue
Block a user