import type { Credential, CredentialFormSchema, ModelProvider } from '../declarations' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { ConfigurationMethodEnum, CurrentSystemQuotaTypeEnum, CustomConfigurationStatusEnum, ModelModalModeEnum, ModelTypeEnum, PreferredProviderTypeEnum, QuotaUnitEnum, } from '../declarations' import ModelModal from './index' type CredentialData = { credentials: Record available_credentials: Credential[] } type ModelFormSchemas = { formSchemas: CredentialFormSchema[] formValues: Record modelNameAndTypeFormSchemas: CredentialFormSchema[] modelNameAndTypeFormValues: Record } const mockState = vi.hoisted(() => ({ isLoading: false, credentialData: { credentials: {}, available_credentials: [] } as CredentialData, doingAction: false, deleteCredentialId: null as string | null, isCurrentWorkspaceManager: true, formSchemas: [] as CredentialFormSchema[], formValues: {} as Record, modelNameAndTypeFormSchemas: [] as CredentialFormSchema[], modelNameAndTypeFormValues: {} as Record, })) const mockHandlers = vi.hoisted(() => ({ handleSaveCredential: vi.fn(), handleConfirmDelete: vi.fn(), closeConfirmDelete: vi.fn(), openConfirmDelete: vi.fn(), handleActiveCredential: vi.fn(), })) type FormResponse = { isCheckValidated: boolean values: Record } const mockFormState = vi.hoisted(() => ({ responses: [] as FormResponse[], setFieldValue: vi.fn(), })) vi.mock('../model-auth/hooks', () => ({ useCredentialData: () => ({ isLoading: mockState.isLoading, credentialData: mockState.credentialData, }), useAuth: () => ({ handleSaveCredential: mockHandlers.handleSaveCredential, handleConfirmDelete: mockHandlers.handleConfirmDelete, deleteCredentialId: mockState.deleteCredentialId, closeConfirmDelete: mockHandlers.closeConfirmDelete, openConfirmDelete: mockHandlers.openConfirmDelete, doingAction: mockState.doingAction, handleActiveCredential: mockHandlers.handleActiveCredential, }), useModelFormSchemas: (): ModelFormSchemas => ({ formSchemas: mockState.formSchemas, formValues: mockState.formValues, modelNameAndTypeFormSchemas: mockState.modelNameAndTypeFormSchemas, modelNameAndTypeFormValues: mockState.modelNameAndTypeFormValues, }), })) vi.mock('@/context/app-context', () => ({ useAppContext: () => ({ isCurrentWorkspaceManager: mockState.isCurrentWorkspaceManager }), })) vi.mock('@/hooks/use-i18n', () => ({ useRenderI18nObject: () => (value: { en_US: string }) => value.en_US, })) vi.mock('../hooks', () => ({ useLanguage: () => 'en_US', })) vi.mock('@/app/components/base/form/form-scenarios/auth', async () => { const React = await import('react') const AuthForm = React.forwardRef(({ onChange, }: { onChange?: (field: string, value: string) => void }, ref: React.ForwardedRef<{ getFormValues: () => FormResponse, getForm: () => { setFieldValue: (field: string, value: string) => void } }>) => { React.useImperativeHandle(ref, () => ({ getFormValues: () => mockFormState.responses.shift() || { isCheckValidated: false, values: {} }, getForm: () => ({ setFieldValue: mockFormState.setFieldValue }), })) return (
) }) return { default: AuthForm } }) vi.mock('../model-auth', () => ({ CredentialSelector: ({ onSelect }: { onSelect: (credential: Credential & { addNewCredential?: boolean }) => void }) => (
), })) const createI18n = (text: string) => ({ en_US: text, zh_Hans: text }) const createProvider = (overrides?: Partial): ModelProvider => ({ provider: 'openai', label: createI18n('OpenAI'), help: { title: createI18n('Help'), url: createI18n('https://example.com'), }, icon_small: createI18n('icon'), supported_model_types: [ModelTypeEnum.textGeneration], configurate_methods: [ConfigurationMethodEnum.predefinedModel], provider_credential_schema: { credential_form_schemas: [] }, model_credential_schema: { model: { label: createI18n('Model'), placeholder: createI18n('Model') }, credential_form_schemas: [], }, preferred_provider_type: PreferredProviderTypeEnum.system, custom_configuration: { status: CustomConfigurationStatusEnum.active, available_credentials: [], custom_models: [], can_added_models: [], }, system_configuration: { enabled: true, current_quota_type: CurrentSystemQuotaTypeEnum.trial, quota_configurations: [ { quota_type: CurrentSystemQuotaTypeEnum.trial, quota_unit: QuotaUnitEnum.times, quota_limit: 0, quota_used: 0, last_used: 0, is_valid: true, }, ], }, allow_custom_token: true, ...overrides, }) const renderModal = (overrides?: Partial>) => { const provider = createProvider() const props = { provider, configurateMethod: ConfigurationMethodEnum.predefinedModel, onCancel: vi.fn(), onSave: vi.fn(), onRemove: vi.fn(), ...overrides, } const view = render() return { ...props, unmount: view.unmount, } } describe('ModelModal', () => { beforeEach(() => { vi.clearAllMocks() mockState.isLoading = false mockState.credentialData = { credentials: {}, available_credentials: [] } mockState.doingAction = false mockState.deleteCredentialId = null mockState.isCurrentWorkspaceManager = true mockState.formSchemas = [] mockState.formValues = {} mockState.modelNameAndTypeFormSchemas = [] mockState.modelNameAndTypeFormValues = {} mockFormState.responses = [] }) it('should show title, description, and loading state for predefined models', () => { mockState.isLoading = true const predefined = renderModal() expect(screen.getByText('common.modelProvider.auth.apiKeyModal.title')).toBeInTheDocument() expect(screen.getByText('common.modelProvider.auth.apiKeyModal.desc')).toBeInTheDocument() expect(screen.getByRole('status')).toBeInTheDocument() expect(screen.getByRole('button', { name: 'common.operation.save' })).toBeDisabled() predefined.unmount() const customizable = renderModal({ configurateMethod: ConfigurationMethodEnum.customizableModel }) expect(screen.queryByText('common.modelProvider.auth.apiKeyModal.desc')).not.toBeInTheDocument() customizable.unmount() mockState.credentialData = { credentials: {}, available_credentials: [] } renderModal({ mode: ModelModalModeEnum.configModelCredential, model: { model: 'gpt-4', model_type: ModelTypeEnum.textGeneration } }) expect(screen.getByText('common.modelProvider.auth.addModelCredential')).toBeInTheDocument() }) it('should reveal the credential label when adding a new credential', () => { renderModal({ mode: ModelModalModeEnum.addCustomModelToModelList }) expect(screen.queryByText('common.modelProvider.auth.modelCredential')).not.toBeInTheDocument() fireEvent.click(screen.getByText('Add New')) expect(screen.getByText('common.modelProvider.auth.modelCredential')).toBeInTheDocument() }) it('should call onCancel when the cancel button is clicked', () => { const { onCancel } = renderModal() fireEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' })) expect(onCancel).toHaveBeenCalledTimes(1) }) it('should call onCancel when the escape key is pressed', () => { const { onCancel } = renderModal() fireEvent.keyDown(document, { key: 'Escape' }) expect(onCancel).toHaveBeenCalledTimes(1) }) it('should confirm deletion when a delete dialog is shown', () => { mockState.credentialData = { credentials: { api_key: 'secret' }, available_credentials: [] } mockState.deleteCredentialId = 'delete-id' const credential: Credential = { credential_id: 'cred-1' } const { onCancel } = renderModal({ credential }) expect(screen.getByText('common.modelProvider.confirmDelete')).toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: 'common.operation.confirm' })) expect(mockHandlers.handleConfirmDelete).toHaveBeenCalledTimes(1) expect(onCancel).toHaveBeenCalledTimes(1) }) it('should handle save flows for different modal modes', async () => { mockState.modelNameAndTypeFormSchemas = [{ variable: '__model_name', type: 'text-input' } as unknown as CredentialFormSchema] mockState.formSchemas = [{ variable: 'api_key', type: 'secret-input' } as unknown as CredentialFormSchema] mockFormState.responses = [ { isCheckValidated: true, values: { __model_name: 'custom-model', __model_type: ModelTypeEnum.textGeneration } }, { isCheckValidated: true, values: { __authorization_name__: 'Auth Name', api_key: 'secret' } }, ] const configCustomModel = renderModal({ mode: ModelModalModeEnum.configCustomModel }) fireEvent.click(screen.getAllByText('Model Name Change')[0]) fireEvent.click(screen.getByRole('button', { name: 'common.operation.add' })) expect(mockFormState.setFieldValue).toHaveBeenCalledWith('__model_name', 'updated-model') await waitFor(() => { expect(mockHandlers.handleSaveCredential).toHaveBeenCalledWith({ credential_id: undefined, credentials: { api_key: 'secret' }, name: 'Auth Name', model: 'custom-model', model_type: ModelTypeEnum.textGeneration, }) }) expect(configCustomModel.onSave).toHaveBeenCalledWith({ __authorization_name__: 'Auth Name', api_key: 'secret' }) configCustomModel.unmount() mockFormState.responses = [{ isCheckValidated: true, values: { __authorization_name__: 'Model Auth', api_key: 'abc' } }] const model = { model: 'gpt-4', model_type: ModelTypeEnum.textGeneration } const configModelCredential = renderModal({ mode: ModelModalModeEnum.configModelCredential, model, credential: { credential_id: 'cred-123' }, }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) await waitFor(() => { expect(mockHandlers.handleSaveCredential).toHaveBeenCalledWith({ credential_id: 'cred-123', credentials: { api_key: 'abc' }, name: 'Model Auth', model: 'gpt-4', model_type: ModelTypeEnum.textGeneration, }) }) expect(configModelCredential.onSave).toHaveBeenCalledWith({ __authorization_name__: 'Model Auth', api_key: 'abc' }) configModelCredential.unmount() mockFormState.responses = [{ isCheckValidated: true, values: { __authorization_name__: 'Provider Auth', api_key: 'provider-key' } }] const configProviderCredential = renderModal({ mode: ModelModalModeEnum.configProviderCredential }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) await waitFor(() => { expect(mockHandlers.handleSaveCredential).toHaveBeenCalledWith({ credential_id: undefined, credentials: { api_key: 'provider-key' }, name: 'Provider Auth', }) }) configProviderCredential.unmount() const addToModelList = renderModal({ mode: ModelModalModeEnum.addCustomModelToModelList, model, }) fireEvent.click(screen.getByText('Choose Existing')) fireEvent.click(screen.getByRole('button', { name: 'common.operation.add' })) expect(mockHandlers.handleActiveCredential).toHaveBeenCalledWith({ credential_id: 'existing' }, model) expect(addToModelList.onCancel).toHaveBeenCalled() addToModelList.unmount() mockFormState.responses = [{ isCheckValidated: true, values: { __authorization_name__: 'New Auth', api_key: 'new-key' } }] const addToModelListWithNew = renderModal({ mode: ModelModalModeEnum.addCustomModelToModelList, model, }) fireEvent.click(screen.getByText('Add New')) fireEvent.click(screen.getByRole('button', { name: 'common.operation.add' })) await waitFor(() => { expect(mockHandlers.handleSaveCredential).toHaveBeenCalledWith({ credential_id: undefined, credentials: { api_key: 'new-key' }, name: 'New Auth', model: 'gpt-4', model_type: ModelTypeEnum.textGeneration, }) }) addToModelListWithNew.unmount() mockFormState.responses = [{ isCheckValidated: false, values: {} }] const invalidSave = renderModal({ mode: ModelModalModeEnum.configProviderCredential }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) await waitFor(() => { expect(mockHandlers.handleSaveCredential).toHaveBeenCalledTimes(4) }) invalidSave.unmount() mockState.credentialData = { credentials: { api_key: 'value' }, available_credentials: [] } mockState.formValues = { api_key: 'value' } const removable = renderModal({ credential: { credential_id: 'remove-1' } }) fireEvent.click(screen.getByRole('button', { name: 'common.operation.remove' })) expect(mockHandlers.openConfirmDelete).toHaveBeenCalledWith({ credential_id: 'remove-1' }, undefined) removable.unmount() }) })