mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 03:36:36 +08:00
fix: web unittests
This commit is contained in:
parent
c3c84419e7
commit
5c88acc5f4
@ -15,6 +15,7 @@ const mockOpenAsyncWindow = vi.fn()
|
|||||||
const mockFetchInstalledAppList = vi.fn()
|
const mockFetchInstalledAppList = vi.fn()
|
||||||
const mockFetchAppDetailDirect = vi.fn()
|
const mockFetchAppDetailDirect = vi.fn()
|
||||||
const mockToastError = vi.fn()
|
const mockToastError = vi.fn()
|
||||||
|
const mockInvalidateAppWorkflow = vi.fn()
|
||||||
|
|
||||||
const sectionProps = vi.hoisted(() => ({
|
const sectionProps = vi.hoisted(() => ({
|
||||||
summary: null as null | Record<string, any>,
|
summary: null as null | Record<string, any>,
|
||||||
@ -88,6 +89,10 @@ vi.mock('@/service/apps', () => ({
|
|||||||
fetchAppDetailDirect: (...args: unknown[]) => mockFetchAppDetailDirect(...args),
|
fetchAppDetailDirect: (...args: unknown[]) => mockFetchAppDetailDirect(...args),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/service/use-workflow', () => ({
|
||||||
|
useInvalidateAppWorkflow: () => mockInvalidateAppWorkflow,
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||||
toast: {
|
toast: {
|
||||||
error: (...args: unknown[]) => mockToastError(...args),
|
error: (...args: unknown[]) => mockToastError(...args),
|
||||||
|
|||||||
@ -386,7 +386,7 @@ describe('List', () => {
|
|||||||
|
|
||||||
describe('Edge Cases', () => {
|
describe('Edge Cases', () => {
|
||||||
it('should handle multiple renders without issues', () => {
|
it('should handle multiple renders without issues', () => {
|
||||||
const { rerender } = renderWithNuqs(<List />)
|
const { rerender, unmount } = renderWithNuqs(<List />)
|
||||||
expect(screen.getByText('app.types.all')).toBeInTheDocument()
|
expect(screen.getByText('app.types.all')).toBeInTheDocument()
|
||||||
|
|
||||||
unmount()
|
unmount()
|
||||||
|
|||||||
@ -31,6 +31,9 @@ const mocks = vi.hoisted(() => {
|
|||||||
registerNodeTransform: vi.fn(() => vi.fn()),
|
registerNodeTransform: vi.fn(() => vi.fn()),
|
||||||
dispatchCommand: vi.fn(),
|
dispatchCommand: vi.fn(),
|
||||||
getRootElement: vi.fn(() => rootElement),
|
getRootElement: vi.fn(() => rootElement),
|
||||||
|
getEditorState: vi.fn(() => ({
|
||||||
|
read: (fn: () => boolean) => fn(),
|
||||||
|
})),
|
||||||
parseEditorState: vi.fn(() => ({ state: 'parsed' })),
|
parseEditorState: vi.fn(() => ({ state: 'parsed' })),
|
||||||
setEditorState: vi.fn(),
|
setEditorState: vi.fn(),
|
||||||
focus: vi.fn(),
|
focus: vi.fn(),
|
||||||
|
|||||||
@ -85,6 +85,10 @@ vi.mock('@/config', () => ({
|
|||||||
get isAmplitudeEnabled() { return mockConfig.IS_CLOUD_EDITION && !!mockConfig.AMPLITUDE_API_KEY },
|
get isAmplitudeEnabled() { return mockConfig.IS_CLOUD_EDITION && !!mockConfig.AMPLITUDE_API_KEY },
|
||||||
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
||||||
get SUPPORT_EMAIL_ADDRESS() { return mockConfig.SUPPORT_EMAIL_ADDRESS },
|
get SUPPORT_EMAIL_ADDRESS() { return mockConfig.SUPPORT_EMAIL_ADDRESS },
|
||||||
|
API_PREFIX: 'http://localhost:5001/console/api',
|
||||||
|
APP_VERSION: '1.0.0',
|
||||||
|
IS_MARKETPLACE: false,
|
||||||
|
MARKETPLACE_API_PREFIX: 'http://localhost:5001/marketplace/api',
|
||||||
IS_DEV: false,
|
IS_DEV: false,
|
||||||
IS_CE_EDITION: false,
|
IS_CE_EDITION: false,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -59,6 +59,9 @@ vi.mock('@/app/components/base/features/hooks', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/workflow/store', () => ({
|
vi.mock('@/app/components/workflow/store', () => ({
|
||||||
|
useStore: <T,>(selector: (state: { appId: string }) => T) => selector({
|
||||||
|
appId: 'app-1',
|
||||||
|
}),
|
||||||
useWorkflowStore: () => ({
|
useWorkflowStore: () => ({
|
||||||
getState: () => ({
|
getState: () => ({
|
||||||
setConversationVariables: mockSetConversationVariables,
|
setConversationVariables: mockSetConversationVariables,
|
||||||
@ -67,6 +70,32 @@ vi.mock('@/app/components/workflow/store', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('reactflow', () => ({
|
||||||
|
useReactFlow: () => ({
|
||||||
|
getNodes: () => [],
|
||||||
|
setNodes: vi.fn(),
|
||||||
|
getEdges: () => [],
|
||||||
|
setEdges: vi.fn(),
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/app/components/workflow/collaboration/hooks/use-collaboration', () => ({
|
||||||
|
useCollaboration: () => ({
|
||||||
|
startCursorTracking: vi.fn(),
|
||||||
|
stopCursorTracking: vi.fn(),
|
||||||
|
onlineUsers: [],
|
||||||
|
cursors: {},
|
||||||
|
isConnected: false,
|
||||||
|
isEnabled: false,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/app/components/workflow/hooks/use-workflow-interactions', () => ({
|
||||||
|
useWorkflowUpdate: () => ({
|
||||||
|
handleUpdateWorkflowCanvas: vi.fn(),
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/workflow', () => ({
|
vi.mock('@/app/components/workflow', () => ({
|
||||||
WorkflowWithInnerContext: ({
|
WorkflowWithInnerContext: ({
|
||||||
nodes,
|
nodes,
|
||||||
@ -87,7 +116,7 @@ vi.mock('@/app/components/workflow', () => ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onWorkflowDataUpdate?.({
|
onClick={() => onWorkflowDataUpdate?.({
|
||||||
features: { file: { enabled: true } },
|
features: { file_upload: { enabled: true } },
|
||||||
conversation_variables: [{ id: 'conversation-1' }],
|
conversation_variables: [{ id: 'conversation-1' }],
|
||||||
environment_variables: [{ id: 'env-1' }],
|
environment_variables: [{ id: 'env-1' }],
|
||||||
})}
|
})}
|
||||||
@ -204,7 +233,9 @@ describe('WorkflowMain', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: /update-workflow-data/i }))
|
fireEvent.click(screen.getByRole('button', { name: /update-workflow-data/i }))
|
||||||
|
|
||||||
expect(mockSetFeatures).toHaveBeenCalledWith({ file: { enabled: true } })
|
expect(mockSetFeatures).toHaveBeenCalledWith(expect.objectContaining({
|
||||||
|
file: expect.objectContaining({ enabled: true }),
|
||||||
|
}))
|
||||||
expect(mockSetConversationVariables).toHaveBeenCalledWith([{ id: 'conversation-1' }])
|
expect(mockSetConversationVariables).toHaveBeenCalledWith([{ id: 'conversation-1' }])
|
||||||
expect(mockSetEnvironmentVariables).toHaveBeenCalledWith([{ id: 'env-1' }])
|
expect(mockSetEnvironmentVariables).toHaveBeenCalledWith([{ id: 'env-1' }])
|
||||||
})
|
})
|
||||||
|
|||||||
@ -8,8 +8,25 @@ import { InputVarType } from '../types'
|
|||||||
import { createStartNode } from './fixtures'
|
import { createStartNode } from './fixtures'
|
||||||
import { renderWorkflowFlowComponent } from './workflow-test-env'
|
import { renderWorkflowFlowComponent } from './workflow-test-env'
|
||||||
|
|
||||||
const mockHandleSyncWorkflowDraft = vi.fn()
|
|
||||||
const mockHandleAddVariable = vi.fn()
|
const mockHandleAddVariable = vi.fn()
|
||||||
|
const mockUpdateFeatures = vi.fn()
|
||||||
|
const mockFeaturesStore = {
|
||||||
|
getState: () => ({
|
||||||
|
features: {
|
||||||
|
opening: {
|
||||||
|
enabled: false,
|
||||||
|
opening_statement: '',
|
||||||
|
suggested_questions: [],
|
||||||
|
},
|
||||||
|
suggested: false,
|
||||||
|
text2speech: false,
|
||||||
|
speech2text: false,
|
||||||
|
citation: false,
|
||||||
|
moderation: false,
|
||||||
|
file: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
let mockIsChatMode = true
|
let mockIsChatMode = true
|
||||||
let mockNodesReadOnly = false
|
let mockNodesReadOnly = false
|
||||||
@ -22,9 +39,6 @@ vi.mock('../hooks', async () => {
|
|||||||
useNodesReadOnly: () => ({
|
useNodesReadOnly: () => ({
|
||||||
nodesReadOnly: mockNodesReadOnly,
|
nodesReadOnly: mockNodesReadOnly,
|
||||||
}),
|
}),
|
||||||
useNodesSyncDraft: () => ({
|
|
||||||
handleSyncWorkflowDraft: mockHandleSyncWorkflowDraft,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -34,6 +48,14 @@ vi.mock('../nodes/start/use-config', () => ({
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/service/workflow', () => ({
|
||||||
|
updateFeatures: (...args: unknown[]) => mockUpdateFeatures(...args),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/app/components/base/features/hooks', () => ({
|
||||||
|
useFeaturesStore: () => mockFeaturesStore,
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/base/features/new-feature-panel', () => ({
|
vi.mock('@/app/components/base/features/new-feature-panel', () => ({
|
||||||
default: ({
|
default: ({
|
||||||
show,
|
show,
|
||||||
@ -112,21 +134,29 @@ const DelayedFeatures = () => {
|
|||||||
return <Features />
|
return <Features />
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderFeatures = (options?: Omit<Parameters<typeof renderWorkflowFlowComponent>[1], 'nodes' | 'edges'>) =>
|
const renderFeatures = (options?: Omit<Parameters<typeof renderWorkflowFlowComponent>[1], 'nodes' | 'edges'>) => {
|
||||||
renderWorkflowFlowComponent(
|
const mergedInitialStoreState = {
|
||||||
|
appId: 'app-1',
|
||||||
|
...(options?.initialStoreState || {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderWorkflowFlowComponent(
|
||||||
<DelayedFeatures />,
|
<DelayedFeatures />,
|
||||||
{
|
{
|
||||||
nodes: [startNode],
|
nodes: [startNode],
|
||||||
edges: [],
|
edges: [],
|
||||||
...options,
|
...options,
|
||||||
|
initialStoreState: mergedInitialStoreState,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
describe('Features', () => {
|
describe('Features', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
mockIsChatMode = true
|
mockIsChatMode = true
|
||||||
mockNodesReadOnly = false
|
mockNodesReadOnly = false
|
||||||
|
mockUpdateFeatures.mockResolvedValue(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Rendering', () => {
|
describe('Rendering', () => {
|
||||||
@ -146,8 +176,10 @@ describe('Features', () => {
|
|||||||
|
|
||||||
await user.click(screen.getByRole('button', { name: 'open features' }))
|
await user.click(screen.getByRole('button', { name: 'open features' }))
|
||||||
|
|
||||||
expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledTimes(1)
|
await vi.waitFor(() => {
|
||||||
expect(store.getState().showFeaturesPanel).toBe(true)
|
expect(mockUpdateFeatures).toHaveBeenCalledTimes(1)
|
||||||
|
expect(store.getState().showFeaturesPanel).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should close the workflow feature panel and transform required prompt variables', async () => {
|
it('should close the workflow feature panel and transform required prompt variables', async () => {
|
||||||
|
|||||||
@ -110,6 +110,12 @@ vi.mock('@/next/dynamic', () => ({
|
|||||||
default: () => () => null,
|
default: () => () => null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('@/next/navigation', () => ({
|
||||||
|
useParams: () => ({
|
||||||
|
appId: 'app-1',
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/event-emitter', () => ({
|
vi.mock('@/context/event-emitter', () => ({
|
||||||
useEventEmitterContextContext: () => ({
|
useEventEmitterContextContext: () => ({
|
||||||
eventEmitter: {
|
eventEmitter: {
|
||||||
@ -254,6 +260,7 @@ vi.mock('../hooks', () => ({
|
|||||||
useWorkflowRefreshDraft: () => ({
|
useWorkflowRefreshDraft: () => ({
|
||||||
handleRefreshWorkflowDraft: vi.fn(),
|
handleRefreshWorkflowDraft: vi.fn(),
|
||||||
}),
|
}),
|
||||||
|
useLeaderRestoreListener: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../hooks/use-workflow-search', () => ({
|
vi.mock('../hooks/use-workflow-search', () => ({
|
||||||
|
|||||||
@ -9,6 +9,7 @@ const mockRestoreWorkflow = vi.fn()
|
|||||||
const mockInvalidAllLastRun = vi.fn()
|
const mockInvalidAllLastRun = vi.fn()
|
||||||
const mockHandleLoadBackupDraft = vi.fn()
|
const mockHandleLoadBackupDraft = vi.fn()
|
||||||
const mockHandleRefreshWorkflowDraft = vi.fn()
|
const mockHandleRefreshWorkflowDraft = vi.fn()
|
||||||
|
const mockRequestRestore = vi.fn()
|
||||||
|
|
||||||
vi.mock('@/hooks/use-theme', () => ({
|
vi.mock('@/hooks/use-theme', () => ({
|
||||||
default: () => ({
|
default: () => ({
|
||||||
@ -42,6 +43,9 @@ vi.mock('../../hooks', () => ({
|
|||||||
useWorkflowRefreshDraft: () => ({
|
useWorkflowRefreshDraft: () => ({
|
||||||
handleRefreshWorkflowDraft: mockHandleRefreshWorkflowDraft,
|
handleRefreshWorkflowDraft: mockHandleRefreshWorkflowDraft,
|
||||||
}),
|
}),
|
||||||
|
useLeaderRestore: () => ({
|
||||||
|
requestRestore: mockRequestRestore,
|
||||||
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const createVersion = (overrides: Partial<VersionHistory> = {}): VersionHistory => ({
|
const createVersion = (overrides: Partial<VersionHistory> = {}): VersionHistory => ({
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const mockHandleNodeSelect = vi.fn()
|
|||||||
const mockHandleRefreshWorkflowDraft = vi.fn()
|
const mockHandleRefreshWorkflowDraft = vi.fn()
|
||||||
const mockCloseAllInputFieldPanels = vi.fn()
|
const mockCloseAllInputFieldPanels = vi.fn()
|
||||||
const mockInvalidAllLastRun = vi.fn()
|
const mockInvalidAllLastRun = vi.fn()
|
||||||
const mockRestoreWorkflow = vi.fn()
|
const mockRequestRestore = vi.fn()
|
||||||
const mockNotify = vi.fn()
|
const mockNotify = vi.fn()
|
||||||
const mockRunAndHistory = vi.fn()
|
const mockRunAndHistory = vi.fn()
|
||||||
const mockViewHistory = vi.fn()
|
const mockViewHistory = vi.fn()
|
||||||
@ -33,6 +33,9 @@ vi.mock('../../hooks', () => ({
|
|||||||
handleBackupDraft: mockHandleBackupDraft,
|
handleBackupDraft: mockHandleBackupDraft,
|
||||||
handleLoadBackupDraft: mockHandleLoadBackupDraft,
|
handleLoadBackupDraft: mockHandleLoadBackupDraft,
|
||||||
}),
|
}),
|
||||||
|
useLeaderRestore: () => ({
|
||||||
|
requestRestore: mockRequestRestore,
|
||||||
|
}),
|
||||||
useNodesSyncDraft: () => ({
|
useNodesSyncDraft: () => ({
|
||||||
handleSyncWorkflowDraft: vi.fn(),
|
handleSyncWorkflowDraft: vi.fn(),
|
||||||
}),
|
}),
|
||||||
@ -55,9 +58,6 @@ vi.mock('@/hooks/use-theme', () => ({
|
|||||||
|
|
||||||
vi.mock('@/service/use-workflow', () => ({
|
vi.mock('@/service/use-workflow', () => ({
|
||||||
useInvalidAllLastRun: () => mockInvalidAllLastRun,
|
useInvalidAllLastRun: () => mockInvalidAllLastRun,
|
||||||
useRestoreWorkflow: () => ({
|
|
||||||
mutateAsync: mockRestoreWorkflow,
|
|
||||||
}),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||||
@ -77,6 +77,10 @@ vi.mock('../scroll-to-selected-node-button', () => ({
|
|||||||
default: () => <div>scroll-button</div>,
|
default: () => <div>scroll-button</div>,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
vi.mock('../online-users', () => ({
|
||||||
|
default: () => <div data-testid="online-users" />,
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('../env-button', () => ({
|
vi.mock('../env-button', () => ({
|
||||||
default: ({ disabled }: { disabled: boolean }) => <div data-testid="env-button">{`${disabled}`}</div>,
|
default: ({ disabled }: { disabled: boolean }) => <div data-testid="env-button">{`${disabled}`}</div>,
|
||||||
}))
|
}))
|
||||||
@ -162,7 +166,13 @@ describe('Header layout components', () => {
|
|||||||
mockNodesReadOnly = false
|
mockNodesReadOnly = false
|
||||||
mockTheme = 'light'
|
mockTheme = 'light'
|
||||||
mockUseNodes.mockReturnValue([])
|
mockUseNodes.mockReturnValue([])
|
||||||
mockRestoreWorkflow.mockResolvedValue(undefined)
|
mockRequestRestore.mockImplementation((_payload: unknown, callbacks?: {
|
||||||
|
onSuccess?: () => void
|
||||||
|
onSettled?: () => void
|
||||||
|
}) => {
|
||||||
|
callbacks?.onSuccess?.()
|
||||||
|
callbacks?.onSettled?.()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('HeaderInNormal', () => {
|
describe('HeaderInNormal', () => {
|
||||||
@ -267,7 +277,7 @@ describe('Header layout components', () => {
|
|||||||
fireEvent.click(screen.getByRole('button', { name: 'workflow.common.restore' }))
|
fireEvent.click(screen.getByRole('button', { name: 'workflow.common.restore' }))
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockRestoreWorkflow).toHaveBeenCalledWith('/apps/flow-1/workflows/version-1/restore')
|
expect(mockRequestRestore).toHaveBeenCalledTimes(1)
|
||||||
expect(store.getState().showWorkflowVersionHistoryPanel).toBe(false)
|
expect(store.getState().showWorkflowVersionHistoryPanel).toBe(false)
|
||||||
expect(store.getState().isRestoring).toBe(false)
|
expect(store.getState().isRestoring).toBe(false)
|
||||||
expect(store.getState().backupDraft).toBeUndefined()
|
expect(store.getState().backupDraft).toBeUndefined()
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
import { act, fireEvent, render, screen } from '@testing-library/react'
|
import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||||
import UndoRedo from '../undo-redo'
|
import UndoRedo from '../undo-redo'
|
||||||
|
|
||||||
type TemporalSnapshot = {
|
|
||||||
pastStates: unknown[]
|
|
||||||
futureStates: unknown[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const mockUnsubscribe = vi.fn()
|
const mockUnsubscribe = vi.fn()
|
||||||
const mockTemporalSubscribe = 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 latestTemporalListener: ((state: TemporalSnapshot) => void) | undefined
|
let latestUndoRedoListener: ((state: { canUndo: boolean, canRedo: boolean }) => 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', () => ({
|
||||||
@ -26,16 +23,22 @@ 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: mockTemporalSubscribe,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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" />,
|
||||||
}))
|
}))
|
||||||
@ -48,20 +51,19 @@ describe('UndoRedo', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
mockNodesReadOnly = false
|
mockNodesReadOnly = false
|
||||||
latestTemporalListener = undefined
|
latestUndoRedoListener = undefined
|
||||||
mockTemporalSubscribe.mockImplementation((listener: (state: TemporalSnapshot) => void) => {
|
mockCanUndo.mockReturnValue(false)
|
||||||
latestTemporalListener = listener
|
mockCanRedo.mockReturnValue(false)
|
||||||
return mockUnsubscribe
|
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(() => {
|
||||||
latestTemporalListener?.({
|
latestUndoRedoListener?.({
|
||||||
pastStates: [{}],
|
canUndo: true,
|
||||||
futureStates: [{}],
|
canRedo: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -93,9 +95,9 @@ describe('UndoRedo', () => {
|
|||||||
const redoButton = screen.getByRole('button', { name: 'workflow.common.redo' })
|
const redoButton = screen.getByRole('button', { name: 'workflow.common.redo' })
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
latestTemporalListener?.({
|
latestUndoRedoListener?.({
|
||||||
pastStates: [{}],
|
canUndo: true,
|
||||||
futureStates: [{}],
|
canRedo: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { ChatVarType } from '../type'
|
|||||||
|
|
||||||
type MockWorkflowStoreState = {
|
type MockWorkflowStoreState = {
|
||||||
setShowChatVariablePanel: (value: boolean) => void
|
setShowChatVariablePanel: (value: boolean) => void
|
||||||
|
appId: string
|
||||||
conversationVariables: ConversationVariable[]
|
conversationVariables: ConversationVariable[]
|
||||||
setConversationVariables: (value: ConversationVariable[]) => void
|
setConversationVariables: (value: ConversationVariable[]) => void
|
||||||
}
|
}
|
||||||
@ -17,10 +18,8 @@ type MockFlowStore = {
|
|||||||
|
|
||||||
const mockSetShowChatVariablePanel = vi.fn()
|
const mockSetShowChatVariablePanel = vi.fn()
|
||||||
const mockSetConversationVariables = vi.fn()
|
const mockSetConversationVariables = vi.fn()
|
||||||
const mockDoSyncWorkflowDraft = vi.fn((_sync: boolean, options?: { onSuccess?: () => void }) => {
|
|
||||||
options?.onSuccess?.()
|
|
||||||
})
|
|
||||||
const mockInvalidateConversationVarValues = vi.fn()
|
const mockInvalidateConversationVarValues = vi.fn()
|
||||||
|
const mockUpdateConversationVariables = vi.fn().mockResolvedValue(undefined)
|
||||||
const mockFindUsedVarNodes = vi.fn<(selector: string[], nodes: Node[]) => Node[]>()
|
const mockFindUsedVarNodes = vi.fn<(selector: string[], nodes: Node[]) => Node[]>()
|
||||||
const mockUpdateNodeVars = vi.fn<(node: Node, current: string[], next: string[]) => Node>()
|
const mockUpdateNodeVars = vi.fn<(node: Node, current: string[], next: string[]) => Node>()
|
||||||
|
|
||||||
@ -62,15 +61,14 @@ vi.mock('reactflow', () => ({
|
|||||||
vi.mock('@/app/components/workflow/store', () => ({
|
vi.mock('@/app/components/workflow/store', () => ({
|
||||||
useStore: <T,>(selector: (state: MockWorkflowStoreState) => T) => selector({
|
useStore: <T,>(selector: (state: MockWorkflowStoreState) => T) => selector({
|
||||||
setShowChatVariablePanel: mockSetShowChatVariablePanel,
|
setShowChatVariablePanel: mockSetShowChatVariablePanel,
|
||||||
|
appId: 'app-1',
|
||||||
conversationVariables: mockConversationVariables,
|
conversationVariables: mockConversationVariables,
|
||||||
setConversationVariables: mockSetConversationVariables,
|
setConversationVariables: mockSetConversationVariables,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('@/app/components/workflow/hooks/use-nodes-sync-draft', () => ({
|
vi.mock('@/service/workflow', () => ({
|
||||||
useNodesSyncDraft: () => ({
|
updateConversationVariables: (...args: unknown[]) => mockUpdateConversationVariables(...args),
|
||||||
doSyncWorkflowDraft: mockDoSyncWorkflowDraft,
|
|
||||||
}),
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
vi.mock('../../../hooks/use-inspect-vars-crud', () => ({
|
vi.mock('../../../hooks/use-inspect-vars-crud', () => ({
|
||||||
@ -175,6 +173,7 @@ describe('ChatVariablePanel', () => {
|
|||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
mockConversationVariables = [createConversationVariable()]
|
mockConversationVariables = [createConversationVariable()]
|
||||||
mockFlowNodes = [createNode('node-1'), createNode('node-2')]
|
mockFlowNodes = [createNode('node-1'), createNode('node-2')]
|
||||||
|
mockUpdateConversationVariables.mockResolvedValue(undefined)
|
||||||
mockFindUsedVarNodes.mockReturnValue([])
|
mockFindUsedVarNodes.mockReturnValue([])
|
||||||
mockUpdateNodeVars.mockImplementation((node: Node) => node)
|
mockUpdateNodeVars.mockImplementation((node: Node) => node)
|
||||||
})
|
})
|
||||||
@ -207,9 +206,15 @@ describe('ChatVariablePanel', () => {
|
|||||||
expect.objectContaining({ id: 'var-added', name: 'fresh_var' }),
|
expect.objectContaining({ id: 'var-added', name: 'fresh_var' }),
|
||||||
createConversationVariable(),
|
createConversationVariable(),
|
||||||
])
|
])
|
||||||
|
expect(mockUpdateConversationVariables).toHaveBeenCalledWith({
|
||||||
|
appId: 'app-1',
|
||||||
|
conversationVariables: [
|
||||||
|
expect.objectContaining({ id: 'var-added', name: 'fresh_var' }),
|
||||||
|
createConversationVariable(),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
expect(mockInvalidateConversationVarValues).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
expect(mockDoSyncWorkflowDraft).toHaveBeenCalledTimes(1)
|
|
||||||
expect(mockInvalidateConversationVarValues).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should rename existing variables and update affected node references', async () => {
|
it('should rename existing variables and update affected node references', async () => {
|
||||||
|
|||||||
@ -324,7 +324,7 @@ const SelectionContextmenu = () => {
|
|||||||
if (alignType === AlignType.DistributeHorizontal || alignType === AlignType.DistributeVertical) {
|
if (alignType === AlignType.DistributeHorizontal || alignType === AlignType.DistributeVertical) {
|
||||||
const distributedNodes = distributeNodes(nodesToAlign, nodes, alignType)
|
const distributedNodes = distributeNodes(nodesToAlign, nodes, alignType)
|
||||||
if (distributedNodes) {
|
if (distributedNodes) {
|
||||||
setNodes(distributeNodes)
|
setNodes(distributedNodes)
|
||||||
handleSelectionContextmenuCancel()
|
handleSelectionContextmenuCancel()
|
||||||
|
|
||||||
const { setHelpLineHorizontal, setHelpLineVertical } = workflowStore.getState()
|
const { setHelpLineHorizontal, setHelpLineVertical } = workflowStore.getState()
|
||||||
|
|||||||
@ -173,9 +173,9 @@ describe('createWorkflowStore', () => {
|
|||||||
expect(store.getState().controlMode).toBe('pointer')
|
expect(store.getState().controlMode).toBe('pointer')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should default controlMode to hand when localStorage has no value', () => {
|
it('should default controlMode to pointer when localStorage has no value', () => {
|
||||||
const store = createStore()
|
const store = createStore()
|
||||||
expect(store.getState().controlMode).toBe('hand')
|
expect(store.getState().controlMode).toBe('pointer')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should read panelWidth from localStorage', () => {
|
it('should read panelWidth from localStorage', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user