dify/web/app/components/tools/provider/empty.spec.tsx
qiuqiua 9ef6b90843
feat: sync main branch (#31938)
Signed-off-by: majiayu000 <1835304752@qq.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Signed-off-by: -LAN- <laipz8200@outlook.com>
Signed-off-by: yihong0618 <zouzou0208@gmail.com>
Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com>
Co-authored-by: 盐粒 Yanli <yanli@dify.ai>
Co-authored-by: wangxiaolei <fatelei@gmail.com>
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Cursx <33718736+Cursx@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: lif <1835304752@qq.com>
Co-authored-by: 非法操作 <hjlarry@163.com>
Co-authored-by: Asuka Minato <i@asukaminato.eu.org>
Co-authored-by: fenglin <790872612@qq.com>
Co-authored-by: qiaofenglin <qiaofenglin@baidu.com>
Co-authored-by: -LAN- <laipz8200@outlook.com>
Co-authored-by: TomoOkuyama <49631611+TomoOkuyama@users.noreply.github.com>
Co-authored-by: Tomo Okuyama <tomo.okuyama@intersystems.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: zyssyz123 <916125788@qq.com>
Co-authored-by: hj24 <mambahj24@gmail.com>
Co-authored-by: Coding On Star <447357187@qq.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.ai>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Xiangxuan Qu <fghpdf@outlook.com>
Co-authored-by: fghpdf <fghpdf@users.noreply.github.com>
Co-authored-by: coopercoder <whitetiger0127@163.com>
Co-authored-by: zhaiguangpeng <zhaiguangpeng@didiglobal.com>
Co-authored-by: Junyan Qin (Chin) <rockchinq@gmail.com>
Co-authored-by: E.G <146701565+GlobalStar117@users.noreply.github.com>
Co-authored-by: GlobalStar117 <GlobalStar117@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: heyszt <270985384@qq.com>
Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com>
Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com>
Co-authored-by: zxhlyh <jasonapring2015@outlook.com>
Co-authored-by: moonpanda <chuanzegao@163.com>
Co-authored-by: warlocgao <warlocgao@tencent.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: KVOJJJin <jzongcode@gmail.com>
Co-authored-by: eux <euxx@users.noreply.github.com>
Co-authored-by: bangjiehan <bangjiehan@gmail.com>
Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com>
Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com>
Co-authored-by: Nie Ronghua <nieronghua@sf-express.com>
Co-authored-by: JQSevenMiao <141806521+JQSevenMiao@users.noreply.github.com>
Co-authored-by: jiasiqi <jiasiqi3@tal.com>
Co-authored-by: Seokrin Taron Sung <sungsjade@gmail.com>
Co-authored-by: CrabSAMA <40541269+CrabSAMA@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: yihong <zouzou0208@gmail.com>
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com>
Co-authored-by: yessenia <yessenia.contact@gmail.com>
Co-authored-by: Jax <anobaka@qq.com>
Co-authored-by: niveshdandyan <155956228+niveshdandyan@users.noreply.github.com>
Co-authored-by: OSS Contributor <oss-contributor@example.com>
Co-authored-by: niveshdandyan <niveshdandyan@users.noreply.github.com>
Co-authored-by: Sean Kenneth Doherty <Smaster7772@gmail.com>
2026-02-04 19:04:24 +08:00

180 lines
5.8 KiB
TypeScript

import { render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
// Import the mock to control it in tests
import useTheme from '@/hooks/use-theme'
import { ToolTypeEnum } from '../../workflow/block-selector/types'
import Empty from './empty'
// Mock useTheme hook
vi.mock('@/hooks/use-theme', () => ({
default: vi.fn(() => ({ theme: 'light' })),
}))
describe('Empty', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(useTheme).mockReturnValue({ theme: 'light' } as ReturnType<typeof useTheme>)
})
// Tests for basic rendering scenarios
describe('Rendering', () => {
it('should render without crashing', () => {
render(<Empty />)
expect(screen.getByText('No tools available')).toBeInTheDocument()
})
it('should render placeholder icon', () => {
render(<Empty />)
// NoToolPlaceholder should be rendered
const container = document.querySelector('.flex.flex-col')
expect(container).toBeInTheDocument()
})
it('should render fallback title when no type provided', () => {
render(<Empty />)
expect(screen.getByText('No tools available')).toBeInTheDocument()
})
})
// Tests for different type prop values
describe('Type Props', () => {
it('should render with Custom type and include link to /tools?category=api', () => {
render(<Empty type={ToolTypeEnum.Custom} />)
const link = document.querySelector('a[href="/tools?category=api"]')
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('target', '_blank')
})
it('should render with MCP type and include link to /tools?category=mcp', () => {
render(<Empty type={ToolTypeEnum.MCP} />)
const link = document.querySelector('a[href="/tools?category=mcp"]')
expect(link).toBeInTheDocument()
expect(link).toHaveAttribute('target', '_blank')
})
it('should render arrow icon for types with links', () => {
render(<Empty type={ToolTypeEnum.Custom} />)
// Check for RiArrowRightUpLine icon (has class h-3 w-3)
const arrowIcon = document.querySelector('.h-3.w-3')
expect(arrowIcon).toBeInTheDocument()
})
it('should not render link for BuiltIn type', () => {
render(<Empty type={ToolTypeEnum.BuiltIn} />)
const link = document.querySelector('a')
expect(link).not.toBeInTheDocument()
})
it('should not render link for Workflow type', () => {
render(<Empty type={ToolTypeEnum.Workflow} />)
const link = document.querySelector('a')
expect(link).not.toBeInTheDocument()
})
})
// Tests for isAgent prop
describe('isAgent Prop', () => {
it('should render as agent without link', () => {
render(<Empty type={ToolTypeEnum.Custom} isAgent />)
// When isAgent is true, no link should be rendered
const link = document.querySelector('a')
expect(link).not.toBeInTheDocument()
})
it('should not render tip text when isAgent is true', () => {
render(<Empty type={ToolTypeEnum.Custom} isAgent />)
// Arrow icon should not be present when isAgent is true
const arrowIcon = document.querySelector('.h-3.w-3')
expect(arrowIcon).not.toBeInTheDocument()
})
})
// Tests for theme-based styling
describe('Theme Support', () => {
it('should not apply invert class in light theme', () => {
vi.mocked(useTheme).mockReturnValue({ theme: 'light' } as ReturnType<typeof useTheme>)
render(<Empty />)
// The NoToolPlaceholder should not have 'invert' class in light mode
// We check the first svg or container within the component
const placeholder = document.querySelector('.flex.flex-col > *:first-child')
expect(placeholder).not.toHaveClass('invert')
})
it('should apply invert class in dark theme', () => {
vi.mocked(useTheme).mockReturnValue({ theme: 'dark' } as ReturnType<typeof useTheme>)
render(<Empty />)
// The NoToolPlaceholder should have 'invert' class in dark mode
const placeholder = document.querySelector('.invert')
expect(placeholder).toBeInTheDocument()
})
})
// Tests for translation key handling
describe('Translation Keys', () => {
it('should use correct translation namespace for tools', () => {
render(<Empty type={ToolTypeEnum.Custom} />)
// The component should render translation keys with 'tools' namespace
// Translation mock returns the key itself
expect(screen.getByText(/addToolModal\.custom\.title/i)).toBeInTheDocument()
})
it('should render tip text for types with hasTitle', () => {
render(<Empty type={ToolTypeEnum.Custom} />)
// Should show the tip text with translation key
expect(screen.getByText(/addToolModal\.custom\.tip/i)).toBeInTheDocument()
})
})
// Tests for edge cases
describe('Edge Cases', () => {
it('should handle undefined type gracefully', () => {
render(<Empty type={undefined} />)
expect(screen.getByText('No tools available')).toBeInTheDocument()
})
it('should handle All type without link', () => {
render(<Empty type={ToolTypeEnum.All} />)
const link = document.querySelector('a')
expect(link).not.toBeInTheDocument()
})
})
// Tests for link styling
describe('Link Styling', () => {
it('should apply hover styling classes to link', () => {
render(<Empty type={ToolTypeEnum.Custom} />)
const link = document.querySelector('a')
expect(link).toHaveClass('cursor-pointer')
expect(link).toHaveClass('hover:text-text-accent')
})
it('should render div instead of link when hasLink is false', () => {
render(<Empty type={ToolTypeEnum.BuiltIn} />)
// No anchor tags should be rendered
const anchors = document.querySelectorAll('a')
expect(anchors.length).toBe(0)
})
})
})