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,4 +1,4 @@
import type { FilterState } from '../filter-management'
import type { FilterState } from '../../filter-management'
import type { SystemFeatures } from '@/types/feature'
import { act, fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
@ -6,7 +6,7 @@ import { defaultSystemFeatures, InstallationScope } from '@/types/feature'
// ==================== Imports (after mocks) ====================
import Empty from './index'
import Empty from '../index'
// ==================== Mock Setup ====================
@ -15,7 +15,6 @@ const {
mockSetActiveTab,
mockUseInstalledPluginList,
mockState,
stableT,
} = vi.hoisted(() => {
const state = {
filters: {
@ -32,20 +31,16 @@ const {
} as Partial<SystemFeatures>,
pluginList: { plugins: [] as Array<{ id: string }> } as { plugins: Array<{ id: string }> } | undefined,
}
// Stable t function to prevent infinite re-renders
// The component's useEffect and useMemo depend on t
const t = (key: string) => key
return {
mockSetActiveTab: vi.fn(),
mockUseInstalledPluginList: vi.fn(() => ({ data: state.pluginList })),
mockState: state,
stableT: t,
}
})
// Mock plugin page context
vi.mock('../context', () => ({
usePluginPageContext: (selector: (value: any) => any) => {
vi.mock('../../context', () => ({
usePluginPageContext: (selector: (value: Record<string, unknown>) => unknown) => {
const contextValue = {
filters: mockState.filters,
setActiveTab: mockSetActiveTab,
@ -56,7 +51,7 @@ vi.mock('../context', () => ({
// Mock global public store (Zustand store)
vi.mock('@/context/global-public-context', () => ({
useGlobalPublicStore: (selector: (state: any) => any) => {
useGlobalPublicStore: (selector: (state: Record<string, unknown>) => unknown) => {
return selector({
systemFeatures: {
...defaultSystemFeatures,
@ -92,22 +87,10 @@ vi.mock('@/app/components/plugins/install-plugin/install-from-local-package', ()
}))
// Mock Line component
vi.mock('../../marketplace/empty/line', () => ({
vi.mock('../../../marketplace/empty/line', () => ({
default: ({ className }: { className?: string }) => <div data-testid="line-component" className={className} />,
}))
// Override react-i18next with stable t function reference to prevent infinite re-renders
// The component's useEffect and useMemo depend on t, so it MUST be stable
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: stableT,
i18n: {
language: 'en',
changeLanguage: vi.fn(),
},
}),
}))
// ==================== Test Utilities ====================
const resetMockState = () => {
@ -191,7 +174,7 @@ describe('Empty Component', () => {
await flushEffects()
// Assert
expect(screen.getByText('list.noInstalled')).toBeInTheDocument()
expect(screen.getByText('plugin.list.noInstalled')).toBeInTheDocument()
})
it('should display "notFound" text when filters are active with plugins', async () => {
@ -202,19 +185,19 @@ describe('Empty Component', () => {
setMockFilters({ categories: ['model'] })
const { rerender } = render(<Empty />)
await flushEffects()
expect(screen.getByText('list.notFound')).toBeInTheDocument()
expect(screen.getByText('plugin.list.notFound')).toBeInTheDocument()
// Test tags filter
setMockFilters({ categories: [], tags: ['tag1'] })
rerender(<Empty />)
await flushEffects()
expect(screen.getByText('list.notFound')).toBeInTheDocument()
expect(screen.getByText('plugin.list.notFound')).toBeInTheDocument()
// Test searchQuery filter
setMockFilters({ tags: [], searchQuery: 'test query' })
rerender(<Empty />)
await flushEffects()
expect(screen.getByText('list.notFound')).toBeInTheDocument()
expect(screen.getByText('plugin.list.notFound')).toBeInTheDocument()
})
it('should prioritize "noInstalled" over "notFound" when no plugins exist', async () => {
@ -227,7 +210,7 @@ describe('Empty Component', () => {
await flushEffects()
// Assert
expect(screen.getByText('list.noInstalled')).toBeInTheDocument()
expect(screen.getByText('plugin.list.noInstalled')).toBeInTheDocument()
})
})
@ -250,15 +233,15 @@ describe('Empty Component', () => {
// Assert
const buttons = screen.getAllByRole('button')
expect(buttons).toHaveLength(3)
expect(screen.getByText('source.marketplace')).toBeInTheDocument()
expect(screen.getByText('source.github')).toBeInTheDocument()
expect(screen.getByText('source.local')).toBeInTheDocument()
expect(screen.getByText('plugin.source.marketplace')).toBeInTheDocument()
expect(screen.getByText('plugin.source.github')).toBeInTheDocument()
expect(screen.getByText('plugin.source.local')).toBeInTheDocument()
// Verify button order
const buttonTexts = buttons.map(btn => btn.textContent)
expect(buttonTexts[0]).toContain('source.marketplace')
expect(buttonTexts[1]).toContain('source.github')
expect(buttonTexts[2]).toContain('source.local')
expect(buttonTexts[0]).toContain('plugin.source.marketplace')
expect(buttonTexts[1]).toContain('plugin.source.github')
expect(buttonTexts[2]).toContain('plugin.source.local')
})
it('should render only marketplace method when restricted to marketplace only', async () => {
@ -278,9 +261,9 @@ describe('Empty Component', () => {
// Assert
const buttons = screen.getAllByRole('button')
expect(buttons).toHaveLength(1)
expect(screen.getByText('source.marketplace')).toBeInTheDocument()
expect(screen.queryByText('source.github')).not.toBeInTheDocument()
expect(screen.queryByText('source.local')).not.toBeInTheDocument()
expect(screen.getByText('plugin.source.marketplace')).toBeInTheDocument()
expect(screen.queryByText('plugin.source.github')).not.toBeInTheDocument()
expect(screen.queryByText('plugin.source.local')).not.toBeInTheDocument()
})
it('should render github and local methods when marketplace is disabled', async () => {
@ -300,9 +283,9 @@ describe('Empty Component', () => {
// Assert
const buttons = screen.getAllByRole('button')
expect(buttons).toHaveLength(2)
expect(screen.queryByText('source.marketplace')).not.toBeInTheDocument()
expect(screen.getByText('source.github')).toBeInTheDocument()
expect(screen.getByText('source.local')).toBeInTheDocument()
expect(screen.queryByText('plugin.source.marketplace')).not.toBeInTheDocument()
expect(screen.getByText('plugin.source.github')).toBeInTheDocument()
expect(screen.getByText('plugin.source.local')).toBeInTheDocument()
})
it('should render no methods when marketplace disabled and restricted', async () => {
@ -333,7 +316,7 @@ describe('Empty Component', () => {
await flushEffects()
// Act
fireEvent.click(screen.getByText('source.marketplace'))
fireEvent.click(screen.getByText('plugin.source.marketplace'))
// Assert
expect(mockSetActiveTab).toHaveBeenCalledWith('discover')
@ -348,7 +331,7 @@ describe('Empty Component', () => {
expect(screen.queryByTestId('install-from-github-modal')).not.toBeInTheDocument()
// Act - open modal
fireEvent.click(screen.getByText('source.github'))
fireEvent.click(screen.getByText('plugin.source.github'))
// Assert - modal is open
expect(screen.getByTestId('install-from-github-modal')).toBeInTheDocument()
@ -368,7 +351,7 @@ describe('Empty Component', () => {
const clickSpy = vi.spyOn(fileInput, 'click')
// Act
fireEvent.click(screen.getByText('source.local'))
fireEvent.click(screen.getByText('plugin.source.local'))
// Assert
expect(clickSpy).toHaveBeenCalled()
@ -422,13 +405,13 @@ describe('Empty Component', () => {
await flushEffects()
// Act - Open, close, and reopen GitHub modal
fireEvent.click(screen.getByText('source.github'))
fireEvent.click(screen.getByText('plugin.source.github'))
expect(screen.getByTestId('install-from-github-modal')).toBeInTheDocument()
fireEvent.click(screen.getByTestId('github-modal-close'))
expect(screen.queryByTestId('install-from-github-modal')).not.toBeInTheDocument()
fireEvent.click(screen.getByText('source.github'))
fireEvent.click(screen.getByText('plugin.source.github'))
expect(screen.getByTestId('install-from-github-modal')).toBeInTheDocument()
})
@ -480,7 +463,7 @@ describe('Empty Component', () => {
render(<Empty />)
await flushEffects()
expect(screen.getAllByRole('button')).toHaveLength(1)
expect(screen.getByText('source.marketplace')).toBeInTheDocument()
expect(screen.getByText('plugin.source.marketplace')).toBeInTheDocument()
})
it('should render correct text based on plugin list and filters', async () => {
@ -490,7 +473,7 @@ describe('Empty Component', () => {
const { unmount: unmount1 } = render(<Empty />)
await flushEffects()
expect(screen.getByText('list.noInstalled')).toBeInTheDocument()
expect(screen.getByText('plugin.list.noInstalled')).toBeInTheDocument()
unmount1()
// Test 2: notFound when filters are active with plugins
@ -499,7 +482,7 @@ describe('Empty Component', () => {
render(<Empty />)
await flushEffects()
expect(screen.getByText('list.notFound')).toBeInTheDocument()
expect(screen.getByText('plugin.list.notFound')).toBeInTheDocument()
})
})
@ -529,8 +512,8 @@ describe('Empty Component', () => {
it('should be wrapped with React.memo and have displayName', () => {
// Assert
expect(Empty).toBeDefined()
expect((Empty as any).$$typeof?.toString()).toContain('Symbol')
expect((Empty as any).displayName || (Empty as any).type?.displayName).toBeDefined()
expect((Empty as { $$typeof?: symbol }).$$typeof?.toString()).toContain('Symbol')
expect((Empty as unknown as { displayName?: string, type?: { displayName?: string } }).displayName || (Empty as unknown as { type?: { displayName?: string } }).type?.displayName).toBeDefined()
})
})
@ -542,7 +525,7 @@ describe('Empty Component', () => {
await flushEffects()
// Test GitHub modal onSuccess
fireEvent.click(screen.getByText('source.github'))
fireEvent.click(screen.getByText('plugin.source.github'))
fireEvent.click(screen.getByTestId('github-modal-success'))
expect(screen.getByTestId('install-from-github-modal')).toBeInTheDocument()
@ -570,12 +553,12 @@ describe('Empty Component', () => {
expect(screen.queryByTestId('install-from-local-modal')).not.toBeInTheDocument()
// Open GitHub modal - only GitHub modal visible
fireEvent.click(screen.getByText('source.github'))
fireEvent.click(screen.getByText('plugin.source.github'))
expect(screen.getByTestId('install-from-github-modal')).toBeInTheDocument()
expect(screen.queryByTestId('install-from-local-modal')).not.toBeInTheDocument()
// Click local button - triggers file input, no modal yet (no file selected)
fireEvent.click(screen.getByText('source.local'))
fireEvent.click(screen.getByText('plugin.source.local'))
// GitHub modal should still be visible, local modal requires file selection
expect(screen.queryByTestId('install-from-local-modal')).not.toBeInTheDocument()
})