import type { ReactNode } from 'react'
import type { PluginPayload } from '../types'
import type { FormSchema } from '@/app/components/base/form/types'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AuthCategory } from '../types'
// Create a wrapper with QueryClientProvider
const createTestQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
},
})
const createWrapper = () => {
const testQueryClient = createTestQueryClient()
return ({ children }: { children: ReactNode }) => (
{children}
)
}
// Mock API hooks - these make network requests so must be mocked
const mockGetPluginOAuthUrl = vi.fn()
const mockGetPluginOAuthClientSchema = vi.fn()
const mockSetPluginOAuthCustomClient = vi.fn()
const mockDeletePluginOAuthCustomClient = vi.fn()
const mockInvalidPluginOAuthClientSchema = vi.fn()
const mockAddPluginCredential = vi.fn()
const mockUpdatePluginCredential = vi.fn()
const mockGetPluginCredentialSchema = vi.fn()
vi.mock('../hooks/use-credential', () => ({
useGetPluginOAuthUrlHook: () => ({
mutateAsync: mockGetPluginOAuthUrl,
}),
useGetPluginOAuthClientSchemaHook: () => ({
data: mockGetPluginOAuthClientSchema(),
isLoading: false,
}),
useSetPluginOAuthCustomClientHook: () => ({
mutateAsync: mockSetPluginOAuthCustomClient,
}),
useDeletePluginOAuthCustomClientHook: () => ({
mutateAsync: mockDeletePluginOAuthCustomClient,
}),
useInvalidPluginOAuthClientSchemaHook: () => mockInvalidPluginOAuthClientSchema,
useAddPluginCredentialHook: () => ({
mutateAsync: mockAddPluginCredential,
}),
useUpdatePluginCredentialHook: () => ({
mutateAsync: mockUpdatePluginCredential,
}),
useGetPluginCredentialSchemaHook: () => ({
data: mockGetPluginCredentialSchema(),
isLoading: false,
}),
}))
// Mock openOAuthPopup - requires window operations
const mockOpenOAuthPopup = vi.fn()
vi.mock('@/hooks/use-oauth', () => ({
openOAuthPopup: (...args: unknown[]) => mockOpenOAuthPopup(...args),
}))
// Mock service/use-triggers - API service
vi.mock('@/service/use-triggers', () => ({
useTriggerPluginDynamicOptions: () => ({
data: { options: [] },
isLoading: false,
}),
useTriggerPluginDynamicOptionsInfo: () => ({
data: null,
isLoading: false,
}),
useInvalidTriggerDynamicOptions: () => vi.fn(),
}))
// Mock AuthForm to control form validation in tests
const mockGetFormValues = vi.fn()
vi.mock('@/app/components/base/form/form-scenarios/auth', () => ({
default: vi.fn().mockImplementation(({ ref }: { ref: { current: unknown } }) => {
if (ref)
ref.current = { getFormValues: mockGetFormValues }
return
Auth Form
}),
}))
// Mock useToastContext
const mockNotify = vi.fn()
vi.mock('@/app/components/base/toast', () => ({
useToastContext: () => ({ notify: mockNotify }),
}))
// Factory function for creating test PluginPayload
const createPluginPayload = (overrides: Partial = {}): PluginPayload => ({
category: AuthCategory.tool,
provider: 'test-provider',
...overrides,
})
// Factory for form schemas
const createFormSchema = (overrides: Partial = {}): FormSchema => ({
type: 'text-input' as FormSchema['type'],
name: 'test-field',
label: 'Test Field',
required: false,
...overrides,
})
// ==================== AddApiKeyButton Tests ====================
describe('AddApiKeyButton', () => {
let AddApiKeyButton: typeof import('./add-api-key-button').default
beforeEach(async () => {
vi.clearAllMocks()
mockGetPluginCredentialSchema.mockReturnValue([])
const importedAddApiKeyButton = await import('./add-api-key-button')
AddApiKeyButton = importedAddApiKeyButton.default
})
describe('Rendering', () => {
it('should render button with default text', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
expect(screen.getByRole('button')).toHaveTextContent('Use Api Key')
})
it('should render button with custom text', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button')).toHaveTextContent('Custom API Key')
})
it('should apply button variant', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button').className).toContain('btn-primary')
})
it('should use secondary-accent variant by default', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
// Verify the default button has secondary-accent variant class
expect(screen.getByRole('button').className).toContain('btn-secondary-accent')
})
})
describe('Props Testing', () => {
it('should disable button when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button')).toBeDisabled()
})
it('should not disable button when disabled prop is false', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button')).not.toBeDisabled()
})
it('should accept formSchemas prop', () => {
const pluginPayload = createPluginPayload()
const formSchemas = [createFormSchema({ name: 'api_key', label: 'API Key' })]
expect(() => {
render(
,
{ wrapper: createWrapper() },
)
}).not.toThrow()
})
})
describe('User Interactions', () => {
it('should open modal when button is clicked', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByRole('button'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
})
it('should not open modal when button is disabled', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
const button = screen.getByRole('button')
fireEvent.click(button)
// Modal should not appear
expect(screen.queryByText('plugin.auth.useApiAuth')).not.toBeInTheDocument()
})
})
describe('Edge Cases', () => {
it('should handle empty pluginPayload properties', () => {
const pluginPayload = createPluginPayload({
provider: '',
providerType: undefined,
})
expect(() => {
render(, { wrapper: createWrapper() })
}).not.toThrow()
})
it('should handle all auth categories', () => {
const categories = [AuthCategory.tool, AuthCategory.datasource, AuthCategory.model, AuthCategory.trigger]
categories.forEach((category) => {
const pluginPayload = createPluginPayload({ category })
const { unmount } = render(, { wrapper: createWrapper() })
expect(screen.getByRole('button')).toBeInTheDocument()
unmount()
})
})
})
describe('Modal Behavior', () => {
it('should close modal when onClose is called from ApiKeyModal', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
render(, { wrapper: createWrapper() })
// Open modal
fireEvent.click(screen.getByRole('button'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
// Close modal via cancel button
fireEvent.click(screen.getByText('common.operation.cancel'))
await waitFor(() => {
expect(screen.queryByText('plugin.auth.useApiAuth')).not.toBeInTheDocument()
})
})
it('should call onUpdate when provided and modal triggers update', async () => {
const pluginPayload = createPluginPayload()
const onUpdate = vi.fn()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
render(
,
{ wrapper: createWrapper() },
)
// Open modal
fireEvent.click(screen.getByRole('button'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
})
})
describe('Memoization', () => {
it('should be a memoized component', async () => {
const AddApiKeyButtonDefault = (await import('./add-api-key-button')).default
expect(typeof AddApiKeyButtonDefault).toBe('object')
})
})
})
// ==================== AddOAuthButton Tests ====================
describe('AddOAuthButton', () => {
let AddOAuthButton: typeof import('./add-oauth-button').default
beforeEach(async () => {
vi.clearAllMocks()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
client_params: {},
redirect_uri: 'https://example.com/callback',
})
mockGetPluginOAuthUrl.mockResolvedValue({ authorization_url: 'https://oauth.example.com/auth' })
const importedAddOAuthButton = await import('./add-oauth-button')
AddOAuthButton = importedAddOAuthButton.default
})
describe('Rendering - Not Configured State', () => {
it('should render setup OAuth button when not configured', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
})
render(, { wrapper: createWrapper() })
expect(screen.getByText('plugin.auth.setupOAuth')).toBeInTheDocument()
})
it('should apply button variant to setup button', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
})
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button').className).toContain('btn-secondary')
})
})
describe('Rendering - Configured State', () => {
it('should render OAuth button when system OAuth params exist', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
})
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('Connect OAuth')).toBeInTheDocument()
})
it('should render OAuth button when custom client is enabled', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('OAuth')).toBeInTheDocument()
})
it('should show custom badge when custom client is enabled', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
render(, { wrapper: createWrapper() })
expect(screen.getByText('plugin.auth.custom')).toBeInTheDocument()
})
})
describe('Props Testing', () => {
it('should disable button when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
})
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button')).toBeDisabled()
})
it('should apply custom className', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByRole('button').className).toContain('custom-class')
})
it('should use oAuthData prop when provided', () => {
const pluginPayload = createPluginPayload()
const oAuthData = {
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: true,
client_params: {},
redirect_uri: 'https://custom.example.com/callback',
}
render(
,
{ wrapper: createWrapper() },
)
// Should render configured button since oAuthData has is_system_oauth_params_exists=true
expect(screen.queryByText('plugin.auth.setupOAuth')).not.toBeInTheDocument()
})
})
describe('User Interactions', () => {
it('should trigger OAuth flow when configured button is clicked', async () => {
const pluginPayload = createPluginPayload()
const onUpdate = vi.fn()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
mockGetPluginOAuthUrl.mockResolvedValue({ authorization_url: 'https://oauth.example.com/auth' })
render(
,
{ wrapper: createWrapper() },
)
// Click the main button area (left side)
const buttonText = screen.getByText('use oauth')
fireEvent.click(buttonText)
await waitFor(() => {
expect(mockGetPluginOAuthUrl).toHaveBeenCalled()
})
})
it('should open settings when setup button is clicked', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByText('plugin.auth.setupOAuth'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
it('should not trigger OAuth when no authorization_url is returned', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
mockGetPluginOAuthUrl.mockResolvedValue({ authorization_url: '' })
render(, { wrapper: createWrapper() })
const buttonText = screen.getByText('use oauth')
fireEvent.click(buttonText)
await waitFor(() => {
expect(mockGetPluginOAuthUrl).toHaveBeenCalled()
})
expect(mockOpenOAuthPopup).not.toHaveBeenCalled()
})
it('should call onUpdate callback after successful OAuth', async () => {
const pluginPayload = createPluginPayload()
const onUpdate = vi.fn()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
})
mockGetPluginOAuthUrl.mockResolvedValue({ authorization_url: 'https://oauth.example.com/auth' })
// Simulate openOAuthPopup calling the success callback
mockOpenOAuthPopup.mockImplementation((url, callback) => {
callback?.()
})
render(
,
{ wrapper: createWrapper() },
)
const buttonText = screen.getByText('use oauth')
fireEvent.click(buttonText)
await waitFor(() => {
expect(mockOpenOAuthPopup).toHaveBeenCalledWith(
'https://oauth.example.com/auth',
expect.any(Function),
)
})
// Verify onUpdate was called through the callback
expect(onUpdate).toHaveBeenCalled()
})
it('should open OAuth settings when settings icon is clicked', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
// Click the settings icon using data-testid for reliable selection
const settingsButton = screen.getByTestId('oauth-settings-button')
fireEvent.click(settingsButton)
await waitFor(() => {
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
it('should close OAuth settings modal when onClose is called', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
// Open settings
fireEvent.click(screen.getByText('plugin.auth.setupOAuth'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
// Close settings via cancel button
fireEvent.click(screen.getByText('common.operation.cancel'))
await waitFor(() => {
expect(screen.queryByText('plugin.auth.oauthClientSettings')).not.toBeInTheDocument()
})
})
})
describe('Schema Processing', () => {
it('should handle is_system_oauth_params_exists state', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
// Should show the configured button, not setup button
expect(screen.queryByText('plugin.auth.setupOAuth')).not.toBeInTheDocument()
})
it('should open OAuth settings modal with correct data', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID', required: true })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByText('plugin.auth.setupOAuth'))
await waitFor(() => {
// OAuthClientSettings modal should open
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
it('should handle client_params defaults in schema', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [
createFormSchema({ name: 'client_id', label: 'Client ID' }),
createFormSchema({ name: 'client_secret', label: 'Client Secret' }),
],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
client_params: {
client_id: 'preset-client-id',
client_secret: 'preset-secret',
},
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
// Open settings by clicking the gear icon
const button = screen.getByRole('button')
const gearIconContainer = button.querySelector('[class*="shrink-0"][class*="w-8"]')
if (gearIconContainer)
fireEvent.click(gearIconContainer)
await waitFor(() => {
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
it('should handle __auth_client__ logic when configured with system OAuth and no custom client', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
client_params: {},
})
render(, { wrapper: createWrapper() })
// Should render configured button (not setup button)
expect(screen.queryByText('plugin.auth.setupOAuth')).not.toBeInTheDocument()
})
it('should open OAuth settings when system OAuth params exist', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID', required: true })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
// Click the settings icon
const button = screen.getByRole('button')
const gearIconContainer = button.querySelector('[class*="shrink-0"][class*="w-8"]')
if (gearIconContainer)
fireEvent.click(gearIconContainer)
await waitFor(() => {
// OAuthClientSettings modal should open
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
})
describe('Clipboard Operations', () => {
it('should have clipboard API available for copy operations', async () => {
const pluginPayload = createPluginPayload()
const mockWriteText = vi.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
value: { writeText: mockWriteText },
configurable: true,
})
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID', required: true })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByText('plugin.auth.setupOAuth'))
await waitFor(() => {
// OAuthClientSettings modal opens
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
// Verify clipboard API is available
expect(navigator.clipboard.writeText).toBeDefined()
})
})
describe('__auth_client__ Logic', () => {
it('should return default when not configured and system OAuth params exist', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: true,
client_params: {},
})
render(, { wrapper: createWrapper() })
// When isConfigured is true (is_system_oauth_params_exists=true), it should show the configured button
expect(screen.queryByText('plugin.auth.setupOAuth')).not.toBeInTheDocument()
})
it('should return custom when not configured and no system OAuth params', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
client_params: {},
})
render(, { wrapper: createWrapper() })
// When not configured, it should show the setup button
expect(screen.getByText('plugin.auth.setupOAuth')).toBeInTheDocument()
})
})
describe('Edge Cases', () => {
it('should handle empty schema', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
})
expect(() => {
render(, { wrapper: createWrapper() })
}).not.toThrow()
})
it('should handle undefined oAuthData fields', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue(undefined)
expect(() => {
render(, { wrapper: createWrapper() })
}).not.toThrow()
})
it('should handle null client_params', () => {
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'test' })],
is_oauth_custom_client_enabled: true,
is_system_oauth_params_exists: true,
client_params: null,
})
expect(() => {
render(, { wrapper: createWrapper() })
}).not.toThrow()
})
})
})
// ==================== ApiKeyModal Tests ====================
describe('ApiKeyModal', () => {
let ApiKeyModal: typeof import('./api-key-modal').default
beforeEach(async () => {
vi.clearAllMocks()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key', required: true }),
])
mockAddPluginCredential.mockResolvedValue({})
mockUpdatePluginCredential.mockResolvedValue({})
// Reset form values mock to return validation failed by default
mockGetFormValues.mockReturnValue({
isCheckValidated: false,
values: {},
})
const importedApiKeyModal = await import('./api-key-modal')
ApiKeyModal = importedApiKeyModal.default
})
describe('Rendering', () => {
it('should render modal with title', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
it('should render modal with subtitle', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
expect(screen.getByText('plugin.auth.useApiAuthDesc')).toBeInTheDocument()
})
it('should render form when data is loaded', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
// AuthForm is mocked, so check for the mock element
expect(screen.getByTestId('mock-auth-form')).toBeInTheDocument()
})
})
describe('Props Testing', () => {
it('should call onClose when modal is closed', () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
render(
,
{ wrapper: createWrapper() },
)
// Find and click cancel button
const cancelButton = screen.getByText('common.operation.cancel')
fireEvent.click(cancelButton)
expect(onClose).toHaveBeenCalled()
})
it('should disable confirm button when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
const confirmButton = screen.getByText('common.operation.save')
expect(confirmButton.closest('button')).toBeDisabled()
})
it('should show modal when editValues is provided', () => {
const pluginPayload = createPluginPayload()
const editValues = {
__name__: 'Test Name',
__credential_id__: 'test-id',
api_key: 'test-key',
}
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
it('should use formSchemas from props when provided', () => {
const pluginPayload = createPluginPayload()
const customSchemas = [
createFormSchema({ name: 'custom_field', label: 'Custom Field' }),
]
render(
,
{ wrapper: createWrapper() },
)
// AuthForm is mocked, verify modal renders
expect(screen.getByTestId('mock-auth-form')).toBeInTheDocument()
})
})
describe('Form Behavior', () => {
it('should render AuthForm component', () => {
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
// AuthForm is mocked, verify it's rendered
expect(screen.getByTestId('mock-auth-form')).toBeInTheDocument()
})
it('should render modal with editValues', () => {
const pluginPayload = createPluginPayload()
const editValues = {
__name__: 'Existing Name',
api_key: 'existing-key',
}
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
})
describe('Form Submission - handleConfirm', () => {
beforeEach(() => {
// Default: form validation passes with empty values
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: {
__name__: 'Test Name',
api_key: 'test-api-key',
},
})
})
it('should call addPluginCredential when creating new credential', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
mockAddPluginCredential.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
// Click confirm button
const confirmButton = screen.getByText('common.operation.save')
fireEvent.click(confirmButton)
await waitFor(() => {
expect(mockAddPluginCredential).toHaveBeenCalled()
})
})
it('should call updatePluginCredential when editing existing credential', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
const editValues = {
__name__: 'Test Credential',
__credential_id__: 'test-credential-id',
api_key: 'existing-key',
}
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
mockUpdatePluginCredential.mockResolvedValue({})
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: {
__name__: 'Test Credential',
__credential_id__: 'test-credential-id',
api_key: 'updated-key',
},
})
render(
,
{ wrapper: createWrapper() },
)
// Click confirm button
const confirmButton = screen.getByText('common.operation.save')
fireEvent.click(confirmButton)
await waitFor(() => {
expect(mockUpdatePluginCredential).toHaveBeenCalled()
})
})
it('should call onClose and onUpdate after successful submission', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
mockAddPluginCredential.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
// Click confirm button
const confirmButton = screen.getByText('common.operation.save')
fireEvent.click(confirmButton)
await waitFor(() => {
expect(onClose).toHaveBeenCalled()
expect(onUpdate).toHaveBeenCalled()
})
})
it('should not call API when form validation fails', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key', required: true }),
])
mockGetFormValues.mockReturnValue({
isCheckValidated: false,
values: {},
})
render(
,
{ wrapper: createWrapper() },
)
// Click confirm button
const confirmButton = screen.getByText('common.operation.save')
fireEvent.click(confirmButton)
// Verify API was not called since validation failed synchronously
expect(mockAddPluginCredential).not.toHaveBeenCalled()
})
it('should handle doingAction state to prevent double submission', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
// Make the API call slow
mockAddPluginCredential.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
render(
,
{ wrapper: createWrapper() },
)
// Click confirm button twice quickly
const confirmButton = screen.getByText('common.operation.save')
fireEvent.click(confirmButton)
fireEvent.click(confirmButton)
// Should only be called once due to doingAction guard
await waitFor(() => {
expect(mockAddPluginCredential).toHaveBeenCalledTimes(1)
})
})
it('should return early if doingActionRef is true during concurrent clicks', async () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
// Create a promise that we can control
let resolveFirstCall: (value?: unknown) => void = () => {}
let apiCallCount = 0
mockAddPluginCredential.mockImplementation(() => {
apiCallCount++
if (apiCallCount === 1) {
// First call: return a pending promise
return new Promise((resolve) => {
resolveFirstCall = resolve
})
}
// Subsequent calls should not happen but return resolved promise
return Promise.resolve({})
})
render(
,
{ wrapper: createWrapper() },
)
const confirmButton = screen.getByText('common.operation.save')
// First click starts the request
fireEvent.click(confirmButton)
// Wait for the first API call to be made
await waitFor(() => {
expect(apiCallCount).toBe(1)
})
// Second click while first request is still pending should be ignored
fireEvent.click(confirmButton)
// Verify only one API call was made (no additional calls)
expect(apiCallCount).toBe(1)
// Clean up by resolving the promise
resolveFirstCall()
})
it('should call onRemove when extra button is clicked in edit mode', async () => {
const pluginPayload = createPluginPayload()
const onRemove = vi.fn()
const editValues = {
__name__: 'Test Credential',
__credential_id__: 'test-credential-id',
}
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
render(
,
{ wrapper: createWrapper() },
)
// Find and click the remove button
const removeButton = screen.getByText('common.operation.remove')
fireEvent.click(removeButton)
expect(onRemove).toHaveBeenCalled()
})
})
describe('Edge Cases', () => {
it('should handle empty credentials schema', () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([])
render(, { wrapper: createWrapper() })
// Should still render the modal with authorization name field
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
it('should handle undefined detail in pluginPayload', () => {
const pluginPayload = createPluginPayload({ detail: undefined })
expect(() => {
render(, { wrapper: createWrapper() })
}).not.toThrow()
})
it('should handle form schema with default values', () => {
const pluginPayload = createPluginPayload()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key', default: 'default-key' }),
])
expect(() => {
render(
,
{ wrapper: createWrapper() },
)
}).not.toThrow()
expect(screen.getByTestId('mock-auth-form')).toBeInTheDocument()
})
})
})
// ==================== OAuthClientSettings Tests ====================
describe('OAuthClientSettings', () => {
let OAuthClientSettings: typeof import('./oauth-client-settings').default
beforeEach(async () => {
vi.clearAllMocks()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
mockDeletePluginOAuthCustomClient.mockResolvedValue({})
const importedOAuthClientSettings = await import('./oauth-client-settings')
OAuthClientSettings = importedOAuthClientSettings.default
})
const defaultSchemas: FormSchema[] = [
createFormSchema({ name: 'client_id', label: 'Client ID', required: true }),
createFormSchema({ name: 'client_secret', label: 'Client Secret', required: true }),
]
describe('Rendering', () => {
it('should render modal with correct title', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
it('should render Save and Auth button', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.saveAndAuth')).toBeInTheDocument()
})
it('should render Save Only button', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.saveOnly')).toBeInTheDocument()
})
it('should render Cancel button', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('common.operation.cancel')).toBeInTheDocument()
})
it('should render form from schemas', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
// AuthForm is mocked
expect(screen.getByTestId('mock-auth-form')).toBeInTheDocument()
})
})
describe('Props Testing', () => {
it('should call onClose when cancel button is clicked', () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('common.operation.cancel'))
expect(onClose).toHaveBeenCalled()
})
it('should disable buttons when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
const confirmButton = screen.getByText('plugin.auth.saveAndAuth')
expect(confirmButton.closest('button')).toBeDisabled()
})
it('should render with editValues', () => {
const pluginPayload = createPluginPayload()
const editValues = {
client_id: 'existing-client-id',
client_secret: 'existing-secret',
__oauth_client__: 'custom',
}
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
describe('Remove Button', () => {
it('should show remove button when custom client and hasOriginalClientParams', () => {
const pluginPayload = createPluginPayload()
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('common.operation.remove')).toBeInTheDocument()
})
it('should not show remove button when using default client', () => {
const pluginPayload = createPluginPayload()
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'default',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.queryByText('common.operation.remove')).not.toBeInTheDocument()
})
})
describe('Form Submission', () => {
beforeEach(() => {
// Default: form validation passes
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: {
__oauth_client__: 'custom',
client_id: 'test-client-id',
client_secret: 'test-secret',
},
})
})
it('should render Save and Auth button that is clickable', async () => {
const pluginPayload = createPluginPayload()
const onAuth = vi.fn().mockResolvedValue(undefined)
render(
,
{ wrapper: createWrapper() },
)
const saveAndAuthButton = screen.getByText('plugin.auth.saveAndAuth')
expect(saveAndAuthButton).toBeInTheDocument()
expect(saveAndAuthButton.closest('button')).not.toBeDisabled()
})
it('should call setPluginOAuthCustomClient when Save Only is clicked', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
// Click Save Only button
fireEvent.click(screen.getByText('plugin.auth.saveOnly'))
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalled()
})
})
it('should call onClose and onUpdate after successful submission', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('plugin.auth.saveOnly'))
await waitFor(() => {
expect(onClose).toHaveBeenCalled()
expect(onUpdate).toHaveBeenCalled()
})
})
it('should call onAuth after handleConfirmAndAuthorize', async () => {
const pluginPayload = createPluginPayload()
const onAuth = vi.fn().mockResolvedValue(undefined)
const onClose = vi.fn()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
// Click Save and Auth button
fireEvent.click(screen.getByText('plugin.auth.saveAndAuth'))
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalled()
expect(onAuth).toHaveBeenCalled()
})
})
it('should handle form with empty values', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
// Modal should render with save buttons
expect(screen.getByText('plugin.auth.saveOnly')).toBeInTheDocument()
expect(screen.getByText('plugin.auth.saveAndAuth')).toBeInTheDocument()
})
it('should call deletePluginOAuthCustomClient when Remove is clicked', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockDeletePluginOAuthCustomClient.mockResolvedValue({})
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
// Click Remove button
fireEvent.click(screen.getByText('common.operation.remove'))
await waitFor(() => {
expect(mockDeletePluginOAuthCustomClient).toHaveBeenCalled()
})
})
it('should call onClose and onUpdate after successful removal', async () => {
const pluginPayload = createPluginPayload()
const onClose = vi.fn()
const onUpdate = vi.fn()
mockDeletePluginOAuthCustomClient.mockResolvedValue({})
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('common.operation.remove'))
await waitFor(() => {
expect(onClose).toHaveBeenCalled()
expect(onUpdate).toHaveBeenCalled()
})
})
it('should prevent double submission when doingAction is true', async () => {
const pluginPayload = createPluginPayload()
// Make the API call slow
mockSetPluginOAuthCustomClient.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
render(
,
{ wrapper: createWrapper() },
)
// Click Save Only button twice quickly
const saveButton = screen.getByText('plugin.auth.saveOnly')
fireEvent.click(saveButton)
fireEvent.click(saveButton)
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalledTimes(1)
})
})
it('should return early from handleConfirm if doingActionRef is true', async () => {
const pluginPayload = createPluginPayload()
let resolveFirstCall: (value?: unknown) => void = () => {}
let apiCallCount = 0
mockSetPluginOAuthCustomClient.mockImplementation(() => {
apiCallCount++
if (apiCallCount === 1) {
return new Promise((resolve) => {
resolveFirstCall = resolve
})
}
return Promise.resolve({})
})
render(
,
{ wrapper: createWrapper() },
)
const saveButton = screen.getByText('plugin.auth.saveOnly')
// First click starts the request
fireEvent.click(saveButton)
// Wait for the first API call to be made
await waitFor(() => {
expect(apiCallCount).toBe(1)
})
// Second click while first request is pending should be ignored
fireEvent.click(saveButton)
// Verify only one API call was made (no additional calls)
expect(apiCallCount).toBe(1)
// Clean up
resolveFirstCall()
})
it('should return early from handleRemove if doingActionRef is true', async () => {
const pluginPayload = createPluginPayload()
let resolveFirstCall: (value?: unknown) => void = () => {}
let deleteCallCount = 0
mockDeletePluginOAuthCustomClient.mockImplementation(() => {
deleteCallCount++
if (deleteCallCount === 1) {
return new Promise((resolve) => {
resolveFirstCall = resolve
})
}
return Promise.resolve({})
})
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
const removeButton = screen.getByText('common.operation.remove')
// First click starts the delete request
fireEvent.click(removeButton)
// Wait for the first delete call to be made
await waitFor(() => {
expect(deleteCallCount).toBe(1)
})
// Second click while first request is pending should be ignored
fireEvent.click(removeButton)
// Verify only one delete call was made (no additional calls)
expect(deleteCallCount).toBe(1)
// Clean up
resolveFirstCall()
})
})
describe('Edge Cases', () => {
it('should handle empty schemas', () => {
const pluginPayload = createPluginPayload()
expect(() => {
render(
,
{ wrapper: createWrapper() },
)
}).not.toThrow()
})
it('should handle schemas without default values', () => {
const pluginPayload = createPluginPayload()
const schemasWithoutDefaults: FormSchema[] = [
createFormSchema({ name: 'field1', label: 'Field 1', default: undefined }),
]
expect(() => {
render(
,
{ wrapper: createWrapper() },
)
}).not.toThrow()
})
it('should handle undefined editValues', () => {
const pluginPayload = createPluginPayload()
expect(() => {
render(
,
{ wrapper: createWrapper() },
)
}).not.toThrow()
})
})
describe('Branch Coverage - defaultValues computation', () => {
it('should compute defaultValues from schemas with default values', () => {
const pluginPayload = createPluginPayload()
const schemasWithDefaults: FormSchema[] = [
createFormSchema({ name: 'client_id', label: 'Client ID', default: 'default-id' }),
createFormSchema({ name: 'client_secret', label: 'Client Secret', default: 'default-secret' }),
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
it('should skip schemas without default values in defaultValues computation', () => {
const pluginPayload = createPluginPayload()
const mixedSchemas: FormSchema[] = [
createFormSchema({ name: 'field_with_default', label: 'With Default', default: 'value' }),
createFormSchema({ name: 'field_without_default', label: 'Without Default', default: undefined }),
createFormSchema({ name: 'field_with_empty', label: 'Empty Default', default: '' }),
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
describe('Branch Coverage - __oauth_client__ value', () => {
beforeEach(() => {
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: {
__oauth_client__: 'default',
client_id: 'test-id',
},
})
})
it('should send enable_oauth_custom_client=false when __oauth_client__ is default', async () => {
const pluginPayload = createPluginPayload()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('plugin.auth.saveOnly'))
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalledWith(
expect.objectContaining({
enable_oauth_custom_client: false,
}),
)
})
})
it('should send enable_oauth_custom_client=true when __oauth_client__ is custom', async () => {
const pluginPayload = createPluginPayload()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: {
__oauth_client__: 'custom',
client_id: 'test-id',
},
})
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('plugin.auth.saveOnly'))
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalledWith(
expect.objectContaining({
enable_oauth_custom_client: true,
}),
)
})
})
})
describe('Branch Coverage - onAuth callback', () => {
beforeEach(() => {
mockGetFormValues.mockReturnValue({
isCheckValidated: true,
values: { __oauth_client__: 'custom' },
})
})
it('should call onAuth when provided and Save and Auth is clicked', async () => {
const pluginPayload = createPluginPayload()
const onAuth = vi.fn().mockResolvedValue(undefined)
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('plugin.auth.saveAndAuth'))
await waitFor(() => {
expect(onAuth).toHaveBeenCalled()
})
})
it('should not call onAuth when not provided', async () => {
const pluginPayload = createPluginPayload()
mockSetPluginOAuthCustomClient.mockResolvedValue({})
render(
,
{ wrapper: createWrapper() },
)
fireEvent.click(screen.getByText('plugin.auth.saveAndAuth'))
await waitFor(() => {
expect(mockSetPluginOAuthCustomClient).toHaveBeenCalled()
})
// No onAuth to call, but should not throw
})
})
describe('Branch Coverage - disabled states', () => {
it('should disable buttons when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.saveAndAuth').closest('button')).toBeDisabled()
expect(screen.getByText('plugin.auth.saveOnly').closest('button')).toBeDisabled()
})
it('should disable Remove button when editValues is undefined', () => {
const pluginPayload = createPluginPayload()
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
// Remove button should exist but be disabled
const removeButton = screen.queryByText('common.operation.remove')
if (removeButton) {
expect(removeButton.closest('button')).toBeDisabled()
}
})
it('should disable Remove button when disabled prop is true', () => {
const pluginPayload = createPluginPayload()
const schemasWithOAuthClient: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
const removeButton = screen.getByText('common.operation.remove')
expect(removeButton.closest('button')).toBeDisabled()
})
})
describe('Branch Coverage - pluginPayload.detail', () => {
it('should render ReadmeEntrance when pluginPayload has detail', () => {
const pluginPayload = createPluginPayload({
detail: {
name: 'test-plugin',
label: { en_US: 'Test Plugin' },
} as unknown as PluginPayload['detail'],
})
render(
,
{ wrapper: createWrapper() },
)
// ReadmeEntrance should be rendered (it's mocked in vitest.setup)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
it('should not render ReadmeEntrance when pluginPayload has no detail', () => {
const pluginPayload = createPluginPayload({ detail: undefined })
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
describe('Branch Coverage - footerSlot conditions', () => {
it('should show Remove button only when __oauth_client__=custom AND hasOriginalClientParams=true', () => {
const pluginPayload = createPluginPayload()
const schemasWithCustomOAuth: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.getByText('common.operation.remove')).toBeInTheDocument()
})
it('should not show Remove button when hasOriginalClientParams=false', () => {
const pluginPayload = createPluginPayload()
const schemasWithCustomOAuth: FormSchema[] = [
{
name: '__oauth_client__',
label: 'OAuth Client',
type: 'radio' as FormSchema['type'],
options: [
{ label: 'Default', value: 'default' },
{ label: 'Custom', value: 'custom' },
],
default: 'custom',
required: false,
},
...defaultSchemas,
]
render(
,
{ wrapper: createWrapper() },
)
expect(screen.queryByText('common.operation.remove')).not.toBeInTheDocument()
})
})
describe('Memoization', () => {
it('should be a memoized component', async () => {
const OAuthClientSettingsDefault = (await import('./oauth-client-settings')).default
expect(typeof OAuthClientSettingsDefault).toBe('object')
})
})
})
// ==================== Integration Tests ====================
describe('Authorize Components Integration', () => {
beforeEach(() => {
vi.clearAllMocks()
mockGetPluginCredentialSchema.mockReturnValue([
createFormSchema({ name: 'api_key', label: 'API Key' }),
])
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
})
describe('AddApiKeyButton -> ApiKeyModal Flow', () => {
it('should open ApiKeyModal when AddApiKeyButton is clicked', async () => {
const AddApiKeyButton = (await import('./add-api-key-button')).default
const pluginPayload = createPluginPayload()
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByRole('button'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.useApiAuth')).toBeInTheDocument()
})
})
})
describe('AddOAuthButton -> OAuthClientSettings Flow', () => {
it('should open OAuthClientSettings when setup button is clicked', async () => {
const AddOAuthButton = (await import('./add-oauth-button')).default
const pluginPayload = createPluginPayload()
mockGetPluginOAuthClientSchema.mockReturnValue({
schema: [createFormSchema({ name: 'client_id', label: 'Client ID' })],
is_oauth_custom_client_enabled: false,
is_system_oauth_params_exists: false,
redirect_uri: 'https://example.com/callback',
})
render(, { wrapper: createWrapper() })
fireEvent.click(screen.getByText('plugin.auth.setupOAuth'))
await waitFor(() => {
expect(screen.getByText('plugin.auth.oauthClientSettings')).toBeInTheDocument()
})
})
})
})