mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 02:18:08 +08:00
178 lines
5.6 KiB
TypeScript
178 lines
5.6 KiB
TypeScript
import type { DataSourceAuth } from './types'
|
|
import type { Plugin } from '@/app/components/plugins/types'
|
|
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import { useTheme } from 'next-themes'
|
|
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
|
import { useMarketplaceAllPlugins } from './hooks'
|
|
import InstallFromMarketplace from './install-from-marketplace'
|
|
|
|
/**
|
|
* InstallFromMarketplace Component Tests
|
|
* Using Unit approach to focus on the component's internal state and conditional rendering.
|
|
*/
|
|
|
|
// Mock external dependencies
|
|
vi.mock('next-themes', () => ({
|
|
useTheme: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('next/link', () => ({
|
|
default: ({ children, href }: { children: React.ReactNode, href: string }) => (
|
|
<a href={href} data-testid="mock-link">{children}</a>
|
|
),
|
|
}))
|
|
|
|
vi.mock('@/utils/var', () => ({
|
|
getMarketplaceUrl: vi.fn((path: string, { theme }: { theme: string }) => `https://marketplace.url${path}?theme=${theme}`),
|
|
}))
|
|
|
|
// Mock marketplace components
|
|
|
|
vi.mock('@/app/components/plugins/marketplace/list', () => ({
|
|
default: ({ plugins, cardRender, cardContainerClassName, emptyClassName }: {
|
|
plugins: Plugin[]
|
|
cardRender: (p: Plugin) => React.ReactNode
|
|
cardContainerClassName?: string
|
|
emptyClassName?: string
|
|
}) => (
|
|
<div data-testid="mock-list" className={cardContainerClassName}>
|
|
{plugins.length === 0 && <div className={emptyClassName} aria-label="empty-state" />}
|
|
{plugins.map(plugin => (
|
|
<div key={plugin.plugin_id} data-testid={`list-item-${plugin.plugin_id}`}>
|
|
{cardRender(plugin)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
vi.mock('@/app/components/plugins/provider-card', () => ({
|
|
default: ({ payload }: { payload: Plugin }) => (
|
|
<div data-testid={`mock-provider-card-${payload.plugin_id}`}>
|
|
{payload.name}
|
|
</div>
|
|
),
|
|
}))
|
|
|
|
vi.mock('./hooks', () => ({
|
|
useMarketplaceAllPlugins: vi.fn(),
|
|
}))
|
|
|
|
describe('InstallFromMarketplace Component', () => {
|
|
const mockProviders: DataSourceAuth[] = [
|
|
{
|
|
author: 'Author',
|
|
provider: 'provider',
|
|
plugin_id: 'p1',
|
|
plugin_unique_identifier: 'u1',
|
|
icon: 'icon',
|
|
name: 'name',
|
|
label: { en_US: 'Label', zh_Hans: '标签' },
|
|
description: { en_US: 'Desc', zh_Hans: '描述' },
|
|
credentials_list: [],
|
|
},
|
|
]
|
|
|
|
const mockPlugins: Plugin[] = [
|
|
{
|
|
type: 'plugin',
|
|
plugin_id: 'plugin-1',
|
|
name: 'Plugin 1',
|
|
category: PluginCategoryEnum.datasource,
|
|
// ...other minimal fields
|
|
} as Plugin,
|
|
{
|
|
type: 'bundle',
|
|
plugin_id: 'bundle-1',
|
|
name: 'Bundle 1',
|
|
category: PluginCategoryEnum.datasource,
|
|
} as Plugin,
|
|
]
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.mocked(useTheme).mockReturnValue({
|
|
theme: 'light',
|
|
setTheme: vi.fn(),
|
|
themes: ['light', 'dark'],
|
|
systemTheme: 'light',
|
|
resolvedTheme: 'light',
|
|
} as unknown as ReturnType<typeof useTheme>)
|
|
})
|
|
|
|
describe('Rendering', () => {
|
|
it('should render correctly when not loading and not collapsed', () => {
|
|
// Arrange
|
|
vi.mocked(useMarketplaceAllPlugins).mockReturnValue({
|
|
plugins: mockPlugins,
|
|
isLoading: false,
|
|
})
|
|
|
|
// Act
|
|
render(<InstallFromMarketplace providers={mockProviders} searchText="" />)
|
|
|
|
// Assert
|
|
expect(screen.getByText('common.modelProvider.installDataSourceProvider')).toBeInTheDocument()
|
|
expect(screen.getByText('common.modelProvider.discoverMore')).toBeInTheDocument()
|
|
expect(screen.getByTestId('mock-link')).toHaveAttribute('href', 'https://marketplace.url?theme=light')
|
|
expect(screen.getByTestId('mock-list')).toBeInTheDocument()
|
|
expect(screen.getByTestId('mock-provider-card-plugin-1')).toBeInTheDocument()
|
|
expect(screen.queryByTestId('mock-provider-card-bundle-1')).not.toBeInTheDocument()
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should show loading state when marketplace plugins are loading and component is not collapsed', () => {
|
|
// Arrange
|
|
vi.mocked(useMarketplaceAllPlugins).mockReturnValue({
|
|
plugins: [],
|
|
isLoading: true,
|
|
})
|
|
|
|
// Act
|
|
render(<InstallFromMarketplace providers={mockProviders} searchText="" />)
|
|
|
|
// Assert
|
|
expect(screen.getByRole('status')).toBeInTheDocument()
|
|
expect(screen.queryByTestId('mock-list')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe('Interactions', () => {
|
|
it('should toggle collapse state when clicking the header', () => {
|
|
// Arrange
|
|
vi.mocked(useMarketplaceAllPlugins).mockReturnValue({
|
|
plugins: mockPlugins,
|
|
isLoading: false,
|
|
})
|
|
render(<InstallFromMarketplace providers={mockProviders} searchText="" />)
|
|
const toggleHeader = screen.getByText('common.modelProvider.installDataSourceProvider')
|
|
|
|
// Act (Collapse)
|
|
fireEvent.click(toggleHeader)
|
|
// Assert
|
|
expect(screen.queryByTestId('mock-list')).not.toBeInTheDocument()
|
|
|
|
// Act (Expand)
|
|
fireEvent.click(toggleHeader)
|
|
// Assert
|
|
expect(screen.getByTestId('mock-list')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not show loading state even if isLoading is true when component is collapsed', () => {
|
|
// Arrange
|
|
vi.mocked(useMarketplaceAllPlugins).mockReturnValue({
|
|
plugins: [],
|
|
isLoading: true,
|
|
})
|
|
render(<InstallFromMarketplace providers={mockProviders} searchText="" />)
|
|
const toggleHeader = screen.getByText('common.modelProvider.installDataSourceProvider')
|
|
|
|
// Act (Collapse)
|
|
fireEvent.click(toggleHeader)
|
|
|
|
// Assert
|
|
expect(screen.queryByRole('status')).not.toBeInTheDocument()
|
|
})
|
|
})
|
|
})
|