mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 00:48:04 +08:00
fix: resolve import migrations and test failures after segment 3 merge
- Migrate core.model_runtime -> dify_graph.model_runtime across 20+ files - Migrate core.workflow.file -> dify_graph.file across 15+ files - Migrate core.workflow.enums -> dify_graph.enums in service files - Fix SandboxContext phantom import in dify_graph/context/__init__.py - Fix core.app.workflow.node_factory -> core.workflow.node_factory - Fix toast import paths (useToastContext from toast/context) - Fix app-info.tsx import paths for relocated app-operations - Fix 15 frontend test files for API changes, missing QueryClientProvider, i18n key renames, and component behavior changes Made-with: Cursor
This commit is contained in:
@ -3,11 +3,19 @@ import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import AppInfo from '..'
|
||||
import AppInfo from '../index'
|
||||
|
||||
let mockIsCurrentWorkspaceEditor = true
|
||||
const mockSetPanelOpen = vi.fn()
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({ replace: vi.fn() }),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-apps', () => ({
|
||||
useInvalidateAppList: () => vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
isCurrentWorkspaceEditor: mockIsCurrentWorkspaceEditor,
|
||||
|
||||
@ -263,11 +263,10 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should render app icon', () => {
|
||||
// AppIcon component renders the emoji icon from app data
|
||||
const { container } = render(<AppCard app={mockApp} />)
|
||||
// Check that the icon container is rendered (AppIcon renders within the card)
|
||||
const iconElement = container.querySelector('[class*="icon"]') || container.querySelector('img')
|
||||
expect(iconElement || screen.getByText(mockApp.icon)).toBeTruthy()
|
||||
const emojiElement = container.querySelector('em-emoji')
|
||||
expect(emojiElement).toBeTruthy()
|
||||
expect(emojiElement?.getAttribute('id')).toBe(mockApp.icon)
|
||||
})
|
||||
|
||||
it('should render app type icon', () => {
|
||||
|
||||
@ -20,6 +20,11 @@ vi.mock('@/app/education-apply/hooks', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({ replace: vi.fn() }),
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-import-dsl', () => ({
|
||||
useImportDSL: () => ({
|
||||
handleImportDSL: vi.fn(),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, fireEvent, screen } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { useStore as useTagStore } from '@/app/components/base/tag-management/store'
|
||||
@ -200,9 +201,17 @@ beforeAll(() => {
|
||||
} as unknown as typeof IntersectionObserver
|
||||
})
|
||||
|
||||
// Render helper wrapping with shared nuqs testing helper.
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
})
|
||||
|
||||
const renderList = (searchParams = '') => {
|
||||
return renderWithNuqs(<List />, { searchParams })
|
||||
return renderWithNuqs(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<List />
|
||||
</QueryClientProvider>,
|
||||
{ searchParams },
|
||||
)
|
||||
}
|
||||
|
||||
describe('List', () => {
|
||||
@ -399,10 +408,14 @@ describe('List', () => {
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle multiple renders without issues', () => {
|
||||
const { rerender } = renderWithNuqs(<List />)
|
||||
const { rerender } = renderWithNuqs(
|
||||
<QueryClientProvider client={queryClient}><List /></QueryClientProvider>,
|
||||
)
|
||||
expect(screen.getByText('app.types.all')).toBeInTheDocument()
|
||||
|
||||
rerender(<List />)
|
||||
rerender(
|
||||
<QueryClientProvider client={queryClient}><List /></QueryClientProvider>,
|
||||
)
|
||||
expect(screen.getByText('app.types.all')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
@ -71,7 +71,7 @@ describe('CreateAppCard', () => {
|
||||
|
||||
expect(screen.getByText('app.newApp.startFromBlank')).toBeInTheDocument()
|
||||
expect(screen.getByText('app.newApp.startFromTemplate')).toBeInTheDocument()
|
||||
expect(screen.getByText('app.importDSL')).toBeInTheDocument()
|
||||
expect(screen.getByText('app.importApp')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render all buttons as clickable', () => {
|
||||
@ -190,7 +190,7 @@ describe('CreateAppCard', () => {
|
||||
it('should open DSL modal when clicking Import DSL', () => {
|
||||
render(<CreateAppCard ref={defaultRef} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.importDSL'))
|
||||
fireEvent.click(screen.getByText('app.importApp'))
|
||||
|
||||
expect(screen.getByTestId('create-dsl-modal')).toBeInTheDocument()
|
||||
})
|
||||
@ -198,7 +198,7 @@ describe('CreateAppCard', () => {
|
||||
it('should close DSL modal when clicking close button', () => {
|
||||
render(<CreateAppCard ref={defaultRef} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.importDSL'))
|
||||
fireEvent.click(screen.getByText('app.importApp'))
|
||||
expect(screen.getByTestId('create-dsl-modal')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByTestId('close-dsl-modal'))
|
||||
@ -209,7 +209,7 @@ describe('CreateAppCard', () => {
|
||||
const mockOnSuccess = vi.fn()
|
||||
render(<CreateAppCard ref={defaultRef} onSuccess={mockOnSuccess} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.importDSL'))
|
||||
fireEvent.click(screen.getByText('app.importApp'))
|
||||
fireEvent.click(screen.getByTestId('success-dsl-modal'))
|
||||
|
||||
expect(mockOnPlanInfoChanged).toHaveBeenCalled()
|
||||
@ -245,7 +245,7 @@ describe('CreateAppCard', () => {
|
||||
fireEvent.click(screen.getByText('app.newApp.startFromTemplate'))
|
||||
fireEvent.click(screen.getByTestId('close-template-dialog'))
|
||||
|
||||
fireEvent.click(screen.getByText('app.importDSL'))
|
||||
fireEvent.click(screen.getByText('app.importApp'))
|
||||
fireEvent.click(screen.getByTestId('close-dsl-modal'))
|
||||
|
||||
expect(screen.queryByTestId('create-app-modal')).not.toBeInTheDocument()
|
||||
|
||||
@ -14,6 +14,7 @@ const getPromptEditor = () => {
|
||||
vi.mock('@/utils/var', () => ({
|
||||
checkKeys: (_keys: string[]) => ({ isValid: true }),
|
||||
getNewVar: (key: string, type: string) => ({ key, name: key, type, required: true }),
|
||||
basePath: '',
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/app/configuration/config-prompt/confirm-add-var', () => ({
|
||||
|
||||
@ -68,6 +68,7 @@ vi.mock('lexical', async (importOriginal) => {
|
||||
getChildren: () => mocks.rootLines.map(line => ({
|
||||
getTextContent: () => line,
|
||||
})),
|
||||
getAllTextNodes: () => [],
|
||||
}),
|
||||
TextNode: class TextNode {
|
||||
__text: string
|
||||
|
||||
@ -77,12 +77,16 @@ const { mockConfig, mockEnv } = vi.hoisted(() => ({
|
||||
},
|
||||
},
|
||||
}))
|
||||
vi.mock('@/config', () => ({
|
||||
get IS_CLOUD_EDITION() { return mockConfig.IS_CLOUD_EDITION },
|
||||
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
||||
IS_DEV: false,
|
||||
IS_CE_EDITION: false,
|
||||
}))
|
||||
vi.mock('@/config', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/config')>()
|
||||
return {
|
||||
...actual,
|
||||
get IS_CLOUD_EDITION() { return mockConfig.IS_CLOUD_EDITION },
|
||||
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
||||
IS_DEV: false,
|
||||
IS_CE_EDITION: false,
|
||||
}
|
||||
})
|
||||
vi.mock('@/env', () => mockEnv)
|
||||
|
||||
const baseAppContextValue: AppContextValue = {
|
||||
|
||||
@ -55,8 +55,20 @@ vi.mock('@/service/workflow', () => ({
|
||||
syncWorkflowDraft: (p: unknown) => mockSyncWorkflowDraft(p),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/fetch', () => ({ postWithKeepalive: vi.fn() }))
|
||||
vi.mock('@/config', () => ({ API_PREFIX: '/api' }))
|
||||
vi.mock('@/service/fetch', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/service/fetch')>()
|
||||
return {
|
||||
...actual,
|
||||
postWithKeepalive: vi.fn(),
|
||||
}
|
||||
})
|
||||
vi.mock('@/config', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/config')>()
|
||||
return {
|
||||
...actual,
|
||||
API_PREFIX: '/api',
|
||||
}
|
||||
})
|
||||
|
||||
const mockHandleRefreshWorkflowDraft = vi.fn()
|
||||
vi.mock('@/app/components/workflow-app/hooks', () => ({
|
||||
|
||||
@ -11,7 +11,7 @@ vi.mock('@/app/components/workflow/store', () => ({
|
||||
getState: () => ({
|
||||
appId: 'app-1',
|
||||
isWorkflowDataLoaded: true,
|
||||
debouncedSyncWorkflowDraft: undefined,
|
||||
debouncedSyncWorkflowDraft: { cancel: vi.fn() },
|
||||
setSyncWorkflowDraftHash: mockSetSyncWorkflowDraftHash,
|
||||
setIsSyncingWorkflowDraft: vi.fn(),
|
||||
setEnvironmentVariables: vi.fn(),
|
||||
|
||||
@ -26,6 +26,7 @@ vi.mock('../use-workflow', () => ({
|
||||
|
||||
vi.mock('../../utils', () => ({
|
||||
getNodesConnectedSourceOrTargetHandleIdsMap: vi.fn(() => ({})),
|
||||
genNodeMetaData: vi.fn(({ type, sort }: { type: string, sort: number }) => ({ type, sort })),
|
||||
}))
|
||||
|
||||
// useNodesSyncDraft is used REAL — via renderWorkflowHook + hooksStoreProps
|
||||
|
||||
@ -53,7 +53,7 @@ describe('useWorkflowTextChunk', () => {
|
||||
},
|
||||
})
|
||||
|
||||
result.current.handleWorkflowTextChunk({ data: { text: ' World' } } as TextChunkResponse)
|
||||
result.current.handleWorkflowTextChunk({ data: { text: ' World', chunk_type: 'text' } } as TextChunkResponse)
|
||||
|
||||
const state = store.getState().workflowRunningData!
|
||||
expect(state.resultText).toBe('Hello World')
|
||||
|
||||
@ -172,9 +172,9 @@ describe('createWorkflowStore', () => {
|
||||
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()
|
||||
expect(store.getState().controlMode).toBe('hand')
|
||||
expect(store.getState().controlMode).toBe('pointer')
|
||||
})
|
||||
|
||||
it('should read panelWidth from localStorage', () => {
|
||||
|
||||
Reference in New Issue
Block a user