refactor(web): migrate dataset-related toast callsites to base/ui/toast and update tests (#33892)

This commit is contained in:
yyh 2026-03-23 13:13:52 +08:00 committed by GitHub
parent 110b8c925e
commit 0478023900
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 160 additions and 208 deletions

View File

@ -19,6 +19,10 @@ import { RETRIEVE_METHOD } from '@/types/app'
// --- Mocks ---
const { mockToastError } = vi.hoisted(() => ({
mockToastError: vi.fn(),
}))
const mockMutateDatasets = vi.fn()
const mockInvalidDatasetList = vi.fn()
const mockUpdateDatasetSetting = vi.fn().mockResolvedValue({})
@ -55,8 +59,11 @@ vi.mock('@/app/components/datasets/common/check-rerank-model', () => ({
isReRankModelSelected: () => true,
}))
vi.mock('@/app/components/base/toast', () => ({
default: { notify: vi.fn() },
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
error: mockToastError,
success: vi.fn(),
},
}))
// --- Dataset factory ---
@ -311,7 +318,7 @@ describe('Dataset Settings Flow - Cross-Module Configuration Cascade', () => {
describe('Form Submission Validation → All Fields Together', () => {
it('should reject empty name on save', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
const { result } = renderHook(() => useFormState())
act(() => {
@ -322,10 +329,7 @@ describe('Dataset Settings Flow - Cross-Module Configuration Cascade', () => {
await result.current.handleSave()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
})

View File

@ -3,7 +3,7 @@ import type { DatasetConfigs } from '@/models/debug'
import { render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import {
useCurrentProviderAndModel,
useModelListAndDefaultModelAndCurrentProviderAndModel,
@ -75,7 +75,7 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-param
const mockedUseModelListAndDefaultModelAndCurrentProviderAndModel = useModelListAndDefaultModelAndCurrentProviderAndModel as MockedFunction<typeof useModelListAndDefaultModelAndCurrentProviderAndModel>
const mockedUseCurrentProviderAndModel = useCurrentProviderAndModel as MockedFunction<typeof useCurrentProviderAndModel>
let toastNotifySpy: MockInstance
let toastErrorSpy: MockInstance
const createDatasetConfigs = (overrides: Partial<DatasetConfigs> = {}): DatasetConfigs => {
return {
@ -140,7 +140,7 @@ describe('dataset-config/params-config', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.useRealTimers()
toastNotifySpy = vi.spyOn(Toast, 'notify').mockImplementation(() => ({}))
toastErrorSpy = vi.spyOn(toast, 'error').mockImplementation(() => '')
mockedUseModelListAndDefaultModelAndCurrentProviderAndModel.mockReturnValue({
modelList: [],
defaultModel: undefined,
@ -154,7 +154,7 @@ describe('dataset-config/params-config', () => {
})
afterEach(() => {
toastNotifySpy.mockRestore()
toastErrorSpy.mockRestore()
})
// Rendering tests (REQUIRED)
@ -254,10 +254,7 @@ describe('dataset-config/params-config', () => {
await user.click(dialogScope.getByRole('button', { name: 'common.operation.save' }))
// Assert
expect(toastNotifySpy).toHaveBeenCalledWith({
type: 'error',
message: 'appDebug.datasetConfig.rerankModelRequired',
})
expect(toastErrorSpy).toHaveBeenCalledWith('appDebug.datasetConfig.rerankModelRequired')
expect(screen.getByRole('dialog')).toBeInTheDocument()
})
})

View File

@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import Button from '@/app/components/base/button'
import Modal from '@/app/components/base/modal'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import {
@ -66,10 +66,7 @@ const ParamsConfig = ({
}
}
if (errMsg) {
Toast.notify({
type: 'error',
message: errMsg,
})
toast.error(errMsg)
}
return !errMsg
}

View File

@ -1,10 +1,14 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { useAutoDisabledDocuments } from '@/service/knowledge/use-document'
import AutoDisabledDocument from '../auto-disabled-document'
const { mockToastSuccess } = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
}))
type AutoDisabledDocumentsResponse = { document_ids: string[] }
const createMockQueryResult = (
@ -26,9 +30,9 @@ vi.mock('@/service/knowledge/use-document', () => ({
useInvalidDisabledDocument: vi.fn(() => mockInvalidDisabledDocument),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
success: mockToastSuccess,
},
}))
@ -134,10 +138,7 @@ describe('AutoDisabledDocument', () => {
fireEvent.click(actionButton)
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'success',
message: expect.any(String),
})
expect(toast.success).toHaveBeenCalledWith(expect.any(String))
})
})
})

View File

@ -3,7 +3,7 @@ import type { FC } from 'react'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { useAutoDisabledDocuments, useDocumentEnable, useInvalidDisabledDocument } from '@/service/knowledge/use-document'
import StatusWithAction from './status-with-action'
@ -23,7 +23,7 @@ const AutoDisabledDocument: FC<Props> = ({
const handleEnableDocuments = useCallback(async () => {
await enableDocument({ datasetId, documentIds })
invalidDisabledDocument()
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
}, [])
if (!hasDisabledDocument || isLoading)
return null

View File

@ -3,10 +3,14 @@ import type { FileEntity } from '../../types'
import { act, fireEvent, render, renderHook, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { FileContextProvider } from '../../store'
import { useUpload } from '../use-upload'
const { mockToastError } = vi.hoisted(() => ({
mockToastError: vi.fn(),
}))
vi.mock('@/service/use-common', () => ({
useFileUploadConfig: vi.fn(() => ({
data: {
@ -17,9 +21,9 @@ vi.mock('@/service/use-common', () => ({
})),
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
error: mockToastError,
},
}))
@ -177,10 +181,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
})
@ -204,13 +205,11 @@ describe('useUpload hook', () => {
result.current.fileChangeHandle(mockEvent)
})
// Should not show type error for valid image type
type ToastCall = [{ type: string, message: string }]
const mockNotify = vi.mocked(Toast.notify)
// Should not show file-extension error for valid image type
type ToastCall = [string]
const mockNotify = vi.mocked(toast.error)
const calls = mockNotify.mock.calls as ToastCall[]
const typeErrorCalls = calls.filter(
(call: ToastCall) => call[0].type === 'error' && call[0].message.includes('Extension'),
)
const typeErrorCalls = calls.filter(call => call[0].includes('common.fileUploader.fileExtensionNotSupport'))
expect(typeErrorCalls.length).toBe(0)
})
})
@ -261,7 +260,7 @@ describe('useUpload hook', () => {
})
// Should not throw and not show error
expect(Toast.notify).not.toHaveBeenCalled()
expect(toast.error).not.toHaveBeenCalled()
})
it('should handle null files', () => {
@ -314,10 +313,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
})
})
@ -419,10 +415,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: 'Upload error',
})
expect(toast.error).toHaveBeenCalledWith('Upload error')
})
})
})
@ -481,10 +474,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: 'Upload error',
})
expect(toast.error).toHaveBeenCalledWith('Upload error')
})
})
})
@ -522,10 +512,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
})
})
@ -610,10 +597,7 @@ describe('useUpload hook', () => {
})
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
// Restore original MockFileReader
@ -773,10 +757,7 @@ describe('useUpload hook', () => {
// Should show error toast for invalid file type
await waitFor(() => {
expect(Toast.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
})

View File

@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { v4 as uuid4 } from 'uuid'
import { fileUpload, getFileUploadErrorMessage } from '@/app/components/base/file-uploader/utils'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { useFileUploadConfig } from '@/service/use-common'
import { ACCEPT_TYPES } from '../constants'
import { useFileStore } from '../store'
@ -54,9 +54,9 @@ export const useUpload = () => {
const showErrorMessage = useCallback((type: 'type' | 'size') => {
if (type === 'type')
Toast.notify({ type: 'error', message: t('fileUploader.fileExtensionNotSupport', { ns: 'common' }) })
toast.error(t('fileUploader.fileExtensionNotSupport', { ns: 'common' }))
else
Toast.notify({ type: 'error', message: t('imageUploader.fileSizeLimitExceeded', { ns: 'dataset', size: fileUploadConfig.imageFileSizeLimit }) })
toast.error(t('imageUploader.fileSizeLimitExceeded', { ns: 'dataset', size: fileUploadConfig.imageFileSizeLimit }))
}, [fileUploadConfig, t])
const getValidFiles = useCallback((files: File[]) => {
@ -146,7 +146,7 @@ export const useUpload = () => {
},
onErrorCallback: (error?: any) => {
const errorMessage = getFileUploadErrorMessage(error, t('fileUploader.uploadFromComputerUploadError', { ns: 'common' }), t)
Toast.notify({ type: 'error', message: errorMessage })
toast.error(errorMessage)
handleUpdateFile({ ...uploadingFile, progress: -1 })
},
})
@ -188,7 +188,7 @@ export const useUpload = () => {
},
onErrorCallback: (error?: any) => {
const errorMessage = getFileUploadErrorMessage(error, t('fileUploader.uploadFromComputerUploadError', { ns: 'common' }), t)
Toast.notify({ type: 'error', message: errorMessage })
toast.error(errorMessage)
handleUpdateFile({ ...uploadingFile, progress: -1 })
},
})
@ -198,7 +198,7 @@ export const useUpload = () => {
reader.addEventListener(
'error',
() => {
Toast.notify({ type: 'error', message: t('fileUploader.uploadFromComputerReadError', { ns: 'common' }) })
toast.error(t('fileUploader.uploadFromComputerReadError', { ns: 'common' }))
},
false,
)
@ -211,10 +211,7 @@ export const useUpload = () => {
if (newFiles.length === 0)
return
if (files.length + newFiles.length > singleChunkAttachmentLimit) {
Toast.notify({
type: 'error',
message: t('imageUploader.singleChunkAttachmentLimitTooltip', { ns: 'datasetHitTesting', limit: singleChunkAttachmentLimit }),
})
toast.error(t('imageUploader.singleChunkAttachmentLimitTooltip', { ns: 'datasetHitTesting', limit: singleChunkAttachmentLimit }))
return
}
for (const file of newFiles)

View File

@ -5,9 +5,9 @@ import { RETRIEVE_METHOD } from '@/types/app'
import RetrievalParamConfig from '../index'
const mockNotify = vi.fn()
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: (params: { type: string, message: string }) => mockNotify(params),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
error: (message: string) => mockNotify(message),
},
}))
@ -260,10 +260,7 @@ describe('RetrievalParamConfig', () => {
fireEvent.click(screen.getByTestId('rerank-switch'))
expect(mockNotify).toHaveBeenCalledWith({
type: 'error',
message: 'workflow.errorMsg.rerankModelRequired',
})
expect(mockNotify).toHaveBeenCalledWith('workflow.errorMsg.rerankModelRequired')
})
it('should update reranking model on selection', () => {
@ -618,10 +615,7 @@ describe('RetrievalParamConfig', () => {
const rerankModelCard = radioCards.find(card => card.getAttribute('data-title') === 'common.modelProvider.rerankModel.key')
fireEvent.click(rerankModelCard!)
expect(mockNotify).toHaveBeenCalledWith({
type: 'error',
message: 'workflow.errorMsg.rerankModelRequired',
})
expect(mockNotify).toHaveBeenCalledWith('workflow.errorMsg.rerankModelRequired')
})
it('should update weights when WeightedScore changes', () => {

View File

@ -11,8 +11,8 @@ import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold
import TopKItem from '@/app/components/base/param-item/top-k-item'
import RadioCard from '@/app/components/base/radio-card'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import Tooltip from '@/app/components/base/tooltip'
import { toast } from '@/app/components/base/ui/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useCurrentProviderAndModel, useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
@ -59,7 +59,7 @@ const RetrievalParamConfig: FC<Props> = ({
const handleToggleRerankEnable = useCallback((enable: boolean) => {
if (enable && !currentModel)
Toast.notify({ type: 'error', message: t('errorMsg.rerankModelRequired', { ns: 'workflow' }) })
toast.error(t('errorMsg.rerankModelRequired', { ns: 'workflow' }))
onChange({
...value,
reranking_enable: enable,
@ -96,7 +96,7 @@ const RetrievalParamConfig: FC<Props> = ({
}
}
if (v === RerankingModeEnum.RerankingModel && !currentModel)
Toast.notify({ type: 'error', message: t('errorMsg.rerankModelRequired', { ns: 'workflow' }) })
toast.error(t('errorMsg.rerankModelRequired', { ns: 'workflow' }))
onChange(result)
}

View File

@ -5,11 +5,23 @@ import { renameDocumentName } from '@/service/datasets'
import RenameModal from '../rename-modal'
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
mockToastError: vi.fn(),
}))
// Mock the service
vi.mock('@/service/datasets', () => ({
renameDocumentName: vi.fn(),
}))
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
success: mockToastSuccess,
error: mockToastError,
},
}))
const mockRenameDocumentName = vi.mocked(renameDocumentName)
describe('RenameModal', () => {
@ -118,6 +130,7 @@ describe('RenameModal', () => {
await waitFor(() => {
expect(handleSaved).toHaveBeenCalledTimes(1)
expect(handleClose).toHaveBeenCalledTimes(1)
expect(mockToastSuccess).toHaveBeenCalledWith(expect.any(String))
})
})
})
@ -163,6 +176,7 @@ describe('RenameModal', () => {
// onSaved and onClose should not be called on error
expect(handleSaved).not.toHaveBeenCalled()
expect(handleClose).not.toHaveBeenCalled()
expect(mockToastError).toHaveBeenCalledWith('Error: API Error')
})
})
})

