mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 19:32:16 +08:00
refactor(web): migrate to Vitest and esm (#29974)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import type { Mock } from 'vitest'
|
||||
import React from 'react'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
|
||||
// Mock next/navigation
|
||||
const mockPush = jest.fn()
|
||||
jest.mock('next/navigation', () => ({
|
||||
const mockPush = vi.fn()
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
push: mockPush,
|
||||
}),
|
||||
@@ -13,8 +14,8 @@ jest.mock('next/navigation', () => ({
|
||||
|
||||
// Mock use-context-selector with stable mockNotify reference for tracking calls
|
||||
// Include createContext for components that use it (like Toast)
|
||||
const mockNotify = jest.fn()
|
||||
jest.mock('use-context-selector', () => {
|
||||
const mockNotify = vi.fn()
|
||||
vi.mock('use-context-selector', () => {
|
||||
const React = require('react')
|
||||
return {
|
||||
createContext: (defaultValue: any) => React.createContext(defaultValue),
|
||||
@@ -28,15 +29,15 @@ jest.mock('use-context-selector', () => {
|
||||
})
|
||||
|
||||
// Mock app context
|
||||
jest.mock('@/context/app-context', () => ({
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
isCurrentWorkspaceEditor: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock provider context
|
||||
const mockOnPlanInfoChanged = jest.fn()
|
||||
jest.mock('@/context/provider-context', () => ({
|
||||
const mockOnPlanInfoChanged = vi.fn()
|
||||
vi.mock('@/context/provider-context', () => ({
|
||||
useProviderContext: () => ({
|
||||
onPlanInfoChanged: mockOnPlanInfoChanged,
|
||||
}),
|
||||
@@ -44,7 +45,7 @@ jest.mock('@/context/provider-context', () => ({
|
||||
|
||||
// Mock global public store - allow dynamic configuration
|
||||
let mockWebappAuthEnabled = false
|
||||
jest.mock('@/context/global-public-context', () => ({
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: (selector: (s: any) => any) => selector({
|
||||
systemFeatures: {
|
||||
webapp_auth: { enabled: mockWebappAuthEnabled },
|
||||
@@ -56,23 +57,24 @@ jest.mock('@/context/global-public-context', () => ({
|
||||
// Mock API services - import for direct manipulation
|
||||
import * as appsService from '@/service/apps'
|
||||
import * as workflowService from '@/service/workflow'
|
||||
import * as exploreService from '@/service/explore'
|
||||
|
||||
jest.mock('@/service/apps', () => ({
|
||||
deleteApp: jest.fn(() => Promise.resolve()),
|
||||
updateAppInfo: jest.fn(() => Promise.resolve()),
|
||||
copyApp: jest.fn(() => Promise.resolve({ id: 'new-app-id' })),
|
||||
exportAppConfig: jest.fn(() => Promise.resolve({ data: 'yaml: content' })),
|
||||
vi.mock('@/service/apps', () => ({
|
||||
deleteApp: vi.fn(() => Promise.resolve()),
|
||||
updateAppInfo: vi.fn(() => Promise.resolve()),
|
||||
copyApp: vi.fn(() => Promise.resolve({ id: 'new-app-id' })),
|
||||
exportAppConfig: vi.fn(() => Promise.resolve({ data: 'yaml: content' })),
|
||||
}))
|
||||
|
||||
jest.mock('@/service/workflow', () => ({
|
||||
fetchWorkflowDraft: jest.fn(() => Promise.resolve({ environment_variables: [] })),
|
||||
vi.mock('@/service/workflow', () => ({
|
||||
fetchWorkflowDraft: vi.fn(() => Promise.resolve({ environment_variables: [] })),
|
||||
}))
|
||||
|
||||
jest.mock('@/service/explore', () => ({
|
||||
fetchInstalledAppList: jest.fn(() => Promise.resolve({ installed_apps: [{ id: 'installed-1' }] })),
|
||||
vi.mock('@/service/explore', () => ({
|
||||
fetchInstalledAppList: vi.fn(() => Promise.resolve({ installed_apps: [{ id: 'installed-1' }] })),
|
||||
}))
|
||||
|
||||
jest.mock('@/service/access-control', () => ({
|
||||
vi.mock('@/service/access-control', () => ({
|
||||
useGetUserCanAccessApp: () => ({
|
||||
data: { result: true },
|
||||
isLoading: false,
|
||||
@@ -80,108 +82,114 @@ jest.mock('@/service/access-control', () => ({
|
||||
}))
|
||||
|
||||
// Mock hooks
|
||||
const mockOpenAsyncWindow = jest.fn()
|
||||
jest.mock('@/hooks/use-async-window-open', () => ({
|
||||
const mockOpenAsyncWindow = vi.fn()
|
||||
vi.mock('@/hooks/use-async-window-open', () => ({
|
||||
useAsyncWindowOpen: () => mockOpenAsyncWindow,
|
||||
}))
|
||||
|
||||
// Mock utils
|
||||
jest.mock('@/utils/app-redirection', () => ({
|
||||
getRedirection: jest.fn(),
|
||||
const { mockGetRedirection } = vi.hoisted(() => ({
|
||||
mockGetRedirection: vi.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/utils/var', () => ({
|
||||
vi.mock('@/utils/app-redirection', () => ({
|
||||
getRedirection: mockGetRedirection,
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/var', () => ({
|
||||
basePath: '',
|
||||
}))
|
||||
|
||||
jest.mock('@/utils/time', () => ({
|
||||
vi.mock('@/utils/time', () => ({
|
||||
formatTime: () => 'Jan 1, 2024',
|
||||
}))
|
||||
|
||||
// Mock dynamic imports
|
||||
jest.mock('next/dynamic', () => {
|
||||
vi.mock('next/dynamic', () => {
|
||||
const React = require('react')
|
||||
return (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
return {
|
||||
default: (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
|
||||
if (fnString.includes('create-app-modal') || fnString.includes('explore/create-app-modal')) {
|
||||
return function MockEditAppModal({ show, onHide, onConfirm }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'edit-app-modal' },
|
||||
React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-edit-modal' }, 'Close'),
|
||||
React.createElement('button', {
|
||||
'onClick': () => onConfirm?.({
|
||||
name: 'Updated App',
|
||||
icon_type: 'emoji',
|
||||
icon: '🎯',
|
||||
icon_background: '#FFEAD5',
|
||||
description: 'Updated description',
|
||||
use_icon_as_answer_icon: false,
|
||||
max_active_requests: null,
|
||||
}),
|
||||
'data-testid': 'confirm-edit-modal',
|
||||
}, 'Confirm'),
|
||||
)
|
||||
if (fnString.includes('create-app-modal') || fnString.includes('explore/create-app-modal')) {
|
||||
return function MockEditAppModal({ show, onHide, onConfirm }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'edit-app-modal' },
|
||||
React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-edit-modal' }, 'Close'),
|
||||
React.createElement('button', {
|
||||
'onClick': () => onConfirm?.({
|
||||
name: 'Updated App',
|
||||
icon_type: 'emoji',
|
||||
icon: '🎯',
|
||||
icon_background: '#FFEAD5',
|
||||
description: 'Updated description',
|
||||
use_icon_as_answer_icon: false,
|
||||
max_active_requests: null,
|
||||
}),
|
||||
'data-testid': 'confirm-edit-modal',
|
||||
}, 'Confirm'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('duplicate-modal')) {
|
||||
return function MockDuplicateAppModal({ show, onHide, onConfirm }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'duplicate-modal' },
|
||||
React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-duplicate-modal' }, 'Close'),
|
||||
React.createElement('button', {
|
||||
'onClick': () => onConfirm?.({
|
||||
name: 'Copied App',
|
||||
icon_type: 'emoji',
|
||||
icon: '📋',
|
||||
icon_background: '#E4FBCC',
|
||||
}),
|
||||
'data-testid': 'confirm-duplicate-modal',
|
||||
}, 'Confirm'),
|
||||
)
|
||||
if (fnString.includes('duplicate-modal')) {
|
||||
return function MockDuplicateAppModal({ show, onHide, onConfirm }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'duplicate-modal' },
|
||||
React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-duplicate-modal' }, 'Close'),
|
||||
React.createElement('button', {
|
||||
'onClick': () => onConfirm?.({
|
||||
name: 'Copied App',
|
||||
icon_type: 'emoji',
|
||||
icon: '📋',
|
||||
icon_background: '#E4FBCC',
|
||||
}),
|
||||
'data-testid': 'confirm-duplicate-modal',
|
||||
}, 'Confirm'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('switch-app-modal')) {
|
||||
return function MockSwitchAppModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'switch-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-switch-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'confirm-switch-modal' }, 'Switch'),
|
||||
)
|
||||
if (fnString.includes('switch-app-modal')) {
|
||||
return function MockSwitchAppModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'switch-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-switch-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'confirm-switch-modal' }, 'Switch'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('base/confirm')) {
|
||||
return function MockConfirm({ isShow, onCancel, onConfirm }: any) {
|
||||
if (!isShow) return null
|
||||
return React.createElement('div', { 'data-testid': 'confirm-dialog' },
|
||||
React.createElement('button', { 'onClick': onCancel, 'data-testid': 'cancel-confirm' }, 'Cancel'),
|
||||
React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-confirm' }, 'Confirm'),
|
||||
)
|
||||
if (fnString.includes('base/confirm')) {
|
||||
return function MockConfirm({ isShow, onCancel, onConfirm }: any) {
|
||||
if (!isShow) return null
|
||||
return React.createElement('div', { 'data-testid': 'confirm-dialog' },
|
||||
React.createElement('button', { 'onClick': onCancel, 'data-testid': 'cancel-confirm' }, 'Cancel'),
|
||||
React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-confirm' }, 'Confirm'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('dsl-export-confirm-modal')) {
|
||||
return function MockDSLExportModal({ onClose, onConfirm }: any) {
|
||||
return React.createElement('div', { 'data-testid': 'dsl-export-modal' },
|
||||
React.createElement('button', { 'onClick': () => onClose?.(), 'data-testid': 'close-dsl-export' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': () => onConfirm?.(true), 'data-testid': 'confirm-dsl-export' }, 'Export with secrets'),
|
||||
React.createElement('button', { 'onClick': () => onConfirm?.(false), 'data-testid': 'confirm-dsl-export-no-secrets' }, 'Export without secrets'),
|
||||
)
|
||||
if (fnString.includes('dsl-export-confirm-modal')) {
|
||||
return function MockDSLExportModal({ onClose, onConfirm }: any) {
|
||||
return React.createElement('div', { 'data-testid': 'dsl-export-modal' },
|
||||
React.createElement('button', { 'onClick': () => onClose?.(), 'data-testid': 'close-dsl-export' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': () => onConfirm?.(true), 'data-testid': 'confirm-dsl-export' }, 'Export with secrets'),
|
||||
React.createElement('button', { 'onClick': () => onConfirm?.(false), 'data-testid': 'confirm-dsl-export-no-secrets' }, 'Export without secrets'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('app-access-control')) {
|
||||
return function MockAccessControl({ onClose, onConfirm }: any) {
|
||||
return React.createElement('div', { 'data-testid': 'access-control-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-access-control' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-access-control' }, 'Confirm'),
|
||||
)
|
||||
if (fnString.includes('app-access-control')) {
|
||||
return function MockAccessControl({ onClose, onConfirm }: any) {
|
||||
return React.createElement('div', { 'data-testid': 'access-control-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-access-control' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-access-control' }, 'Confirm'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => null
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Popover uses @headlessui/react portals - mock for controlled interaction testing
|
||||
jest.mock('@/app/components/base/popover', () => {
|
||||
vi.mock('@/app/components/base/popover', () => {
|
||||
const MockPopover = ({ htmlContent, btnElement, btnClassName }: any) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false)
|
||||
const computedClassName = typeof btnClassName === 'function' ? btnClassName(isOpen) : ''
|
||||
@@ -202,13 +210,13 @@ jest.mock('@/app/components/base/popover', () => {
|
||||
})
|
||||
|
||||
// Tooltip uses portals - minimal mock preserving popup content as title attribute
|
||||
jest.mock('@/app/components/base/tooltip', () => ({
|
||||
vi.mock('@/app/components/base/tooltip', () => ({
|
||||
__esModule: true,
|
||||
default: ({ children, popupContent }: any) => React.createElement('div', { title: popupContent }, children),
|
||||
}))
|
||||
|
||||
// TagSelector has API dependency (service/tag) - mock for isolated testing
|
||||
jest.mock('@/app/components/base/tag-management/selector', () => ({
|
||||
vi.mock('@/app/components/base/tag-management/selector', () => ({
|
||||
__esModule: true,
|
||||
default: ({ tags }: any) => {
|
||||
const React = require('react')
|
||||
@@ -219,7 +227,7 @@ jest.mock('@/app/components/base/tag-management/selector', () => ({
|
||||
}))
|
||||
|
||||
// AppTypeIcon has complex icon mapping - mock for focused component testing
|
||||
jest.mock('@/app/components/app/type-selector', () => ({
|
||||
vi.mock('@/app/components/app/type-selector', () => ({
|
||||
AppTypeIcon: () => React.createElement('div', { 'data-testid': 'app-type-icon' }),
|
||||
}))
|
||||
|
||||
@@ -265,10 +273,10 @@ const createMockApp = (overrides: Record<string, any> = {}) => ({
|
||||
|
||||
describe('AppCard', () => {
|
||||
const mockApp = createMockApp()
|
||||
const mockOnRefresh = jest.fn()
|
||||
const mockOnRefresh = vi.fn()
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockOpenAsyncWindow.mockReset()
|
||||
mockWebappAuthEnabled = false
|
||||
})
|
||||
@@ -375,11 +383,10 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should call getRedirection on card click', () => {
|
||||
const { getRedirection } = require('@/utils/app-redirection')
|
||||
render(<AppCard app={mockApp} />)
|
||||
const card = screen.getByTitle('Test App').closest('[class*="cursor-pointer"]')!
|
||||
fireEvent.click(card)
|
||||
expect(getRedirection).toHaveBeenCalledWith(true, mockApp, mockPush)
|
||||
expect(mockGetRedirection).toHaveBeenCalledWith(true, mockApp, mockPush)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -627,7 +634,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should handle delete failure', async () => {
|
||||
(appsService.deleteApp as jest.Mock).mockRejectedValueOnce(new Error('Delete failed'))
|
||||
(appsService.deleteApp as Mock).mockRejectedValueOnce(new Error('Delete failed'))
|
||||
|
||||
render(<AppCard app={mockApp} onRefresh={mockOnRefresh} />)
|
||||
|
||||
@@ -706,7 +713,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should handle copy failure', async () => {
|
||||
(appsService.copyApp as jest.Mock).mockRejectedValueOnce(new Error('Copy failed'))
|
||||
(appsService.copyApp as Mock).mockRejectedValueOnce(new Error('Copy failed'))
|
||||
|
||||
render(<AppCard app={mockApp} onRefresh={mockOnRefresh} />)
|
||||
|
||||
@@ -741,7 +748,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should handle export failure', async () => {
|
||||
(appsService.exportAppConfig as jest.Mock).mockRejectedValueOnce(new Error('Export failed'))
|
||||
(appsService.exportAppConfig as Mock).mockRejectedValueOnce(new Error('Export failed'))
|
||||
|
||||
render(<AppCard app={mockApp} />)
|
||||
|
||||
@@ -855,7 +862,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should show DSL export modal when workflow has secret variables', async () => {
|
||||
(workflowService.fetchWorkflowDraft as jest.Mock).mockResolvedValueOnce({
|
||||
(workflowService.fetchWorkflowDraft as Mock).mockResolvedValueOnce({
|
||||
environment_variables: [{ value_type: 'secret', name: 'API_KEY' }],
|
||||
})
|
||||
|
||||
@@ -887,7 +894,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should close DSL export modal when onClose is called', async () => {
|
||||
(workflowService.fetchWorkflowDraft as jest.Mock).mockResolvedValueOnce({
|
||||
(workflowService.fetchWorkflowDraft as Mock).mockResolvedValueOnce({
|
||||
environment_variables: [{ value_type: 'secret', name: 'API_KEY' }],
|
||||
})
|
||||
|
||||
@@ -981,7 +988,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should handle edit failure', async () => {
|
||||
(appsService.updateAppInfo as jest.Mock).mockRejectedValueOnce(new Error('Edit failed'))
|
||||
(appsService.updateAppInfo as Mock).mockRejectedValueOnce(new Error('Edit failed'))
|
||||
|
||||
render(<AppCard app={mockApp} onRefresh={mockOnRefresh} />)
|
||||
|
||||
@@ -1039,7 +1046,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
it('should handle workflow draft fetch failure during export', async () => {
|
||||
(workflowService.fetchWorkflowDraft as jest.Mock).mockRejectedValueOnce(new Error('Fetch failed'))
|
||||
(workflowService.fetchWorkflowDraft as Mock).mockRejectedValueOnce(new Error('Fetch failed'))
|
||||
|
||||
const workflowApp = { ...mockApp, mode: AppModeEnum.WORKFLOW }
|
||||
render(<AppCard app={workflowApp} />)
|
||||
@@ -1186,15 +1193,13 @@ describe('AppCard', () => {
|
||||
fireEvent.click(openInExploreBtn)
|
||||
})
|
||||
|
||||
const { fetchInstalledAppList } = require('@/service/explore')
|
||||
await waitFor(() => {
|
||||
expect(fetchInstalledAppList).toHaveBeenCalledWith(mockApp.id)
|
||||
expect(exploreService.fetchInstalledAppList).toHaveBeenCalledWith(mockApp.id)
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle open in explore API failure', async () => {
|
||||
const { fetchInstalledAppList } = require('@/service/explore')
|
||||
fetchInstalledAppList.mockRejectedValueOnce(new Error('API Error'))
|
||||
(exploreService.fetchInstalledAppList as Mock).mockRejectedValueOnce(new Error('API Error'))
|
||||
|
||||
// Configure mockOpenAsyncWindow to call the callback and trigger error
|
||||
mockOpenAsyncWindow.mockImplementationOnce(async (callback: () => Promise<string>, options: any) => {
|
||||
@@ -1215,7 +1220,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchInstalledAppList).toHaveBeenCalled()
|
||||
expect(exploreService.fetchInstalledAppList).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1236,8 +1241,7 @@ describe('AppCard', () => {
|
||||
|
||||
describe('Open in Explore - No App Found', () => {
|
||||
it('should handle case when installed_apps is empty array', async () => {
|
||||
const { fetchInstalledAppList } = require('@/service/explore')
|
||||
fetchInstalledAppList.mockResolvedValueOnce({ installed_apps: [] })
|
||||
(exploreService.fetchInstalledAppList as Mock).mockResolvedValueOnce({ installed_apps: [] })
|
||||
|
||||
// Configure mockOpenAsyncWindow to call the callback and trigger error
|
||||
mockOpenAsyncWindow.mockImplementationOnce(async (callback: () => Promise<string>, options: any) => {
|
||||
@@ -1258,13 +1262,12 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchInstalledAppList).toHaveBeenCalled()
|
||||
expect(exploreService.fetchInstalledAppList).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle case when API throws in callback', async () => {
|
||||
const { fetchInstalledAppList } = require('@/service/explore')
|
||||
fetchInstalledAppList.mockRejectedValueOnce(new Error('Network error'))
|
||||
(exploreService.fetchInstalledAppList as Mock).mockRejectedValueOnce(new Error('Network error'))
|
||||
|
||||
// Configure mockOpenAsyncWindow to call the callback without catching
|
||||
mockOpenAsyncWindow.mockImplementationOnce(async (callback: () => Promise<string>) => {
|
||||
@@ -1280,7 +1283,7 @@ describe('AppCard', () => {
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchInstalledAppList).toHaveBeenCalled()
|
||||
expect(exploreService.fetchInstalledAppList).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import Empty from './empty'
|
||||
|
||||
describe('Empty', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import Footer from './footer'
|
||||
|
||||
describe('Footer', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
import { act, renderHook } from '@testing-library/react'
|
||||
|
||||
// Mock Next.js navigation hooks
|
||||
const mockPush = jest.fn()
|
||||
const mockPush = vi.fn()
|
||||
const mockPathname = '/apps'
|
||||
let mockSearchParams = new URLSearchParams()
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
usePathname: jest.fn(() => mockPathname),
|
||||
useRouter: jest.fn(() => ({
|
||||
vi.mock('next/navigation', () => ({
|
||||
usePathname: vi.fn(() => mockPathname),
|
||||
useRouter: vi.fn(() => ({
|
||||
push: mockPush,
|
||||
})),
|
||||
useSearchParams: jest.fn(() => mockSearchParams),
|
||||
useSearchParams: vi.fn(() => mockSearchParams),
|
||||
}))
|
||||
|
||||
// Import the hook after mocks are set up
|
||||
@@ -29,7 +29,7 @@ import useAppsQueryState from './use-apps-query-state'
|
||||
|
||||
describe('useAppsQueryState', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockSearchParams = new URLSearchParams()
|
||||
})
|
||||
|
||||
|
||||
@@ -7,18 +7,19 @@
|
||||
* - Enable/disable toggle for conditional drag-and-drop
|
||||
* - Cleanup on unmount (removes event listeners)
|
||||
*/
|
||||
import type { Mock } from 'vitest'
|
||||
import { act, renderHook } from '@testing-library/react'
|
||||
import { useDSLDragDrop } from './use-dsl-drag-drop'
|
||||
|
||||
describe('useDSLDragDrop', () => {
|
||||
let container: HTMLDivElement
|
||||
let mockOnDSLFileDropped: jest.Mock
|
||||
let mockOnDSLFileDropped: Mock
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
mockOnDSLFileDropped = jest.fn()
|
||||
mockOnDSLFileDropped = vi.fn()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -38,11 +39,11 @@ describe('useDSLDragDrop', () => {
|
||||
writable: false,
|
||||
})
|
||||
Object.defineProperty(event, 'preventDefault', {
|
||||
value: jest.fn(),
|
||||
value: vi.fn(),
|
||||
writable: false,
|
||||
})
|
||||
Object.defineProperty(event, 'stopPropagation', {
|
||||
value: jest.fn(),
|
||||
value: vi.fn(),
|
||||
writable: false,
|
||||
})
|
||||
|
||||
@@ -320,11 +321,11 @@ describe('useDSLDragDrop', () => {
|
||||
writable: false,
|
||||
})
|
||||
Object.defineProperty(event, 'preventDefault', {
|
||||
value: jest.fn(),
|
||||
value: vi.fn(),
|
||||
writable: false,
|
||||
})
|
||||
Object.defineProperty(event, 'stopPropagation', {
|
||||
value: jest.fn(),
|
||||
value: vi.fn(),
|
||||
writable: false,
|
||||
})
|
||||
|
||||
@@ -442,7 +443,7 @@ describe('useDSLDragDrop', () => {
|
||||
describe('Cleanup', () => {
|
||||
it('should remove event listeners on unmount', () => {
|
||||
const containerRef = { current: container }
|
||||
const removeEventListenerSpy = jest.spyOn(container, 'removeEventListener')
|
||||
const removeEventListenerSpy = vi.spyOn(container, 'removeEventListener')
|
||||
|
||||
const { unmount } = renderHook(() =>
|
||||
useDSLDragDrop({
|
||||
|
||||
@@ -6,7 +6,7 @@ let documentTitleCalls: string[] = []
|
||||
let educationInitCalls: number = 0
|
||||
|
||||
// Mock useDocumentTitle hook
|
||||
jest.mock('@/hooks/use-document-title', () => ({
|
||||
vi.mock('@/hooks/use-document-title', () => ({
|
||||
__esModule: true,
|
||||
default: (title: string) => {
|
||||
documentTitleCalls.push(title)
|
||||
@@ -14,14 +14,14 @@ jest.mock('@/hooks/use-document-title', () => ({
|
||||
}))
|
||||
|
||||
// Mock useEducationInit hook
|
||||
jest.mock('@/app/education-apply/hooks', () => ({
|
||||
vi.mock('@/app/education-apply/hooks', () => ({
|
||||
useEducationInit: () => {
|
||||
educationInitCalls++
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock List component
|
||||
jest.mock('./list', () => ({
|
||||
vi.mock('./list', () => ({
|
||||
__esModule: true,
|
||||
default: () => {
|
||||
const React = require('react')
|
||||
@@ -34,7 +34,7 @@ import Apps from './index'
|
||||
|
||||
describe('Apps', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
documentTitleCalls = []
|
||||
educationInitCalls = 0
|
||||
})
|
||||
|
||||
@@ -3,16 +3,16 @@ import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
// Mock next/navigation
|
||||
const mockReplace = jest.fn()
|
||||
const mockReplace = vi.fn()
|
||||
const mockRouter = { replace: mockReplace }
|
||||
jest.mock('next/navigation', () => ({
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => mockRouter,
|
||||
}))
|
||||
|
||||
// Mock app context
|
||||
const mockIsCurrentWorkspaceEditor = jest.fn(() => true)
|
||||
const mockIsCurrentWorkspaceDatasetOperator = jest.fn(() => false)
|
||||
jest.mock('@/context/app-context', () => ({
|
||||
const mockIsCurrentWorkspaceEditor = vi.fn(() => true)
|
||||
const mockIsCurrentWorkspaceDatasetOperator = vi.fn(() => false)
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
isCurrentWorkspaceEditor: mockIsCurrentWorkspaceEditor(),
|
||||
isCurrentWorkspaceDatasetOperator: mockIsCurrentWorkspaceDatasetOperator(),
|
||||
@@ -20,7 +20,7 @@ jest.mock('@/context/app-context', () => ({
|
||||
}))
|
||||
|
||||
// Mock global public store
|
||||
jest.mock('@/context/global-public-context', () => ({
|
||||
vi.mock('@/context/global-public-context', () => ({
|
||||
useGlobalPublicStore: () => ({
|
||||
systemFeatures: {
|
||||
branding: { enabled: false },
|
||||
@@ -29,13 +29,13 @@ jest.mock('@/context/global-public-context', () => ({
|
||||
}))
|
||||
|
||||
// Mock custom hooks - allow dynamic query state
|
||||
const mockSetQuery = jest.fn()
|
||||
const mockSetQuery = vi.fn()
|
||||
const mockQueryState = {
|
||||
tagIDs: [] as string[],
|
||||
keywords: '',
|
||||
isCreatedByMe: false,
|
||||
}
|
||||
jest.mock('./hooks/use-apps-query-state', () => ({
|
||||
vi.mock('./hooks/use-apps-query-state', () => ({
|
||||
__esModule: true,
|
||||
default: () => ({
|
||||
query: mockQueryState,
|
||||
@@ -46,21 +46,21 @@ jest.mock('./hooks/use-apps-query-state', () => ({
|
||||
// Store callback for testing DSL file drop
|
||||
let mockOnDSLFileDropped: ((file: File) => void) | null = null
|
||||
let mockDragging = false
|
||||
jest.mock('./hooks/use-dsl-drag-drop', () => ({
|
||||
vi.mock('./hooks/use-dsl-drag-drop', () => ({
|
||||
useDSLDragDrop: ({ onDSLFileDropped }: { onDSLFileDropped: (file: File) => void }) => {
|
||||
mockOnDSLFileDropped = onDSLFileDropped
|
||||
return { dragging: mockDragging }
|
||||
},
|
||||
}))
|
||||
|
||||
const mockSetActiveTab = jest.fn()
|
||||
jest.mock('@/hooks/use-tab-searchparams', () => ({
|
||||
const mockSetActiveTab = vi.fn()
|
||||
vi.mock('@/hooks/use-tab-searchparams', () => ({
|
||||
useTabSearchParams: () => ['all', mockSetActiveTab],
|
||||
}))
|
||||
|
||||
// Mock service hooks - use object for mutable state (jest.mock is hoisted)
|
||||
const mockRefetch = jest.fn()
|
||||
const mockFetchNextPage = jest.fn()
|
||||
// Mock service hooks - use object for mutable state (vi.mock is hoisted)
|
||||
const mockRefetch = vi.fn()
|
||||
const mockFetchNextPage = vi.fn()
|
||||
|
||||
const mockServiceState = {
|
||||
error: null as Error | null,
|
||||
@@ -103,7 +103,7 @@ const defaultAppData = {
|
||||
}],
|
||||
}
|
||||
|
||||
jest.mock('@/service/use-apps', () => ({
|
||||
vi.mock('@/service/use-apps', () => ({
|
||||
useInfiniteAppList: () => ({
|
||||
data: defaultAppData,
|
||||
isLoading: mockServiceState.isLoading,
|
||||
@@ -116,26 +116,26 @@ jest.mock('@/service/use-apps', () => ({
|
||||
}))
|
||||
|
||||
// Mock tag store
|
||||
jest.mock('@/app/components/base/tag-management/store', () => ({
|
||||
vi.mock('@/app/components/base/tag-management/store', () => ({
|
||||
useStore: (selector: (state: { tagList: any[]; setTagList: any; showTagManagementModal: boolean; setShowTagManagementModal: any }) => any) => {
|
||||
const state = {
|
||||
tagList: [{ id: 'tag-1', name: 'Test Tag', type: 'app' }],
|
||||
setTagList: jest.fn(),
|
||||
setTagList: vi.fn(),
|
||||
showTagManagementModal: false,
|
||||
setShowTagManagementModal: jest.fn(),
|
||||
setShowTagManagementModal: vi.fn(),
|
||||
}
|
||||
return selector(state)
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock tag service to avoid API calls in TagFilter
|
||||
jest.mock('@/service/tag', () => ({
|
||||
fetchTagList: jest.fn().mockResolvedValue([{ id: 'tag-1', name: 'Test Tag', type: 'app' }]),
|
||||
vi.mock('@/service/tag', () => ({
|
||||
fetchTagList: vi.fn().mockResolvedValue([{ id: 'tag-1', name: 'Test Tag', type: 'app' }]),
|
||||
}))
|
||||
|
||||
// Store TagFilter onChange callback for testing
|
||||
let mockTagFilterOnChange: ((value: string[]) => void) | null = null
|
||||
jest.mock('@/app/components/base/tag-management/filter', () => ({
|
||||
vi.mock('@/app/components/base/tag-management/filter', () => ({
|
||||
__esModule: true,
|
||||
default: ({ onChange }: { onChange: (value: string[]) => void }) => {
|
||||
const React = require('react')
|
||||
@@ -145,17 +145,17 @@ jest.mock('@/app/components/base/tag-management/filter', () => ({
|
||||
}))
|
||||
|
||||
// Mock config
|
||||
jest.mock('@/config', () => ({
|
||||
vi.mock('@/config', () => ({
|
||||
NEED_REFRESH_APP_LIST_KEY: 'needRefreshAppList',
|
||||
}))
|
||||
|
||||
// Mock pay hook
|
||||
jest.mock('@/hooks/use-pay', () => ({
|
||||
vi.mock('@/hooks/use-pay', () => ({
|
||||
CheckModal: () => null,
|
||||
}))
|
||||
|
||||
// Mock ahooks - useMount only executes once on mount, not on fn change
|
||||
jest.mock('ahooks', () => ({
|
||||
vi.mock('ahooks', () => ({
|
||||
useDebounceFn: (fn: () => void) => ({ run: fn }),
|
||||
useMount: (fn: () => void) => {
|
||||
const React = require('react')
|
||||
@@ -168,26 +168,28 @@ jest.mock('ahooks', () => ({
|
||||
}))
|
||||
|
||||
// Mock dynamic imports
|
||||
jest.mock('next/dynamic', () => {
|
||||
vi.mock('next/dynamic', () => {
|
||||
const React = require('react')
|
||||
return (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
return {
|
||||
default: (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
|
||||
if (fnString.includes('tag-management')) {
|
||||
return function MockTagManagement() {
|
||||
return React.createElement('div', { 'data-testid': 'tag-management-modal' })
|
||||
if (fnString.includes('tag-management')) {
|
||||
return function MockTagManagement() {
|
||||
return React.createElement('div', { 'data-testid': 'tag-management-modal' })
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-dsl-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success'),
|
||||
)
|
||||
if (fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-dsl-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => null
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -196,7 +198,7 @@ jest.mock('next/dynamic', () => {
|
||||
* These mocks isolate the List component's behavior from its children.
|
||||
* Each child component (AppCard, NewAppCard, Empty, Footer) has its own dedicated tests.
|
||||
*/
|
||||
jest.mock('./app-card', () => ({
|
||||
vi.mock('./app-card', () => ({
|
||||
__esModule: true,
|
||||
default: ({ app }: any) => {
|
||||
const React = require('react')
|
||||
@@ -204,14 +206,16 @@ jest.mock('./app-card', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('./new-app-card', () => {
|
||||
vi.mock('./new-app-card', () => {
|
||||
const React = require('react')
|
||||
return React.forwardRef((_props: any, _ref: any) => {
|
||||
return React.createElement('div', { 'data-testid': 'new-app-card', 'role': 'button' }, 'New App Card')
|
||||
})
|
||||
return {
|
||||
default: React.forwardRef((_props: any, _ref: any) => {
|
||||
return React.createElement('div', { 'data-testid': 'new-app-card', 'role': 'button' }, 'New App Card')
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('./empty', () => ({
|
||||
vi.mock('./empty', () => ({
|
||||
__esModule: true,
|
||||
default: () => {
|
||||
const React = require('react')
|
||||
@@ -219,7 +223,7 @@ jest.mock('./empty', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
jest.mock('./footer', () => ({
|
||||
vi.mock('./footer', () => ({
|
||||
__esModule: true,
|
||||
default: () => {
|
||||
const React = require('react')
|
||||
@@ -232,8 +236,8 @@ import List from './list'
|
||||
|
||||
// Store IntersectionObserver callback
|
||||
let intersectionCallback: IntersectionObserverCallback | null = null
|
||||
const mockObserve = jest.fn()
|
||||
const mockDisconnect = jest.fn()
|
||||
const mockObserve = vi.fn()
|
||||
const mockDisconnect = vi.fn()
|
||||
|
||||
// Mock IntersectionObserver
|
||||
beforeAll(() => {
|
||||
@@ -244,7 +248,7 @@ beforeAll(() => {
|
||||
|
||||
observe = mockObserve
|
||||
disconnect = mockDisconnect
|
||||
unobserve = jest.fn()
|
||||
unobserve = vi.fn()
|
||||
root = null
|
||||
rootMargin = ''
|
||||
thresholds = []
|
||||
@@ -254,7 +258,7 @@ beforeAll(() => {
|
||||
|
||||
describe('List', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
mockIsCurrentWorkspaceEditor.mockReturnValue(true)
|
||||
mockIsCurrentWorkspaceDatasetOperator.mockReturnValue(false)
|
||||
mockDragging = false
|
||||
@@ -649,7 +653,7 @@ describe('List', () => {
|
||||
|
||||
describe('Tag Filter Change', () => {
|
||||
it('should handle tag filter value change', () => {
|
||||
jest.useFakeTimers()
|
||||
vi.useFakeTimers()
|
||||
render(<List />)
|
||||
|
||||
// TagFilter component is rendered
|
||||
@@ -663,17 +667,17 @@ describe('List', () => {
|
||||
|
||||
// Advance timers to trigger debounced setTagIDs
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(500)
|
||||
vi.advanceTimersByTime(500)
|
||||
})
|
||||
|
||||
// setQuery should have been called with updated tagIDs
|
||||
expect(mockSetQuery).toHaveBeenCalled()
|
||||
|
||||
jest.useRealTimers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('should handle empty tag filter selection', () => {
|
||||
jest.useFakeTimers()
|
||||
vi.useFakeTimers()
|
||||
render(<List />)
|
||||
|
||||
// Trigger tag filter change with empty array
|
||||
@@ -684,12 +688,12 @@ describe('List', () => {
|
||||
|
||||
// Advance timers
|
||||
act(() => {
|
||||
jest.advanceTimersByTime(500)
|
||||
vi.advanceTimersByTime(500)
|
||||
})
|
||||
|
||||
expect(mockSetQuery).toHaveBeenCalled()
|
||||
|
||||
jest.useRealTimers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
|
||||
// Mock next/navigation
|
||||
const mockReplace = jest.fn()
|
||||
jest.mock('next/navigation', () => ({
|
||||
const mockReplace = vi.fn()
|
||||
vi.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
replace: mockReplace,
|
||||
}),
|
||||
@@ -11,54 +11,56 @@ jest.mock('next/navigation', () => ({
|
||||
}))
|
||||
|
||||
// Mock provider context
|
||||
const mockOnPlanInfoChanged = jest.fn()
|
||||
jest.mock('@/context/provider-context', () => ({
|
||||
const mockOnPlanInfoChanged = vi.fn()
|
||||
vi.mock('@/context/provider-context', () => ({
|
||||
useProviderContext: () => ({
|
||||
onPlanInfoChanged: mockOnPlanInfoChanged,
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock next/dynamic to immediately resolve components
|
||||
jest.mock('next/dynamic', () => {
|
||||
vi.mock('next/dynamic', () => {
|
||||
const React = require('react')
|
||||
return (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
return {
|
||||
default: (importFn: () => Promise<any>) => {
|
||||
const fnString = importFn.toString()
|
||||
|
||||
if (fnString.includes('create-app-modal') && !fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateAppModal({ show, onClose, onSuccess, onCreateFromTemplate }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-app-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-create-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-create-modal' }, 'Success'),
|
||||
React.createElement('button', { 'onClick': onCreateFromTemplate, 'data-testid': 'to-template-modal' }, 'To Template'),
|
||||
)
|
||||
if (fnString.includes('create-app-modal') && !fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateAppModal({ show, onClose, onSuccess, onCreateFromTemplate }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-app-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-create-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-create-modal' }, 'Success'),
|
||||
React.createElement('button', { 'onClick': onCreateFromTemplate, 'data-testid': 'to-template-modal' }, 'To Template'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('create-app-dialog')) {
|
||||
return function MockCreateAppTemplateDialog({ show, onClose, onSuccess, onCreateFromBlank }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-template-dialog' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-template-dialog' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-template-dialog' }, 'Success'),
|
||||
React.createElement('button', { 'onClick': onCreateFromBlank, 'data-testid': 'to-blank-modal' }, 'To Blank'),
|
||||
)
|
||||
if (fnString.includes('create-app-dialog')) {
|
||||
return function MockCreateAppTemplateDialog({ show, onClose, onSuccess, onCreateFromBlank }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-template-dialog' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-template-dialog' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-template-dialog' }, 'Success'),
|
||||
React.createElement('button', { 'onClick': onCreateFromBlank, 'data-testid': 'to-blank-modal' }, 'To Blank'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-dsl-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success'),
|
||||
)
|
||||
if (fnString.includes('create-from-dsl-modal')) {
|
||||
return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) {
|
||||
if (!show) return null
|
||||
return React.createElement('div', { 'data-testid': 'create-dsl-modal' },
|
||||
React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'),
|
||||
React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success'),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => null
|
||||
return () => null
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Mock CreateFromDSLModalTab enum
|
||||
jest.mock('@/app/components/app/create-from-dsl-modal', () => ({
|
||||
vi.mock('@/app/components/app/create-from-dsl-modal', () => ({
|
||||
CreateFromDSLModalTab: {
|
||||
FROM_URL: 'from-url',
|
||||
},
|
||||
@@ -71,7 +73,7 @@ describe('CreateAppCard', () => {
|
||||
const defaultRef = { current: null } as React.RefObject<HTMLDivElement | null>
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
@@ -135,7 +137,7 @@ describe('CreateAppCard', () => {
|
||||
})
|
||||
|
||||
it('should call onSuccess and onPlanInfoChanged on create app success', () => {
|
||||
const mockOnSuccess = jest.fn()
|
||||
const mockOnSuccess = vi.fn()
|
||||
render(<CreateAppCard ref={defaultRef} onSuccess={mockOnSuccess} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.newApp.startFromBlank'))
|
||||
@@ -178,7 +180,7 @@ describe('CreateAppCard', () => {
|
||||
})
|
||||
|
||||
it('should call onSuccess and onPlanInfoChanged on template success', () => {
|
||||
const mockOnSuccess = jest.fn()
|
||||
const mockOnSuccess = vi.fn()
|
||||
render(<CreateAppCard ref={defaultRef} onSuccess={mockOnSuccess} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.newApp.startFromTemplate'))
|
||||
@@ -221,7 +223,7 @@ describe('CreateAppCard', () => {
|
||||
})
|
||||
|
||||
it('should call onSuccess and onPlanInfoChanged on DSL import success', () => {
|
||||
const mockOnSuccess = jest.fn()
|
||||
const mockOnSuccess = vi.fn()
|
||||
render(<CreateAppCard ref={defaultRef} onSuccess={mockOnSuccess} />)
|
||||
|
||||
fireEvent.click(screen.getByText('app.importDSL'))
|
||||
|
||||
Reference in New Issue
Block a user