fix: web tests

This commit is contained in:
hjlarry 2026-04-11 09:17:00 +08:00
parent 7520d65a7e
commit 341fdc8201
11 changed files with 128 additions and 29 deletions

View File

@ -69,6 +69,7 @@ vi.mock('lexical', async (importOriginal) => {
getChildren: () => mocks.rootLines.map(line => ({ getChildren: () => mocks.rootLines.map(line => ({
getTextContent: () => line, getTextContent: () => line,
})), })),
getAllTextNodes: () => [],
}), }),
TextNode: class TextNode { TextNode: class TextNode {
__text: string __text: string

View File

@ -21,6 +21,16 @@ vi.mock('@/context/dataset-detail', () => ({
selector({ dataset: { doc_form: ChunkingMode.text } }), 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({ const createTestQueryClient = () => new QueryClient({
defaultOptions: { defaultOptions: {
queries: { retry: false, gcTime: 0 }, queries: { retry: false, gcTime: 0 },

View File

@ -8,6 +8,7 @@ const mockUseStore = vi.hoisted(() => vi.fn())
const mockUseNodesInteractions = vi.hoisted(() => vi.fn()) const mockUseNodesInteractions = vi.hoisted(() => vi.fn())
const mockUsePanelInteractions = vi.hoisted(() => vi.fn()) const mockUsePanelInteractions = vi.hoisted(() => vi.fn())
const mockUseWorkflowStartRun = vi.hoisted(() => vi.fn()) const mockUseWorkflowStartRun = vi.hoisted(() => vi.fn())
const mockUseWorkflowMoveMode = vi.hoisted(() => vi.fn())
const mockUseOperator = vi.hoisted(() => vi.fn()) const mockUseOperator = vi.hoisted(() => vi.fn())
const mockUseDSL = vi.hoisted(() => vi.fn()) const mockUseDSL = vi.hoisted(() => vi.fn())
@ -23,6 +24,9 @@ vi.mock('@/app/components/workflow/store', () => ({
useStore: (selector: (state: { useStore: (selector: (state: {
panelMenu?: { left: number, top: number } panelMenu?: { left: number, top: number }
clipboardElements: unknown[] clipboardElements: unknown[]
pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
setCommentPlacing: (placing: boolean) => void
setCommentQuickAdd: (quickAdd: boolean) => void
setShowImportDSLModal: (visible: boolean) => void setShowImportDSLModal: (visible: boolean) => void
}) => unknown) => mockUseStore(selector), }) => unknown) => mockUseStore(selector),
})) }))
@ -31,6 +35,7 @@ vi.mock('@/app/components/workflow/hooks', () => ({
useNodesInteractions: () => mockUseNodesInteractions(), useNodesInteractions: () => mockUseNodesInteractions(),
usePanelInteractions: () => mockUsePanelInteractions(), usePanelInteractions: () => mockUsePanelInteractions(),
useWorkflowStartRun: () => mockUseWorkflowStartRun(), useWorkflowStartRun: () => mockUseWorkflowStartRun(),
useWorkflowMoveMode: () => mockUseWorkflowMoveMode(),
useDSL: () => mockUseDSL(), useDSL: () => mockUseDSL(),
})) }))
@ -62,14 +67,18 @@ describe('PanelContextmenu', () => {
const mockHandleAddNote = vi.fn() const mockHandleAddNote = vi.fn()
const mockExportCheck = vi.fn() const mockExportCheck = vi.fn()
const mockSetShowImportDSLModal = vi.fn() const mockSetShowImportDSLModal = vi.fn()
const mockSetCommentPlacing = vi.fn()
const mockSetCommentQuickAdd = vi.fn()
let panelMenu: { left: number, top: number } | undefined let panelMenu: { left: number, top: number } | undefined
let clipboardElements: unknown[] let clipboardElements: unknown[]
let pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
let clickAwayHandler: (() => void) | undefined let clickAwayHandler: (() => void) | undefined
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks() vi.clearAllMocks()
panelMenu = undefined panelMenu = undefined
clipboardElements = [] clipboardElements = []
pendingComment = null
clickAwayHandler = undefined clickAwayHandler = undefined
mockUseClickAway.mockImplementation((handler: () => void) => { mockUseClickAway.mockImplementation((handler: () => void) => {
@ -81,10 +90,16 @@ describe('PanelContextmenu', () => {
mockUseStore.mockImplementation((selector: (state: { mockUseStore.mockImplementation((selector: (state: {
panelMenu?: { left: number, top: number } panelMenu?: { left: number, top: number }
clipboardElements: unknown[] clipboardElements: unknown[]
pendingComment: null | { pageX: number, pageY: number, elementX: number, elementY: number }
setCommentPlacing: (placing: boolean) => void
setCommentQuickAdd: (quickAdd: boolean) => void
setShowImportDSLModal: (visible: boolean) => void setShowImportDSLModal: (visible: boolean) => void
}) => unknown) => selector({ }) => unknown) => selector({
panelMenu, panelMenu,
clipboardElements, clipboardElements,
pendingComment,
setCommentPlacing: mockSetCommentPlacing,
setCommentQuickAdd: mockSetCommentQuickAdd,
setShowImportDSLModal: mockSetShowImportDSLModal, setShowImportDSLModal: mockSetShowImportDSLModal,
})) }))
mockUseNodesInteractions.mockReturnValue({ mockUseNodesInteractions.mockReturnValue({
@ -96,6 +111,9 @@ describe('PanelContextmenu', () => {
mockUseWorkflowStartRun.mockReturnValue({ mockUseWorkflowStartRun.mockReturnValue({
handleStartWorkflowRun: mockHandleStartWorkflowRun, handleStartWorkflowRun: mockHandleStartWorkflowRun,
}) })
mockUseWorkflowMoveMode.mockReturnValue({
isCommentModeAvailable: false,
})
mockUseOperator.mockReturnValue({ mockUseOperator.mockReturnValue({
handleAddNote: mockHandleAddNote, handleAddNote: mockHandleAddNote,
}) })

View File

@ -2,13 +2,10 @@ import { act, fireEvent, render, screen } from '@testing-library/react'
import UndoRedo from '../undo-redo' import UndoRedo from '../undo-redo'
const mockUnsubscribe = vi.fn() const mockUnsubscribe = vi.fn()
const mockCanUndo = vi.fn()
const mockCanRedo = vi.fn()
const mockOnUndoRedoStateChange = vi.fn()
const mockHandleUndo = vi.fn() const mockHandleUndo = vi.fn()
const mockHandleRedo = 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 let mockNodesReadOnly = false
vi.mock('@/app/components/workflow/header/view-workflow-history', () => ({ 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', () => ({ vi.mock('@/app/components/workflow/workflow-history-store', () => ({
useWorkflowHistoryStore: () => ({ useWorkflowHistoryStore: () => ({
store: {
temporal: {
subscribe: (listener: (state: { pastStates: unknown[], futureStates: unknown[] }) => void) => {
latestTemporalListener = listener
return mockUnsubscribe
},
},
},
shortcutsEnabled: true, shortcutsEnabled: true,
setShortcutsEnabled: vi.fn(), 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', () => ({ vi.mock('@/app/components/base/divider', () => ({
default: () => <div data-testid="divider" />, default: () => <div data-testid="divider" />,
})) }))
@ -51,19 +45,16 @@ describe('UndoRedo', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks() vi.clearAllMocks()
mockNodesReadOnly = false mockNodesReadOnly = false
latestUndoRedoListener = undefined latestTemporalListener = undefined
mockCanUndo.mockReturnValue(false)
mockCanRedo.mockReturnValue(false)
mockOnUndoRedoStateChange.mockReturnValue(mockUnsubscribe)
}) })
it('enables undo and redo when history exists and triggers the callbacks', () => { it('enables undo and redo when history exists and triggers the callbacks', () => {
render(<UndoRedo handleRedo={mockHandleRedo} handleUndo={mockHandleUndo} />) render(<UndoRedo handleRedo={mockHandleRedo} handleUndo={mockHandleUndo} />)
act(() => { act(() => {
latestUndoRedoListener?.({ latestTemporalListener?.({
canUndo: true, pastStates: [{}],
canRedo: true, futureStates: [{}],
}) })
}) })
@ -95,9 +86,9 @@ describe('UndoRedo', () => {
const redoButton = screen.getByRole('button', { name: 'workflow.common.redo' }) const redoButton = screen.getByRole('button', { name: 'workflow.common.redo' })
act(() => { act(() => {
latestUndoRedoListener?.({ latestTemporalListener?.({
canUndo: true, pastStates: [{}],
canRedo: true, futureStates: [{}],
}) })
}) })

View File

@ -32,6 +32,18 @@ vi.mock('@/service/use-triggers', () => ({
useTriggerPluginDynamicOptions: () => mockTriggerDynamicOptionsState, 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', () => ({ vi.mock('@/app/components/plugins/plugin-detail-panel/app-selector', () => ({
default: ({ onSelect }: { onSelect: (value: string) => void }) => ( default: ({ onSelect }: { onSelect: (value: string) => void }) => (
<button onClick={() => onSelect('app-1')}>app-selector</button> <button onClick={() => onSelect('app-1')}>app-selector</button>

View File

@ -6,6 +6,18 @@ import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__
import { VarKindType } from '../../types' import { VarKindType } from '../../types'
import FormInputItem from '../form-input-item' import FormInputItem from '../form-input-item'
vi.mock('@/app/components/workflow/hooks', () => ({
useIsChatMode: () => false,
useWorkflow: () => ({
getTreeLeafNodes: () => [],
getNodeById: () => undefined,
getBeforeNodesInSameBranchIncludeParent: () => [],
}),
useWorkflowVariables: () => ({
getNodeAvailableVars: () => [],
}),
}))
const createSchema = ( const createSchema = (
overrides: Partial<CredentialFormSchema & { overrides: Partial<CredentialFormSchema & {
_type?: FormTypeEnum _type?: FormTypeEnum

View File

@ -20,6 +20,19 @@ vi.mock('@/service/use-plugins', () => ({
}), }),
})) }))
vi.mock('@/app/components/workflow/hooks', () => ({
useIsChatMode: () => false,
useWorkflow: () => ({
getTreeLeafNodes: () => [],
getNodeById: () => undefined,
getBeforeNodesInSameBranchIncludeParent: () => [],
}),
useWorkflowVariables: () => ({
getNodeAvailableVars: () => [],
getCurrentVariableType: () => undefined,
}),
}))
vi.mock('../var-reference-popup', () => ({ vi.mock('../var-reference-popup', () => ({
default: ({ default: ({
onChange, onChange,

View File

@ -6,6 +6,19 @@ import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
import VarReferencePicker from '../var-reference-picker' 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', () => { describe('VarReferencePicker', () => {
const startNode = createStartNode({ const startNode = createStartNode({
id: 'start-node', id: 'start-node',

View File

@ -7,6 +7,19 @@ import { BlockEnum, VarType } from '@/app/components/workflow/types'
import { AssignerNodeInputType, WriteMode } from '../../../types' import { AssignerNodeInputType, WriteMode } from '../../../types'
import VarList from '../index' import VarList from '../index'
vi.mock('@/app/components/workflow/hooks', () => ({
useIsChatMode: () => false,
useWorkflow: () => ({
getTreeLeafNodes: () => [],
getNodeById: () => undefined,
getBeforeNodesInSameBranchIncludeParent: () => [],
}),
useWorkflowVariables: () => ({
getNodeAvailableVars: () => [],
getCurrentVariableType: () => undefined,
}),
}))
const sourceNode = createNode({ const sourceNode = createNode({
id: 'node-a', id: 'node-a',
data: { data: {

View File

@ -19,6 +19,8 @@ const {
mockFindUsedVarNodes, mockFindUsedVarNodes,
mockUpdateNodeVars, mockUpdateNodeVars,
mockVariableTriggerState, mockVariableTriggerState,
mockUpdateEnvironmentVariables,
mockGetSocket,
} = vi.hoisted(() => ({ } = vi.hoisted(() => ({
mockDoSyncWorkflowDraft: vi.fn(() => Promise.resolve()), mockDoSyncWorkflowDraft: vi.fn(() => Promise.resolve()),
mockGetNodes: vi.fn<() => MockWorkflowNode[]>(() => []), mockGetNodes: vi.fn<() => MockWorkflowNode[]>(() => []),
@ -34,6 +36,8 @@ const {
mockVariableTriggerState: { mockVariableTriggerState: {
savePayload: undefined as EnvironmentVariable | undefined, savePayload: undefined as EnvironmentVariable | undefined,
}, },
mockUpdateEnvironmentVariables: vi.fn(() => Promise.resolve({})),
mockGetSocket: vi.fn(() => null),
})) }))
vi.mock('@/app/components/workflow/hooks/use-nodes-sync-draft', () => ({ 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, 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', () => ({ vi.mock('@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm', () => ({
default: ({ default: ({
isShow, isShow,
@ -171,6 +185,8 @@ describe('EnvPanel container', () => {
mockGetNodes.mockReturnValue([]) mockGetNodes.mockReturnValue([])
mockFindUsedVarNodes.mockReturnValue([]) mockFindUsedVarNodes.mockReturnValue([])
mockVariableTriggerState.savePayload = undefined mockVariableTriggerState.savePayload = undefined
mockUpdateEnvironmentVariables.mockResolvedValue({})
mockGetSocket.mockReturnValue(null)
}) })
it('should close the panel from the header action', async () => { it('should close the panel from the header action', async () => {

View File

@ -8,15 +8,15 @@ describe('createWorkflowSlice', () => {
localStorage.clear() 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) 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) 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', () => { it('persists control mode updates and stores run state payloads', () => {