diff --git a/web/app/components/base/prompt-editor/__tests__/index.spec.tsx b/web/app/components/base/prompt-editor/__tests__/index.spec.tsx index ccfb7a0c7a..9d75b9e061 100644 --- a/web/app/components/base/prompt-editor/__tests__/index.spec.tsx +++ b/web/app/components/base/prompt-editor/__tests__/index.spec.tsx @@ -69,6 +69,7 @@ vi.mock('lexical', async (importOriginal) => { getChildren: () => mocks.rootLines.map(line => ({ getTextContent: () => line, })), + getAllTextNodes: () => [], }), TextNode: class TextNode { __text: string diff --git a/web/app/components/datasets/documents/components/document-list/__tests__/index.spec.tsx b/web/app/components/datasets/documents/components/document-list/__tests__/index.spec.tsx index 48e6b58766..478302d983 100644 --- a/web/app/components/datasets/documents/components/document-list/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/components/document-list/__tests__/index.spec.tsx @@ -21,6 +21,16 @@ vi.mock('@/context/dataset-detail', () => ({ selector({ dataset: { doc_form: ChunkingMode.text } }), })) +vi.mock('@/app/components/datasets/metadata/hooks/use-batch-edit-document-metadata', () => ({ + default: () => ({ + isShowEditModal: false, + showEditModal: vi.fn(), + hideEditModal: vi.fn(), + originalList: [], + handleSave: vi.fn(), + }), +})) + const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 }, diff --git a/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx b/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx index 914c1be617..7a02781c17 100644 --- a/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx +++ b/web/app/components/workflow/__tests__/panel-contextmenu.spec.tsx @@ -8,6 +8,7 @@ const mockUseStore = vi.hoisted(() => vi.fn()) const mockUseNodesInteractions = vi.hoisted(() => vi.fn()) const mockUsePanelInteractions = vi.hoisted(() => vi.fn()) const mockUseWorkflowStartRun = vi.hoisted(() => vi.fn()) +const mockUseWorkflowMoveMode = vi.hoisted(() => vi.fn()) const mockUseOperator = vi.hoisted(() => vi.fn()) const mockUseDSL = vi.hoisted(() => vi.fn()) @@ -23,6 +24,9 @@ vi.mock('@/app/components/workflow/store', () => ({ useStore: (selector: (state: { panelMenu?: { left: number, top: number } clipboardElements: unknown[] + pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number } + setCommentPlacing: (placing: boolean) => void + setCommentQuickAdd: (quickAdd: boolean) => void setShowImportDSLModal: (visible: boolean) => void }) => unknown) => mockUseStore(selector), })) @@ -31,6 +35,7 @@ vi.mock('@/app/components/workflow/hooks', () => ({ useNodesInteractions: () => mockUseNodesInteractions(), usePanelInteractions: () => mockUsePanelInteractions(), useWorkflowStartRun: () => mockUseWorkflowStartRun(), + useWorkflowMoveMode: () => mockUseWorkflowMoveMode(), useDSL: () => mockUseDSL(), })) @@ -62,14 +67,18 @@ describe('PanelContextmenu', () => { const mockHandleAddNote = vi.fn() const mockExportCheck = vi.fn() const mockSetShowImportDSLModal = vi.fn() + const mockSetCommentPlacing = vi.fn() + const mockSetCommentQuickAdd = vi.fn() let panelMenu: { left: number, top: number } | undefined let clipboardElements: unknown[] + let pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number } let clickAwayHandler: (() => void) | undefined beforeEach(() => { vi.clearAllMocks() panelMenu = undefined clipboardElements = [] + pendingComment = null clickAwayHandler = undefined mockUseClickAway.mockImplementation((handler: () => void) => { @@ -81,10 +90,16 @@ describe('PanelContextmenu', () => { mockUseStore.mockImplementation((selector: (state: { panelMenu?: { left: number, top: number } clipboardElements: unknown[] + pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number } + setCommentPlacing: (placing: boolean) => void + setCommentQuickAdd: (quickAdd: boolean) => void setShowImportDSLModal: (visible: boolean) => void }) => unknown) => selector({ panelMenu, clipboardElements, + pendingComment, + setCommentPlacing: mockSetCommentPlacing, + setCommentQuickAdd: mockSetCommentQuickAdd, setShowImportDSLModal: mockSetShowImportDSLModal, })) mockUseNodesInteractions.mockReturnValue({ @@ -96,6 +111,9 @@ describe('PanelContextmenu', () => { mockUseWorkflowStartRun.mockReturnValue({ handleStartWorkflowRun: mockHandleStartWorkflowRun, }) + mockUseWorkflowMoveMode.mockReturnValue({ + isCommentModeAvailable: false, + }) mockUseOperator.mockReturnValue({ handleAddNote: mockHandleAddNote, }) diff --git a/web/app/components/workflow/header/__tests__/undo-redo.spec.tsx b/web/app/components/workflow/header/__tests__/undo-redo.spec.tsx index cb63bee53e..9afe3efd60 100644 --- a/web/app/components/workflow/header/__tests__/undo-redo.spec.tsx +++ b/web/app/components/workflow/header/__tests__/undo-redo.spec.tsx @@ -2,13 +2,10 @@ import { act, fireEvent, render, screen } from '@testing-library/react' import UndoRedo from '../undo-redo' const mockUnsubscribe = vi.fn() -const mockCanUndo = vi.fn() -const mockCanRedo = vi.fn() -const mockOnUndoRedoStateChange = vi.fn() const mockHandleUndo = vi.fn() const mockHandleRedo = vi.fn() -let latestUndoRedoListener: ((state: { canUndo: boolean, canRedo: boolean }) => void) | undefined +let latestTemporalListener: ((state: { pastStates: unknown[], futureStates: unknown[] }) => void) | undefined let mockNodesReadOnly = false vi.mock('@/app/components/workflow/header/view-workflow-history', () => ({ @@ -23,22 +20,19 @@ vi.mock('@/app/components/workflow/hooks', () => ({ vi.mock('@/app/components/workflow/workflow-history-store', () => ({ useWorkflowHistoryStore: () => ({ + store: { + temporal: { + subscribe: (listener: (state: { pastStates: unknown[], futureStates: unknown[] }) => void) => { + latestTemporalListener = listener + return mockUnsubscribe + }, + }, + }, shortcutsEnabled: true, setShortcutsEnabled: vi.fn(), }), })) -vi.mock('@/app/components/workflow/collaboration/core/collaboration-manager', () => ({ - collaborationManager: { - canUndo: () => mockCanUndo(), - canRedo: () => mockCanRedo(), - onUndoRedoStateChange: (listener: (state: { canUndo: boolean, canRedo: boolean }) => void) => { - latestUndoRedoListener = listener - return mockOnUndoRedoStateChange(listener) - }, - }, -})) - vi.mock('@/app/components/base/divider', () => ({ default: () =>
, })) @@ -51,19 +45,16 @@ describe('UndoRedo', () => { beforeEach(() => { vi.clearAllMocks() mockNodesReadOnly = false - latestUndoRedoListener = undefined - mockCanUndo.mockReturnValue(false) - mockCanRedo.mockReturnValue(false) - mockOnUndoRedoStateChange.mockReturnValue(mockUnsubscribe) + latestTemporalListener = undefined }) it('enables undo and redo when history exists and triggers the callbacks', () => { render() act(() => { - latestUndoRedoListener?.({ - canUndo: true, - canRedo: true, + latestTemporalListener?.({ + pastStates: [{}], + futureStates: [{}], }) }) @@ -95,9 +86,9 @@ describe('UndoRedo', () => { const redoButton = screen.getByRole('button', { name: 'workflow.common.redo' }) act(() => { - latestUndoRedoListener?.({ - canUndo: true, - canRedo: true, + latestTemporalListener?.({ + pastStates: [{}], + futureStates: [{}], }) }) diff --git a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx index 49de788314..6ce294d7f3 100644 --- a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.branches.spec.tsx @@ -32,6 +32,18 @@ vi.mock('@/service/use-triggers', () => ({ useTriggerPluginDynamicOptions: () => mockTriggerDynamicOptionsState, })) +vi.mock('@/app/components/workflow/hooks', () => ({ + useIsChatMode: () => false, + useWorkflow: () => ({ + getTreeLeafNodes: () => [], + getNodeById: () => undefined, + getBeforeNodesInSameBranchIncludeParent: () => [], + }), + useWorkflowVariables: () => ({ + getNodeAvailableVars: () => [], + }), +})) + vi.mock('@/app/components/plugins/plugin-detail-panel/app-selector', () => ({ default: ({ onSelect }: { onSelect: (value: string) => void }) => ( diff --git a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.spec.tsx b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.spec.tsx index 49fa4ea29f..b8ba298caf 100644 --- a/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/__tests__/form-input-item.spec.tsx @@ -6,6 +6,18 @@ import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__ import { VarKindType } from '../../types' import FormInputItem from '../form-input-item' +vi.mock('@/app/components/workflow/hooks', () => ({ + useIsChatMode: () => false, + useWorkflow: () => ({ + getTreeLeafNodes: () => [], + getNodeById: () => undefined, + getBeforeNodesInSameBranchIncludeParent: () => [], + }), + useWorkflowVariables: () => ({ + getNodeAvailableVars: () => [], + }), +})) + const createSchema = ( overrides: Partial ({ }), })) +vi.mock('@/app/components/workflow/hooks', () => ({ + useIsChatMode: () => false, + useWorkflow: () => ({ + getTreeLeafNodes: () => [], + getNodeById: () => undefined, + getBeforeNodesInSameBranchIncludeParent: () => [], + }), + useWorkflowVariables: () => ({ + getNodeAvailableVars: () => [], + getCurrentVariableType: () => undefined, + }), +})) + vi.mock('../var-reference-popup', () => ({ default: ({ onChange, diff --git a/web/app/components/workflow/nodes/_base/components/variable/__tests__/var-reference-picker.spec.tsx b/web/app/components/workflow/nodes/_base/components/variable/__tests__/var-reference-picker.spec.tsx index f6f18ca5cf..b915201ca4 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/__tests__/var-reference-picker.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/__tests__/var-reference-picker.spec.tsx @@ -6,6 +6,19 @@ import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__ import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' import VarReferencePicker from '../var-reference-picker' +vi.mock('@/app/components/workflow/hooks', () => ({ + useIsChatMode: () => false, + useWorkflow: () => ({ + getTreeLeafNodes: () => [], + getNodeById: () => undefined, + getBeforeNodesInSameBranchIncludeParent: () => [], + }), + useWorkflowVariables: () => ({ + getNodeAvailableVars: () => [], + getCurrentVariableType: () => undefined, + }), +})) + describe('VarReferencePicker', () => { const startNode = createStartNode({ id: 'start-node', diff --git a/web/app/components/workflow/nodes/assigner/components/var-list/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/assigner/components/var-list/__tests__/index.spec.tsx index f7408ab814..37d2de8d80 100644 --- a/web/app/components/workflow/nodes/assigner/components/var-list/__tests__/index.spec.tsx +++ b/web/app/components/workflow/nodes/assigner/components/var-list/__tests__/index.spec.tsx @@ -7,6 +7,19 @@ import { BlockEnum, VarType } from '@/app/components/workflow/types' import { AssignerNodeInputType, WriteMode } from '../../../types' import VarList from '../index' +vi.mock('@/app/components/workflow/hooks', () => ({ + useIsChatMode: () => false, + useWorkflow: () => ({ + getTreeLeafNodes: () => [], + getNodeById: () => undefined, + getBeforeNodesInSameBranchIncludeParent: () => [], + }), + useWorkflowVariables: () => ({ + getNodeAvailableVars: () => [], + getCurrentVariableType: () => undefined, + }), +})) + const sourceNode = createNode({ id: 'node-a', data: { diff --git a/web/app/components/workflow/panel/env-panel/__tests__/index.spec.tsx b/web/app/components/workflow/panel/env-panel/__tests__/index.spec.tsx index 0beae8b4b5..41a5b27492 100644 --- a/web/app/components/workflow/panel/env-panel/__tests__/index.spec.tsx +++ b/web/app/components/workflow/panel/env-panel/__tests__/index.spec.tsx @@ -19,6 +19,8 @@ const { mockFindUsedVarNodes, mockUpdateNodeVars, mockVariableTriggerState, + mockUpdateEnvironmentVariables, + mockGetSocket, } = vi.hoisted(() => ({ mockDoSyncWorkflowDraft: vi.fn(() => Promise.resolve()), mockGetNodes: vi.fn<() => MockWorkflowNode[]>(() => []), @@ -34,6 +36,8 @@ const { mockVariableTriggerState: { savePayload: undefined as EnvironmentVariable | undefined, }, + mockUpdateEnvironmentVariables: vi.fn(() => Promise.resolve({})), + mockGetSocket: vi.fn(() => null), })) vi.mock('@/app/components/workflow/hooks/use-nodes-sync-draft', () => ({ @@ -56,6 +60,16 @@ vi.mock('@/app/components/workflow/nodes/_base/components/variable/utils', () => updateNodeVars: mockUpdateNodeVars, })) +vi.mock('@/service/workflow', () => ({ + updateEnvironmentVariables: (payload: { appId: string, environmentVariables: EnvironmentVariable[] }) => mockUpdateEnvironmentVariables(payload), +})) + +vi.mock('@/app/components/workflow/collaboration/core/websocket-manager', () => ({ + webSocketClient: { + getSocket: (appId: string) => mockGetSocket(appId), + }, +})) + vi.mock('@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm', () => ({ default: ({ isShow, @@ -171,6 +185,8 @@ describe('EnvPanel container', () => { mockGetNodes.mockReturnValue([]) mockFindUsedVarNodes.mockReturnValue([]) mockVariableTriggerState.savePayload = undefined + mockUpdateEnvironmentVariables.mockResolvedValue({}) + mockGetSocket.mockReturnValue(null) }) it('should close the panel from the header action', async () => { diff --git a/web/app/components/workflow/store/workflow/__tests__/workflow-slice.spec.ts b/web/app/components/workflow/store/workflow/__tests__/workflow-slice.spec.ts index 8c8b40caf7..1149ea3e44 100644 --- a/web/app/components/workflow/store/workflow/__tests__/workflow-slice.spec.ts +++ b/web/app/components/workflow/store/workflow/__tests__/workflow-slice.spec.ts @@ -8,15 +8,15 @@ describe('createWorkflowSlice', () => { localStorage.clear() }) - it('defaults to hand mode until a persisted pointer mode is present', () => { + it('defaults to pointer mode and restores persisted control mode', () => { const defaultStore = createStore(createWorkflowSlice) - expect(defaultStore.getState().controlMode).toBe('hand') + expect(defaultStore.getState().controlMode).toBe('pointer') - localStorage.setItem('workflow-operation-mode', 'pointer') + localStorage.setItem('workflow-operation-mode', 'hand') const persistedStore = createStore(createWorkflowSlice) - expect(persistedStore.getState().controlMode).toBe('pointer') + expect(persistedStore.getState().controlMode).toBe('hand') }) it('persists control mode updates and stores run state payloads', () => {