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', () => {