test(workflow): add unit tests for workflow components (#33910)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
This commit is contained in:
Coding On Star
2026-03-23 16:37:03 +08:00
committed by GitHub
parent abda859075
commit fdc880bc67
54 changed files with 12469 additions and 189 deletions

View File

@ -0,0 +1,138 @@
import { render, screen } from '@testing-library/react'
import { BlockEnum, NodeRunningStatus } from '../../types'
import SimpleNode from '../index'
let mockNodesReadOnly = false
vi.mock('@/app/components/workflow/hooks', () => ({
useNodesReadOnly: () => ({
nodesReadOnly: mockNodesReadOnly,
}),
}))
vi.mock('@/app/components/workflow/block-icon', () => ({
__esModule: true,
default: ({ type }: { type: BlockEnum }) => <div>{`block-icon:${type}`}</div>,
}))
vi.mock('@/app/components/workflow/nodes/_base/components/node-control', () => ({
__esModule: true,
default: ({ id }: { id: string }) => <div>{`node-control:${id}`}</div>,
}))
vi.mock('@/app/components/workflow/nodes/_base/components/node-handle', () => ({
NodeTargetHandle: ({ handleId }: { handleId: string }) => <div>{`node-handle:${handleId}`}</div>,
}))
const createData = (overrides: Record<string, unknown> = {}) => ({
title: 'Answer',
desc: '',
type: BlockEnum.Answer,
...overrides,
})
describe('simple-node', () => {
beforeEach(() => {
vi.clearAllMocks()
mockNodesReadOnly = false
})
it('should render the block shell, target handle, and node control by default', () => {
render(
<SimpleNode
id="simple-node"
data={createData()}
/>,
)
expect(screen.getByText('Answer')).toBeInTheDocument()
expect(screen.getByText('block-icon:answer')).toBeInTheDocument()
expect(screen.getByText('node-handle:target')).toBeInTheDocument()
expect(screen.getByText('node-control:simple-node')).toBeInTheDocument()
})
it('should show the running state border and spinner', () => {
const { container } = render(
<SimpleNode
id="simple-node"
data={createData({
_runningStatus: NodeRunningStatus.Running,
})}
/>,
)
expect(container.querySelector('.text-text-accent')).not.toBeNull()
expect(container.innerHTML).toContain('!border-state-accent-solid')
expect(screen.queryByText('node-control:simple-node')).not.toBeInTheDocument()
})
it('should show success, failed, and exception status indicators', () => {
const { container, rerender } = render(
<SimpleNode
id="simple-node"
data={createData({
_runningStatus: NodeRunningStatus.Succeeded,
})}
/>,
)
expect(container.querySelector('.text-text-success')).not.toBeNull()
expect(container.innerHTML).toContain('!border-state-success-solid')
rerender(
<SimpleNode
id="simple-node"
data={createData({
_runningStatus: NodeRunningStatus.Failed,
})}
/>,
)
expect(container.querySelector('.text-text-destructive')).not.toBeNull()
expect(container.innerHTML).toContain('!border-state-destructive-solid')
rerender(
<SimpleNode
id="simple-node"
data={createData({
_runningStatus: NodeRunningStatus.Exception,
})}
/>,
)
expect(container.querySelector('.text-text-warning-secondary')).not.toBeNull()
expect(container.innerHTML).toContain('!border-state-warning-solid')
})
it('should hide handles and controls for candidate or read-only nodes and show selected waiting styles', () => {
mockNodesReadOnly = true
const { container } = render(
<SimpleNode
id="simple-node"
data={createData({
selected: true,
_waitingRun: true,
_isCandidate: true,
})}
/>,
)
expect(screen.queryByText('node-handle:target')).not.toBeInTheDocument()
expect(screen.queryByText('node-control:simple-node')).not.toBeInTheDocument()
expect(container.querySelector('.border-components-option-card-option-selected-border')).not.toBeNull()
expect(container.querySelector('.opacity-70')).not.toBeNull()
})
it('should show a spinner when a single run is still running', () => {
const { container } = render(
<SimpleNode
id="simple-node"
data={createData({
_singleRunningStatus: NodeRunningStatus.Running,
})}
/>,
)
expect(container.querySelector('.animate-spin')).not.toBeNull()
})
})