From ef0d18bb61a228d6b2449ddf539862110badada3 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:31:21 +0800 Subject: [PATCH] test: fix test (#31975) --- .github/workflows/web-tests.yml | 2 +- web/app/components/base/tooltip/index.tsx | 29 +++++++++++++++++-- .../create/common-modal.spec.tsx | 29 ++++++++++++------- .../components/update-dsl-modal.spec.tsx | 11 +++++++ web/package.json | 6 ++-- web/pnpm-lock.yaml | 15 ++++++++++ 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml index 191ce56aaa..78d0b2af40 100644 --- a/.github/workflows/web-tests.yml +++ b/.github/workflows/web-tests.yml @@ -39,7 +39,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Run tests - run: pnpm test:coverage + run: pnpm test:ci - name: Coverage Summary if: always() diff --git a/web/app/components/base/tooltip/index.tsx b/web/app/components/base/tooltip/index.tsx index ee9928745d..d1047ff902 100644 --- a/web/app/components/base/tooltip/index.tsx +++ b/web/app/components/base/tooltip/index.tsx @@ -4,7 +4,7 @@ import type { FC } from 'react' import { RiQuestionLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import * as React from 'react' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' import { tooltipManager } from './TooltipManager' @@ -61,6 +61,20 @@ const Tooltip: FC = ({ isHoverTriggerRef.current = isHoverTrigger }, [isHoverTrigger]) + const closeTimeoutRef = useRef | null>(null) + const clearCloseTimeout = useCallback(() => { + if (closeTimeoutRef.current) { + clearTimeout(closeTimeoutRef.current) + closeTimeoutRef.current = null + } + }, []) + + useEffect(() => { + return () => { + clearCloseTimeout() + } + }, [clearCloseTimeout]) + const close = () => setOpen(false) const handleLeave = (isTrigger: boolean) => { @@ -71,7 +85,9 @@ const Tooltip: FC = ({ // give time to move to the popup if (needsDelay) { - setTimeout(() => { + clearCloseTimeout() + closeTimeoutRef.current = setTimeout(() => { + closeTimeoutRef.current = null if (!isHoverPopupRef.current && !isHoverTriggerRef.current) { setOpen(false) tooltipManager.clear(close) @@ -79,6 +95,7 @@ const Tooltip: FC = ({ }, 300) } else { + clearCloseTimeout() setOpen(false) tooltipManager.clear(close) } @@ -95,6 +112,7 @@ const Tooltip: FC = ({ onClick={() => triggerMethod === 'click' && setOpen(v => !v)} onMouseEnter={() => { if (triggerMethod === 'hover') { + clearCloseTimeout() setHoverTrigger() tooltipManager.register(close) setOpen(true) @@ -115,7 +133,12 @@ const Tooltip: FC = ({ !noDecoration && 'system-xs-regular relative max-w-[300px] break-words rounded-md bg-components-panel-bg px-3 py-2 text-left text-text-tertiary shadow-lg', popupClassName, )} - onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()} + onMouseEnter={() => { + if (triggerMethod === 'hover') { + clearCloseTimeout() + setHoverPopup() + } + }} onMouseLeave={() => triggerMethod === 'hover' && handleLeave(false)} > {popupContent} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.spec.tsx index 0c1b5efc29..6edb493e17 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.spec.tsx @@ -599,20 +599,30 @@ describe('CommonCreateModal', () => { }, }) mockUsePluginStore.mockReturnValue(detailWithCredentials) + const existingBuilder = createMockSubscriptionBuilder() mockVerifyCredentials.mockImplementation((params, { onSuccess }) => { onSuccess() }) - render() - - await waitFor(() => { - expect(mockCreateBuilder).toHaveBeenCalled() - }) + render() fireEvent.click(screen.getByTestId('modal-confirm')) await waitFor(() => { - expect(mockVerifyCredentials).toHaveBeenCalled() + expect(mockVerifyCredentials).toHaveBeenCalledWith( + expect.objectContaining({ + provider: 'test-provider', + subscriptionBuilderId: existingBuilder.id, + }), + expect.objectContaining({ + onSuccess: expect.any(Function), + onError: expect.any(Function), + }), + ) + }) + + await waitFor(() => { + expect(screen.getByTestId('modal-confirm')).toHaveTextContent('pluginTrigger.modal.common.create') }) }) @@ -629,15 +639,12 @@ describe('CommonCreateModal', () => { }, }) mockUsePluginStore.mockReturnValue(detailWithCredentials) + const existingBuilder = createMockSubscriptionBuilder() mockVerifyCredentials.mockImplementation((params, { onError }) => { onError(new Error('Verification failed')) }) - render() - - await waitFor(() => { - expect(mockCreateBuilder).toHaveBeenCalled() - }) + render() fireEvent.click(screen.getByTestId('modal-confirm')) diff --git a/web/app/components/rag-pipeline/components/update-dsl-modal.spec.tsx b/web/app/components/rag-pipeline/components/update-dsl-modal.spec.tsx index 317f2b19d4..6643d8239d 100644 --- a/web/app/components/rag-pipeline/components/update-dsl-modal.spec.tsx +++ b/web/app/components/rag-pipeline/components/update-dsl-modal.spec.tsx @@ -4,6 +4,17 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { DSLImportStatus } from '@/models/app' import UpdateDSLModal from './update-dsl-modal' +class MockFileReader { + onload: ((this: FileReader, event: ProgressEvent) => void) | null = null + + readAsText(_file: Blob) { + const event = { target: { result: 'test content' } } as unknown as ProgressEvent + this.onload?.call(this as unknown as FileReader, event) + } +} + +vi.stubGlobal('FileReader', MockFileReader as unknown as typeof FileReader) + // Mock react-i18next vi.mock('react-i18next', () => ({ useTranslation: () => ({ diff --git a/web/package.json b/web/package.json index 219a613363..b37a46681f 100644 --- a/web/package.json +++ b/web/package.json @@ -46,7 +46,8 @@ "uglify-embed": "node ./bin/uglify-embed", "i18n:check": "tsx ./scripts/check-i18n.js", "test": "vitest run", - "test:coverage": "vitest run --coverage --reporter=dot --silent=passed-only", + "test:coverage": "vitest run --coverage", + "test:ci": "vitest run --coverage --reporter vitest-tiny-reporter --silent=passed-only", "test:watch": "vitest --watch", "analyze-component": "node ./scripts/analyze-component.js", "refactor-component": "node ./scripts/refactor-component.js", @@ -234,7 +235,8 @@ "vite": "7.3.1", "vite-tsconfig-paths": "6.0.4", "vitest": "4.0.17", - "vitest-canvas-mock": "1.1.3" + "vitest-canvas-mock": "1.1.3", + "vitest-tiny-reporter": "1.3.1" }, "pnpm": { "overrides": { diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 9119d2554a..5266b4ac6f 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -582,6 +582,9 @@ importers: vitest-canvas-mock: specifier: 1.1.3 version: 1.1.3(vitest@4.0.17) + vitest-tiny-reporter: + specifier: 1.3.1 + version: 1.3.1(@vitest/runner@4.0.17)(vitest@4.0.17) packages: @@ -7230,6 +7233,12 @@ packages: peerDependencies: vitest: ^3.0.0 || ^4.0.0 + vitest-tiny-reporter@1.3.1: + resolution: {integrity: sha512-9WfLruQBbxm4EqMIS0jDZmQjvMgsWgHUso9mHQWgjA6hM3tEVhjdG8wYo7ePFh1XbwEFzEo3XUQqkGoKZ/Td2Q==} + peerDependencies: + '@vitest/runner': ^2.0.0 || ^3.0.2 || ^4.0.0 + vitest: ^2.0.0 || ^3.0.2 || ^4.0.0 + vitest@4.0.17: resolution: {integrity: sha512-FQMeF0DJdWY0iOnbv466n/0BudNdKj1l5jYgl5JVTwjSsZSlqyXFt/9+1sEyhR6CLowbZpV7O1sCHrzBhucKKg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -15228,6 +15237,12 @@ snapshots: moo-color: 1.0.3 vitest: 4.0.17(@types/node@18.15.0)(@vitest/browser-playwright@4.0.17)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.1))(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vitest-tiny-reporter@1.3.1(@vitest/runner@4.0.17)(vitest@4.0.17): + dependencies: + '@vitest/runner': 4.0.17 + tinyrainbow: 3.0.3 + vitest: 4.0.17(@types/node@18.15.0)(@vitest/browser-playwright@4.0.17)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.1))(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vitest@4.0.17(@types/node@18.15.0)(@vitest/browser-playwright@4.0.17)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.1))(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.17