- Next.js 14+ with App Router and TypeScript - Tailwind CSS and ShadCN UI styling - Zustand state management - Dexie.js for IndexedDB (local-first data) - Auth.js v5 for authentication - BMAD framework integration Co-Authored-By: Claude <noreply@anthropic.com>
133 lines
4.3 KiB
TypeScript
133 lines
4.3 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import { ProviderForm } from './provider-form';
|
|
import { useSettingsStore } from '@/store/use-settings';
|
|
|
|
// Mock ConnectionStatus component
|
|
vi.mock('./connection-status', () => ({
|
|
ConnectionStatus: () => <div data-testid="connection-status">Connection Status</div>,
|
|
}));
|
|
|
|
describe('ProviderForm', () => {
|
|
beforeEach(() => {
|
|
// Reset store state
|
|
useSettingsStore.setState({
|
|
apiKey: '',
|
|
baseUrl: 'https://api.openai.com/v1',
|
|
modelName: 'gpt-4-turbo-preview',
|
|
isConfigured: false,
|
|
});
|
|
});
|
|
|
|
it('renders all required input fields', () => {
|
|
render(<ProviderForm />);
|
|
|
|
expect(screen.getByLabelText(/base url/i)).toBeInTheDocument();
|
|
expect(screen.getByLabelText(/model name/i)).toBeInTheDocument();
|
|
// Use placeholder for API key since show/hide button has "API key" in aria-label
|
|
expect(screen.getByPlaceholderText('sk-...')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows helper text for API key', () => {
|
|
render(<ProviderForm />);
|
|
|
|
expect(screen.getByText(/stored locally in your browser with basic encoding/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('has show/hide toggle for API key', () => {
|
|
render(<ProviderForm />);
|
|
|
|
const apiKeyInput = screen.getByPlaceholderText('sk-...') as HTMLInputElement;
|
|
const toggleButton = screen.getByRole('button', { name: /show/i });
|
|
|
|
// Initially password type
|
|
expect(apiKeyInput.type).toBe('password');
|
|
|
|
// Click show button
|
|
fireEvent.click(toggleButton);
|
|
|
|
// Should change to text type
|
|
expect(apiKeyInput.type).toBe('text');
|
|
});
|
|
|
|
it('updates base URL when user types', () => {
|
|
render(<ProviderForm />);
|
|
|
|
const baseUrlInput = screen.getByLabelText(/base url/i);
|
|
fireEvent.change(baseUrlInput, { target: { value: 'https://api.deepseek.com/v1' } });
|
|
|
|
const state = useSettingsStore.getState();
|
|
expect(state.baseUrl).toBe('https://api.deepseek.com/v1');
|
|
});
|
|
|
|
it('updates model name when user types', () => {
|
|
render(<ProviderForm />);
|
|
|
|
const modelInput = screen.getByLabelText(/model name/i);
|
|
fireEvent.change(modelInput, { target: { value: 'deepseek-chat' } });
|
|
|
|
const state = useSettingsStore.getState();
|
|
expect(state.modelName).toBe('deepseek-chat');
|
|
});
|
|
|
|
it('updates API key when user types', () => {
|
|
render(<ProviderForm />);
|
|
|
|
const apiKeyInput = screen.getByPlaceholderText('sk-...');
|
|
fireEvent.change(apiKeyInput, { target: { value: 'sk-test-key-12345' } });
|
|
|
|
const state = useSettingsStore.getState();
|
|
expect(state.apiKey).toBe('sk-test-key-12345');
|
|
expect(state.isConfigured).toBe(true);
|
|
});
|
|
|
|
it('displays current values from store', () => {
|
|
useSettingsStore.setState({
|
|
apiKey: 'sk-existing-key',
|
|
baseUrl: 'https://api.custom.com/v1',
|
|
modelName: 'custom-model',
|
|
isConfigured: true,
|
|
});
|
|
|
|
render(<ProviderForm />);
|
|
|
|
expect(screen.getByPlaceholderText('sk-...')).toHaveValue('sk-existing-key');
|
|
expect(screen.getByLabelText(/base url/i)).toHaveValue('https://api.custom.com/v1');
|
|
expect(screen.getByLabelText(/model name/i)).toHaveValue('custom-model');
|
|
});
|
|
|
|
it('renders ConnectionStatus component', () => {
|
|
render(<ProviderForm />);
|
|
|
|
expect(screen.getByTestId('connection-status')).toBeInTheDocument();
|
|
});
|
|
|
|
it('has proper accessibility attributes', () => {
|
|
render(<ProviderForm />);
|
|
|
|
// All inputs should have associated labels
|
|
expect(screen.getByLabelText(/base url/i)).toBeInTheDocument();
|
|
expect(screen.getByLabelText(/model name/i)).toBeInTheDocument();
|
|
expect(screen.getByPlaceholderText('sk-...')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays provider preset buttons', () => {
|
|
render(<ProviderForm />);
|
|
|
|
expect(screen.getByRole('button', { name: 'OpenAI' })).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: 'DeepSeek' })).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: 'OpenRouter' })).toBeInTheDocument();
|
|
});
|
|
|
|
it('applies preset when clicked', () => {
|
|
render(<ProviderForm />);
|
|
|
|
const deepseekButton = screen.getByRole('button', { name: 'DeepSeek' });
|
|
fireEvent.click(deepseekButton);
|
|
|
|
const state = useSettingsStore.getState();
|
|
expect(state.baseUrl).toBe('https://api.deepseek.com/v1');
|
|
expect(state.modelName).toBe('deepseek-chat');
|
|
});
|
|
});
|