mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 04:36:31 +08:00
test: stabilize remaining async leak specs
Co-authored-by: hyoban <38493346+hyoban@users.noreply.github.com>
This commit is contained in:
parent
039b448b90
commit
e823e09afb
@ -2,6 +2,7 @@ import type { Area } from 'react-easy-crop'
|
||||
import type { ImageFile } from '@/types/app'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import AppIconPicker from '../index'
|
||||
import 'vitest-canvas-mock'
|
||||
@ -93,6 +94,71 @@ vi.mock('react-easy-crop', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/emoji-picker/Inner', () => ({
|
||||
default: function MockEmojiPickerInner({ onSelect, className }: { onSelect: (emoji: string, background: string) => void, className?: string }) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<input placeholder="search" />
|
||||
<button
|
||||
type="button"
|
||||
data-testid="emoji-container-grinning"
|
||||
onClick={() => onSelect('😀', '#FFEAD5')}
|
||||
>
|
||||
grinning
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../ImageInput', () => ({
|
||||
default: function MockImageInput({ onImageInput, className }: {
|
||||
onImageInput: (isCropped: boolean, fileOrTempUrl: string | File, croppedAreaPixels?: Area, fileName?: string) => void
|
||||
className?: string
|
||||
}) {
|
||||
const [selectedFile, setSelectedFile] = React.useState<File | null>(null)
|
||||
const [animatedUrl, setAnimatedUrl] = React.useState<string | null>(null)
|
||||
const [showCropper, setShowCropper] = React.useState(false)
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div>drop image here</div>
|
||||
<input
|
||||
data-testid="image-input"
|
||||
type="file"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (!file)
|
||||
return
|
||||
setSelectedFile(file)
|
||||
if (file.type === 'image/gif') {
|
||||
const nextUrl = URL.createObjectURL(file)
|
||||
setAnimatedUrl(nextUrl)
|
||||
setShowCropper(false)
|
||||
onImageInput(false, file)
|
||||
return
|
||||
}
|
||||
setAnimatedUrl(null)
|
||||
setShowCropper(true)
|
||||
}}
|
||||
/>
|
||||
{showCropper && selectedFile && (
|
||||
<div data-testid="mock-cropper">
|
||||
<button
|
||||
type="button"
|
||||
data-testid="trigger-crop"
|
||||
onClick={() => onImageInput(true, 'blob:crop-temp-url', { x: 0, y: 0, width: 100, height: 100 }, selectedFile.name)}
|
||||
>
|
||||
Trigger Crop
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{animatedUrl && <img data-testid="animated-image" src={animatedUrl} alt="animated preview" />}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../../image-uploader/hooks', () => ({
|
||||
useLocalFileUploader: (options: LocalFileUploaderOptions) => {
|
||||
mocks.onUpload = options.onUpload
|
||||
@ -104,6 +170,14 @@ vi.mock('@/utils/emoji', () => ({
|
||||
searchEmoji: vi.fn().mockResolvedValue(['grinning', 'sunglasses']),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/modal', () => ({
|
||||
default: ({ children, className }: { children: React.ReactNode, className?: string }) => (
|
||||
<div data-testid="mock-modal" className={className}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
describe('AppIconPicker', () => {
|
||||
const originalCreateElement = document.createElement.bind(document)
|
||||
const originalCreateObjectURL = globalThis.URL.createObjectURL
|
||||
@ -183,10 +257,10 @@ describe('AppIconPicker', () => {
|
||||
it('should switch between emoji and image tabs', async () => {
|
||||
renderPicker()
|
||||
|
||||
await userEvent.click(screen.getByText(/image/i))
|
||||
fireEvent.click(screen.getByText(/image/i))
|
||||
expect(screen.getByText(/drop.*here/i)).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(screen.getByText(/emoji/i))
|
||||
fireEvent.click(screen.getByText(/emoji/i))
|
||||
expect(screen.getByPlaceholderText(/search/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
@ -56,6 +56,51 @@ vi.mock('@/service/use-triggers', () => ({
|
||||
useInvalidTriggerDynamicOptions: () => vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../index', () => ({
|
||||
Authorized: ({
|
||||
credentials = [],
|
||||
extraAuthorizationItems = [],
|
||||
isOpen,
|
||||
onItemClick,
|
||||
onOpenChange,
|
||||
renderTrigger,
|
||||
}: {
|
||||
credentials?: Credential[]
|
||||
extraAuthorizationItems?: Credential[]
|
||||
isOpen?: boolean
|
||||
onItemClick?: (id: string) => void
|
||||
onOpenChange?: (open: boolean) => void
|
||||
renderTrigger?: (isOpen?: boolean) => ReactNode
|
||||
}) => (
|
||||
<div>
|
||||
<div data-testid="authorized-trigger" onClick={() => onOpenChange?.(!isOpen)}>
|
||||
{renderTrigger?.(isOpen)}
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div>
|
||||
{extraAuthorizationItems.map(item => (
|
||||
<button key={item.id} type="button" onClick={() => onItemClick?.(item.id)}>{item.name}</button>
|
||||
))}
|
||||
{credentials.map(item => (
|
||||
<button key={item.id} type="button" onClick={() => onItemClick?.(item.id)}>{item.name}</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
usePluginAuth: () => {
|
||||
const credentialInfo = mockGetPluginCredentialInfo() ?? { credentials: [] }
|
||||
return {
|
||||
canApiKey: true,
|
||||
canOAuth: false,
|
||||
credentials: credentialInfo.credentials ?? [],
|
||||
disabled: false,
|
||||
invalidPluginCredentialInfo: vi.fn(),
|
||||
notAllowCustomCredential: false,
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
// ==================== Test Utilities ====================
|
||||
|
||||
const createTestQueryClient = () =>
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { AuthCategory, CredentialTypeEnum } from '../types'
|
||||
|
||||
vi.mock('../authorize/add-api-key-button', () => ({ default: () => null }))
|
||||
vi.mock('../authorize/add-oauth-button', () => ({ default: () => null }))
|
||||
vi.mock('../authorize/api-key-modal', () => ({ default: () => null }))
|
||||
vi.mock('../authorized', () => ({ default: () => null }))
|
||||
vi.mock('../authorized-in-data-source-node', () => ({ default: () => null }))
|
||||
vi.mock('../authorized-in-node', () => ({ default: () => null }))
|
||||
vi.mock('../plugin-auth', () => ({ default: () => null }))
|
||||
vi.mock('../plugin-auth-in-agent', () => ({ default: () => null }))
|
||||
vi.mock('../plugin-auth-in-datasource-node', () => ({ default: () => null }))
|
||||
vi.mock('../hooks/use-plugin-auth', () => ({ usePluginAuth: () => ({}) }))
|
||||
vi.mock('../hooks/use-plugin-auth-action', () => ({}))
|
||||
|
||||
describe('plugin-auth index exports', () => {
|
||||
it('should export all required components and hooks', async () => {
|
||||
const exports = await import('../index')
|
||||
|
||||
@ -56,6 +56,48 @@ vi.mock('@/service/use-triggers', () => ({
|
||||
useInvalidTriggerDynamicOptions: () => vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../authorize', () => ({
|
||||
default: () => (
|
||||
<button type="button">
|
||||
plugin.auth.useApiAuth
|
||||
</button>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('../authorized', () => ({
|
||||
default: ({
|
||||
credentials = [],
|
||||
extraAuthorizationItems = [],
|
||||
isOpen,
|
||||
onItemClick,
|
||||
onOpenChange,
|
||||
renderTrigger,
|
||||
}: {
|
||||
credentials?: Credential[]
|
||||
extraAuthorizationItems?: Credential[]
|
||||
isOpen?: boolean
|
||||
onItemClick?: (id: string) => void
|
||||
onOpenChange?: (open: boolean) => void
|
||||
renderTrigger?: (isOpen?: boolean) => ReactNode
|
||||
}) => (
|
||||
<div>
|
||||
<div data-testid="authorized-trigger" onClick={() => onOpenChange?.(!isOpen)}>
|
||||
{renderTrigger?.(isOpen)}
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div>
|
||||
{extraAuthorizationItems.map(item => (
|
||||
<button key={item.id} type="button" onClick={() => onItemClick?.(item.isWorkspaceDefault ? '' : item.id)}>{item.name}</button>
|
||||
))}
|
||||
{credentials.map(item => (
|
||||
<button key={item.id} type="button" onClick={() => onItemClick?.(item.id)}>{item.name}</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}))
|
||||
|
||||
// ==================== Test Utilities ====================
|
||||
|
||||
const createTestQueryClient = () =>
|
||||
@ -120,7 +162,7 @@ describe('PluginAuthInAgent Component', () => {
|
||||
<PluginAuthInAgent pluginPayload={pluginPayload} />,
|
||||
{ wrapper: createWrapper() },
|
||||
)
|
||||
expect(screen.getByRole('button')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'plugin.auth.useApiAuth' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render Authorized with workspace default when authorized', async () => {
|
||||
@ -130,7 +172,7 @@ describe('PluginAuthInAgent Component', () => {
|
||||
<PluginAuthInAgent pluginPayload={pluginPayload} />,
|
||||
{ wrapper: createWrapper() },
|
||||
)
|
||||
expect(screen.getByRole('button')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /plugin\.auth\.workspaceDefault/i })).toBeInTheDocument()
|
||||
expect(screen.getByText('plugin.auth.workspaceDefault')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user