dify/web/app/components/base/prompt-editor/plugins/hitl-input-block/node.spec.tsx
Poojan b5f62b98f9
test: add unit tests for base-components-part-5 (#32457)
Co-authored-by: sahil-infocusp <73810410+sahil-infocusp@users.noreply.github.com>
2026-02-25 22:13:10 +08:00

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)
})
})