refactor(components): reorder class names for consistency in various plugin components and add unit tests for CardMoreInfo and other components

This commit is contained in:
CodingOnStar
2026-02-11 11:15:11 +08:00
parent c36de51771
commit 0e36aa9c67
182 changed files with 2006 additions and 1844 deletions

View File

@ -1,32 +1,24 @@
import type { Plugin } from '../types'
import type { Plugin } from '../../types'
import { fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PluginCategoryEnum } from '../types'
import PluginMutationModal from './index'
import { PluginCategoryEnum } from '../../types'
import PluginMutationModal from '../index'
// ================================
// Mock External Dependencies Only
// ================================
// Mock useTheme hook
vi.mock('@/hooks/use-theme', () => ({
default: () => ({ theme: 'light' }),
}))
// Mock i18n-config
vi.mock('@/i18n-config', () => ({
renderI18nObject: (obj: Record<string, string>, locale: string) => {
return obj?.[locale] || obj?.['en-US'] || ''
},
}))
// Mock i18n-config/language
vi.mock('@/i18n-config/language', () => ({
getLanguage: (locale: string) => locale || 'en-US',
}))
// Mock useCategories hook
const mockCategoriesMap: Record<string, { label: string }> = {
'tool': { label: 'Tool' },
'model': { label: 'Model' },
@ -37,18 +29,16 @@ const mockCategoriesMap: Record<string, { label: string }> = {
'bundle': { label: 'Bundle' },
}
vi.mock('../hooks', () => ({
vi.mock('../../hooks', () => ({
useCategories: () => ({
categoriesMap: mockCategoriesMap,
}),
}))
// Mock formatNumber utility
vi.mock('@/utils/format', () => ({
formatNumber: (num: number) => num.toLocaleString(),
}))
// Mock shouldUseMcpIcon utility
vi.mock('@/utils/mcp', () => ({
shouldUseMcpIcon: (src: unknown) =>
typeof src === 'object'
@ -56,7 +46,6 @@ vi.mock('@/utils/mcp', () => ({
&& (src as { content?: string })?.content === '🔗',
}))
// Mock AppIcon component
vi.mock('@/app/components/base/app-icon', () => ({
default: ({
icon,
@ -83,7 +72,6 @@ vi.mock('@/app/components/base/app-icon', () => ({
),
}))
// Mock Mcp icon component
vi.mock('@/app/components/base/icons/src/vender/other', () => ({
Mcp: ({ className }: { className?: string }) => (
<div data-testid="mcp-icon" className={className}>
@ -97,8 +85,7 @@ vi.mock('@/app/components/base/icons/src/vender/other', () => ({
),
}))
// Mock LeftCorner icon component
vi.mock('../../base/icons/src/vender/plugin', () => ({
vi.mock('../../../base/icons/src/vender/plugin', () => ({
LeftCorner: ({ className }: { className?: string }) => (
<div data-testid="left-corner" className={className}>
LeftCorner
@ -106,8 +93,7 @@ vi.mock('../../base/icons/src/vender/plugin', () => ({
),
}))
// Mock Partner badge
vi.mock('../base/badges/partner', () => ({
vi.mock('../../base/badges/partner', () => ({
default: ({ className, text }: { className?: string, text?: string }) => (
<div data-testid="partner-badge" className={className} title={text}>
Partner
@ -115,8 +101,7 @@ vi.mock('../base/badges/partner', () => ({
),
}))
// Mock Verified badge
vi.mock('../base/badges/verified', () => ({
vi.mock('../../base/badges/verified', () => ({
default: ({ className, text }: { className?: string, text?: string }) => (
<div data-testid="verified-badge" className={className} title={text}>
Verified
@ -124,36 +109,6 @@ vi.mock('../base/badges/verified', () => ({
),
}))
// Mock Remix icons
vi.mock('@remixicon/react', () => ({
RiCheckLine: ({ className }: { className?: string }) => (
<span data-testid="ri-check-line" className={className}>
</span>
),
RiCloseLine: ({ className }: { className?: string }) => (
<span data-testid="ri-close-line" className={className}>
</span>
),
RiInstallLine: ({ className }: { className?: string }) => (
<span data-testid="ri-install-line" className={className}>
</span>
),
RiAlertFill: ({ className }: { className?: string }) => (
<span data-testid="ri-alert-fill" className={className}>
</span>
),
RiLoader2Line: ({ className }: { className?: string }) => (
<span data-testid="ri-loader-line" className={className}>
</span>
),
}))
// Mock Skeleton components
vi.mock('@/app/components/base/skeleton', () => ({
SkeletonContainer: ({ children }: { children: React.ReactNode }) => (
<div data-testid="skeleton-container">{children}</div>
@ -330,8 +285,7 @@ describe('PluginMutationModal', () => {
render(<PluginMutationModal {...props} />)
// The modal should have a close button
expect(screen.getByTestId('ri-close-line')).toBeInTheDocument()
expect(screen.getByRole('dialog')).toBeInTheDocument()
})
})
@ -465,9 +419,8 @@ describe('PluginMutationModal', () => {
render(<PluginMutationModal {...props} />)
// Find the close icon - the Modal component handles the onClose callback
const closeIcon = screen.getByTestId('ri-close-line')
expect(closeIcon).toBeInTheDocument()
const dialog = screen.getByRole('dialog')
expect(dialog).toBeInTheDocument()
})
it('should not call mutate when button is disabled during pending', () => {
@ -563,9 +516,7 @@ describe('PluginMutationModal', () => {
render(<PluginMutationModal {...props} />)
// The Card component should receive installed=true
// This will show a check icon
expect(screen.getByTestId('ri-check-line')).toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).toBeInTheDocument()
})
})
@ -577,8 +528,7 @@ describe('PluginMutationModal', () => {
render(<PluginMutationModal {...props} />)
// The check icon should not be present (installed=false)
expect(screen.queryByTestId('ri-check-line')).not.toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).not.toBeInTheDocument()
})
})
@ -593,7 +543,7 @@ describe('PluginMutationModal', () => {
expect(
screen.queryByRole('button', { name: /Cancel/i }),
).not.toBeInTheDocument()
expect(screen.queryByTestId('ri-check-line')).not.toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).not.toBeInTheDocument()
})
it('should handle isPending=false and isSuccess=true', () => {
@ -606,7 +556,7 @@ describe('PluginMutationModal', () => {
expect(
screen.getByRole('button', { name: /Cancel/i }),
).toBeInTheDocument()
expect(screen.getByTestId('ri-check-line')).toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).toBeInTheDocument()
})
it('should handle both isPending=true and isSuccess=true', () => {
@ -619,7 +569,7 @@ describe('PluginMutationModal', () => {
expect(
screen.queryByRole('button', { name: /Cancel/i }),
).not.toBeInTheDocument()
expect(screen.getByTestId('ri-check-line')).toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).toBeInTheDocument()
})
})
})
@ -710,8 +660,8 @@ describe('PluginMutationModal', () => {
it('should have displayName set', () => {
// The component sets displayName = 'PluginMutationModal'
const displayName
= (PluginMutationModal as any).type?.displayName
|| (PluginMutationModal as any).displayName
= (PluginMutationModal as unknown as { type?: { displayName?: string }, displayName?: string }).type?.displayName
|| (PluginMutationModal as unknown as { displayName?: string }).displayName
expect(displayName).toBe('PluginMutationModal')
})
@ -901,8 +851,7 @@ describe('PluginMutationModal', () => {
render(<PluginMutationModal {...props} />)
// Close icon should be present
expect(screen.getByTestId('ri-close-line')).toBeInTheDocument()
expect(screen.getByRole('dialog')).toBeInTheDocument()
})
})
@ -1118,8 +1067,7 @@ describe('PluginMutationModal', () => {
/>,
)
// Should show success state
expect(screen.getByTestId('ri-check-line')).toBeInTheDocument()
expect(document.querySelector('.bg-state-success-solid')).toBeInTheDocument()
})
it('should handle plugin prop changes', () => {