test(workflow): reorganize specs into __tests__ and align with shared test infrastructure (#33625)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Coding On Star
2026-03-18 16:40:28 +08:00
committed by GitHub
parent 387e5a345f
commit db4deb1d6b
39 changed files with 3538 additions and 203 deletions

View File

@ -0,0 +1,84 @@
import type { NodeProps } from 'reactflow'
import type { CommonNodeType } from '@/app/components/workflow/types'
import { render, screen, waitFor } from '@testing-library/react'
import ReactFlow, { ReactFlowProvider } from 'reactflow'
import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types'
import { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types'
import ErrorHandleOnNode from '../error-handle-on-node'
const createNodeData = (overrides: Partial<CommonNodeType> = {}): CommonNodeType => ({
type: BlockEnum.Code,
title: 'Node',
desc: '',
...overrides,
})
const ErrorNode = ({ id, data }: NodeProps<CommonNodeType>) => (
<div>
<ErrorHandleOnNode id={id} data={data} />
</div>
)
const renderErrorNode = (data: CommonNodeType) => {
return render(
<div style={{ width: 800, height: 600 }}>
<ReactFlowProvider>
<ReactFlow
fitView
edges={[]}
nodes={[
{
id: 'node-1',
type: 'errorNode',
position: { x: 0, y: 0 },
data,
},
]}
nodeTypes={{ errorNode: ErrorNode }}
/>
</ReactFlowProvider>
</div>,
)
}
describe('ErrorHandleOnNode', () => {
// Empty and default-value states.
describe('Rendering', () => {
it('should render nothing when the node has no error strategy', () => {
const { container } = renderErrorNode(createNodeData())
expect(screen.queryByText('workflow.common.onFailure')).not.toBeInTheDocument()
expect(container.querySelector('.react-flow__handle')).not.toBeInTheDocument()
})
it('should render the default-value label', async () => {
renderErrorNode(createNodeData({ error_strategy: ErrorHandleTypeEnum.defaultValue }))
await waitFor(() => expect(screen.getByText('workflow.common.onFailure')).toBeInTheDocument())
expect(screen.getByText('workflow.common.onFailure')).toBeInTheDocument()
expect(screen.getByText('workflow.nodes.common.errorHandle.defaultValue.output')).toBeInTheDocument()
})
})
// Fail-branch behavior and warning styling.
describe('Effects', () => {
it('should render the fail-branch source handle', async () => {
const { container } = renderErrorNode(createNodeData({ error_strategy: ErrorHandleTypeEnum.failBranch }))
await waitFor(() => expect(screen.getByText('workflow.nodes.common.errorHandle.failBranch.title')).toBeInTheDocument())
expect(screen.getByText('workflow.nodes.common.errorHandle.failBranch.title')).toBeInTheDocument()
expect(container.querySelector('.react-flow__handle')).toHaveAttribute('data-handleid', ErrorHandleTypeEnum.failBranch)
})
it('should add warning styles when the node is in exception status', async () => {
const { container } = renderErrorNode(createNodeData({
error_strategy: ErrorHandleTypeEnum.defaultValue,
_runningStatus: NodeRunningStatus.Exception,
}))
await waitFor(() => expect(container.querySelector('.bg-state-warning-hover')).toBeInTheDocument())
expect(container.querySelector('.bg-state-warning-hover')).toHaveClass('border-components-badge-status-light-warning-halo')
expect(container.querySelector('.text-text-warning')).toBeInTheDocument()
})
})
})

View File

@ -0,0 +1,130 @@
import type { NodeProps } from 'reactflow'
import type { CommonNodeType } from '@/app/components/workflow/types'
import { render, waitFor } from '@testing-library/react'
import ReactFlow, { ReactFlowProvider } from 'reactflow'
import { BlockEnum } from '@/app/components/workflow/types'
import { NodeSourceHandle, NodeTargetHandle } from '../node-handle'
const createNodeData = (overrides: Partial<CommonNodeType> = {}): CommonNodeType => ({
type: BlockEnum.Code,
title: 'Node',
desc: '',
...overrides,
})
const TargetHandleNode = ({ id, data }: NodeProps<CommonNodeType>) => (
<div>
<NodeTargetHandle
id={id}
data={data}
handleId="target-1"
handleClassName="target-marker"
/>
</div>
)
const SourceHandleNode = ({ id, data }: NodeProps<CommonNodeType>) => (
<div>
<NodeSourceHandle
id={id}
data={data}
handleId="source-1"
handleClassName="source-marker"
/>
</div>
)
const renderFlowNode = (type: 'targetNode' | 'sourceNode', data: CommonNodeType) => {
return render(
<div style={{ width: 800, height: 600 }}>
<ReactFlowProvider>
<ReactFlow
fitView
edges={[]}
nodes={[
{
id: 'node-1',
type,
position: { x: 0, y: 0 },
data,
},
]}
nodeTypes={{
targetNode: TargetHandleNode,
sourceNode: SourceHandleNode,
}}
/>
</ReactFlowProvider>
</div>,
)
}
describe('node-handle', () => {
// Target handle states and visibility rules.
describe('NodeTargetHandle', () => {
it('should hide the connection indicator when the target handle is not connected', async () => {
const { container } = renderFlowNode('targetNode', createNodeData())
await waitFor(() => expect(container.querySelector('.target-marker')).toBeInTheDocument())
const handle = container.querySelector('.target-marker')
expect(handle).toHaveAttribute('data-handleid', 'target-1')
expect(handle).toHaveClass('after:opacity-0')
})
it('should merge custom classes and hide start-like nodes completely', async () => {
const { container } = render(
<div style={{ width: 800, height: 600 }}>
<ReactFlowProvider>
<ReactFlow
fitView
edges={[]}
nodes={[
{
id: 'node-2',
type: 'targetNode',
position: { x: 0, y: 0 },
data: createNodeData({ type: BlockEnum.Start }),
},
]}
nodeTypes={{
targetNode: ({ id, data }: NodeProps<CommonNodeType>) => (
<div>
<NodeTargetHandle
id={id}
data={data}
handleId="target-2"
handleClassName="custom-target"
/>
</div>
),
}}
/>
</ReactFlowProvider>
</div>,
)
await waitFor(() => expect(container.querySelector('.custom-target')).toBeInTheDocument())
const handle = container.querySelector('.custom-target')
expect(handle).toHaveClass('opacity-0')
expect(handle).toHaveClass('custom-target')
})
})
// Source handle connection state.
describe('NodeSourceHandle', () => {
it('should keep the source indicator visible when the handle is connected', async () => {
const { container } = renderFlowNode('sourceNode', createNodeData({ _connectedSourceHandleIds: ['source-1'] }))
await waitFor(() => expect(container.querySelector('.source-marker')).toBeInTheDocument())
const handle = container.querySelector('.source-marker')
expect(handle).toHaveAttribute('data-handleid', 'source-1')
expect(handle).not.toHaveClass('after:opacity-0')
})
})
})