feat(web): add variable inspect for snippet

This commit is contained in:
JzoNg
2026-03-29 20:23:24 +08:00
parent a8cdf6964c
commit 2f88da4a6d
4 changed files with 230 additions and 4 deletions

View File

@@ -14,6 +14,24 @@ const mockSetPublishMenuOpen = vi.fn()
const mockToggleInputPanel = vi.fn()
const mockTogglePublishMenu = vi.fn()
const mockPublishSnippetMutateAsync = vi.fn()
const mockFetchInspectVars = vi.fn()
const mockInspectVarsCrud = {
hasNodeInspectVars: vi.fn(),
hasSetInspectVar: vi.fn(),
fetchInspectVarValue: vi.fn(),
editInspectVarValue: vi.fn(),
renameInspectVarName: vi.fn(),
appendNodeInspectVars: vi.fn(),
deleteInspectVar: vi.fn(),
deleteNodeInspectorVars: vi.fn(),
deleteAllInspectorVars: vi.fn(),
isInspectVarEdited: vi.fn(),
resetToLastRunVar: vi.fn(),
invalidateSysVarValues: vi.fn(),
resetConversationVar: vi.fn(),
invalidateConversationVarValues: vi.fn(),
}
let capturedHooksStore: Record<string, unknown> | undefined
vi.mock('@/hooks/use-breakpoints', () => ({
default: () => 'desktop',
@@ -69,6 +87,16 @@ vi.mock('@/app/components/snippets/hooks/use-configs-map', () => ({
}),
}))
vi.mock('@/app/components/workflow/hooks/use-fetch-workflow-inspect-vars', () => ({
useSetWorkflowVarsWithValue: () => ({
fetchInspectVars: mockFetchInspectVars,
}),
}))
vi.mock('@/app/components/snippets/hooks/use-inspect-vars-crud', () => ({
useInspectVarsCrud: () => mockInspectVarsCrud,
}))
vi.mock('@/app/components/snippets/hooks/use-nodes-sync-draft', () => ({
useNodesSyncDraft: () => ({
doSyncWorkflowDraft: vi.fn(),
@@ -111,9 +139,19 @@ vi.mock('@/app/components/evaluation', () => ({
}))
vi.mock('@/app/components/workflow', () => ({
WorkflowWithInnerContext: ({ children }: { children: React.ReactNode }) => (
<div data-testid="workflow-inner-context">{children}</div>
),
WorkflowWithInnerContext: ({
children,
hooksStore,
}: {
children: React.ReactNode
hooksStore?: Record<string, unknown>
}) => {
capturedHooksStore = hooksStore
return (
<div data-testid="workflow-inner-context">{children}</div>
)
},
}))
vi.mock('@/app/components/snippets/components/snippet-children', () => ({
@@ -193,6 +231,7 @@ describe('SnippetMain', () => {
vi.clearAllMocks()
mockSyncInputFieldsDraft.mockResolvedValue(undefined)
mockPublishSnippetMutateAsync.mockResolvedValue(undefined)
capturedHooksStore = undefined
})
describe('Input Fields Sync', () => {
@@ -243,4 +282,26 @@ describe('SnippetMain', () => {
expect(mockSetPublishMenuOpen).toHaveBeenCalledWith(false)
})
})
describe('Inspect Vars', () => {
it('should pass inspect vars handlers to WorkflowWithInnerContext', () => {
renderSnippetMain()
expect(capturedHooksStore?.fetchInspectVars).toBe(mockFetchInspectVars)
expect(capturedHooksStore?.hasNodeInspectVars).toBe(mockInspectVarsCrud.hasNodeInspectVars)
expect(capturedHooksStore?.hasSetInspectVar).toBe(mockInspectVarsCrud.hasSetInspectVar)
expect(capturedHooksStore?.fetchInspectVarValue).toBe(mockInspectVarsCrud.fetchInspectVarValue)
expect(capturedHooksStore?.editInspectVarValue).toBe(mockInspectVarsCrud.editInspectVarValue)
expect(capturedHooksStore?.renameInspectVarName).toBe(mockInspectVarsCrud.renameInspectVarName)
expect(capturedHooksStore?.appendNodeInspectVars).toBe(mockInspectVarsCrud.appendNodeInspectVars)
expect(capturedHooksStore?.deleteInspectVar).toBe(mockInspectVarsCrud.deleteInspectVar)
expect(capturedHooksStore?.deleteNodeInspectorVars).toBe(mockInspectVarsCrud.deleteNodeInspectorVars)
expect(capturedHooksStore?.deleteAllInspectorVars).toBe(mockInspectVarsCrud.deleteAllInspectorVars)
expect(capturedHooksStore?.isInspectVarEdited).toBe(mockInspectVarsCrud.isInspectVarEdited)
expect(capturedHooksStore?.resetToLastRunVar).toBe(mockInspectVarsCrud.resetToLastRunVar)
expect(capturedHooksStore?.invalidateSysVarValues).toBe(mockInspectVarsCrud.invalidateSysVarValues)
expect(capturedHooksStore?.resetConversationVar).toBe(mockInspectVarsCrud.resetConversationVar)
expect(capturedHooksStore?.invalidateConversationVarValues).toBe(mockInspectVarsCrud.invalidateConversationVarValues)
})
})
})

View File

@@ -21,9 +21,11 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import Evaluation from '@/app/components/evaluation'
import { WorkflowWithInnerContext } from '@/app/components/workflow'
import { useAvailableNodesMetaData } from '@/app/components/workflow-app/hooks'
import { useSetWorkflowVarsWithValue } from '@/app/components/workflow/hooks/use-fetch-workflow-inspect-vars'
import { BlockEnum } from '@/app/components/workflow/types'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useConfigsMap } from '../hooks/use-configs-map'
import { useInspectVarsCrud } from '../hooks/use-inspect-vars-crud'
import { useNodesSyncDraft } from '../hooks/use-nodes-sync-draft'
import { useSnippetRefreshDraft } from '../hooks/use-snippet-refresh-draft'
import { useSnippetDetailStore } from '../store'
@@ -65,6 +67,25 @@ const SnippetMain = ({
} = useNodesSyncDraft(snippetId)
const { handleRefreshWorkflowDraft } = useSnippetRefreshDraft(snippetId)
const configsMap = useConfigsMap(snippetId)
const { fetchInspectVars } = useSetWorkflowVarsWithValue({
...configsMap,
})
const {
hasNodeInspectVars,
hasSetInspectVar,
fetchInspectVarValue,
editInspectVarValue,
renameInspectVarName,
appendNodeInspectVars,
deleteInspectVar,
deleteNodeInspectorVars,
deleteAllInspectorVars,
isInspectVarEdited,
resetToLastRunVar,
invalidateSysVarValues,
resetConversationVar,
invalidateConversationVarValues,
} = useInspectVarsCrud(snippetId)
const workflowAvailableNodesMetaData = useAvailableNodesMetaData()
const availableNodesMetaData = useMemo(() => {
const nodes = workflowAvailableNodesMetaData.nodes.filter(node =>
@@ -128,9 +149,45 @@ const SnippetMain = ({
syncWorkflowDraftWhenPageClose,
handleRefreshWorkflowDraft,
availableNodesMetaData,
fetchInspectVars,
hasNodeInspectVars,
hasSetInspectVar,
fetchInspectVarValue,
editInspectVarValue,
renameInspectVarName,
appendNodeInspectVars,
deleteInspectVar,
deleteNodeInspectorVars,
deleteAllInspectorVars,
isInspectVarEdited,
resetToLastRunVar,
invalidateSysVarValues,
resetConversationVar,
invalidateConversationVarValues,
configsMap,
}
}, [availableNodesMetaData, configsMap, doSyncWorkflowDraft, handleRefreshWorkflowDraft, syncWorkflowDraftWhenPageClose])
}, [
appendNodeInspectVars,
availableNodesMetaData,
configsMap,
deleteAllInspectorVars,
deleteInspectVar,
deleteNodeInspectorVars,
doSyncWorkflowDraft,
editInspectVarValue,
fetchInspectVarValue,
fetchInspectVars,
handleRefreshWorkflowDraft,
hasNodeInspectVars,
hasSetInspectVar,
invalidateConversationVarValues,
invalidateSysVarValues,
isInspectVarEdited,
renameInspectVarName,
resetConversationVar,
resetToLastRunVar,
syncWorkflowDraftWhenPageClose,
])
return (
<div className="relative flex h-full overflow-hidden bg-background-body">

View File

@@ -0,0 +1,95 @@
import { renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useInspectVarsCrud } from '../use-inspect-vars-crud'
const mockApis = {
hasNodeInspectVars: vi.fn(),
hasSetInspectVar: vi.fn(),
fetchInspectVarValue: vi.fn(),
editInspectVarValue: vi.fn(),
renameInspectVarName: vi.fn(),
appendNodeInspectVars: vi.fn(),
deleteInspectVar: vi.fn(),
deleteNodeInspectorVars: vi.fn(),
deleteAllInspectorVars: vi.fn(),
isInspectVarEdited: vi.fn(),
resetToLastRunVar: vi.fn(),
invalidateSysVarValues: vi.fn(),
resetConversationVar: vi.fn(),
invalidateConversationVarValues: vi.fn(),
}
const mockUseInspectVarsCrudCommon = vi.fn(() => mockApis)
vi.mock('../../../workflow/hooks/use-inspect-vars-crud-common', () => ({
useInspectVarsCrudCommon: (...args: Parameters<typeof mockUseInspectVarsCrudCommon>) => mockUseInspectVarsCrudCommon(...args),
}))
const mockConfigsMap = {
flowId: 'snippet-123',
flowType: 'snippet',
fileSettings: {
image: { enabled: false },
fileUploadConfig: {},
},
}
vi.mock('../use-configs-map', () => ({
useConfigsMap: () => mockConfigsMap,
}))
describe('useInspectVarsCrud', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('Composition', () => {
it('should pass configsMap to useInspectVarsCrudCommon', () => {
renderHook(() => useInspectVarsCrud('snippet-123'))
expect(mockUseInspectVarsCrudCommon).toHaveBeenCalledWith(
expect.objectContaining({
flowId: 'snippet-123',
flowType: 'snippet',
}),
)
})
it('should return all APIs from useInspectVarsCrudCommon', () => {
const { result } = renderHook(() => useInspectVarsCrud('snippet-123'))
expect(result.current.hasNodeInspectVars).toBe(mockApis.hasNodeInspectVars)
expect(result.current.fetchInspectVarValue).toBe(mockApis.fetchInspectVarValue)
expect(result.current.editInspectVarValue).toBe(mockApis.editInspectVarValue)
expect(result.current.deleteInspectVar).toBe(mockApis.deleteInspectVar)
expect(result.current.deleteAllInspectorVars).toBe(mockApis.deleteAllInspectorVars)
expect(result.current.resetToLastRunVar).toBe(mockApis.resetToLastRunVar)
expect(result.current.resetConversationVar).toBe(mockApis.resetConversationVar)
})
})
describe('API Surface', () => {
it('should expose all expected API methods', () => {
const { result } = renderHook(() => useInspectVarsCrud('snippet-123'))
const expectedKeys = [
'hasNodeInspectVars',
'hasSetInspectVar',
'fetchInspectVarValue',
'editInspectVarValue',
'renameInspectVarName',
'appendNodeInspectVars',
'deleteInspectVar',
'deleteNodeInspectorVars',
'deleteAllInspectorVars',
'isInspectVarEdited',
'resetToLastRunVar',
'invalidateSysVarValues',
'resetConversationVar',
'invalidateConversationVarValues',
]
for (const key of expectedKeys)
expect(result.current).toHaveProperty(key)
})
})
})

View File

@@ -0,0 +1,13 @@
import { useInspectVarsCrudCommon } from '../../workflow/hooks/use-inspect-vars-crud-common'
import { useConfigsMap } from './use-configs-map'
export const useInspectVarsCrud = (snippetId: string) => {
const configsMap = useConfigsMap(snippetId)
const apis = useInspectVarsCrudCommon({
...configsMap,
})
return {
...apis,
}
}