From 98eba106b8232a9475073031a35a98b6c7255279 Mon Sep 17 00:00:00 2001 From: caiyi Date: Tue, 27 Oct 2026 12:28:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(plugins):=20=E4=BF=AE=E5=A4=8D=E5=B7=B2?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E4=BB=BB=E5=8A=A1=E6=8F=92=E4=BB=B6=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/plugins-table.test.tsx | 68 ++++++++++++++++++- .../task-plugins/components/plugins-table.tsx | 1 + 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/web/src/features/task-plugins/__tests__/plugins-table.test.tsx b/web/src/features/task-plugins/__tests__/plugins-table.test.tsx index 1d02ecddf817..400444f65b9e 100644 --- a/web/src/features/task-plugins/__tests__/plugins-table.test.tsx +++ b/web/src/features/task-plugins/__tests__/plugins-table.test.tsx @@ -38,7 +38,8 @@ afterEach(() => { function renderPlugins( enabled: boolean, view: 'card' | 'table' = 'card', - source: TaskPluginListItem['source'] = 'override' + source: TaskPluginListItem['source'] = 'override', + additionalPlugins: { key: string; name: string }[] = [] ) { localStorage.setItem('task-plugins-view-mode', view) const item: TaskPluginListItem = { @@ -70,7 +71,16 @@ function renderPlugins( }, }) clients.push(client) - client.setQueryData(['task-plugins'], [item]) + client.setQueryData( + ['task-plugins'], + [ + item, + ...additionalPlugins.map((meta) => ({ + ...item, + meta: { ...item.meta, ...meta }, + })), + ] + ) vi.spyOn(api, 'get').mockResolvedValue({ data: { success: true, data: [{ ...item, enabled: !enabled }] }, }) @@ -82,6 +92,60 @@ function renderPlugins( return screen.getAllByRole('switch', { name: 'Enable plugin example' })[0] } +test.each(['card', 'table'] as const)( + '%s view: searching filters plugin names and keys and clearing restores the list', + async (view) => { + const user = userEvent.setup() + renderPlugins(true, view, 'factory', [ + { key: 'google', name: 'Google Veo (Gemini API)' }, + { key: 'vertex-ai', name: 'Google Veo (Vertex AI)' }, + { key: 'doubao', name: 'Doubao Video' }, + ]) + const search = screen.getByPlaceholderText('Filter plugins...') + + await user.type(search, 'GOOGLE') + await waitFor(() => { + expect( + screen.queryAllByRole('switch', { name: 'Enable plugin example' }) + ).toHaveLength(0) + expect( + screen.queryAllByRole('switch', { name: 'Enable plugin doubao' }) + ).toHaveLength(0) + }) + expect( + screen.getAllByRole('switch', { name: 'Enable plugin google' }).length + ).toBeGreaterThan(0) + expect( + screen.getAllByRole('switch', { name: 'Enable plugin vertex-ai' }).length + ).toBeGreaterThan(0) + + await user.clear(search) + await user.type(search, 'vertex-ai') + await waitFor(() => + expect( + screen.queryAllByRole('switch', { name: 'Enable plugin google' }) + ).toHaveLength(0) + ) + expect( + screen.getAllByRole('switch', { name: 'Enable plugin vertex-ai' }).length + ).toBeGreaterThan(0) + + await user.clear(search) + await user.type(search, 'no-matching-plugin') + await screen.findByText('No task plugins found') + expect(screen.queryAllByRole('switch')).toHaveLength(0) + + await user.clear(search) + await waitFor(() => { + for (const key of ['example', 'google', 'vertex-ai', 'doubao']) { + expect( + screen.getAllByRole('switch', { name: `Enable plugin ${key}` }).length + ).toBeGreaterThan(0) + } + }) + } +) + test.each([ ['card', true], ['card', false], diff --git a/web/src/features/task-plugins/components/plugins-table.tsx b/web/src/features/task-plugins/components/plugins-table.tsx index 6e550d2e986a..45959718b6bc 100644 --- a/web/src/features/task-plugins/components/plugins-table.tsx +++ b/web/src/features/task-plugins/components/plugins-table.tsx @@ -329,6 +329,7 @@ export function PluginsTable(props: PluginsTableProps) { columnFilters, onColumnFiltersChange: setColumnFilters, globalFilter, + globalFilterFn: 'includesString', onGlobalFilterChange: setGlobalFilter, withFilteredRowModel: true, withPaginationRowModel: true, From 361fd101fc3d4aee7de64552ecf3722be0fc2ba6 Mon Sep 17 00:00:00 2001 From: caiyi Date: Tue, 27 Oct 2026 12:44:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(plugins):=20=E6=8C=89=E7=AD=9B=E9=80=89?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=AE=A1=E7=AE=97=E5=88=86=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/plugins-table.test.tsx | 38 ++++++++++++++++++- .../task-plugins/components/plugins-table.tsx | 1 - 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/web/src/features/task-plugins/__tests__/plugins-table.test.tsx b/web/src/features/task-plugins/__tests__/plugins-table.test.tsx index 400444f65b9e..44815e707e3e 100644 --- a/web/src/features/task-plugins/__tests__/plugins-table.test.tsx +++ b/web/src/features/task-plugins/__tests__/plugins-table.test.tsx @@ -21,6 +21,7 @@ import { act, render, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { afterEach, expect, test, vi } from 'vitest' +import { PageFooterProvider } from '@/components/layout/components/page-footer' import { api } from '@/lib/api' import { PluginsTable } from '../components/plugins-table' @@ -84,11 +85,15 @@ function renderPlugins( vi.spyOn(api, 'get').mockResolvedValue({ data: { success: true, data: [{ ...item, enabled: !enabled }] }, }) - render( + const footer = document.createElement('div') + const rendered = render( - undefined} onUpload={() => undefined} /> + + undefined} onUpload={() => undefined} /> + ) + rendered.container.append(footer) return screen.getAllByRole('switch', { name: 'Enable plugin example' })[0] } @@ -146,6 +151,35 @@ test.each(['card', 'table'] as const)( } ) +test('searching from a later page uses filtered pagination and clearing restores all pages', async () => { + const user = userEvent.setup() + renderPlugins(true, 'table', 'factory', [ + ...Array.from({ length: 20 }, (_, index) => ({ + key: `plugin-${index}`, + name: `Plugin ${index}`, + })), + { key: 'google', name: 'Google Veo' }, + ]) + const next = screen.getByRole('button', { name: 'Go to next page' }) + const previous = screen.getByRole('button', { name: 'Go to previous page' }) + expect(next).toBeEnabled() + await user.click(next) + expect(previous).toBeEnabled() + + const search = screen.getByPlaceholderText('Filter plugins...') + await user.type(search, 'google') + await waitFor(() => { + expect( + screen.getByRole('switch', { name: 'Enable plugin google' }) + ).toBeVisible() + expect(previous).toBeDisabled() + expect(next).toBeDisabled() + }) + + await user.clear(search) + await waitFor(() => expect(next).toBeEnabled()) +}) + test.each([ ['card', true], ['card', false], diff --git a/web/src/features/task-plugins/components/plugins-table.tsx b/web/src/features/task-plugins/components/plugins-table.tsx index 45959718b6bc..e6fc6638ca40 100644 --- a/web/src/features/task-plugins/components/plugins-table.tsx +++ b/web/src/features/task-plugins/components/plugins-table.tsx @@ -325,7 +325,6 @@ export function PluginsTable(props: PluginsTableProps) { const { table } = useDataTable({ data: pluginsQuery.data ?? [], columns, - totalCount: pluginsQuery.data?.length ?? 0, columnFilters, onColumnFiltersChange: setColumnFilters, globalFilter,