From d494e42166ac0729033b02ff1686c1a989443e8c Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 22 Apr 2026 08:45:23 +0800 Subject: [PATCH] Test human input form submissions across entry points --- .../form/[token]/__tests__/form.spec.tsx | 164 ++++++++++++++++++ .../__tests__/single-run-form.spec.tsx | 106 +++++++++++ 2 files changed, 270 insertions(+) create mode 100644 web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx create mode 100644 web/app/components/workflow/nodes/human-input/components/__tests__/single-run-form.spec.tsx diff --git a/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx new file mode 100644 index 0000000000..0106fd564d --- /dev/null +++ b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx @@ -0,0 +1,164 @@ +import type { FormData } from '../form' +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types' +import { InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types' +import { TransferMethod } from '@/types/app' +import FormContent from '../form' + +const mockSubmitForm = vi.hoisted(() => vi.fn()) +const mockUseGetHumanInputForm = vi.hoisted(() => vi.fn()) + +vi.mock('@/next/navigation', () => ({ + useParams: () => ({ token: 'token-123' }), +})) + +vi.mock('@/service/use-share', () => ({ + useGetHumanInputForm: (...args: unknown[]) => mockUseGetHumanInputForm(...args), + useSubmitHumanInputForm: () => ({ + mutate: mockSubmitForm, + isPending: false, + }), +})) + +vi.mock('@/hooks/use-document-title', () => ({ + __esModule: true, + default: vi.fn(), +})) + +vi.mock('@/app/components/base/chat/chat/answer/human-input-content/content-item', () => ({ + __esModule: true, + default: ({ content, onInputChange }: { content: string, onInputChange: (name: string, value: unknown) => void }) => ( +
+ {content} + + +
+ ), +})) + +vi.mock('@/app/components/base/chat/chat/answer/human-input-content/expiration-time', () => ({ + __esModule: true, + default: () =>
expiration-time
, +})) + +vi.mock('@/app/components/base/loading', () => ({ + __esModule: true, + default: () =>
loading
, +})) + +vi.mock('@/app/components/base/logo/dify-logo', () => ({ + __esModule: true, + default: () =>
dify-logo
, +})) + +vi.mock('@/app/components/base/app-icon', () => ({ + __esModule: true, + default: () =>
app-icon
, +})) + +describe('Human input share form', () => { + const formData: FormData = { + site: { + site: { + title: 'Review App', + icon_type: 'emoji', + icon: 'R', + icon_background: '#fff', + icon_url: '', + default_language: 'en-US', + description: '', + copyright: '', + privacy_policy: '', + custom_disclaimer: '', + prompt_public: false, + use_icon_as_answer_icon: false, + }, + }, + form_content: '{{#$output.summary#}} {{#$output.attachments#}}', + inputs: [ + { + type: InputVarType.paragraph, + output_variable_name: 'summary', + default: { + type: 'constant', + value: 'initial summary', + selector: [], + }, + }, + { + type: InputVarType.multiFiles, + output_variable_name: 'attachments', + allowed_file_extensions: ['.pdf'], + allowed_file_types: [SupportUploadFileTypes.document], + allowed_file_upload_methods: [TransferMethod.local_file], + max_upload_count: 3, + }, + ], + resolved_default_values: {}, + user_actions: [ + { + id: 'approve', + title: 'Approve', + button_style: UserActionButtonType.Primary, + }, + ], + expiration_time: 60, + } + + beforeEach(() => { + vi.clearAllMocks() + mockUseGetHumanInputForm.mockReturnValue({ + data: formData, + isLoading: false, + error: null, + }) + }) + + it('submits typed human input values through the share form mutation', async () => { + const user = userEvent.setup() + + render() + + await user.click(screen.getAllByRole('button', { name: 'share-update-summary' })[0]!) + await user.click(screen.getAllByRole('button', { name: 'share-update-attachments' })[0]!) + await user.click(screen.getByRole('button', { name: 'Approve' })) + + expect(mockSubmitForm).toHaveBeenCalledWith({ + token: 'token-123', + data: { + action: 'approve', + inputs: { + summary: 'updated summary', + attachments: [{ + id: 'file-1', + name: 'review.pdf', + size: 128, + type: 'document', + progress: 100, + transferMethod: TransferMethod.local_file, + supportFileType: 'document', + } satisfies FileEntity], + }, + }, + }, expect.objectContaining({ + onSuccess: expect.any(Function), + })) + }) +}) diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/single-run-form.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/single-run-form.spec.tsx new file mode 100644 index 0000000000..55edfc5e58 --- /dev/null +++ b/web/app/components/workflow/nodes/human-input/components/__tests__/single-run-form.spec.tsx @@ -0,0 +1,106 @@ +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import type { HumanInputFormData } from '@/types/workflow' +import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types' +import { TransferMethod } from '@/types/app' +import { UserActionButtonType } from '../../types' +import SingleRunForm from '../single-run-form' + +vi.mock('@/app/components/base/chat/chat/answer/human-input-content/content-item', () => ({ + __esModule: true, + default: ({ content, onInputChange }: { content: string, onInputChange: (name: string, value: unknown) => void }) => ( +
+ {content} + + +
+ ), +})) + +describe('SingleRunForm', () => { + const formData: HumanInputFormData = { + form_id: 'form-1', + node_id: 'node-1', + node_title: 'Human Input', + form_content: '{{#$output.summary#}} {{#$output.attachments#}}', + inputs: [ + { + type: InputVarType.paragraph, + output_variable_name: 'summary', + default: { + type: 'constant', + value: 'initial summary', + selector: [], + }, + }, + { + type: InputVarType.multiFiles, + output_variable_name: 'attachments', + allowed_file_extensions: ['.pdf'], + allowed_file_types: [SupportUploadFileTypes.document], + allowed_file_upload_methods: [TransferMethod.local_file], + max_upload_count: 3, + }, + ], + actions: [ + { + id: 'approve', + title: 'Approve', + button_style: UserActionButtonType.Primary, + }, + ], + form_token: 'token-1', + resolved_default_values: {}, + display_in_ui: true, + expiration_time: 0, + } + + it('submits typed human input values from the single-run form', async () => { + const user = userEvent.setup() + const onSubmit = vi.fn().mockResolvedValue(undefined) + + render( + , + ) + + await user.click(screen.getAllByRole('button', { name: 'update-summary' })[0]!) + await user.click(screen.getAllByRole('button', { name: 'update-attachments' })[0]!) + await user.click(screen.getByRole('button', { name: 'Approve' })) + + expect(onSubmit).toHaveBeenCalledWith({ + action: 'approve', + inputs: { + summary: 'updated summary', + attachments: [{ + id: 'file-1', + name: 'review.pdf', + size: 128, + type: 'document', + progress: 100, + transferMethod: TransferMethod.local_file, + supportFileType: 'document', + } satisfies FileEntity], + }, + }) + }) +})