suppress all and fix typecheck

This commit is contained in:
Stephen Zhou
2026-04-01 11:18:32 +08:00
parent 99cb5ea8c2
commit c52f18f205
18 changed files with 289 additions and 282 deletions

View File

@@ -45,7 +45,7 @@ export const Markdown = memo((props: MarkdownProps) => {
<div className={cn('markdown-body', '!text-text-primary', className)} data-testid="markdown-body">
<StreamdownWrapper
pluginInfo={pluginInfo}
content={props.content}
latexContent={latexContent}
customComponents={customComponents}
customDisallowedElements={customDisallowedElements}
remarkPlugins={remarkPlugins}

View File

@@ -1,50 +0,0 @@
import { render, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import CardMoreInfo from '../card-more-info'
vi.mock('../base/download-count', () => ({
default: ({ downloadCount }: { downloadCount: number }) => (
<span data-testid="download-count">{downloadCount}</span>
),
}))
describe('CardMoreInfo', () => {
it('renders tags with # prefix', () => {
render(<CardMoreInfo tags={['search', 'agent']} />)
expect(screen.getByText('search')).toBeInTheDocument()
expect(screen.getByText('agent')).toBeInTheDocument()
// # prefixes
const hashmarks = screen.getAllByText('#')
expect(hashmarks).toHaveLength(2)
})
it('renders download count when provided', () => {
render(<CardMoreInfo downloadCount={1000} tags={[]} />)
expect(screen.getByTestId('download-count')).toHaveTextContent('1000')
})
it('does not render download count when undefined', () => {
render(<CardMoreInfo tags={['tag1']} />)
expect(screen.queryByTestId('download-count')).not.toBeInTheDocument()
})
it('renders separator between download count and tags', () => {
render(<CardMoreInfo downloadCount={500} tags={['test']} />)
expect(screen.getByText('·')).toBeInTheDocument()
})
it('does not render separator when no tags', () => {
render(<CardMoreInfo downloadCount={500} tags={[]} />)
expect(screen.queryByText('·')).not.toBeInTheDocument()
})
it('does not render separator when no download count', () => {
render(<CardMoreInfo tags={['tag1']} />)
expect(screen.queryByText('·')).not.toBeInTheDocument()
})
it('handles empty tags array', () => {
const { container } = render(<CardMoreInfo tags={[]} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@@ -4,16 +4,16 @@ import { Provider as JotaiProvider } from 'jotai'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createNuqsTestWrapper } from '@/test/nuqs-testing'
import {
useActivePluginType,
useActivePluginCategory,
useFilterPluginTags,
useMarketplaceMoreClick,
useMarketplacePluginSort,
useMarketplacePluginSortValue,
useMarketplaceSearchMode,
useMarketplaceSort,
useMarketplaceSortValue,
useSearchPluginText,
useSetMarketplaceSort,
useSearchText,
useSetMarketplacePluginSort,
} from '../atoms'
import { DEFAULT_SORT } from '../constants'
import { DEFAULT_PLUGIN_SORT } from '../constants'
const createWrapper = (searchParams = '') => {
const { wrapper: NuqsWrapper } = createNuqsTestWrapper({ searchParams })
@@ -34,24 +34,24 @@ describe('Marketplace sort atoms', () => {
it('should return default sort value from useMarketplaceSort', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useMarketplaceSort(), { wrapper })
const { result } = renderHook(() => useMarketplacePluginSort(), { wrapper })
expect(result.current[0]).toEqual(DEFAULT_SORT)
expect(result.current[0]).toEqual(DEFAULT_PLUGIN_SORT)
expect(typeof result.current[1]).toBe('function')
})
it('should return default sort value from useMarketplaceSortValue', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useMarketplaceSortValue(), { wrapper })
const { result } = renderHook(() => useMarketplacePluginSortValue(), { wrapper })
expect(result.current).toEqual(DEFAULT_SORT)
expect(result.current).toEqual(DEFAULT_PLUGIN_SORT)
})
it('should return setter from useSetMarketplaceSort', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => ({
setSort: useSetMarketplaceSort(),
sortValue: useMarketplaceSortValue(),
setSort: useSetMarketplacePluginSort(),
sortValue: useMarketplacePluginSortValue(),
}), { wrapper })
act(() => {
@@ -63,7 +63,7 @@ describe('Marketplace sort atoms', () => {
it('should update sort value via useMarketplaceSort setter', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useMarketplaceSort(), { wrapper })
const { result } = renderHook(() => useMarketplacePluginSort(), { wrapper })
act(() => {
result.current[1]({ sortBy: 'created_at', sortOrder: 'ASC' })
@@ -73,14 +73,14 @@ describe('Marketplace sort atoms', () => {
})
})
describe('useSearchPluginText', () => {
describe('useSearchText', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should return empty string as default', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useSearchPluginText(), { wrapper })
const { result } = renderHook(() => useSearchText(), { wrapper })
expect(result.current[0]).toBe('')
expect(typeof result.current[1]).toBe('function')
@@ -88,14 +88,14 @@ describe('useSearchPluginText', () => {
it('should parse q from search params', () => {
const { wrapper } = createWrapper('?q=hello')
const { result } = renderHook(() => useSearchPluginText(), { wrapper })
const { result } = renderHook(() => useSearchText(), { wrapper })
expect(result.current[0]).toBe('hello')
})
it('should expose a setter function for search text', async () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useSearchPluginText(), { wrapper })
const { result } = renderHook(() => useSearchText(), { wrapper })
await act(async () => {
result.current[1]('search term')
@@ -105,21 +105,21 @@ describe('useSearchPluginText', () => {
})
})
describe('useActivePluginType', () => {
describe('useActivePluginCategory', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should return "all" as default category', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => useActivePluginType(), { wrapper })
const { result } = renderHook(() => useActivePluginCategory(), { wrapper })
expect(result.current[0]).toBe('all')
})
it('should parse category from search params', () => {
const { wrapper } = createWrapper('?category=tool')
const { result } = renderHook(() => useActivePluginType(), { wrapper })
const { result } = renderHook(() => useActivePluginCategory(), { wrapper })
expect(result.current[0]).toBe('tool')
})
@@ -202,8 +202,8 @@ describe('useMarketplaceMoreClick', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => ({
handleMoreClick: useMarketplaceMoreClick(),
sort: useMarketplaceSortValue(),
searchText: useSearchPluginText()[0],
sort: useMarketplacePluginSortValue(),
searchText: useSearchText()[0],
}), { wrapper })
const sortBefore = result.current.sort
@@ -222,7 +222,7 @@ describe('useMarketplaceMoreClick', () => {
const { result } = renderHook(() => ({
handleMoreClick: useMarketplaceMoreClick(),
sort: useMarketplaceSortValue(),
sort: useMarketplacePluginSortValue(),
}), { wrapper })
act(() => {
@@ -240,13 +240,13 @@ describe('useMarketplaceMoreClick', () => {
const { wrapper } = createWrapper()
const { result } = renderHook(() => ({
handleMoreClick: useMarketplaceMoreClick(),
sort: useMarketplaceSortValue(),
sort: useMarketplacePluginSortValue(),
}), { wrapper })
act(() => {
result.current.handleMoreClick({})
})
expect(result.current.sort).toEqual(DEFAULT_SORT)
expect(result.current.sort).toEqual(DEFAULT_PLUGIN_SORT)
})
})

View File

@@ -90,8 +90,8 @@ describe('useMarketplaceCollectionsAndPlugins (integration)', () => {
expect(result.current.isSuccess).toBe(true)
})
expect(result.current.marketplaceCollections).toBeDefined()
expect(result.current.marketplaceCollectionPluginsMap).toBeDefined()
expect(result.current.pluginCollections).toBeDefined()
expect(result.current.pluginCollectionPluginsMap).toBeDefined()
})
it('should handle query with empty params (truthy)', async () => {

View File

@@ -118,10 +118,10 @@ describe('useMarketplaceCollectionsAndPlugins', () => {
expect(result.current.isLoading).toBe(false)
expect(result.current.isSuccess).toBe(false)
expect(typeof result.current.queryMarketplaceCollectionsAndPlugins).toBe('function')
expect(typeof result.current.setMarketplaceCollections).toBe('function')
expect(typeof result.current.setMarketplaceCollectionPluginsMap).toBe('function')
expect(result.current.marketplaceCollections).toBeUndefined()
expect(result.current.marketplaceCollectionPluginsMap).toBeUndefined()
expect(typeof result.current.setPluginCollections).toBe('function')
expect(typeof result.current.setPluginCollectionPluginsMap).toBe('function')
expect(result.current.pluginCollections).toBeUndefined()
expect(result.current.pluginCollectionPluginsMap).toBeUndefined()
})
})
@@ -427,8 +427,8 @@ describe('Hooks queryFn Coverage', () => {
await waitFor(() => {
expect(result.current.isSuccess).toBe(true)
})
expect(result.current.marketplaceCollections).toBeDefined()
expect(result.current.marketplaceCollectionPluginsMap).toBeDefined()
expect(result.current.pluginCollections).toBeDefined()
expect(result.current.pluginCollectionPluginsMap).toBeDefined()
})
it('should test getNextPageParam via fetchNextPage behavior', async () => {

View File

@@ -8,24 +8,44 @@ vi.mock('@/context/query-client', () => ({
}))
vi.mock('../hydration-server', () => ({
HydrateQueryClient: ({ children }: { children: React.ReactNode }) => (
<div data-testid="hydration-client">{children}</div>
HydrateQueryClient: ({
children,
isMarketplacePlatform,
}: {
children: React.ReactNode
isMarketplacePlatform?: boolean
}) => (
<div data-testid="hydrate-query-client" data-marketplace-platform={String(Boolean(isMarketplacePlatform))}>{children}</div>
),
}))
vi.mock('../description', () => ({
default: () => <div data-testid="description">Description</div>,
}))
vi.mock('../list/list-wrapper', () => ({
default: ({ showInstallButton }: { showInstallButton: boolean }) => (
<div data-testid="list-wrapper" data-show-install={showInstallButton}>ListWrapper</div>
vi.mock('../hydration-client', () => ({
HydrateClient: ({
children,
isMarketplacePlatform,
}: {
children: React.ReactNode
isMarketplacePlatform?: boolean
}) => (
<div data-testid="hydrate-client" data-marketplace-platform={String(Boolean(isMarketplacePlatform))}>{children}</div>
),
}))
vi.mock('../sticky-search-and-switch-wrapper', () => ({
default: ({ pluginTypeSwitchClassName }: { pluginTypeSwitchClassName?: string }) => (
<div data-testid="sticky-wrapper" data-classname={pluginTypeSwitchClassName}>StickyWrapper</div>
vi.mock('../marketplace-header', () => ({
default: ({
marketplaceNav,
}: {
marketplaceNav?: React.ReactNode
}) => (
<div data-testid="marketplace-header">
{marketplaceNav}
</div>
),
}))
vi.mock('../marketplace-content', () => ({
default: ({ showInstallButton }: { showInstallButton?: boolean }) => (
<div data-testid="marketplace-content" data-show-install={String(Boolean(showInstallButton))}>MarketplaceContent</div>
),
}))
@@ -47,20 +67,20 @@ describe('Marketplace', () => {
const { getByTestId } = render(element as React.ReactElement)
expect(getByTestId('tanstack-initializer')).toBeInTheDocument()
expect(getByTestId('hydration-client')).toBeInTheDocument()
expect(getByTestId('description')).toBeInTheDocument()
expect(getByTestId('sticky-wrapper')).toBeInTheDocument()
expect(getByTestId('list-wrapper')).toBeInTheDocument()
expect(getByTestId('hydrate-query-client')).toBeInTheDocument()
expect(getByTestId('hydrate-client')).toBeInTheDocument()
expect(getByTestId('marketplace-header')).toBeInTheDocument()
expect(getByTestId('marketplace-content')).toBeInTheDocument()
})
it('should pass showInstallButton=true by default to ListWrapper', async () => {
it('should pass showInstallButton=true by default to MarketplaceContent', async () => {
const Marketplace = (await import('../index')).default
const element = await Marketplace({})
const { getByTestId } = render(element as React.ReactElement)
const listWrapper = getByTestId('list-wrapper')
expect(listWrapper.getAttribute('data-show-install')).toBe('true')
const marketplaceContent = getByTestId('marketplace-content')
expect(marketplaceContent.getAttribute('data-show-install')).toBe('true')
})
it('should pass showInstallButton=false when specified', async () => {
@@ -69,27 +89,26 @@ describe('Marketplace', () => {
const { getByTestId } = render(element as React.ReactElement)
const listWrapper = getByTestId('list-wrapper')
expect(listWrapper.getAttribute('data-show-install')).toBe('false')
const marketplaceContent = getByTestId('marketplace-content')
expect(marketplaceContent.getAttribute('data-show-install')).toBe('false')
})
it('should pass pluginTypeSwitchClassName to StickySearchAndSwitchWrapper', async () => {
it('should pass marketplaceNav to MarketplaceHeader', async () => {
const Marketplace = (await import('../index')).default
const element = await Marketplace({ pluginTypeSwitchClassName: 'top-14' })
const element = await Marketplace({ marketplaceNav: <div data-testid="nav">Nav</div> })
const { getByTestId } = render(element as React.ReactElement)
const stickyWrapper = getByTestId('sticky-wrapper')
expect(stickyWrapper.getAttribute('data-classname')).toBe('top-14')
expect(getByTestId('nav')).toBeInTheDocument()
})
it('should render without pluginTypeSwitchClassName', async () => {
it('should pass isMarketplacePlatform to hydrate wrappers', async () => {
const Marketplace = (await import('../index')).default
const element = await Marketplace({})
const element = await Marketplace({ isMarketplacePlatform: true })
const { getByTestId } = render(element as React.ReactElement)
const stickyWrapper = getByTestId('sticky-wrapper')
expect(stickyWrapper.getAttribute('data-classname')).toBeNull()
expect(getByTestId('hydrate-query-client').getAttribute('data-marketplace-platform')).toBe('true')
expect(getByTestId('hydrate-client').getAttribute('data-marketplace-platform')).toBe('true')
})
})

View File

@@ -3,7 +3,7 @@ import { fireEvent, render, screen } from '@testing-library/react'
import { Provider as JotaiProvider } from 'jotai'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createNuqsTestWrapper } from '@/test/nuqs-testing'
import PluginTypeSwitch from '../plugin-type-switch'
import { PluginCategorySwitch } from '../category-switch/plugin'
vi.mock('#i18n', () => ({
useTranslation: () => ({
@@ -35,14 +35,14 @@ const createWrapper = (searchParams = '') => {
return { Wrapper }
}
describe('PluginTypeSwitch', () => {
describe('PluginCategorySwitch', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should render all category options', () => {
const { Wrapper } = createWrapper()
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
expect(screen.getByText('All')).toBeInTheDocument()
expect(screen.getByText('Models')).toBeInTheDocument()
@@ -56,7 +56,7 @@ describe('PluginTypeSwitch', () => {
it('should apply active styling to current category', () => {
const { Wrapper } = createWrapper('?category=all')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
const allButton = screen.getByText('All').closest('div')
expect(allButton?.className).toContain('!bg-components-main-nav-nav-button-bg-active')
@@ -64,7 +64,7 @@ describe('PluginTypeSwitch', () => {
it('should apply custom className', () => {
const { Wrapper } = createWrapper()
const { container } = render(<PluginTypeSwitch className="custom-class" />, { wrapper: Wrapper })
const { container } = render(<PluginCategorySwitch className="custom-class" />, { wrapper: Wrapper })
const outerDiv = container.firstChild as HTMLElement
expect(outerDiv.className).toContain('custom-class')
@@ -72,7 +72,7 @@ describe('PluginTypeSwitch', () => {
it('should update category when option is clicked', () => {
const { Wrapper } = createWrapper('?category=all')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
fireEvent.click(screen.getByText('Models'))
@@ -82,7 +82,7 @@ describe('PluginTypeSwitch', () => {
it('should handle clicking on category with collections (Tools)', () => {
const { Wrapper } = createWrapper('?category=model')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
fireEvent.click(screen.getByText('Tools'))
@@ -92,7 +92,7 @@ describe('PluginTypeSwitch', () => {
it('should handle clicking on category without collections (Models)', () => {
const { Wrapper } = createWrapper('?category=all')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
fireEvent.click(screen.getByText('Models'))
@@ -102,7 +102,7 @@ describe('PluginTypeSwitch', () => {
it('should handle clicking on bundles', () => {
const { Wrapper } = createWrapper('?category=all')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
fireEvent.click(screen.getByText('Bundles'))
@@ -112,7 +112,7 @@ describe('PluginTypeSwitch', () => {
it('should handle clicking on each category', () => {
const { Wrapper } = createWrapper('?category=all')
render(<PluginTypeSwitch />, { wrapper: Wrapper })
render(<PluginCategorySwitch />, { wrapper: Wrapper })
const categories = ['All', 'Models', 'Tools', 'Data Sources', 'Triggers', 'Agents', 'Extensions', 'Bundles']
categories.forEach((category) => {
@@ -125,7 +125,7 @@ describe('PluginTypeSwitch', () => {
it('should render icons for categories that have them', () => {
const { Wrapper } = createWrapper()
const { container } = render(<PluginTypeSwitch />, { wrapper: Wrapper })
const { container } = render(<PluginCategorySwitch />, { wrapper: Wrapper })
// "All" has no icon (icon: null), others should have SVG icons
const svgs = container.querySelectorAll('svg')

View File

@@ -55,7 +55,7 @@ const createWrapper = (searchParams = '') => {
return { Wrapper, queryClient }
}
describe('useMarketplaceData', () => {
describe('usePluginsMarketplaceData', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -80,7 +80,7 @@ describe('useMarketplaceData', () => {
})
it('should return initial state with loading and collections data', async () => {
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
const { Wrapper } = createWrapper('?category=all')
// Create a mock container for scroll
@@ -88,14 +88,14 @@ describe('useMarketplaceData', () => {
container.id = 'marketplace-container'
document.body.appendChild(container)
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.isLoading).toBe(false)
})
expect(result.current.marketplaceCollections).toBeDefined()
expect(result.current.marketplaceCollectionPluginsMap).toBeDefined()
expect(result.current.pluginCollections).toBeDefined()
expect(result.current.pluginCollectionPluginsMap).toBeDefined()
expect(result.current.page).toBeDefined()
expect(result.current.isFetchingNextPage).toBe(false)
@@ -103,14 +103,14 @@ describe('useMarketplaceData', () => {
})
it('should return search mode data when search text is present', async () => {
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
const { Wrapper } = createWrapper('?category=all&q=test')
const container = document.createElement('div')
container.id = 'marketplace-container'
document.body.appendChild(container)
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.isLoading).toBe(false)
@@ -123,7 +123,7 @@ describe('useMarketplaceData', () => {
})
it('should return plugins undefined in collection mode (not search mode)', async () => {
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
// "all" category with no search → collection mode
const { Wrapper } = createWrapper('?category=all')
@@ -131,7 +131,7 @@ describe('useMarketplaceData', () => {
container.id = 'marketplace-container'
document.body.appendChild(container)
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.isLoading).toBe(false)
@@ -144,14 +144,14 @@ describe('useMarketplaceData', () => {
})
it('should enable search for category without collections (e.g. model)', async () => {
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
const { Wrapper } = createWrapper('?category=model')
const container = document.createElement('div')
container.id = 'marketplace-container'
document.body.appendChild(container)
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.isLoading).toBe(false)
@@ -177,7 +177,7 @@ describe('useMarketplaceData', () => {
},
})
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
// Use "model" to force search mode
const { Wrapper } = createWrapper('?category=model')
@@ -189,7 +189,7 @@ describe('useMarketplaceData', () => {
Object.defineProperty(container, 'scrollHeight', { value: 1000, writable: true, configurable: true })
Object.defineProperty(container, 'clientHeight', { value: 200, writable: true, configurable: true })
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
// Wait for data to fully load (isFetching becomes false, plugins become available)
await waitFor(() => {
@@ -206,7 +206,7 @@ describe('useMarketplaceData', () => {
})
it('should handle tags filter in search mode', async () => {
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
// tags in URL triggers search mode
const { Wrapper } = createWrapper('?category=all&tags=search')
@@ -214,7 +214,7 @@ describe('useMarketplaceData', () => {
container.id = 'marketplace-container'
document.body.appendChild(container)
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.isLoading).toBe(false)
@@ -238,7 +238,7 @@ describe('useMarketplaceData', () => {
},
})
const { useMarketplaceData } = await import('../state')
const { usePluginsMarketplaceData } = await import('../state')
const { Wrapper } = createWrapper('?category=model')
const container = document.createElement('div')
@@ -249,7 +249,7 @@ describe('useMarketplaceData', () => {
Object.defineProperty(container, 'scrollHeight', { value: 1000, writable: true, configurable: true })
Object.defineProperty(container, 'clientHeight', { value: 200, writable: true, configurable: true })
const { result } = renderHook(() => useMarketplaceData(), { wrapper: Wrapper })
const { result } = renderHook(() => usePluginsMarketplaceData(), { wrapper: Wrapper })
await waitFor(() => {
expect(result.current.plugins).toBeDefined()

View File

@@ -1,87 +0,0 @@
import type { ReactNode } from 'react'
import { render } from '@testing-library/react'
import { Provider as JotaiProvider } from 'jotai'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createNuqsTestWrapper } from '@/test/nuqs-testing'
import StickySearchAndSwitchWrapper from '../sticky-search-and-switch-wrapper'
vi.mock('#i18n', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}))
// Mock child components to isolate wrapper logic
vi.mock('../plugin-type-switch', () => ({
default: () => <div data-testid="plugin-type-switch">PluginTypeSwitch</div>,
}))
vi.mock('../search-box/search-box-wrapper', () => ({
default: () => <div data-testid="search-box-wrapper">SearchBoxWrapper</div>,
}))
const createWrapper = () => {
const { wrapper: NuqsWrapper } = createNuqsTestWrapper()
const Wrapper = ({ children }: { children: ReactNode }) => (
<JotaiProvider>
<NuqsWrapper>
{children}
</NuqsWrapper>
</JotaiProvider>
)
return { Wrapper }
}
describe('StickySearchAndSwitchWrapper', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should render SearchBoxWrapper and PluginTypeSwitch', () => {
const { Wrapper } = createWrapper()
const { getByTestId } = render(
<StickySearchAndSwitchWrapper />,
{ wrapper: Wrapper },
)
expect(getByTestId('search-box-wrapper')).toBeInTheDocument()
expect(getByTestId('plugin-type-switch')).toBeInTheDocument()
})
it('should not apply sticky class when no pluginTypeSwitchClassName', () => {
const { Wrapper } = createWrapper()
const { container } = render(
<StickySearchAndSwitchWrapper />,
{ wrapper: Wrapper },
)
const outerDiv = container.firstChild as HTMLElement
expect(outerDiv.className).toContain('mt-4')
expect(outerDiv.className).not.toContain('sticky')
})
it('should apply sticky class when pluginTypeSwitchClassName contains top-', () => {
const { Wrapper } = createWrapper()
const { container } = render(
<StickySearchAndSwitchWrapper pluginTypeSwitchClassName="top-10" />,
{ wrapper: Wrapper },
)
const outerDiv = container.firstChild as HTMLElement
expect(outerDiv.className).toContain('sticky')
expect(outerDiv.className).toContain('z-10')
expect(outerDiv.className).toContain('top-10')
})
it('should not apply sticky class when pluginTypeSwitchClassName does not contain top-', () => {
const { Wrapper } = createWrapper()
const { container } = render(
<StickySearchAndSwitchWrapper pluginTypeSwitchClassName="custom-class" />,
{ wrapper: Wrapper },
)
const outerDiv = container.firstChild as HTMLElement
expect(outerDiv.className).not.toContain('sticky')
expect(outerDiv.className).toContain('custom-class')
})
})

View File

@@ -23,9 +23,11 @@ const mockSearchAdvanced = vi.fn()
vi.mock('@/service/client', () => ({
marketplaceClient: {
collections: (...args: unknown[]) => mockCollections(...args),
collectionPlugins: (...args: unknown[]) => mockCollectionPlugins(...args),
searchAdvanced: (...args: unknown[]) => mockSearchAdvanced(...args),
plugins: {
collections: (...args: unknown[]) => mockCollections(...args),
collectionPlugins: (...args: unknown[]) => mockCollectionPlugins(...args),
searchAdvanced: (...args: unknown[]) => mockSearchAdvanced(...args),
},
},
}))
@@ -134,69 +136,69 @@ describe('getPluginDetailLinkInMarketplace', () => {
})
})
describe('getMarketplaceListCondition', () => {
describe('getPluginCondition', () => {
it('should return category condition for tool', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.tool)).toBe('category=tool')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.tool)).toBe('category=tool')
})
it('should return category condition for model', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.model)).toBe('category=model')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.model)).toBe('category=model')
})
it('should return category condition for agent', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.agent)).toBe('category=agent-strategy')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.agent)).toBe('category=agent')
})
it('should return category condition for datasource', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.datasource)).toBe('category=datasource')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.datasource)).toBe('category=datasource')
})
it('should return category condition for trigger', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.trigger)).toBe('category=trigger')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.trigger)).toBe('category=trigger')
})
it('should return endpoint category for extension', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition(PluginCategoryEnum.extension)).toBe('category=endpoint')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition(PluginCategoryEnum.extension)).toBe('category=endpoint')
})
it('should return type condition for bundle', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition('bundle')).toBe('type=bundle')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition('bundle')).toBe('type=bundle')
})
it('should return empty string for all', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition('all')).toBe('')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition('all')).toBe('')
})
it('should return empty string for unknown type', async () => {
const { getMarketplaceListCondition } = await import('../utils')
expect(getMarketplaceListCondition('unknown')).toBe('')
const { getPluginCondition } = await import('../utils')
expect(getPluginCondition('unknown')).toBe('')
})
})
describe('getMarketplaceListFilterType', () => {
describe('getPluginFilterType', () => {
it('should return undefined for all', async () => {
const { getMarketplaceListFilterType } = await import('../utils')
expect(getMarketplaceListFilterType(PLUGIN_TYPE_SEARCH_MAP.all)).toBeUndefined()
const { getPluginFilterType } = await import('../utils')
expect(getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.all)).toBeUndefined()
})
it('should return bundle for bundle', async () => {
const { getMarketplaceListFilterType } = await import('../utils')
expect(getMarketplaceListFilterType(PLUGIN_TYPE_SEARCH_MAP.bundle)).toBe('bundle')
const { getPluginFilterType } = await import('../utils')
expect(getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.bundle)).toBe('bundle')
})
it('should return plugin for other categories', async () => {
const { getMarketplaceListFilterType } = await import('../utils')
expect(getMarketplaceListFilterType(PLUGIN_TYPE_SEARCH_MAP.tool)).toBe('plugin')
expect(getMarketplaceListFilterType(PLUGIN_TYPE_SEARCH_MAP.model)).toBe('plugin')
expect(getMarketplaceListFilterType(PLUGIN_TYPE_SEARCH_MAP.agent)).toBe('plugin')
const { getPluginFilterType } = await import('../utils')
expect(getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.tool)).toBe('plugin')
expect(getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.model)).toBe('plugin')
expect(getPluginFilterType(PLUGIN_TYPE_SEARCH_MAP.agent)).toBe('plugin')
})
})

View File

@@ -1,5 +1,4 @@
import type { SearchParams } from 'nuqs/server'
import type { MarketplaceSearchParams } from './search-params'
import type { CreatorSearchParams, PluginsSearchParams, TemplateSearchParams } from './types'
import { dehydrate, HydrationBoundary } from '@tanstack/react-query'
import { headers } from 'next/headers'

View File

@@ -1,4 +1,4 @@
import type { MarketplaceCollection, SearchParamsFromCollection } from '../../types'
import type { PluginCollection, SearchParamsFromCollection } from '../../types'
import type { Plugin } from '@/app/components/plugins/types'
import { fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
@@ -103,8 +103,8 @@ vi.mock('@/i18n-config/language', () => ({
}))
// Mock marketplace utils
vi.mock('../utils', async (importOriginal) => {
const actual = await importOriginal<typeof import('../utils')>()
vi.mock('../../utils', async (importOriginal) => {
const actual = await importOriginal<typeof import('../../utils')>()
return {
...actual,
getPluginLinkInMarketplace: (plugin: Plugin, _params?: Record<string, string | undefined>) =>

View File

@@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PluginCategoryEnum } from '../../../types'
import SearchBox from '../index'
import SearchBoxWrapper from '../search-box-wrapper'
import SearchDropdown from '../search-dropdown'
import MarketplaceTrigger from '../trigger/marketplace'
import ToolSelectorTrigger from '../trigger/tool-selector'
@@ -80,7 +81,6 @@ const {
vi.mock('../../atoms', () => ({
useSearchText: () => [mockSearchText, mockHandleSearchTextChange],
useSearchPluginText: () => [mockSearchPluginText, mockHandleSearchPluginTextChange],
useFilterPluginTags: () => [mockFilterPluginTags, mockHandleFilterPluginTagsChange],
useActivePluginCategory: () => [mockActivePluginCategory, vi.fn()],
useMarketplacePluginSortValue: () => mockSortValue,
@@ -92,8 +92,8 @@ vi.mock('../../atoms', () => ({
searchModeAtom: {},
}))
vi.mock('../utils', async () => {
const actual = await vi.importActual<typeof import('../utils')>('../utils')
vi.mock('../../utils', async () => {
const actual = await vi.importActual<typeof import('../../utils')>('../../utils')
return {
...actual,
mapUnifiedPluginToPlugin: (item: Plugin) => item,

View File

@@ -1,5 +1,4 @@
import type { inferParserType } from 'nuqs/server'
import type { ActivePluginType } from './constants'
import { parseAsArrayOf, parseAsString, parseAsStringEnum } from 'nuqs/server'
export const CREATION_TYPE = {

View File

@@ -18,16 +18,15 @@ import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@
import { useGlobalPublicStore } from '@/context/global-public-context'
import useDocumentTitle from '@/hooks/use-document-title'
import { usePluginInstallation } from '@/hooks/use-query-params'
import Link from '@/next/link'
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
import { sleep } from '@/utils'
import { PLUGIN_PAGE_TABS_MAP } from '../hooks'
import InstallFromLocalPackage from '../install-plugin/install-from-local-package'
import InstallFromMarketplace from '../install-plugin/install-from-marketplace'
import { PLUGIN_TYPE_SEARCH_MAP } from '../marketplace/constants'
import SearchBoxWrapper from '../marketplace/search-box/search-box-wrapper'
import { usePluginPageContext } from './context'
import { PluginPageContextProvider } from './context-provider'
import SearchBoxWrapper from '../marketplace/search-box/search-box-wrapper'
import DebugInfo from './debug-info'
import InstallPluginDropdown from './install-plugin-dropdown'
import { SubmitRequestDropdown } from './nav-operations'

View File

@@ -3,7 +3,7 @@ import type { Collection } from '@/app/components/tools/types'
import { act, renderHook, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { SCROLL_BOTTOM_THRESHOLD } from '@/app/components/plugins/marketplace/constants'
import { getMarketplaceListCondition } from '@/app/components/plugins/marketplace/utils'
import { getPluginCondition } from '@/app/components/plugins/marketplace/utils'
import { PluginCategoryEnum } from '@/app/components/plugins/types'
import { CollectionType } from '@/app/components/tools/types'
import { useMarketplace } from '../hooks'
@@ -141,7 +141,7 @@ describe('useMarketplace', () => {
await waitFor(() => {
expect(mockQueryMarketplaceCollectionsAndPlugins).toHaveBeenCalledWith({
category: PluginCategoryEnum.tool,
condition: getMarketplaceListCondition(PluginCategoryEnum.tool),
condition: getPluginCondition(PluginCategoryEnum.tool),
exclude: ['plugin-c'],
type: 'plugin',
})

View File

@@ -13,8 +13,8 @@ import Marketplace from '../index'
const listRenderSpy = vi.fn()
vi.mock('@/app/components/plugins/marketplace/list', () => ({
default: (props: {
marketplaceCollections: unknown[]
marketplaceCollectionPluginsMap: Record<string, unknown[]>
pluginCollections: unknown[]
pluginCollectionPluginsMap: Record<string, unknown[]>
plugins?: unknown[]
showInstallButton?: boolean
}) => {
@@ -84,8 +84,8 @@ const createPlugin = (overrides: Partial<Plugin> = {}): Plugin => ({
const createMarketplaceContext = (overrides: Partial<ReturnType<typeof useMarketplace>> = {}) => ({
isLoading: false,
marketplaceCollections: [],
marketplaceCollectionPluginsMap: {},
pluginCollections: [],
pluginCollectionPluginsMap: {},
plugins: [],
handleScroll: vi.fn(),
page: 1,
@@ -104,7 +104,7 @@ describe('Marketplace', () => {
const marketplaceContext = createMarketplaceContext({ isLoading: true, page: 1 })
render(
<Marketplace
searchPluginText=""
searchText=""
filterPluginTags={[]}
isMarketplaceArrowVisible={false}
showMarketplacePanel={vi.fn()}
@@ -125,7 +125,7 @@ describe('Marketplace', () => {
})
render(
<Marketplace
searchPluginText=""
searchText=""
filterPluginTags={[]}
isMarketplaceArrowVisible={false}
showMarketplacePanel={vi.fn()}
@@ -150,7 +150,7 @@ describe('Marketplace', () => {
const showMarketplacePanel = vi.fn()
const { container } = render(
<Marketplace
searchPluginText="vector"
searchText="vector"
filterPluginTags={['tag-a', 'tag-b']}
isMarketplaceArrowVisible
showMarketplacePanel={showMarketplacePanel}

View File

@@ -2456,7 +2456,7 @@
},
"app/components/base/icons/src/public/plugins/index.ts": {
"no-barrel-files/no-barrel-files": {
"count": 7
"count": 9
}
},
"app/components/base/icons/src/public/thought/index.ts": {
@@ -2576,7 +2576,7 @@
},
"app/components/base/icons/src/vender/plugin/index.ts": {
"no-barrel-files/no-barrel-files": {
"count": 3
"count": 5
}
},
"app/components/base/icons/src/vender/solid/FinanceAndECommerce/index.ts": {
@@ -2812,6 +2812,11 @@
"count": 3
}
},
"app/components/base/markdown/index.tsx": {
"unused-imports/no-unused-vars": {
"count": 1
}
},
"app/components/base/markdown/markdown-utils.ts": {
"regexp/no-unused-capturing-group": {
"count": 1
@@ -5502,9 +5507,17 @@
"count": 1
}
},
"app/components/plugins/card/base/org-info.tsx": {
"app/components/plugins/card/base/download-count.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
"count": 1
}
},
"app/components/plugins/card/base/org-info.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 4
}
},
"app/components/plugins/card/base/title.tsx": {
@@ -5512,6 +5525,11 @@
"count": 1
}
},
"app/components/plugins/card/card-tags.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
},
"app/components/plugins/install-plugin/base/installed.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1
@@ -5611,6 +5629,42 @@
"count": 1
}
},
"app/components/plugins/marketplace/atoms.ts": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/plugins/marketplace/category-switch/common.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
}
},
"app/components/plugins/marketplace/category-switch/hero-languages-filter.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 3
}
},
"app/components/plugins/marketplace/category-switch/hero-tags-filter.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
},
"app/components/plugins/marketplace/category-switch/hero-tags-trigger.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
}
},
"app/components/plugins/marketplace/category-switch/index.tsx": {
"no-barrel-files/no-barrel-files": {
"count": 2
}
},
"app/components/plugins/marketplace/description/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
@@ -5626,11 +5680,44 @@
"count": 1
}
},
"app/components/plugins/marketplace/hydration-server.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/plugins/marketplace/list/collection-list.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
}
},
"app/components/plugins/marketplace/list/list-top-info.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
}
},
"app/components/plugins/marketplace/list/template-card.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 4
}
},
"app/components/plugins/marketplace/search-box/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
}
},
"app/components/plugins/marketplace/search-box/search-box-wrapper.tsx": {
"no-restricted-imports": {
"count": 2
}
},
"app/components/plugins/marketplace/search-box/search-dropdown/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 11
}
},
"app/components/plugins/marketplace/search-box/tags-filter.tsx": {
"no-restricted-imports": {
"count": 1
@@ -5639,6 +5726,11 @@
"count": 1
}
},
"app/components/plugins/marketplace/search-box/trigger/index.ts": {
"no-barrel-files/no-barrel-files": {
"count": 2
}
},
"app/components/plugins/marketplace/search-box/trigger/marketplace.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
@@ -5649,6 +5741,32 @@
"count": 2
}
},
"app/components/plugins/marketplace/search-page/creator-card.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 4
}
},
"app/components/plugins/marketplace/search-page/filter-chip.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 5
}
},
"app/components/plugins/marketplace/search-page/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
}
},
"app/components/plugins/marketplace/search-results-header.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
},
"tailwindcss/no-unnecessary-whitespace": {
"count": 2
}
},
"app/components/plugins/marketplace/sort-dropdown/index.tsx": {
"no-restricted-imports": {
"count": 1
@@ -6157,6 +6275,14 @@
"count": 2
}
},
"app/components/plugins/plugin-page/nav-operations.tsx": {
"no-restricted-imports": {
"count": 2
},
"tailwindcss/enforce-consistent-class-order": {
"count": 4
}
},
"app/components/plugins/plugin-page/plugin-info.tsx": {
"no-restricted-imports": {
"count": 1