View File

@ -3,6 +3,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { DocumentActionType } from '@/models/datasets'
import { useDocumentActions } from '../use-document-actions'
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
mockToastError: vi.fn(),
}))
const mockArchive = vi.fn()
const mockSummary = vi.fn()
const mockEnable = vi.fn()
@ -22,9 +27,11 @@ vi.mock('@/service/knowledge/use-document', () => ({
useDocumentDownloadZip: () => ({ mutateAsync: mockDownloadZip, isPending: mockIsDownloadingZip }),
}))
const mockToastNotify = vi.fn()
vi.mock('@/app/components/base/toast', () => ({
default: { notify: (...args: unknown[]) => mockToastNotify(...args) },
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
success: mockToastSuccess,
error: mockToastError,
},
}))
const mockDownloadBlob = vi.fn()
@ -67,9 +74,7 @@ describe('useDocumentActions', () => {
datasetId: 'ds-1',
documentIds: ['doc-1', 'doc-2'],
})
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'success' }),
)
expect(mockToastSuccess).toHaveBeenCalledWith(expect.any(String))
expect(defaultOptions.onUpdate).toHaveBeenCalled()
})
@ -142,9 +147,7 @@ describe('useDocumentActions', () => {
await result.current.handleAction(DocumentActionType.archive)()
})
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
expect(defaultOptions.onUpdate).not.toHaveBeenCalled()
})
})
@ -174,9 +177,7 @@ describe('useDocumentActions', () => {
await result.current.handleBatchReIndex()
})
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
})
})
@ -210,9 +211,7 @@ describe('useDocumentActions', () => {
await result.current.handleBatchDownload()
})
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
})
it('should show error toast when blob is null', async () => {
@ -223,9 +222,7 @@ describe('useDocumentActions', () => {
await result.current.handleBatchDownload()
})
expect(mockToastNotify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
})
})
})

