From a4c68ee1fa5164f0a73a021973066543dc94c7ff Mon Sep 17 00:00:00 2001 From: sawyer-shi Date: Tue, 5 May 2026 15:58:55 +0800 Subject: [PATCH] test(plugins): cover stop all plugin tasks --- .../plugin-tasks/__tests__/index.spec.tsx | 53 ++++++++++++++++++- .../__tests__/plugin-task-list.spec.tsx | 51 +++++++----------- .../plugins/plugin-page/plugin-tasks/hooks.ts | 2 +- .../plugin-page/plugin-tasks/index.tsx | 1 - web/i18n/en-US/plugin.json | 2 +- 5 files changed, 71 insertions(+), 38 deletions(-) diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx index 12fc796531..0cc376093b 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/__tests__/index.spec.tsx @@ -3,7 +3,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { PluginSource, TaskStatus } from '@/app/components/plugins/types' // Import mocked modules -import { useMutationClearTaskPlugin, usePluginTaskList } from '@/service/use-plugins' +import { useMutationClearTaskPlugin, useMutationStopAllTaskPlugins, usePluginTaskList } from '@/service/use-plugins' import PluginTaskList from '../components/plugin-task-list' import TaskStatusIndicator from '../components/task-status-indicator' import { usePluginTaskStatus } from '../hooks' @@ -14,6 +14,7 @@ import PluginTasks from '../index' vi.mock('@/service/use-plugins', () => ({ usePluginTaskList: vi.fn(), useMutationClearTaskPlugin: vi.fn(), + useMutationStopAllTaskPlugins: vi.fn(), })) vi.mock('@/app/components/plugins/install-plugin/base/use-get-icon', () => ({ @@ -45,6 +46,7 @@ const createMockPlugin = (overrides: Partial = {}): PluginStatus = // Helper to setup mock hook returns const setupMocks = (plugins: PluginStatus[] = []) => { const mockMutateAsync = vi.fn().mockResolvedValue({}) + const mockStopAllMutateAsync = vi.fn().mockResolvedValue({}) const mockHandleRefetch = vi.fn() vi.mocked(usePluginTaskList).mockReturnValue({ @@ -58,7 +60,11 @@ const setupMocks = (plugins: PluginStatus[] = []) => { mutateAsync: mockMutateAsync, } as unknown as ReturnType) - return { mockMutateAsync, mockHandleRefetch } + vi.mocked(useMutationStopAllTaskPlugins).mockReturnValue({ + mutateAsync: mockStopAllMutateAsync, + } as unknown as ReturnType) + + return { mockMutateAsync, mockStopAllMutateAsync, mockHandleRefetch } } const getTaskMenuTrigger = () => @@ -273,6 +279,31 @@ describe('usePluginTaskStatus Hook', () => { }) }) }) + + describe('handleStopAllPlugins', () => { + it('should call stop all mutateAsync and handleRefetch', async () => { + const { mockStopAllMutateAsync, mockHandleRefetch } = setupMocks([ + createMockPlugin({ status: TaskStatus.running }), + ]) + + const TestComponent = () => { + const { handleStopAllPlugins } = usePluginTaskStatus() + return ( + + ) + } + + render() + fireEvent.click(screen.getByRole('button')) + + await waitFor(() => { + expect(mockStopAllMutateAsync).toHaveBeenCalledWith() + expect(mockHandleRefetch).toHaveBeenCalled() + }) + }) + }) }) // ============================================================================ @@ -424,6 +455,7 @@ describe('PluginTaskList Component', () => { onClearAll: vi.fn(), onClearErrors: vi.fn(), onClearSingle: vi.fn(), + onStopAll: vi.fn(), } beforeEach(() => { @@ -495,6 +527,23 @@ describe('PluginTaskList Component', () => { expect(handleClearAll).toHaveBeenCalledTimes(1) }) + it('should call onStopAll when stop all button is clicked in running section', () => { + const handleStopAll = vi.fn() + const runningPlugins = [createMockPlugin({ status: TaskStatus.running })] + + render( + , + ) + + fireEvent.click(screen.getByRole('button', { name: /task\.stopAll/i })) + + expect(handleStopAll).toHaveBeenCalledTimes(1) + }) + it('should call onClearErrors when clear all button is clicked in error section', () => { const handleClearErrors = vi.fn() const errorPlugins = [createMockPlugin({ status: TaskStatus.failed })] diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx index 07a651e2c5..8044ef9b83 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/__tests__/plugin-task-list.spec.tsx @@ -57,6 +57,7 @@ describe('PluginTaskList', () => { onClearAll: vi.fn(), onClearErrors: vi.fn(), onClearSingle: vi.fn(), + onStopAll: vi.fn(), } beforeEach(() => { @@ -180,44 +181,28 @@ describe('PluginTaskList', () => { }) describe('Running section', () => { - it('should not render clear buttons for running plugins', () => { + it('should render stop all button for running plugins', () => { render() - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle - // Running section has no headerAction and no onClearSingle + expect(screen.getByRole('button', { name: /plugin\.task\.stopAll/ })).toBeInTheDocument() expect(screen.queryByText(/plugin\.task\.clearAll/)).not.toBeInTheDocument() }) + it('should call onStopAll when stop all button is clicked', () => { + const onStopAll = vi.fn() + render( + , + ) + + fireEvent.click(screen.getByRole('button', { name: /plugin\.task\.stopAll/ })) + + expect(onStopAll).toHaveBeenCalledTimes(1) + }) + it('should show installing hint as status text', () => { render() diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/hooks.ts b/web/app/components/plugins/plugin-page/plugin-tasks/hooks.ts index 00f2cbbb27..96508f3696 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/hooks.ts +++ b/web/app/components/plugins/plugin-page/plugin-tasks/hooks.ts @@ -42,7 +42,7 @@ export const usePluginTaskStatus = () => { }) handleRefetch() }, [mutateAsync, handleRefetch]) - + const handleStopAllPlugins = useCallback(async () => { await mutateStopAllAsync() handleRefetch() diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx index 775eee2e0a..5b5455ed88 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx @@ -82,7 +82,6 @@ const PluginTasks = () => { [clearPluginsAndClose, errorPlugins], ) - const handleStopAll = useCallback(async () => { await handleStopAllPlugins() setOpen(false) diff --git a/web/i18n/en-US/plugin.json b/web/i18n/en-US/plugin.json index 4f7cc3033f..fbedad1cc5 100644 --- a/web/i18n/en-US/plugin.json +++ b/web/i18n/en-US/plugin.json @@ -257,4 +257,4 @@ "upgrade.upgrade": "Install", "upgrade.upgrading": "Installing...", "upgrade.usedInApps": "Used in {{num}} apps" -} \ No newline at end of file +}