From c52f18f205796faef113a3ff4abe3af77904ef8b Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:18:32 +0800 Subject: [PATCH] suppress all and fix typecheck --- web/app/components/base/markdown/index.tsx | 2 +- .../card/__tests__/card-more-info.spec.tsx | 50 ------- .../marketplace/__tests__/atoms.spec.tsx | 50 +++---- .../__tests__/hooks-integration.spec.tsx | 4 +- .../marketplace/__tests__/hooks.spec.tsx | 12 +- .../marketplace/__tests__/index.spec.tsx | 77 ++++++---- .../__tests__/plugin-type-switch.spec.tsx | 22 +-- .../marketplace/__tests__/state.spec.tsx | 34 ++--- .../sticky-search-and-switch-wrapper.spec.tsx | 87 ------------ .../marketplace/__tests__/utils.spec.ts | 64 +++++---- .../plugins/marketplace/hydration-server.tsx | 1 - .../marketplace/list/__tests__/index.spec.tsx | 6 +- .../search-box/__tests__/index.spec.tsx | 6 +- .../plugins/marketplace/search-params.ts | 1 - .../components/plugins/plugin-page/index.tsx | 3 +- .../tools/marketplace/__tests__/hooks.spec.ts | 4 +- .../marketplace/__tests__/index.spec.tsx | 14 +- web/eslint-suppressions.json | 134 +++++++++++++++++- 18 files changed, 289 insertions(+), 282 deletions(-) delete mode 100644 web/app/components/plugins/card/__tests__/card-more-info.spec.tsx delete mode 100644 web/app/components/plugins/marketplace/__tests__/sticky-search-and-switch-wrapper.spec.tsx diff --git a/web/app/components/base/markdown/index.tsx b/web/app/components/base/markdown/index.tsx index 8a340963ab5..5915816d7ac 100644 --- a/web/app/components/base/markdown/index.tsx +++ b/web/app/components/base/markdown/index.tsx @@ -45,7 +45,7 @@ export const Markdown = memo((props: MarkdownProps) => {
({ - default: ({ downloadCount }: { downloadCount: number }) => ( - {downloadCount} - ), -})) - -describe('CardMoreInfo', () => { - it('renders tags with # prefix', () => { - render() - 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() - expect(screen.getByTestId('download-count')).toHaveTextContent('1000') - }) - - it('does not render download count when undefined', () => { - render() - expect(screen.queryByTestId('download-count')).not.toBeInTheDocument() - }) - - it('renders separator between download count and tags', () => { - render() - expect(screen.getByText('·')).toBeInTheDocument() - }) - - it('does not render separator when no tags', () => { - render() - expect(screen.queryByText('·')).not.toBeInTheDocument() - }) - - it('does not render separator when no download count', () => { - render() - expect(screen.queryByText('·')).not.toBeInTheDocument() - }) - - it('handles empty tags array', () => { - const { container } = render() - expect(container.firstChild).toBeInTheDocument() - }) -}) diff --git a/web/app/components/plugins/marketplace/__tests__/atoms.spec.tsx b/web/app/components/plugins/marketplace/__tests__/atoms.spec.tsx index 0a532f537dd..16cb6aef565 100644 --- a/web/app/components/plugins/marketplace/__tests__/atoms.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/atoms.spec.tsx @@ -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) }) }) diff --git a/web/app/components/plugins/marketplace/__tests__/hooks-integration.spec.tsx b/web/app/components/plugins/marketplace/__tests__/hooks-integration.spec.tsx index ac583d66c56..e7655877a4d 100644 --- a/web/app/components/plugins/marketplace/__tests__/hooks-integration.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/hooks-integration.spec.tsx @@ -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 () => { diff --git a/web/app/components/plugins/marketplace/__tests__/hooks.spec.tsx b/web/app/components/plugins/marketplace/__tests__/hooks.spec.tsx index 2555a41f6b3..80a3e27becd 100644 --- a/web/app/components/plugins/marketplace/__tests__/hooks.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/hooks.spec.tsx @@ -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 () => { diff --git a/web/app/components/plugins/marketplace/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/__tests__/index.spec.tsx index e5a90801a5e..aaac2740a38 100644 --- a/web/app/components/plugins/marketplace/__tests__/index.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/index.spec.tsx @@ -8,24 +8,44 @@ vi.mock('@/context/query-client', () => ({ })) vi.mock('../hydration-server', () => ({ - HydrateQueryClient: ({ children }: { children: React.ReactNode }) => ( -
{children}
+ HydrateQueryClient: ({ + children, + isMarketplacePlatform, + }: { + children: React.ReactNode + isMarketplacePlatform?: boolean + }) => ( +
{children}
), })) -vi.mock('../description', () => ({ - default: () =>
Description
, -})) - -vi.mock('../list/list-wrapper', () => ({ - default: ({ showInstallButton }: { showInstallButton: boolean }) => ( -
ListWrapper
+vi.mock('../hydration-client', () => ({ + HydrateClient: ({ + children, + isMarketplacePlatform, + }: { + children: React.ReactNode + isMarketplacePlatform?: boolean + }) => ( +
{children}
), })) -vi.mock('../sticky-search-and-switch-wrapper', () => ({ - default: ({ pluginTypeSwitchClassName }: { pluginTypeSwitchClassName?: string }) => ( -
StickyWrapper
+vi.mock('../marketplace-header', () => ({ + default: ({ + marketplaceNav, + }: { + marketplaceNav?: React.ReactNode + }) => ( +
+ {marketplaceNav} +
+ ), +})) + +vi.mock('../marketplace-content', () => ({ + default: ({ showInstallButton }: { showInstallButton?: boolean }) => ( +
MarketplaceContent
), })) @@ -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:
Nav
}) 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') }) }) diff --git a/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx b/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx index 7cf3b10e25b..d950c30993b 100644 --- a/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/plugin-type-switch.spec.tsx @@ -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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + const { container } = render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + render(, { 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(, { wrapper: Wrapper }) + const { container } = render(, { wrapper: Wrapper }) // "All" has no icon (icon: null), others should have SVG icons const svgs = container.querySelectorAll('svg') diff --git a/web/app/components/plugins/marketplace/__tests__/state.spec.tsx b/web/app/components/plugins/marketplace/__tests__/state.spec.tsx index f4330f31b4e..348b6d1ce09 100644 --- a/web/app/components/plugins/marketplace/__tests__/state.spec.tsx +++ b/web/app/components/plugins/marketplace/__tests__/state.spec.tsx @@ -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() diff --git a/web/app/components/plugins/marketplace/__tests__/sticky-search-and-switch-wrapper.spec.tsx b/web/app/components/plugins/marketplace/__tests__/sticky-search-and-switch-wrapper.spec.tsx deleted file mode 100644 index 1876692dadc..00000000000 --- a/web/app/components/plugins/marketplace/__tests__/sticky-search-and-switch-wrapper.spec.tsx +++ /dev/null @@ -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: () =>
PluginTypeSwitch
, -})) - -vi.mock('../search-box/search-box-wrapper', () => ({ - default: () =>
SearchBoxWrapper
, -})) - -const createWrapper = () => { - const { wrapper: NuqsWrapper } = createNuqsTestWrapper() - const Wrapper = ({ children }: { children: ReactNode }) => ( - - - {children} - - - ) - return { Wrapper } -} - -describe('StickySearchAndSwitchWrapper', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - it('should render SearchBoxWrapper and PluginTypeSwitch', () => { - const { Wrapper } = createWrapper() - const { getByTestId } = render( - , - { 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( - , - { 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( - , - { 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( - , - { wrapper: Wrapper }, - ) - - const outerDiv = container.firstChild as HTMLElement - expect(outerDiv.className).not.toContain('sticky') - expect(outerDiv.className).toContain('custom-class') - }) -}) diff --git a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts index 92bed9be62c..6514b01031a 100644 --- a/web/app/components/plugins/marketplace/__tests__/utils.spec.ts +++ b/web/app/components/plugins/marketplace/__tests__/utils.spec.ts @@ -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') }) }) diff --git a/web/app/components/plugins/marketplace/hydration-server.tsx b/web/app/components/plugins/marketplace/hydration-server.tsx index e04016ff98a..1a6e65af1a7 100644 --- a/web/app/components/plugins/marketplace/hydration-server.tsx +++ b/web/app/components/plugins/marketplace/hydration-server.tsx @@ -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' diff --git a/web/app/components/plugins/marketplace/list/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/list/__tests__/index.spec.tsx index b8deb4e6b0f..8bd930f2103 100644 --- a/web/app/components/plugins/marketplace/list/__tests__/index.spec.tsx +++ b/web/app/components/plugins/marketplace/list/__tests__/index.spec.tsx @@ -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() +vi.mock('../../utils', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, getPluginLinkInMarketplace: (plugin: Plugin, _params?: Record) => diff --git a/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx index 6c58d9ce7b4..fcafc9e7481 100644 --- a/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx +++ b/web/app/components/plugins/marketplace/search-box/__tests__/index.spec.tsx @@ -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('../utils') +vi.mock('../../utils', async () => { + const actual = await vi.importActual('../../utils') return { ...actual, mapUnifiedPluginToPlugin: (item: Plugin) => item, diff --git a/web/app/components/plugins/marketplace/search-params.ts b/web/app/components/plugins/marketplace/search-params.ts index 5979e7c7c29..f3b2aad3a53 100644 --- a/web/app/components/plugins/marketplace/search-params.ts +++ b/web/app/components/plugins/marketplace/search-params.ts @@ -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 = { diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index 5034a287cfe..7e10de1cb33 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -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' diff --git a/web/app/components/tools/marketplace/__tests__/hooks.spec.ts b/web/app/components/tools/marketplace/__tests__/hooks.spec.ts index 14244f763cb..c704446a683 100644 --- a/web/app/components/tools/marketplace/__tests__/hooks.spec.ts +++ b/web/app/components/tools/marketplace/__tests__/hooks.spec.ts @@ -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', }) diff --git a/web/app/components/tools/marketplace/__tests__/index.spec.tsx b/web/app/components/tools/marketplace/__tests__/index.spec.tsx index 43c303b0753..b17387c28e9 100644 --- a/web/app/components/tools/marketplace/__tests__/index.spec.tsx +++ b/web/app/components/tools/marketplace/__tests__/index.spec.tsx @@ -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 + pluginCollections: unknown[] + pluginCollectionPluginsMap: Record plugins?: unknown[] showInstallButton?: boolean }) => { @@ -84,8 +84,8 @@ const createPlugin = (overrides: Partial = {}): Plugin => ({ const createMarketplaceContext = (overrides: Partial> = {}) => ({ 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( { }) render( { const showMarketplacePanel = vi.fn() const { container } = render(