mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
refactor(web): replace query hooks with queryOptions factories (#32520)
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import type { FileAppearanceType } from '@/app/components/base/file-uploader/types'
|
||||
import type { AppAssetTreeView } from '@/types/app-asset'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -11,7 +12,7 @@ import SkillEditor from '@/app/components/workflow/skill/editor/skill-editor'
|
||||
import { useFileTypeInfo } from '@/app/components/workflow/skill/hooks/use-file-type-info'
|
||||
import { getFileIconType } from '@/app/components/workflow/skill/utils/file-utils'
|
||||
import ReadOnlyFilePreview from '@/app/components/workflow/skill/viewer/read-only-file-preview'
|
||||
import { useGetAppAssetFileContent, useGetAppAssetFileDownloadUrl } from '@/service/use-app-asset'
|
||||
import { appAssetFileContentOptions, appAssetFileDownloadUrlOptions } from '@/service/use-app-asset'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type FilePreviewPanelProps = {
|
||||
@ -36,7 +37,8 @@ const FilePreviewPanel = ({ resourceId, currentNode, className, style, onClose }
|
||||
data: fileContent,
|
||||
isLoading: isContentLoading,
|
||||
error: contentError,
|
||||
} = useGetAppAssetFileContent(appId, resourceId, {
|
||||
} = useQuery({
|
||||
...appAssetFileContentOptions(appId, resourceId),
|
||||
enabled: isMarkdownPreview,
|
||||
})
|
||||
|
||||
@ -44,7 +46,8 @@ const FilePreviewPanel = ({ resourceId, currentNode, className, style, onClose }
|
||||
data: downloadUrlData,
|
||||
isLoading: isDownloadLoading,
|
||||
error: downloadError,
|
||||
} = useGetAppAssetFileDownloadUrl(appId, resourceId, {
|
||||
} = useQuery({
|
||||
...appAssetFileDownloadUrlOptions(appId, resourceId),
|
||||
enabled: isReadOnlyPreview,
|
||||
})
|
||||
|
||||
|
||||
@ -8,12 +8,22 @@ import {
|
||||
useSkillAssetTreeData,
|
||||
} from './use-skill-asset-tree'
|
||||
|
||||
const { mockUseGetAppAssetTree } = vi.hoisted(() => ({
|
||||
mockUseGetAppAssetTree: vi.fn(),
|
||||
const { mockUseQuery, mockAppAssetTreeOptions } = vi.hoisted(() => ({
|
||||
mockUseQuery: vi.fn(),
|
||||
mockAppAssetTreeOptions: vi.fn().mockReturnValue({
|
||||
queryKey: ['test', 'tree'],
|
||||
queryFn: vi.fn(),
|
||||
enabled: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@tanstack/react-query', async importOriginal => ({
|
||||
...await importOriginal<typeof import('@tanstack/react-query')>(),
|
||||
useQuery: (options: unknown) => mockUseQuery(options),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-app-asset', () => ({
|
||||
useGetAppAssetTree: (...args: unknown[]) => mockUseGetAppAssetTree(...args),
|
||||
appAssetTreeOptions: (...args: unknown[]) => mockAppAssetTreeOptions(...args),
|
||||
}))
|
||||
|
||||
const createTreeNode = (
|
||||
@ -34,22 +44,21 @@ describe('useSkillAssetTree', () => {
|
||||
useAppStore.setState({
|
||||
appDetail: { id: 'app-1' } as App & Partial<AppSSO>,
|
||||
})
|
||||
mockUseGetAppAssetTree.mockReturnValue({
|
||||
mockUseQuery.mockReturnValue({
|
||||
data: null,
|
||||
isPending: false,
|
||||
error: null,
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: should pass app id from app store to the data query hook.
|
||||
describe('useSkillAssetTreeData', () => {
|
||||
it('should request tree data with current app id', () => {
|
||||
const expectedResult = { data: { children: [] }, isPending: false }
|
||||
mockUseGetAppAssetTree.mockReturnValue(expectedResult)
|
||||
mockUseQuery.mockReturnValue(expectedResult)
|
||||
|
||||
const { result } = renderHook(() => useSkillAssetTreeData())
|
||||
|
||||
expect(mockUseGetAppAssetTree).toHaveBeenCalledWith('app-1')
|
||||
expect(mockAppAssetTreeOptions).toHaveBeenCalledWith('app-1')
|
||||
expect(result.current).toBe(expectedResult)
|
||||
})
|
||||
|
||||
@ -58,16 +67,15 @@ describe('useSkillAssetTree', () => {
|
||||
|
||||
renderHook(() => useSkillAssetTreeData())
|
||||
|
||||
expect(mockUseGetAppAssetTree).toHaveBeenCalledWith('')
|
||||
expect(mockAppAssetTreeOptions).toHaveBeenCalledWith('')
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: should expose a select transform that builds node lookup maps.
|
||||
describe('useSkillAssetNodeMap', () => {
|
||||
it('should build a map including nested nodes', () => {
|
||||
renderHook(() => useSkillAssetNodeMap())
|
||||
|
||||
const options = mockUseGetAppAssetTree.mock.calls[0][1] as {
|
||||
const options = mockUseQuery.mock.calls[0][0] as {
|
||||
select: (data: AppAssetTreeResponse) => Map<string, AppAssetTreeView>
|
||||
}
|
||||
|
||||
@ -97,7 +105,7 @@ describe('useSkillAssetTree', () => {
|
||||
it('should return an empty map when tree response has no children', () => {
|
||||
renderHook(() => useSkillAssetNodeMap())
|
||||
|
||||
const options = mockUseGetAppAssetTree.mock.calls[0][1] as {
|
||||
const options = mockUseQuery.mock.calls[0][0] as {
|
||||
select: (data: AppAssetTreeResponse) => Map<string, AppAssetTreeView>
|
||||
}
|
||||
|
||||
@ -107,12 +115,11 @@ describe('useSkillAssetTree', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// Scenario: should expose root-level existing skill folder names.
|
||||
describe('useExistingSkillNames', () => {
|
||||
it('should collect only root folder names', () => {
|
||||
renderHook(() => useExistingSkillNames())
|
||||
|
||||
const options = mockUseGetAppAssetTree.mock.calls[0][1] as {
|
||||
const options = mockUseQuery.mock.calls[0][0] as {
|
||||
select: (data: AppAssetTreeResponse) => Set<string>
|
||||
}
|
||||
|
||||
@ -153,7 +160,7 @@ describe('useSkillAssetTree', () => {
|
||||
it('should return an empty set when tree response has no children', () => {
|
||||
renderHook(() => useExistingSkillNames())
|
||||
|
||||
const options = mockUseGetAppAssetTree.mock.calls[0][1] as {
|
||||
const options = mockUseQuery.mock.calls[0][0] as {
|
||||
select: (data: AppAssetTreeResponse) => Set<string>
|
||||
}
|
||||
|
||||
|
||||
@ -1,33 +1,23 @@
|
||||
import type { AppAssetTreeResponse, AppAssetTreeView } from '@/types/app-asset'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { useGetAppAssetTree } from '@/service/use-app-asset'
|
||||
import { appAssetTreeOptions } from '@/service/use-app-asset'
|
||||
import { buildNodeMap } from '../../../utils/tree-utils'
|
||||
|
||||
/**
|
||||
* Get the current app ID from the app store.
|
||||
* Used internally by skill asset tree hooks.
|
||||
*/
|
||||
function useSkillAppId(): string {
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
return appDetail?.id || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the asset tree data for the current skill app.
|
||||
* Returns the raw tree data along with loading and error states.
|
||||
*/
|
||||
export function useSkillAssetTreeData() {
|
||||
const appId = useSkillAppId()
|
||||
return useGetAppAssetTree(appId)
|
||||
return useQuery(appAssetTreeOptions(appId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the node map (id -> node) for the current skill app.
|
||||
* Uses TanStack Query's select option to compute and cache the map.
|
||||
*/
|
||||
export function useSkillAssetNodeMap() {
|
||||
const appId = useSkillAppId()
|
||||
return useGetAppAssetTree(appId, {
|
||||
return useQuery({
|
||||
...appAssetTreeOptions(appId),
|
||||
select: (data: AppAssetTreeResponse): Map<string, AppAssetTreeView> => {
|
||||
if (!data?.children)
|
||||
return new Map()
|
||||
@ -36,13 +26,10 @@ export function useSkillAssetNodeMap() {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the set of root-level folder names in the skill asset tree.
|
||||
* Useful for checking whether a skill template has already been added.
|
||||
*/
|
||||
export function useExistingSkillNames() {
|
||||
const appId = useSkillAppId()
|
||||
return useGetAppAssetTree(appId, {
|
||||
return useQuery({
|
||||
...appAssetTreeOptions(appId),
|
||||
select: (data: AppAssetTreeResponse): Set<string> => {
|
||||
if (!data?.children)
|
||||
return new Set()
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
import { renderHook } from '@testing-library/react'
|
||||
import { useSkillFileData } from './use-skill-file-data'
|
||||
|
||||
const {
|
||||
mockUseGetAppAssetFileContent,
|
||||
mockUseGetAppAssetFileDownloadUrl,
|
||||
} = vi.hoisted(() => ({
|
||||
mockUseGetAppAssetFileContent: vi.fn(),
|
||||
mockUseGetAppAssetFileDownloadUrl: vi.fn(),
|
||||
const { mockUseQuery, mockContentOptions, mockDownloadUrlOptions } = vi.hoisted(() => ({
|
||||
mockUseQuery: vi.fn(),
|
||||
mockContentOptions: vi.fn().mockReturnValue({
|
||||
queryKey: ['test', 'content'],
|
||||
queryFn: vi.fn(),
|
||||
}),
|
||||
mockDownloadUrlOptions: vi.fn().mockReturnValue({
|
||||
queryKey: ['test', 'downloadUrl'],
|
||||
queryFn: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@tanstack/react-query', async importOriginal => ({
|
||||
...await importOriginal<typeof import('@tanstack/react-query')>(),
|
||||
useQuery: (options: unknown) => mockUseQuery(options),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-app-asset', () => ({
|
||||
useGetAppAssetFileContent: (...args: unknown[]) => mockUseGetAppAssetFileContent(...args),
|
||||
useGetAppAssetFileDownloadUrl: (...args: unknown[]) => mockUseGetAppAssetFileDownloadUrl(...args),
|
||||
appAssetFileContentOptions: (...args: unknown[]) => mockContentOptions(...args),
|
||||
appAssetFileDownloadUrlOptions: (...args: unknown[]) => mockDownloadUrlOptions(...args),
|
||||
}))
|
||||
|
||||
describe('useSkillFileData', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockUseGetAppAssetFileContent.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
})
|
||||
mockUseGetAppAssetFileDownloadUrl.mockReturnValue({
|
||||
mockUseQuery.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
@ -33,51 +37,62 @@ describe('useSkillFileData', () => {
|
||||
it('should disable both queries when mode is none', () => {
|
||||
const { result } = renderHook(() => useSkillFileData('app-1', 'node-1', 'none'))
|
||||
|
||||
expect(mockUseGetAppAssetFileContent).toHaveBeenCalledWith('app-1', 'node-1', { enabled: false })
|
||||
expect(mockUseGetAppAssetFileDownloadUrl).toHaveBeenCalledWith('app-1', 'node-1', { enabled: false })
|
||||
expect(mockContentOptions).toHaveBeenCalledWith('app-1', 'node-1')
|
||||
expect(mockDownloadUrlOptions).toHaveBeenCalledWith('app-1', 'node-1')
|
||||
expect(mockUseQuery.mock.calls[0][0].enabled).toBe(false)
|
||||
expect(mockUseQuery.mock.calls[1][0].enabled).toBe(false)
|
||||
expect(result.current.isLoading).toBe(false)
|
||||
expect(result.current.error).toBeNull()
|
||||
})
|
||||
|
||||
it('should fetch content data when mode is content', () => {
|
||||
const contentError = new Error('content-error')
|
||||
mockUseGetAppAssetFileContent.mockReturnValue({
|
||||
data: { content: 'hello' },
|
||||
isLoading: true,
|
||||
error: contentError,
|
||||
})
|
||||
mockUseGetAppAssetFileDownloadUrl.mockReturnValue({
|
||||
data: { download_url: 'https://example.com/file' },
|
||||
isLoading: true,
|
||||
error: new Error('download-error'),
|
||||
})
|
||||
mockUseQuery
|
||||
.mockReturnValueOnce({
|
||||
data: { content: 'hello' },
|
||||
isLoading: true,
|
||||
error: contentError,
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
data: { download_url: 'https://example.com/file' },
|
||||
isLoading: true,
|
||||
error: new Error('download-error'),
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useSkillFileData('app-1', 'node-1', 'content'))
|
||||
|
||||
expect(mockUseGetAppAssetFileContent).toHaveBeenCalledWith('app-1', 'node-1', { enabled: true })
|
||||
expect(mockUseGetAppAssetFileDownloadUrl).toHaveBeenCalledWith('app-1', 'node-1', { enabled: false })
|
||||
expect(mockUseQuery.mock.calls[0][0].enabled).toBe(true)
|
||||
expect(mockUseQuery.mock.calls[1][0].enabled).toBe(false)
|
||||
expect(result.current.fileContent).toEqual({ content: 'hello' })
|
||||
expect(result.current.isLoading).toBe(true)
|
||||
expect(result.current.error).toBe(contentError)
|
||||
})
|
||||
|
||||
it('should disable content query when nodeId is null even if mode is content', () => {
|
||||
const { result } = renderHook(() => useSkillFileData('app-1', null, 'content'))
|
||||
|
||||
expect(mockUseQuery.mock.calls[0][0].enabled).toBe(false)
|
||||
expect(result.current.isLoading).toBe(false)
|
||||
})
|
||||
|
||||
it('should fetch download URL data when mode is download', () => {
|
||||
const downloadError = new Error('download-error')
|
||||
mockUseGetAppAssetFileContent.mockReturnValue({
|
||||
data: { content: 'hello' },
|
||||
isLoading: true,
|
||||
error: new Error('content-error'),
|
||||
})
|
||||
mockUseGetAppAssetFileDownloadUrl.mockReturnValue({
|
||||
data: { download_url: 'https://example.com/file' },
|
||||
isLoading: true,
|
||||
error: downloadError,
|
||||
})
|
||||
mockUseQuery
|
||||
.mockReturnValueOnce({
|
||||
data: { content: 'hello' },
|
||||
isLoading: true,
|
||||
error: new Error('content-error'),
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
data: { download_url: 'https://example.com/file' },
|
||||
isLoading: true,
|
||||
error: downloadError,
|
||||
})
|
||||
|
||||
const { result } = renderHook(() => useSkillFileData('app-1', 'node-1', 'download'))
|
||||
|
||||
expect(mockUseGetAppAssetFileContent).toHaveBeenCalledWith('app-1', 'node-1', { enabled: false })
|
||||
expect(mockUseGetAppAssetFileDownloadUrl).toHaveBeenCalledWith('app-1', 'node-1', { enabled: true })
|
||||
expect(mockUseQuery.mock.calls[0][0].enabled).toBe(false)
|
||||
expect(mockUseQuery.mock.calls[1][0].enabled).toBe(true)
|
||||
expect(result.current.downloadUrlData).toEqual({ download_url: 'https://example.com/file' })
|
||||
expect(result.current.isLoading).toBe(true)
|
||||
expect(result.current.error).toBe(downloadError)
|
||||
|
||||
@ -1,40 +1,29 @@
|
||||
import { useGetAppAssetFileContent, useGetAppAssetFileDownloadUrl } from '@/service/use-app-asset'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { appAssetFileContentOptions, appAssetFileDownloadUrlOptions } from '@/service/use-app-asset'
|
||||
|
||||
export type SkillFileDataMode = 'none' | 'content' | 'download'
|
||||
|
||||
export type SkillFileDataResult = {
|
||||
fileContent: ReturnType<typeof useGetAppAssetFileContent>['data']
|
||||
downloadUrlData: ReturnType<typeof useGetAppAssetFileDownloadUrl>['data']
|
||||
isLoading: boolean
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to fetch file data for skill documents.
|
||||
* Uses explicit mode to control data fetching:
|
||||
* - 'content': fetch editable file content
|
||||
* - 'download': fetch non-editable file download URL
|
||||
* - 'none': skip file-related requests while node metadata is unresolved
|
||||
*/
|
||||
export function useSkillFileData(
|
||||
appId: string,
|
||||
nodeId: string | null | undefined,
|
||||
mode: SkillFileDataMode,
|
||||
): SkillFileDataResult {
|
||||
) {
|
||||
const {
|
||||
data: fileContent,
|
||||
isLoading: isContentLoading,
|
||||
error: contentError,
|
||||
} = useGetAppAssetFileContent(appId, nodeId || '', {
|
||||
enabled: mode === 'content',
|
||||
} = useQuery({
|
||||
...appAssetFileContentOptions(appId, nodeId || ''),
|
||||
enabled: mode === 'content' && !!appId && !!nodeId,
|
||||
})
|
||||
|
||||
const {
|
||||
data: downloadUrlData,
|
||||
isLoading: isDownloadUrlLoading,
|
||||
error: downloadUrlError,
|
||||
} = useGetAppAssetFileDownloadUrl(appId, nodeId || '', {
|
||||
enabled: mode === 'download' && !!nodeId,
|
||||
} = useQuery({
|
||||
...appAssetFileDownloadUrlOptions(appId, nodeId || ''),
|
||||
enabled: mode === 'download' && !!appId && !!nodeId,
|
||||
})
|
||||
|
||||
const isLoading = mode === 'content'
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import ArtifactContentPanel from './artifact-content-panel'
|
||||
|
||||
@ -12,39 +11,32 @@ const mocks = vi.hoisted(() => ({
|
||||
activeTabId: 'artifact:/assets/report.bin',
|
||||
appId: 'app-1',
|
||||
} as WorkflowStoreState,
|
||||
useSandboxFileDownloadUrl: vi.fn(),
|
||||
mockUseQuery: vi.fn(),
|
||||
mockDownloadUrlOptions: vi.fn().mockReturnValue({
|
||||
queryKey: ['sandboxFile', 'downloadFile'],
|
||||
queryFn: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/workflow/store', () => ({
|
||||
useStore: (selector: (state: WorkflowStoreState) => unknown) => selector(mocks.workflowState),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-sandbox-file', () => ({
|
||||
useSandboxFileDownloadUrl: (...args: unknown[]) => mocks.useSandboxFileDownloadUrl(...args),
|
||||
vi.mock('@tanstack/react-query', async importOriginal => ({
|
||||
...await importOriginal<typeof import('@tanstack/react-query')>(),
|
||||
useQuery: (options: unknown) => mocks.mockUseQuery(options),
|
||||
}))
|
||||
|
||||
const renderPanel = () => {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ArtifactContentPanel />
|
||||
</QueryClientProvider>,
|
||||
)
|
||||
}
|
||||
vi.mock('@/service/use-sandbox-file', () => ({
|
||||
sandboxFileDownloadUrlOptions: (...args: unknown[]) => mocks.mockDownloadUrlOptions(...args),
|
||||
}))
|
||||
|
||||
describe('ArtifactContentPanel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.workflowState.activeTabId = 'artifact:/assets/report.bin'
|
||||
mocks.workflowState.appId = 'app-1'
|
||||
mocks.useSandboxFileDownloadUrl.mockReturnValue({
|
||||
mocks.mockUseQuery.mockReturnValue({
|
||||
data: { download_url: 'https://example.com/report.bin' },
|
||||
isLoading: false,
|
||||
})
|
||||
@ -52,38 +44,30 @@ describe('ArtifactContentPanel', () => {
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should show loading indicator when download ticket is loading', () => {
|
||||
// Arrange
|
||||
mocks.useSandboxFileDownloadUrl.mockReturnValue({
|
||||
mocks.mockUseQuery.mockReturnValue({
|
||||
data: undefined,
|
||||
isLoading: true,
|
||||
})
|
||||
|
||||
// Act
|
||||
renderPanel()
|
||||
render(<ArtifactContentPanel />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show load error message when download url is unavailable', () => {
|
||||
// Arrange
|
||||
mocks.useSandboxFileDownloadUrl.mockReturnValue({
|
||||
mocks.mockUseQuery.mockReturnValue({
|
||||
data: { download_url: '' },
|
||||
isLoading: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
renderPanel()
|
||||
render(<ArtifactContentPanel />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByText('workflow.skillSidebar.loadError')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render preview panel when ticket contains download url', () => {
|
||||
// Act
|
||||
renderPanel()
|
||||
render(<ArtifactContentPanel />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByText('report.bin')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /common\.operation\.download/i })).toBeInTheDocument()
|
||||
})
|
||||
@ -91,25 +75,19 @@ describe('ArtifactContentPanel', () => {
|
||||
|
||||
describe('Data flow', () => {
|
||||
it('should request ticket using app id and artifact path when tab is selected', () => {
|
||||
// Act
|
||||
renderPanel()
|
||||
render(<ArtifactContentPanel />)
|
||||
|
||||
// Assert
|
||||
expect(mocks.useSandboxFileDownloadUrl).toHaveBeenCalledTimes(1)
|
||||
expect(mocks.useSandboxFileDownloadUrl).toHaveBeenCalledWith('app-1', '/assets/report.bin')
|
||||
expect(mocks.mockDownloadUrlOptions).toHaveBeenCalledWith('app-1', '/assets/report.bin')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should request ticket with undefined path when active tab id is null', () => {
|
||||
// Arrange
|
||||
it('should pass undefined path to options factory when active tab id is null', () => {
|
||||
mocks.workflowState.activeTabId = null
|
||||
|
||||
// Act
|
||||
renderPanel()
|
||||
render(<ArtifactContentPanel />)
|
||||
|
||||
// Assert
|
||||
expect(mocks.useSandboxFileDownloadUrl).toHaveBeenCalledWith('app-1', undefined)
|
||||
expect(mocks.mockDownloadUrlOptions).toHaveBeenCalledWith('app-1', undefined)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import { useSandboxFileDownloadUrl } from '@/service/use-sandbox-file'
|
||||
import { sandboxFileDownloadUrlOptions } from '@/service/use-sandbox-file'
|
||||
import { getArtifactPath } from '../../constants'
|
||||
import { getFileExtension } from '../../utils/file-utils'
|
||||
import ReadOnlyFilePreview from '../../viewer/read-only-file-preview'
|
||||
@ -18,7 +19,7 @@ const ArtifactContentPanel = () => {
|
||||
const fileName = path?.split('/').pop() ?? ''
|
||||
const extension = getFileExtension(fileName)
|
||||
|
||||
const { data: ticket, isLoading } = useSandboxFileDownloadUrl(appId, path)
|
||||
const { data: ticket, isLoading } = useQuery(sandboxFileDownloadUrlOptions(appId, path))
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user