mirror of https://github.com/langgenius/dify.git
412 lines
10 KiB
TypeScript
412 lines
10 KiB
TypeScript
import type { TryAppInfo } from '@/service/try-app'
|
|
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import TryApp from './index'
|
|
import { TypeEnum } from './tab'
|
|
|
|
vi.mock('react-i18next', () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => {
|
|
const translations: Record<string, string> = {
|
|
'tryApp.tabHeader.try': 'Try',
|
|
'tryApp.tabHeader.detail': 'Detail',
|
|
}
|
|
return translations[key] || key
|
|
},
|
|
}),
|
|
}))
|
|
|
|
const mockUseGetTryAppInfo = vi.fn()
|
|
|
|
vi.mock('@/service/use-try-app', () => ({
|
|
useGetTryAppInfo: (...args: unknown[]) => mockUseGetTryAppInfo(...args),
|
|
}))
|
|
|
|
vi.mock('./app', () => ({
|
|
default: ({ appId, appDetail }: { appId: string, appDetail: TryAppInfo }) => (
|
|
<div data-testid="app-component" data-app-id={appId} data-mode={appDetail?.mode}>
|
|
App Component
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
vi.mock('./preview', () => ({
|
|
default: ({ appId, appDetail }: { appId: string, appDetail: TryAppInfo }) => (
|
|
<div data-testid="preview-component" data-app-id={appId} data-mode={appDetail?.mode}>
|
|
Preview Component
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
vi.mock('./app-info', () => ({
|
|
default: ({
|
|
appId,
|
|
appDetail,
|
|
category,
|
|
className,
|
|
onCreate,
|
|
}: { appId: string, appDetail: TryAppInfo, category?: string, className?: string, onCreate: () => void }) => (
|
|
<div
|
|
data-testid="app-info-component"
|
|
data-app-id={appId}
|
|
data-category={category}
|
|
className={className}
|
|
>
|
|
<button data-testid="create-button" onClick={onCreate}>Create</button>
|
|
App Info:
|
|
{' '}
|
|
{appDetail?.name}
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
const createMockAppDetail = (mode: string = 'chat'): TryAppInfo => ({
|
|
id: 'test-app-id',
|
|
name: 'Test App Name',
|
|
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',
|
|
},
|
|
dataset_configs: {
|
|
datasets: {
|
|
datasets: [],
|
|
},
|
|
},
|
|
agent_mode: {
|
|
tools: [],
|
|
},
|
|
user_input_form: [],
|
|
},
|
|
} as unknown as TryAppInfo)
|
|
|
|
describe('TryApp (main index.tsx)', () => {
|
|
beforeEach(() => {
|
|
mockUseGetTryAppInfo.mockReturnValue({
|
|
data: createMockAppDetail(),
|
|
isLoading: false,
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
cleanup()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('loading state', () => {
|
|
it('renders loading when isLoading is true', () => {
|
|
mockUseGetTryAppInfo.mockReturnValue({
|
|
data: null,
|
|
isLoading: true,
|
|
})
|
|
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
expect(document.body.querySelector('[role="status"]')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('content rendering', () => {
|
|
it('renders Tab component', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Try')).toBeInTheDocument()
|
|
expect(screen.getByText('Detail')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('renders App component by default (TRY mode)', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(document.body.querySelector('[data-testid="app-component"]')).toBeInTheDocument()
|
|
expect(document.body.querySelector('[data-testid="preview-component"]')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('renders AppInfo component', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(document.body.querySelector('[data-testid="app-info-component"]')).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('renders close button', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
// Find the close button (the one with RiCloseLine icon)
|
|
const buttons = document.body.querySelectorAll('button')
|
|
expect(buttons.length).toBeGreaterThan(0)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('tab switching', () => {
|
|
it('switches to Preview when Detail tab is clicked', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Detail')).toBeInTheDocument()
|
|
})
|
|
|
|
fireEvent.click(screen.getByText('Detail'))
|
|
|
|
await waitFor(() => {
|
|
expect(document.body.querySelector('[data-testid="preview-component"]')).toBeInTheDocument()
|
|
expect(document.body.querySelector('[data-testid="app-component"]')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
it('switches back to App when Try tab is clicked', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Detail')).toBeInTheDocument()
|
|
})
|
|
|
|
// First switch to Detail
|
|
fireEvent.click(screen.getByText('Detail'))
|
|
|
|
await waitFor(() => {
|
|
expect(document.body.querySelector('[data-testid="preview-component"]')).toBeInTheDocument()
|
|
})
|
|
|
|
// Then switch back to Try
|
|
fireEvent.click(screen.getByText('Try'))
|
|
|
|
await waitFor(() => {
|
|
expect(document.body.querySelector('[data-testid="app-component"]')).toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('close functionality', () => {
|
|
it('calls onClose when close button is clicked', async () => {
|
|
const mockOnClose = vi.fn()
|
|
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={mockOnClose}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
// Find the button with close icon
|
|
const buttons = document.body.querySelectorAll('button')
|
|
const closeButton = Array.from(buttons).find(btn =>
|
|
btn.querySelector('svg') || btn.className.includes('rounded-[10px]'),
|
|
)
|
|
expect(closeButton).toBeInTheDocument()
|
|
|
|
if (closeButton)
|
|
fireEvent.click(closeButton)
|
|
})
|
|
|
|
expect(mockOnClose).toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('create functionality', () => {
|
|
it('calls onCreate when create button in AppInfo is clicked', async () => {
|
|
const mockOnCreate = vi.fn()
|
|
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={mockOnCreate}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const createButton = document.body.querySelector('[data-testid="create-button"]')
|
|
expect(createButton).toBeInTheDocument()
|
|
|
|
if (createButton)
|
|
fireEvent.click(createButton)
|
|
})
|
|
|
|
expect(mockOnCreate).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|
|
|
|
describe('category prop', () => {
|
|
it('passes category to AppInfo when provided', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
category="AI Assistant"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const appInfo = document.body.querySelector('[data-testid="app-info-component"]')
|
|
expect(appInfo).toHaveAttribute('data-category', 'AI Assistant')
|
|
})
|
|
})
|
|
|
|
it('does not pass category to AppInfo when not provided', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const appInfo = document.body.querySelector('[data-testid="app-info-component"]')
|
|
expect(appInfo).not.toHaveAttribute('data-category', expect.any(String))
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('hook calls', () => {
|
|
it('calls useGetTryAppInfo with correct appId', () => {
|
|
render(
|
|
<TryApp
|
|
appId="my-specific-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
expect(mockUseGetTryAppInfo).toHaveBeenCalledWith('my-specific-app-id')
|
|
})
|
|
})
|
|
|
|
describe('props passing', () => {
|
|
it('passes appId to App component', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="my-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const appComponent = document.body.querySelector('[data-testid="app-component"]')
|
|
expect(appComponent).toHaveAttribute('data-app-id', 'my-app-id')
|
|
})
|
|
})
|
|
|
|
it('passes appId to Preview component when in Detail mode', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="my-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Detail')).toBeInTheDocument()
|
|
})
|
|
|
|
fireEvent.click(screen.getByText('Detail'))
|
|
|
|
await waitFor(() => {
|
|
const previewComponent = document.body.querySelector('[data-testid="preview-component"]')
|
|
expect(previewComponent).toHaveAttribute('data-app-id', 'my-app-id')
|
|
})
|
|
})
|
|
|
|
it('passes appId to AppInfo component', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="my-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const appInfoComponent = document.body.querySelector('[data-testid="app-info-component"]')
|
|
expect(appInfoComponent).toHaveAttribute('data-app-id', 'my-app-id')
|
|
})
|
|
})
|
|
|
|
it('passes appDetail to AppInfo component', async () => {
|
|
render(
|
|
<TryApp
|
|
appId="test-app-id"
|
|
onClose={vi.fn()}
|
|
onCreate={vi.fn()}
|
|
/>,
|
|
)
|
|
|
|
await waitFor(() => {
|
|
const appInfoComponent = document.body.querySelector('[data-testid="app-info-component"]')
|
|
expect(appInfoComponent?.textContent).toContain('Test App Name')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('TypeEnum export', () => {
|
|
it('exports TypeEnum correctly', () => {
|
|
expect(TypeEnum.TRY).toBe('try')
|
|
expect(TypeEnum.DETAIL).toBe('detail')
|
|
})
|
|
})
|
|
})
|