mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
test: add comprehensive tests (#31649)
Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,527 @@
|
||||
import { cleanup, render, screen, waitFor } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import BasicAppPreview from './basic-app-preview'
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
const mockUseGetTryAppInfo = vi.fn()
|
||||
const mockUseAllToolProviders = vi.fn()
|
||||
const mockUseGetTryAppDataSets = vi.fn()
|
||||
const mockUseTextGenerationCurrentProviderAndModelAndModelList = vi.fn()
|
||||
|
||||
vi.mock('@/service/use-try-app', () => ({
|
||||
useGetTryAppInfo: (...args: unknown[]) => mockUseGetTryAppInfo(...args),
|
||||
useGetTryAppDataSets: (...args: unknown[]) => mockUseGetTryAppDataSets(...args),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-tools', () => ({
|
||||
useAllToolProviders: () => mockUseAllToolProviders(),
|
||||
}))
|
||||
|
||||
vi.mock('../../../header/account-setting/model-provider-page/hooks', () => ({
|
||||
useTextGenerationCurrentProviderAndModelAndModelList: (...args: unknown[]) =>
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList(...args),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-breakpoints', () => ({
|
||||
default: () => 'pc',
|
||||
MediaType: {
|
||||
mobile: 'mobile',
|
||||
pc: 'pc',
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/app/configuration/config', () => ({
|
||||
default: () => <div data-testid="config-component">Config</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/app/configuration/debug', () => ({
|
||||
default: () => <div data-testid="debug-component">Debug</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/features', () => ({
|
||||
FeaturesProvider: ({ children }: { children: React.ReactNode }) => (
|
||||
<div data-testid="features-provider">{children}</div>
|
||||
),
|
||||
}))
|
||||
|
||||
const createMockAppDetail = (mode: string = 'chat'): Record<string, unknown> => ({
|
||||
id: 'test-app-id',
|
||||
name: 'Test App',
|
||||
description: 'Test Description',
|
||||
mode,
|
||||
site: {
|
||||
title: 'Test Site Title',
|
||||
icon: '🚀',
|
||||
icon_type: 'emoji',
|
||||
icon_background: '#FFFFFF',
|
||||
icon_url: '',
|
||||
},
|
||||
model_config: {
|
||||
model: {
|
||||
provider: 'langgenius/openai/openai',
|
||||
name: 'gpt-4',
|
||||
mode: 'chat',
|
||||
},
|
||||
pre_prompt: 'You are a helpful assistant',
|
||||
user_input_form: [] as unknown[],
|
||||
external_data_tools: [] as unknown[],
|
||||
dataset_configs: {
|
||||
datasets: {
|
||||
datasets: [] as unknown[],
|
||||
},
|
||||
},
|
||||
agent_mode: {
|
||||
tools: [] as unknown[],
|
||||
enabled: false,
|
||||
},
|
||||
more_like_this: { enabled: false },
|
||||
opening_statement: 'Hello!',
|
||||
suggested_questions: ['Question 1'],
|
||||
sensitive_word_avoidance: null,
|
||||
speech_to_text: null,
|
||||
text_to_speech: null,
|
||||
file_upload: null as unknown,
|
||||
suggested_questions_after_answer: null,
|
||||
retriever_resource: null,
|
||||
annotation_reply: null,
|
||||
},
|
||||
deleted_tools: [] as unknown[],
|
||||
})
|
||||
|
||||
describe('BasicAppPreview', () => {
|
||||
beforeEach(() => {
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: createMockAppDetail(),
|
||||
isLoading: false,
|
||||
})
|
||||
mockUseAllToolProviders.mockReturnValue({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
})
|
||||
mockUseGetTryAppDataSets.mockReturnValue({
|
||||
data: { data: [] },
|
||||
isLoading: false,
|
||||
})
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList.mockReturnValue({
|
||||
currentModel: {
|
||||
features: [],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('loading state', () => {
|
||||
it('renders loading when app detail is loading', () => {
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders loading when tool providers are loading', () => {
|
||||
mockUseAllToolProviders.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders loading when datasets are loading', () => {
|
||||
mockUseGetTryAppDataSets.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('content rendering', () => {
|
||||
it('renders Config component when data is loaded', async () => {
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('renders Debug component when data is loaded on PC', async () => {
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('debug-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('renders FeaturesProvider', async () => {
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('features-provider')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('different app modes', () => {
|
||||
it('handles chat mode', async () => {
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: createMockAppDetail('chat'),
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles completion mode', async () => {
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: createMockAppDetail('completion'),
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles agent-chat mode', async () => {
|
||||
const agentAppDetail = createMockAppDetail('agent-chat')
|
||||
const modelConfig = agentAppDetail.model_config as Record<string, unknown>
|
||||
modelConfig.agent_mode = {
|
||||
tools: [
|
||||
{
|
||||
provider_id: 'test-provider',
|
||||
provider_name: 'test-provider',
|
||||
provider_type: 'builtin',
|
||||
tool_name: 'test-tool',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
enabled: true,
|
||||
max_iteration: 5,
|
||||
}
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: agentAppDetail,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
mockUseAllToolProviders.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
id: 'test-provider',
|
||||
is_team_authorization: true,
|
||||
icon: '/icon.png',
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('hook calls', () => {
|
||||
it('calls useGetTryAppInfo with correct appId', () => {
|
||||
render(<BasicAppPreview appId="my-app-id" />)
|
||||
|
||||
expect(mockUseGetTryAppInfo).toHaveBeenCalledWith('my-app-id')
|
||||
})
|
||||
|
||||
it('calls useTextGenerationCurrentProviderAndModelAndModelList with model config', async () => {
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUseTextGenerationCurrentProviderAndModelAndModelList).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('model features', () => {
|
||||
it('handles vision feature', async () => {
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList.mockReturnValue({
|
||||
currentModel: {
|
||||
features: ['vision'],
|
||||
},
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles document feature', async () => {
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList.mockReturnValue({
|
||||
currentModel: {
|
||||
features: ['document'],
|
||||
},
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles audio feature', async () => {
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList.mockReturnValue({
|
||||
currentModel: {
|
||||
features: ['audio'],
|
||||
},
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles video feature', async () => {
|
||||
mockUseTextGenerationCurrentProviderAndModelAndModelList.mockReturnValue({
|
||||
currentModel: {
|
||||
features: ['video'],
|
||||
},
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('dataset handling', () => {
|
||||
it('handles app with datasets in agent mode', async () => {
|
||||
const appWithDatasets = createMockAppDetail('agent-chat')
|
||||
const modelConfig = appWithDatasets.model_config as Record<string, unknown>
|
||||
modelConfig.agent_mode = {
|
||||
tools: [
|
||||
{
|
||||
dataset: {
|
||||
enabled: true,
|
||||
id: 'dataset-1',
|
||||
},
|
||||
},
|
||||
],
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithDatasets,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUseGetTryAppDataSets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles app with datasets in dataset_configs', async () => {
|
||||
const appWithDatasets = createMockAppDetail('chat')
|
||||
const modelConfig = appWithDatasets.model_config as Record<string, unknown>
|
||||
modelConfig.dataset_configs = {
|
||||
datasets: {
|
||||
datasets: [
|
||||
{ dataset: { id: 'dataset-1' } },
|
||||
{ dataset: { id: 'dataset-2' } },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithDatasets,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUseGetTryAppDataSets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('advanced prompt mode', () => {
|
||||
it('handles advanced prompt mode', async () => {
|
||||
const appWithAdvancedPrompt = createMockAppDetail('chat')
|
||||
const modelConfig = appWithAdvancedPrompt.model_config as Record<string, unknown>
|
||||
modelConfig.prompt_type = 'advanced'
|
||||
modelConfig.chat_prompt_config = {
|
||||
prompt: [{ role: 'system', text: 'You are helpful' }],
|
||||
}
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithAdvancedPrompt,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('file upload config', () => {
|
||||
it('handles file upload config', async () => {
|
||||
const appWithFileUpload = createMockAppDetail('chat')
|
||||
const modelConfig = appWithFileUpload.model_config as Record<string, unknown>
|
||||
modelConfig.file_upload = {
|
||||
enabled: true,
|
||||
image: {
|
||||
enabled: true,
|
||||
detail: 'high',
|
||||
number_limits: 5,
|
||||
transfer_methods: ['local_file', 'remote_url'],
|
||||
},
|
||||
allowed_file_types: ['image'],
|
||||
allowed_file_extensions: ['.jpg', '.png'],
|
||||
allowed_file_upload_methods: ['local_file'],
|
||||
number_limits: 3,
|
||||
}
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithFileUpload,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('external data tools', () => {
|
||||
it('handles app with external_data_tools', async () => {
|
||||
const appWithExternalTools = createMockAppDetail('chat')
|
||||
const modelConfig = appWithExternalTools.model_config as Record<string, unknown>
|
||||
modelConfig.external_data_tools = [
|
||||
{
|
||||
variable: 'test_var',
|
||||
label: 'Test Label',
|
||||
enabled: true,
|
||||
type: 'text',
|
||||
config: {},
|
||||
icon: '/icon.png',
|
||||
icon_background: '#FFFFFF',
|
||||
},
|
||||
]
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithExternalTools,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('deleted tools handling', () => {
|
||||
it('handles app with deleted tools', async () => {
|
||||
const agentAppDetail = createMockAppDetail('agent-chat')
|
||||
const modelConfig = agentAppDetail.model_config as Record<string, unknown>
|
||||
modelConfig.agent_mode = {
|
||||
tools: [
|
||||
{
|
||||
id: 'tool-1',
|
||||
provider_id: 'test-provider',
|
||||
provider_name: 'test-provider',
|
||||
provider_type: 'builtin',
|
||||
tool_name: 'test-tool',
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
enabled: true,
|
||||
max_iteration: 5,
|
||||
}
|
||||
agentAppDetail.deleted_tools = [
|
||||
{
|
||||
id: 'tool-1',
|
||||
tool_name: 'test-tool',
|
||||
},
|
||||
]
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: agentAppDetail,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
mockUseAllToolProviders.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
id: 'test-provider',
|
||||
is_team_authorization: false,
|
||||
icon: '/icon.png',
|
||||
},
|
||||
],
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('config-component')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('handles app without model_config', async () => {
|
||||
const appWithoutModelConfig = createMockAppDetail('chat')
|
||||
appWithoutModelConfig.model_config = undefined
|
||||
|
||||
mockUseGetTryAppInfo.mockReturnValue({
|
||||
data: appWithoutModelConfig,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<BasicAppPreview appId="test-app-id" />)
|
||||
|
||||
// Should still render (with default model config)
|
||||
await waitFor(() => {
|
||||
expect(mockUseGetTryAppDataSets).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,179 @@
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import FlowAppPreview from './flow-app-preview'
|
||||
|
||||
const mockUseGetTryAppFlowPreview = vi.fn()
|
||||
|
||||
vi.mock('@/service/use-try-app', () => ({
|
||||
useGetTryAppFlowPreview: (...args: unknown[]) => mockUseGetTryAppFlowPreview(...args),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/workflow/workflow-preview', () => ({
|
||||
default: ({
|
||||
className,
|
||||
miniMapToRight,
|
||||
nodes,
|
||||
edges,
|
||||
}: { className?: string, miniMapToRight?: boolean, nodes?: unknown[], edges?: unknown[] }) => (
|
||||
<div
|
||||
data-testid="workflow-preview"
|
||||
className={className}
|
||||
data-mini-map-to-right={miniMapToRight}
|
||||
data-nodes-count={nodes?.length}
|
||||
data-edges-count={edges?.length}
|
||||
>
|
||||
WorkflowPreview
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
describe('FlowAppPreview', () => {
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('loading state', () => {
|
||||
it('renders Loading component when isLoading is true', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('workflow-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('no data state', () => {
|
||||
it('returns null when data is null', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const { container } = render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(container.firstChild).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null when data is undefined', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const { container } = render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(container.firstChild).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('data loaded state', () => {
|
||||
it('renders WorkflowPreview when data is loaded', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [{ id: 'node1' }],
|
||||
edges: [{ id: 'edge1' }],
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
expect(screen.getByTestId('workflow-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('passes graph data to WorkflowPreview', () => {
|
||||
const mockNodes = [{ id: 'node1' }, { id: 'node2' }, { id: 'node3' }]
|
||||
const mockEdges = [{ id: 'edge1' }, { id: 'edge2' }]
|
||||
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: {
|
||||
graph: {
|
||||
nodes: mockNodes,
|
||||
edges: mockEdges,
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
const workflowPreview = screen.getByTestId('workflow-preview')
|
||||
expect(workflowPreview).toHaveAttribute('data-nodes-count', '3')
|
||||
expect(workflowPreview).toHaveAttribute('data-edges-count', '2')
|
||||
})
|
||||
|
||||
it('passes miniMapToRight=true to WorkflowPreview', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
const workflowPreview = screen.getByTestId('workflow-preview')
|
||||
expect(workflowPreview).toHaveAttribute('data-mini-map-to-right', 'true')
|
||||
})
|
||||
|
||||
it('passes className to WorkflowPreview', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="test-app-id" className="custom-class" />)
|
||||
|
||||
const workflowPreview = screen.getByTestId('workflow-preview')
|
||||
expect(workflowPreview).toHaveClass('custom-class')
|
||||
})
|
||||
})
|
||||
|
||||
describe('hook calls', () => {
|
||||
it('calls useGetTryAppFlowPreview with correct appId', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: null,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
render(<FlowAppPreview appId="my-specific-app-id" />)
|
||||
|
||||
expect(mockUseGetTryAppFlowPreview).toHaveBeenCalledWith('my-specific-app-id')
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapper styling', () => {
|
||||
it('renders with correct wrapper classes when data is loaded', () => {
|
||||
mockUseGetTryAppFlowPreview.mockReturnValue({
|
||||
data: {
|
||||
graph: {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
const { container } = render(<FlowAppPreview appId="test-app-id" />)
|
||||
|
||||
const wrapper = container.firstChild as HTMLElement
|
||||
expect(wrapper).toHaveClass('h-full', 'w-full')
|
||||
})
|
||||
})
|
||||
})
|
||||
127
web/app/components/explore/try-app/preview/index.spec.tsx
Normal file
127
web/app/components/explore/try-app/preview/index.spec.tsx
Normal file
@ -0,0 +1,127 @@
|
||||
import type { TryAppInfo } from '@/service/try-app'
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import Preview from './index'
|
||||
|
||||
vi.mock('./basic-app-preview', () => ({
|
||||
default: ({ appId }: { appId: string }) => (
|
||||
<div data-testid="basic-app-preview" data-app-id={appId}>
|
||||
BasicAppPreview
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('./flow-app-preview', () => ({
|
||||
default: ({ appId, className }: { appId: string, className?: string }) => (
|
||||
<div data-testid="flow-app-preview" data-app-id={appId} className={className}>
|
||||
FlowAppPreview
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
const createMockAppDetail = (mode: string): TryAppInfo => ({
|
||||
id: 'test-app-id',
|
||||
name: 'Test App',
|
||||
description: 'Test Description',
|
||||
mode,
|
||||
site: {
|
||||
title: 'Test Site Title',
|
||||
icon: 'icon',
|
||||
icon_type: 'emoji',
|
||||
icon_background: '#FFFFFF',
|
||||
icon_url: '',
|
||||
},
|
||||
model_config: {
|
||||
model: {
|
||||
provider: 'test/provider',
|
||||
name: 'test-model',
|
||||
mode: 'chat',
|
||||
},
|
||||
dataset_configs: {
|
||||
datasets: {
|
||||
datasets: [],
|
||||
},
|
||||
},
|
||||
agent_mode: {
|
||||
tools: [],
|
||||
},
|
||||
user_input_form: [],
|
||||
},
|
||||
} as unknown as TryAppInfo)
|
||||
|
||||
describe('Preview', () => {
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
describe('basic app rendering', () => {
|
||||
it('renders BasicAppPreview for agent-chat mode', () => {
|
||||
const appDetail = createMockAppDetail('agent-chat')
|
||||
render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByTestId('basic-app-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('flow-app-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders BasicAppPreview for chat mode', () => {
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByTestId('basic-app-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('flow-app-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders BasicAppPreview for completion mode', () => {
|
||||
const appDetail = createMockAppDetail('completion')
|
||||
render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByTestId('basic-app-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('flow-app-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('passes appId to BasicAppPreview', () => {
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
render(<Preview appId="my-app-id" appDetail={appDetail} />)
|
||||
|
||||
const basicPreview = screen.getByTestId('basic-app-preview')
|
||||
expect(basicPreview).toHaveAttribute('data-app-id', 'my-app-id')
|
||||
})
|
||||
})
|
||||
|
||||
describe('flow app rendering', () => {
|
||||
it('renders FlowAppPreview for workflow mode', () => {
|
||||
const appDetail = createMockAppDetail('workflow')
|
||||
render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByTestId('flow-app-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('basic-app-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders FlowAppPreview for advanced-chat mode', () => {
|
||||
const appDetail = createMockAppDetail('advanced-chat')
|
||||
render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
expect(screen.getByTestId('flow-app-preview')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('basic-app-preview')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('passes appId and className to FlowAppPreview', () => {
|
||||
const appDetail = createMockAppDetail('workflow')
|
||||
render(<Preview appId="my-flow-app-id" appDetail={appDetail} />)
|
||||
|
||||
const flowPreview = screen.getByTestId('flow-app-preview')
|
||||
expect(flowPreview).toHaveAttribute('data-app-id', 'my-flow-app-id')
|
||||
expect(flowPreview).toHaveClass('h-full')
|
||||
})
|
||||
})
|
||||
|
||||
describe('wrapper styling', () => {
|
||||
it('renders with correct wrapper classes', () => {
|
||||
const appDetail = createMockAppDetail('chat')
|
||||
const { container } = render(<Preview appId="test-app-id" appDetail={appDetail} />)
|
||||
|
||||
const wrapper = container.firstChild as HTMLElement
|
||||
expect(wrapper).toHaveClass('h-full', 'w-full')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user