mirror of
https://github.com/langgenius/dify.git
synced 2026-04-29 23:18:05 +08:00
171 lines
6.2 KiB
TypeScript
171 lines
6.2 KiB
TypeScript
import { renderHook } from '@testing-library/react'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { BlockEnum } from '@/app/components/workflow/types'
|
|
import { useAvailableNodesMetaData } from './use-available-nodes-meta-data'
|
|
|
|
let mockIsChatMode = false
|
|
let mockSandboxEnabled = false
|
|
let mockRuntimeType: 'sandboxed' | 'classic' = 'classic'
|
|
|
|
function createNodeDefault(type: BlockEnum, helpLinkUri?: string) {
|
|
return {
|
|
metaData: {
|
|
type,
|
|
...(helpLinkUri ? { helpLinkUri } : {}),
|
|
},
|
|
defaultValue: { type },
|
|
}
|
|
}
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => key,
|
|
}),
|
|
}))
|
|
|
|
vi.mock('@/context/i18n', () => ({
|
|
useDocLink: () => (path: string) => `https://docs.dify.ai${path}`,
|
|
}))
|
|
|
|
vi.mock('./use-is-chat-mode', () => ({
|
|
useIsChatMode: () => mockIsChatMode,
|
|
}))
|
|
|
|
vi.mock('@/app/components/base/features/hooks', () => ({
|
|
useFeatures: (selector: (state: { features: { sandbox: { enabled: boolean } } }) => unknown) => selector({
|
|
features: {
|
|
sandbox: {
|
|
enabled: mockSandboxEnabled,
|
|
},
|
|
},
|
|
}),
|
|
}))
|
|
|
|
vi.mock('@/app/components/app/store', () => ({
|
|
useStore: (selector: (state: { appDetail: { runtime_type: 'sandboxed' | 'classic' } }) => unknown) => selector({
|
|
appDetail: {
|
|
runtime_type: mockRuntimeType,
|
|
},
|
|
}),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/constants/node', () => ({
|
|
WORKFLOW_COMMON_NODES: [
|
|
createNodeDefault(BlockEnum.LLM, 'llm-help'),
|
|
createNodeDefault(BlockEnum.Agent),
|
|
createNodeDefault(BlockEnum.Command),
|
|
createNodeDefault(BlockEnum.FileUpload),
|
|
createNodeDefault(BlockEnum.VariableAggregator),
|
|
],
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/start/default', () => ({
|
|
default: createNodeDefault(BlockEnum.Start),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/end/default', () => ({
|
|
default: createNodeDefault(BlockEnum.End),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/answer/default', () => ({
|
|
default: createNodeDefault(BlockEnum.Answer),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/trigger-webhook/default', () => ({
|
|
default: createNodeDefault(BlockEnum.TriggerWebhook),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/trigger-schedule/default', () => ({
|
|
default: createNodeDefault(BlockEnum.TriggerSchedule),
|
|
}))
|
|
|
|
vi.mock('@/app/components/workflow/nodes/trigger-plugin/default', () => ({
|
|
default: createNodeDefault(BlockEnum.TriggerPlugin),
|
|
}))
|
|
|
|
describe('workflow-app/useAvailableNodesMetaData', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mockIsChatMode = false
|
|
mockSandboxEnabled = false
|
|
mockRuntimeType = 'classic'
|
|
})
|
|
|
|
it('should include workflow-only nodes when chat mode is disabled', () => {
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
const nodeTypes = result.current.nodes.map(node => node.metaData.type)
|
|
const startNode = result.current.nodes.find(node => node.metaData.type === BlockEnum.Start)
|
|
|
|
expect(nodeTypes).toContain(BlockEnum.End)
|
|
expect(nodeTypes).toContain(BlockEnum.TriggerWebhook)
|
|
expect(nodeTypes).toContain(BlockEnum.TriggerSchedule)
|
|
expect(nodeTypes).toContain(BlockEnum.TriggerPlugin)
|
|
expect(nodeTypes).not.toContain(BlockEnum.Answer)
|
|
expect(startNode?.metaData.isUndeletable).toBe(false)
|
|
expect(startNode?.metaData.isTypeFixed).toBe(false)
|
|
})
|
|
|
|
it('should include chatflow-only nodes when chat mode is enabled', () => {
|
|
mockIsChatMode = true
|
|
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
const nodeTypes = result.current.nodes.map(node => node.metaData.type)
|
|
const startNode = result.current.nodes.find(node => node.metaData.type === BlockEnum.Start)
|
|
|
|
expect(nodeTypes).toContain(BlockEnum.Answer)
|
|
expect(nodeTypes).not.toContain(BlockEnum.End)
|
|
expect(nodeTypes).not.toContain(BlockEnum.TriggerWebhook)
|
|
expect(nodeTypes).not.toContain(BlockEnum.TriggerSchedule)
|
|
expect(nodeTypes).not.toContain(BlockEnum.TriggerPlugin)
|
|
expect(startNode?.metaData.isUndeletable).toBe(true)
|
|
expect(startNode?.metaData.isTypeFixed).toBe(true)
|
|
})
|
|
|
|
it('should hide sandbox-only nodes and keep agent when sandbox is disabled', () => {
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
const nodeTypes = result.current.nodes.map(node => node.metaData.type)
|
|
const llmNode = result.current.nodesMap[BlockEnum.LLM]
|
|
|
|
expect(nodeTypes).not.toContain(BlockEnum.Command)
|
|
expect(nodeTypes).not.toContain(BlockEnum.FileUpload)
|
|
expect(nodeTypes).toContain(BlockEnum.Agent)
|
|
expect(llmNode.metaData.title).toBe('blocks.llm')
|
|
expect(llmNode.metaData.iconType).toBeUndefined()
|
|
})
|
|
|
|
it('should show sandbox-only nodes and hide agent when sandbox feature is enabled', () => {
|
|
mockSandboxEnabled = true
|
|
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
const nodeTypes = result.current.nodes.map(node => node.metaData.type)
|
|
const llmNode = result.current.nodesMap[BlockEnum.LLM]
|
|
const fileUploadNode = result.current.nodesMap[BlockEnum.FileUpload]
|
|
|
|
expect(nodeTypes).toContain(BlockEnum.Command)
|
|
expect(nodeTypes).toContain(BlockEnum.FileUpload)
|
|
expect(nodeTypes).not.toContain(BlockEnum.Agent)
|
|
expect(llmNode.metaData.title).toBe('blocks.agent')
|
|
expect(llmNode.metaData.iconType).toBe(BlockEnum.Agent)
|
|
expect(llmNode.defaultValue._iconTypeOverride).toBe(BlockEnum.Agent)
|
|
expect(fileUploadNode.metaData.helpLinkUri).toBe('https://docs.dify.ai/use-dify/nodes/upload-file-to-sandbox')
|
|
})
|
|
|
|
it('should enable sandbox behavior when runtime type is sandboxed', () => {
|
|
mockRuntimeType = 'sandboxed'
|
|
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
const nodeTypes = result.current.nodes.map(node => node.metaData.type)
|
|
|
|
expect(nodeTypes).toContain(BlockEnum.Command)
|
|
expect(nodeTypes).toContain(BlockEnum.FileUpload)
|
|
expect(nodeTypes).not.toContain(BlockEnum.Agent)
|
|
})
|
|
|
|
it('should map variable assigner to variable aggregator metadata', () => {
|
|
const { result } = renderHook(() => useAvailableNodesMetaData())
|
|
|
|
expect(result.current.nodesMap[BlockEnum.VariableAggregator]).toBeDefined()
|
|
expect(result.current.nodesMap[BlockEnum.VariableAssigner]).toBe(result.current.nodesMap[BlockEnum.VariableAggregator])
|
|
})
|
|
})
|