View File

@ -1,7 +1,7 @@
import type { CommonResponse } from '@/models/common'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { DocumentActionType } from '@/models/datasets'
import {
useDocumentArchive,
@ -79,11 +79,11 @@ export const useDocumentActions = ({
if (!e) {
if (actionName === DocumentActionType.delete)
onClearSelection()
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
onUpdate()
}
else {
Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) })
toast.error(t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }))
}
}
}, [actionMutationMap, datasetId, selectedIds, onClearSelection, onUpdate, t])
@ -94,11 +94,11 @@ export const useDocumentActions = ({
)
if (!e) {
onClearSelection()
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
onUpdate()
}
else {
Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) })
toast.error(t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }))
}
}, [retryIndexDocument, datasetId, selectedIds, onClearSelection, onUpdate, t])
@ -110,7 +110,7 @@ export const useDocumentActions = ({
requestDocumentsZip({ datasetId, documentIds: downloadableSelectedIds }),
)
if (e || !blob) {
Toast.notify({ type: 'error', message: t('actionMsg.downloadUnsuccessfully', { ns: 'common' }) })
toast.error(t('actionMsg.downloadUnsuccessfully', { ns: 'common' }))
return
}

View File

@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { renameDocumentName } from '@/service/datasets'
type Props = {
@ -41,13 +41,13 @@ const RenameModal: FC<Props> = ({
documentId,
name: newName,
})
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
onSaved()
onClose()
}
catch (error) {
if (error)
Toast.notify({ type: 'error', message: error.toString() })
toast.error(error.toString())
}
finally {
setSaveLoadingFalse()

View File

@ -5,9 +5,15 @@ import { IndexingType } from '@/app/components/datasets/create/step-two'
import { ChunkingMode, DatasetPermission, DataSourceType } from '@/models/datasets'
import { useDatasetCardState } from '../use-dataset-card-state'
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
mockToastError: vi.fn(),
}))
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
success: mockToastSuccess,
error: mockToastError,
},
}))
@ -299,7 +305,7 @@ describe('useDatasetCardState', () => {
describe('Error Handling', () => {
it('should show error toast when export pipeline fails', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
mockExportPipeline.mockRejectedValue(new Error('Export failed'))
const dataset = createMockDataset({ pipeline_id: 'pipeline-1' })
@ -311,14 +317,11 @@ describe('useDatasetCardState', () => {
await result.current.handleExportPipeline()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
it('should handle Response error in detectIsUsedByApp', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
const mockResponse = new Response(JSON.stringify({ message: 'API Error' }), {
status: 400,
})
@ -333,14 +336,11 @@ describe('useDatasetCardState', () => {
await result.current.detectIsUsedByApp()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.stringContaining('API Error'),
})
expect(toast.error).toHaveBeenCalledWith(expect.stringContaining('API Error'))
})
it('should handle generic Error in detectIsUsedByApp', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
mockCheckUsage.mockRejectedValue(new Error('Network error'))
const dataset = createMockDataset()
@ -352,14 +352,11 @@ describe('useDatasetCardState', () => {
await result.current.detectIsUsedByApp()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: 'Network error',
})
expect(toast.error).toHaveBeenCalledWith('Network error')
})
it('should handle error without message in detectIsUsedByApp', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
mockCheckUsage.mockRejectedValue({})
const dataset = createMockDataset()
@ -371,10 +368,7 @@ describe('useDatasetCardState', () => {
await result.current.detectIsUsedByApp()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: 'Unknown error',
})
expect(toast.error).toHaveBeenCalledWith('dataset.unknownError')
})
it('should handle exporting state correctly', async () => {

View File

@ -2,7 +2,7 @@ import type { Tag } from '@/app/components/base/tag-management/constant'
import type { DataSet } from '@/models/datasets'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { useCheckDatasetUsage, useDeleteDataset } from '@/service/use-dataset-card'
import { useExportPipelineDSL } from '@/service/use-pipeline'
import { downloadBlob } from '@/utils/download'
@ -70,7 +70,7 @@ export const useDatasetCardState = ({ dataset, onSuccess }: UseDatasetCardStateO
downloadBlob({ data: file, fileName: `${name}.pipeline` })
}
catch {
Toast.notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
toast.error(t('exportFailed', { ns: 'app' }))
}
finally {
setExporting(false)
@ -93,10 +93,10 @@ export const useDatasetCardState = ({ dataset, onSuccess }: UseDatasetCardStateO
catch (e: unknown) {
if (e instanceof Response) {
const res = await e.json()
Toast.notify({ type: 'error', message: res?.message || 'Unknown error' })
toast.error(res?.message || t('unknownError', { ns: 'dataset' }))
}
else {
Toast.notify({ type: 'error', message: (e as Error)?.message || 'Unknown error' })
toast.error((e as Error)?.message || t('unknownError', { ns: 'dataset' }))
}
}
}, [dataset.id, checkUsage, t])
@ -104,7 +104,7 @@ export const useDatasetCardState = ({ dataset, onSuccess }: UseDatasetCardStateO
const onConfirmDelete = useCallback(async () => {
try {
await deleteDatasetMutation(dataset.id)
Toast.notify({ type: 'success', message: t('datasetDeleted', { ns: 'dataset' }) })
toast.success(t('datasetDeleted', { ns: 'dataset' }))
onSuccess?.()
}
finally {

View File

@ -6,6 +6,10 @@ import { RETRIEVE_METHOD } from '@/types/app'
import { IndexingType } from '../../../create/step-two'
import Form from '../index'
const { mockToastError } = vi.hoisted(() => ({
mockToastError: vi.fn(),
}))
// Mock contexts
const mockMutateDatasets = vi.fn()
const mockInvalidDatasetList = vi.fn()
@ -189,9 +193,10 @@ vi.mock('@/app/components/datasets/common/check-rerank-model', () => ({
isReRankModelSelected: () => true,
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
error: mockToastError,
success: vi.fn(),
},
}))
@ -391,7 +396,7 @@ describe('Form', () => {
})
it('should show error when trying to save with empty name', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
render(<Form />)
// Clear the name
@ -403,10 +408,7 @@ describe('Form', () => {
fireEvent.click(saveButton)
await waitFor(() => {
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
})

View File

@ -6,6 +6,11 @@ import { RETRIEVE_METHOD } from '@/types/app'
import { IndexingType } from '../../../../create/step-two'
import { useFormState } from '../use-form-state'
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
mockToastError: vi.fn(),
}))
// Mock contexts
const mockMutateDatasets = vi.fn()
const mockInvalidDatasetList = vi.fn()
@ -122,9 +127,10 @@ vi.mock('@/app/components/datasets/common/check-rerank-model', () => ({
isReRankModelSelected: () => true,
}))
vi.mock('@/app/components/base/toast', () => ({
default: {
notify: vi.fn(),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: {
success: mockToastSuccess,
error: mockToastError,
},
}))
@ -423,7 +429,7 @@ describe('useFormState', () => {
describe('handleSave', () => {
it('should show error toast when name is empty', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
const { result } = renderHook(() => useFormState())
act(() => {
@ -434,14 +440,11 @@ describe('useFormState', () => {
await result.current.handleSave()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
it('should show error toast when name is whitespace only', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
const { result } = renderHook(() => useFormState())
act(() => {
@ -452,10 +455,7 @@ describe('useFormState', () => {
await result.current.handleSave()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
it('should call updateDatasetSetting with correct params', async () => {
@ -477,7 +477,7 @@ describe('useFormState', () => {
})
it('should show success toast on successful save', async () => {
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
const { result } = renderHook(() => useFormState())
await act(async () => {
@ -485,10 +485,7 @@ describe('useFormState', () => {
})
await waitFor(() => {
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'success',
message: expect.any(String),
})
expect(toast.success).toHaveBeenCalledWith(expect.any(String))
})
})
@ -553,7 +550,7 @@ describe('useFormState', () => {
it('should show error toast on save failure', async () => {
const { updateDatasetSetting } = await import('@/service/datasets')
const Toast = await import('@/app/components/base/toast')
const { toast } = await import('@/app/components/base/ui/toast')
vi.mocked(updateDatasetSetting).mockRejectedValueOnce(new Error('Network error'))
const { result } = renderHook(() => useFormState())
@ -562,10 +559,7 @@ describe('useFormState', () => {
await result.current.handleSave()
})
expect(Toast.default.notify).toHaveBeenCalledWith({
type: 'error',
message: expect.any(String),
})
expect(toast.error).toHaveBeenCalledWith(expect.any(String))
})
it('should include partial_member_list when permission is partialMembers', async () => {

View File

@ -6,7 +6,7 @@ import type { IconInfo, SummaryIndexSetting as SummaryIndexSettingType } from '@
import type { RetrievalConfig } from '@/types/app'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
@ -123,12 +123,12 @@ export const useFormState = () => {
return
if (!name?.trim()) {
Toast.notify({ type: 'error', message: t('form.nameError', { ns: 'datasetSettings' }) })
toast.error(t('form.nameError', { ns: 'datasetSettings' }))
return
}
if (!isReRankModelSelected({ rerankModelList, retrievalConfig, indexMethod })) {
Toast.notify({ type: 'error', message: t('datasetConfig.rerankModelRequired', { ns: 'appDebug' }) })
toast.error(t('datasetConfig.rerankModelRequired', { ns: 'appDebug' }))
return
}
@ -176,7 +176,7 @@ export const useFormState = () => {
}
await updateDatasetSetting({ datasetId: currentDataset!.id, body })
Toast.notify({ type: 'success', message: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
if (mutateDatasets) {
await mutateDatasets()
@ -184,7 +184,7 @@ export const useFormState = () => {
}
}
catch {
Toast.notify({ type: 'error', message: t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }) })
toast.error(t('actionMsg.modifiedUnsuccessfully', { ns: 'common' }))
}
finally {
setLoading(false)

View File

@ -959,7 +959,7 @@
},
"app/components/app/configuration/dataset-config/params-config/index.tsx": {
"no-restricted-imports": {
"count": 2
"count": 1
},
"react/set-state-in-effect": {
"count": 1
@ -3166,11 +3166,6 @@
"count": 2
}
},
"app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/datasets/common/image-list/more.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1
@ -3190,9 +3185,6 @@
}
},
"app/components/datasets/common/image-uploader/hooks/use-upload.ts": {
"no-restricted-imports": {
"count": 1
},
"ts/no-explicit-any": {
"count": 3
}
@ -3222,7 +3214,7 @@
},
"app/components/datasets/common/retrieval-param-config/index.tsx": {
"no-restricted-imports": {
"count": 2
"count": 1
}
},
"app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/dsl-confirm-modal.tsx": {
@ -3573,11 +3565,6 @@
"count": 1
}
},
"app/components/datasets/documents/components/document-list/hooks/use-document-actions.ts": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/datasets/documents/components/documents-header.tsx": {
"no-restricted-imports": {
"count": 1
@ -3590,7 +3577,7 @@
},
"app/components/datasets/documents/components/rename-modal.tsx": {
"no-restricted-imports": {
"count": 2
"count": 1
}
},
"app/components/datasets/documents/create-from-pipeline/actions/index.tsx": {
@ -4258,9 +4245,6 @@
}
},
"app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts": {
"no-restricted-imports": {
"count": 1
},
"react/set-state-in-effect": {
"count": 1
}
@ -4430,11 +4414,6 @@
"count": 7
}
},
"app/components/datasets/settings/form/hooks/use-form-state.ts": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/datasets/settings/index-method/index.tsx": {
"no-restricted-imports": {
"count": 1

View File

@ -176,6 +176,7 @@
"serviceApi.enabled": "Enabled",
"serviceApi.title": "Service API",
"unavailable": "Unavailable",
"unknownError": "Unknown error",
"updated": "Updated",
"weightedScore.customized": "Customized",
"weightedScore.description": "By adjusting the weights assigned, this rerank strategy determines whether to prioritize semantic or keyword matching.",