diff --git a/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx b/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx
index 8334512047..c78934811a 100644
--- a/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx
+++ b/web/app/components/base/app-icon-picker/__tests__/index.spec.tsx
@@ -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 (
+
+
+
+
+ )
+ },
+}))
+
+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(null)
+ const [animatedUrl, setAnimatedUrl] = React.useState(null)
+ const [showCropper, setShowCropper] = React.useState(false)
+
+ return (
+
+
drop image here
+
{
+ 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 && (
+
+
+
+ )}
+ {animatedUrl &&

}
+
+ )
+ },
+}))
+
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 }) => (
+
+ {children}
+
+ ),
+}))
+
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()
})
diff --git a/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx b/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx
index 91ffbaa24a..e3cd8224cf 100644
--- a/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/__tests__/authorized-in-node.spec.tsx
@@ -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
+ }) => (
+
+
onOpenChange?.(!isOpen)}>
+ {renderTrigger?.(isOpen)}
+
+ {isOpen && (
+
+ {extraAuthorizationItems.map(item => (
+
+ ))}
+ {credentials.map(item => (
+
+ ))}
+
+ )}
+
+ ),
+ usePluginAuth: () => {
+ const credentialInfo = mockGetPluginCredentialInfo() ?? { credentials: [] }
+ return {
+ canApiKey: true,
+ canOAuth: false,
+ credentials: credentialInfo.credentials ?? [],
+ disabled: false,
+ invalidPluginCredentialInfo: vi.fn(),
+ notAllowCustomCredential: false,
+ }
+ },
+}))
+
// ==================== Test Utilities ====================
const createTestQueryClient = () =>
diff --git a/web/app/components/plugins/plugin-auth/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-auth/__tests__/index.spec.tsx
index d259b27c30..e71fabc09d 100644
--- a/web/app/components/plugins/plugin-auth/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/__tests__/index.spec.tsx
@@ -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')
diff --git a/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx b/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx
index 901c3ab49a..316972b006 100644
--- a/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/__tests__/plugin-auth-in-agent.spec.tsx
@@ -56,6 +56,48 @@ vi.mock('@/service/use-triggers', () => ({
useInvalidTriggerDynamicOptions: () => vi.fn(),
}))
+vi.mock('../authorize', () => ({
+ default: () => (
+
+ ),
+}))
+
+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
+ }) => (
+
+
onOpenChange?.(!isOpen)}>
+ {renderTrigger?.(isOpen)}
+
+ {isOpen && (
+
+ {extraAuthorizationItems.map(item => (
+
+ ))}
+ {credentials.map(item => (
+
+ ))}
+
+ )}
+
+ ),
+}))
+
// ==================== Test Utilities ====================
const createTestQueryClient = () =>
@@ -120,7 +162,7 @@ describe('PluginAuthInAgent Component', () => {
,
{ 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', () => {
,
{ wrapper: createWrapper() },
)
- expect(screen.getByRole('button')).toBeInTheDocument()
+ expect(screen.getByRole('button', { name: /plugin\.auth\.workspaceDefault/i })).toBeInTheDocument()
expect(screen.getByText('plugin.auth.workspaceDefault')).toBeInTheDocument()
})