mirror of
https://github.com/langgenius/dify.git
synced 2026-05-13 08:57:28 +08:00
fix(web): tests for component-ui & single-run-form
This commit is contained in:
parent
26c14fd58a
commit
c86fd4cba0
@ -5,7 +5,8 @@ import type { ValueSelector } from '@/app/components/workflow/types'
|
||||
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer'
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { BlockEnum, InputVarType } from '@/app/components/workflow/types'
|
||||
import { BlockEnum, InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import HITLInputComponentUI from '../component-ui'
|
||||
import { HITLInputNode } from '../node'
|
||||
|
||||
@ -215,7 +216,7 @@ describe('HITLInputComponentUI', () => {
|
||||
})
|
||||
|
||||
describe('Default formInput', () => {
|
||||
it('should pass default payload to InputField when formInput is undefined', async () => {
|
||||
it('should open an empty default editor when formInput is undefined', async () => {
|
||||
const { findByRole } = renderComponent({
|
||||
formInput: undefined,
|
||||
})
|
||||
@ -223,10 +224,10 @@ describe('HITLInputComponentUI', () => {
|
||||
fireEvent.click(await screen.findByRole('button', { name: 'common.operation.edit' }))
|
||||
|
||||
const textbox = await findByRole('textbox')
|
||||
const saveButton = await screen.findByRole('button', { name: 'common.operation.save' })
|
||||
|
||||
fireEvent.click(await screen.findByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
expect(textbox).toHaveValue('customer_name')
|
||||
expect(textbox).toHaveValue('')
|
||||
expect(saveButton).toBeDisabled()
|
||||
})
|
||||
|
||||
it('should render variable selector when workflowNodesMap fallback is used', () => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer'
|
||||
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { HumanInputFormData } from '@/types/workflow'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
@ -12,25 +13,54 @@ vi.mock('react-i18next', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@langgenius/dify-ui/button', () => ({
|
||||
Button: ({
|
||||
children,
|
||||
disabled,
|
||||
onClick,
|
||||
}: {
|
||||
children?: ReactNode
|
||||
disabled?: boolean
|
||||
onClick?: () => void
|
||||
}) => (
|
||||
<button type="button" disabled={disabled} onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/chat/chat/answer/human-input-content/content-item', () => ({
|
||||
__esModule: true,
|
||||
default: ({ content }: { content: string }) => <div>{content}</div>,
|
||||
default: ({
|
||||
content,
|
||||
formInputFields,
|
||||
inputs,
|
||||
onInputChange,
|
||||
}: {
|
||||
content: string
|
||||
formInputFields: FormInputItem[]
|
||||
inputs: Record<string, HumanInputFieldValue>
|
||||
onInputChange: (name: string, value: HumanInputFieldValue) => void
|
||||
}) => {
|
||||
const fieldName = /\{\{#\$output\.([^#]+)#\}\}/.exec(content)?.[1]
|
||||
if (!fieldName)
|
||||
return <div>{content}</div>
|
||||
|
||||
const field = formInputFields.find(field => field.output_variable_name === fieldName)
|
||||
if (!field)
|
||||
return null
|
||||
|
||||
if (field.type === 'select') {
|
||||
return (
|
||||
<select
|
||||
aria-label={fieldName}
|
||||
value={typeof inputs[fieldName] === 'string' ? inputs[fieldName] : ''}
|
||||
onChange={event => onInputChange(fieldName, event.target.value)}
|
||||
>
|
||||
<option value="">Select</option>
|
||||
{field.option_source.value.map(option => (
|
||||
<option key={option} value={option}>{option}</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
if (field.type === 'paragraph') {
|
||||
return (
|
||||
<textarea
|
||||
aria-label={fieldName}
|
||||
value={typeof inputs[fieldName] === 'string' ? inputs[fieldName] : ''}
|
||||
onChange={event => onInputChange(fieldName, event.target.value)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return <div>{fieldName}</div>
|
||||
},
|
||||
}))
|
||||
|
||||
const createFormData = (overrides: Partial<HumanInputFormData> = {}): HumanInputFormData => ({
|
||||
@ -60,6 +90,10 @@ const createFormData = (overrides: Partial<HumanInputFormData> = {}): HumanInput
|
||||
})
|
||||
|
||||
describe('SingleRunForm', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders the back action as a named button and forwards clicks', async () => {
|
||||
const user = userEvent.setup()
|
||||
const handleBack = vi.fn()
|
||||
@ -99,4 +133,99 @@ describe('SingleRunForm', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('submits updated paragraph input values', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
render(
|
||||
<SingleRunForm
|
||||
nodeName="Review"
|
||||
data={createFormData()}
|
||||
onSubmit={onSubmit}
|
||||
/>,
|
||||
)
|
||||
|
||||
await user.clear(screen.getByRole('textbox', { name: 'review' }))
|
||||
await user.type(screen.getByRole('textbox', { name: 'review' }), 'updated review')
|
||||
await user.click(screen.getByRole('button', { name: 'Approve' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalledWith({
|
||||
inputs: { review: 'updated review' },
|
||||
action: 'approve',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('uses resolved default values for variable paragraph inputs', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
render(
|
||||
<SingleRunForm
|
||||
nodeName="Review"
|
||||
data={createFormData({
|
||||
inputs: [{
|
||||
type: InputVarType.paragraph,
|
||||
output_variable_name: 'review',
|
||||
default: {
|
||||
selector: ['source', 'answer'],
|
||||
type: 'variable',
|
||||
value: 'fallback review',
|
||||
},
|
||||
}],
|
||||
resolved_default_values: {
|
||||
review: 'resolved review',
|
||||
},
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
/>,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Approve' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalledWith({
|
||||
inputs: { review: 'resolved review' },
|
||||
action: 'approve',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('disables submit actions until a select input has a value', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSubmit = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
render(
|
||||
<SingleRunForm
|
||||
nodeName="Review"
|
||||
data={createFormData({
|
||||
form_content: 'Choose {{#$output.choice#}}',
|
||||
inputs: [{
|
||||
type: InputVarType.select,
|
||||
output_variable_name: 'choice',
|
||||
option_source: {
|
||||
selector: [],
|
||||
type: 'constant',
|
||||
value: ['approve', 'reject'],
|
||||
},
|
||||
}],
|
||||
})}
|
||||
onSubmit={onSubmit}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Approve' })).toBeDisabled()
|
||||
|
||||
await user.selectOptions(screen.getByRole('combobox', { name: 'choice' }), 'approve')
|
||||
await user.click(screen.getByRole('button', { name: 'Approve' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onSubmit).toHaveBeenCalledWith({
|
||||
inputs: { choice: 'approve' },
|
||||
action: 'approve',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user