mirror of
https://github.com/langgenius/dify.git
synced 2026-03-21 02:20:08 +08:00
236 lines
7.2 KiB
TypeScript
236 lines
7.2 KiB
TypeScript
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
|
import type { Var } from '@/app/components/workflow/types'
|
|
import { act } from '@testing-library/react'
|
|
import {
|
|
BlockEnum,
|
|
InputVarType,
|
|
} from '@/app/components/workflow/types'
|
|
import {
|
|
createLexicalTestEditor,
|
|
expectInlineWrapperDom,
|
|
} from '../test-helpers'
|
|
import HITLInputBlockComponent from './component'
|
|
import {
|
|
$createHITLInputNode,
|
|
$isHITLInputNode,
|
|
HITLInputNode,
|
|
} from './node'
|
|
|
|
const createFormInput = (): FormInputItem => ({
|
|
type: InputVarType.paragraph,
|
|
output_variable_name: 'user_name',
|
|
default: {
|
|
type: 'constant',
|
|
selector: [],
|
|
value: 'hello',
|
|
},
|
|
})
|
|
|
|
const createNodeProps = () => {
|
|
return {
|
|
variableName: 'user_name',
|
|
nodeId: 'node-1',
|
|
formInputs: [createFormInput()],
|
|
onFormInputsChange: vi.fn(),
|
|
onFormInputItemRename: vi.fn(),
|
|
onFormInputItemRemove: vi.fn(),
|
|
workflowNodesMap: {
|
|
'node-1': {
|
|
title: 'Node 1',
|
|
type: BlockEnum.Start,
|
|
height: 100,
|
|
width: 100,
|
|
position: { x: 0, y: 0 },
|
|
},
|
|
},
|
|
getVarType: vi.fn(),
|
|
environmentVariables: [{ variable: 'env.var_a', type: 'string' }] as Var[],
|
|
conversationVariables: [{ variable: 'conversation.var_b', type: 'number' }] as Var[],
|
|
ragVariables: [{ variable: 'rag.shared.var_c', type: 'string', isRagVariable: true }] as Var[],
|
|
readonly: true,
|
|
}
|
|
}
|
|
|
|
const createTestEditor = () => {
|
|
return createLexicalTestEditor('hitl-input-node-test', [HITLInputNode])
|
|
}
|
|
|
|
describe('HITLInputNode', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should expose node metadata and configured properties through getters', () => {
|
|
const editor = createTestEditor()
|
|
const props = createNodeProps()
|
|
|
|
expect(HITLInputNode.getType()).toBe('hitl-input-block')
|
|
|
|
act(() => {
|
|
editor.update(() => {
|
|
const node = $createHITLInputNode(
|
|
props.variableName,
|
|
props.nodeId,
|
|
props.formInputs,
|
|
props.onFormInputsChange,
|
|
props.onFormInputItemRename,
|
|
props.onFormInputItemRemove,
|
|
props.workflowNodesMap,
|
|
props.getVarType,
|
|
props.environmentVariables,
|
|
props.conversationVariables,
|
|
props.ragVariables,
|
|
props.readonly,
|
|
)
|
|
|
|
expect(node.isInline()).toBe(true)
|
|
expect(node.isIsolated()).toBe(true)
|
|
expect(node.isTopLevel()).toBe(true)
|
|
expect(node.getVariableName()).toBe(props.variableName)
|
|
expect(node.getNodeId()).toBe(props.nodeId)
|
|
expect(node.getFormInputs()).toEqual(props.formInputs)
|
|
expect(node.getOnFormInputsChange()).toBe(props.onFormInputsChange)
|
|
expect(node.getOnFormInputItemRename()).toBe(props.onFormInputItemRename)
|
|
expect(node.getOnFormInputItemRemove()).toBe(props.onFormInputItemRemove)
|
|
expect(node.getWorkflowNodesMap()).toEqual(props.workflowNodesMap)
|
|
expect(node.getGetVarType()).toBe(props.getVarType)
|
|
expect(node.getEnvironmentVariables()).toEqual(props.environmentVariables)
|
|
expect(node.getConversationVariables()).toEqual(props.conversationVariables)
|
|
expect(node.getRagVariables()).toEqual(props.ragVariables)
|
|
expect(node.getReadonly()).toBe(true)
|
|
expect(node.getTextContent()).toBe('{{#$output.user_name#}}')
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should return default fallback values for optional properties', () => {
|
|
const editor = createTestEditor()
|
|
const props = createNodeProps()
|
|
|
|
act(() => {
|
|
editor.update(() => {
|
|
const node = $createHITLInputNode(
|
|
props.variableName,
|
|
props.nodeId,
|
|
props.formInputs,
|
|
props.onFormInputsChange,
|
|
props.onFormInputItemRename,
|
|
props.onFormInputItemRemove,
|
|
props.workflowNodesMap,
|
|
)
|
|
|
|
expect(node.getEnvironmentVariables()).toEqual([])
|
|
expect(node.getConversationVariables()).toEqual([])
|
|
expect(node.getRagVariables()).toEqual([])
|
|
expect(node.getReadonly()).toBe(false)
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should clone, serialize, import and decorate correctly', () => {
|
|
const editor = createTestEditor()
|
|
const props = createNodeProps()
|
|
|
|
act(() => {
|
|
editor.update(() => {
|
|
const node = $createHITLInputNode(
|
|
props.variableName,
|
|
props.nodeId,
|
|
props.formInputs,
|
|
props.onFormInputsChange,
|
|
props.onFormInputItemRename,
|
|
props.onFormInputItemRemove,
|
|
props.workflowNodesMap,
|
|
props.getVarType,
|
|
props.environmentVariables,
|
|
props.conversationVariables,
|
|
props.ragVariables,
|
|
props.readonly,
|
|
)
|
|
|
|
const serialized = node.exportJSON()
|
|
const cloned = HITLInputNode.clone(node)
|
|
const imported = HITLInputNode.importJSON(serialized)
|
|
|
|
expect(cloned).toBeInstanceOf(HITLInputNode)
|
|
expect(cloned.getKey()).toBe(node.getKey())
|
|
expect(cloned).not.toBe(node)
|
|
expect(imported).toBeInstanceOf(HITLInputNode)
|
|
|
|
const element = node.decorate()
|
|
expect(element.type).toBe(HITLInputBlockComponent)
|
|
expect(element.props.nodeKey).toBe(node.getKey())
|
|
expect(element.props.varName).toBe('user_name')
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should fallback to empty form inputs when imported payload omits formInputs', () => {
|
|
const editor = createTestEditor()
|
|
const props = createNodeProps()
|
|
|
|
act(() => {
|
|
editor.update(() => {
|
|
const source = $createHITLInputNode(
|
|
props.variableName,
|
|
props.nodeId,
|
|
props.formInputs,
|
|
props.onFormInputsChange,
|
|
props.onFormInputItemRename,
|
|
props.onFormInputItemRemove,
|
|
props.workflowNodesMap,
|
|
props.getVarType,
|
|
props.environmentVariables,
|
|
props.conversationVariables,
|
|
props.ragVariables,
|
|
props.readonly,
|
|
)
|
|
|
|
const payload = {
|
|
...source.exportJSON(),
|
|
formInputs: undefined as unknown as FormInputItem[],
|
|
}
|
|
|
|
const imported = HITLInputNode.importJSON(payload)
|
|
const cloned = HITLInputNode.clone(imported)
|
|
|
|
expect(imported.getFormInputs()).toEqual([])
|
|
expect(cloned.getFormInputs()).toEqual([])
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should create and update DOM and support helper type guard', () => {
|
|
const editor = createTestEditor()
|
|
const props = createNodeProps()
|
|
|
|
act(() => {
|
|
editor.update(() => {
|
|
const node = $createHITLInputNode(
|
|
props.variableName,
|
|
props.nodeId,
|
|
props.formInputs,
|
|
props.onFormInputsChange,
|
|
props.onFormInputItemRename,
|
|
props.onFormInputItemRemove,
|
|
props.workflowNodesMap,
|
|
props.getVarType,
|
|
props.environmentVariables,
|
|
props.conversationVariables,
|
|
props.ragVariables,
|
|
props.readonly,
|
|
)
|
|
|
|
const dom = node.createDOM()
|
|
|
|
expectInlineWrapperDom(dom, ['w-[calc(100%-1px)]', 'support-drag'])
|
|
expect(node.updateDOM()).toBe(false)
|
|
expect($isHITLInputNode(node)).toBe(true)
|
|
})
|
|
})
|
|
|
|
expect($isHITLInputNode(null)).toBe(false)
|
|
expect($isHITLInputNode(undefined)).toBe(false)
|
|
})
|
|
})
|