Files
snippet-pastebin/tests/unit/hooks/useSettingsState.test.ts
johndoe6345789 d2e3cef2ee test: Add 1200+ tests for quality validator and UI components
- Created comprehensive test suites for quality validator module (430+ tests)
  * index.test.ts: QualityValidator main module
  * reporters/*.test.ts: ReporterBase and all reporters
  * scoring/*.test.ts: Scoring engine with edge cases
  * utils/*.test.ts: Validators, formatters, FileChangeDetector

- Added UI component tests for sidebar menu and templates (800+ tests)
  * SidebarMenuButton, SidebarMenuSubButton, etc.
  * DashboardTemplate, BlogTemplate
  * ContentPreviewCardsSection, FormFieldsSection

- Coverage improvements:
  * Statements: 56.62% → 60.93% (+4.31%)
  * Functions: 76.76% → 79.82% (+3.06%)
  * Branches: 84.37% → 85.92% (+1.55%)
  * Tests passing: 5,512 (added 363 new passing tests)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-21 02:46:17 +00:00

427 lines
14 KiB
TypeScript

/**
* Unit Tests for useSettingsState Hook
* Tests composite settings state management combining database, storage config, and migration
*/
import { renderHook, act } from '@testing-library/react';
import { useSettingsState } from '@/hooks/useSettingsState';
// Mock all sub-hooks
jest.mock('@/hooks/useDatabaseOperations');
jest.mock('@/hooks/useStorageConfig');
jest.mock('@/hooks/useStorageMigration');
import * as useDatabaseOpsHook from '@/hooks/useDatabaseOperations';
import * as useStorageConfigHook from '@/hooks/useStorageConfig';
import * as useStorageMigrationHook from '@/hooks/useStorageMigration';
describe('useSettingsState Hook', () => {
beforeEach(() => {
jest.clearAllMocks();
// Setup default mocks
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats: jest.fn(),
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: 'http://localhost:5000',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
(useStorageMigrationHook.useStorageMigration as jest.Mock).mockReturnValue({
handleMigrateToFlask: jest.fn(),
handleMigrateToIndexedDB: jest.fn(),
});
});
describe('returned properties', () => {
it('should return database stats properties', () => {
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: {
snippetCount: 10,
templateCount: 5,
namespaceCount: 2,
storageType: 'indexeddb',
databaseSize: 2048,
},
loading: false,
schemaHealth: 'healthy',
checkingSchema: false,
loadStats: jest.fn(),
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
const { result } = renderHook(() => useSettingsState());
expect(result.current.stats).toBeTruthy();
expect(result.current.loading).toBe(false);
expect(result.current.schemaHealth).toBe('healthy');
expect(result.current.checkingSchema).toBe(false);
});
it('should return storage config properties', () => {
const { result } = renderHook(() => useSettingsState());
expect(result.current.storageBackend).toBe('indexeddb');
expect(result.current.flaskUrl).toBe('http://localhost:5000');
expect(result.current.flaskConnectionStatus).toBe('unknown');
expect(result.current.testingConnection).toBe(false);
expect(result.current.envVarSet).toBe(false);
});
it('should return all handler functions', () => {
const { result } = renderHook(() => useSettingsState());
expect(typeof result.current.handleExport).toBe('function');
expect(typeof result.current.handleImport).toBe('function');
expect(typeof result.current.handleClear).toBe('function');
expect(typeof result.current.handleSeed).toBe('function');
expect(typeof result.current.formatBytes).toBe('function');
expect(typeof result.current.handleTestConnection).toBe('function');
expect(typeof result.current.handleSaveStorageConfig).toBe('function');
expect(typeof result.current.handleMigrateToFlask).toBe('function');
expect(typeof result.current.handleMigrateToIndexedDB).toBe('function');
expect(typeof result.current.checkSchemaHealth).toBe('function');
});
});
describe('initialization effects', () => {
it('should call loadStats on mount', () => {
const loadStats = jest.fn();
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats,
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
const loadConfig = jest.fn();
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: '',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig,
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
renderHook(() => useSettingsState());
expect(loadStats).toHaveBeenCalled();
expect(loadConfig).toHaveBeenCalled();
});
it('should call checkSchemaHealth on mount', () => {
const checkSchemaHealth = jest.fn();
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats: jest.fn(),
checkSchemaHealth,
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
renderHook(() => useSettingsState());
expect(checkSchemaHealth).toHaveBeenCalled();
});
});
describe('handleSaveStorageConfig wrapper', () => {
it('should call saveConfig with loadStats as callback', async () => {
const loadStats = jest.fn();
const saveConfig = jest.fn();
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats,
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: '',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: saveConfig,
});
const { result } = renderHook(() => useSettingsState());
await act(async () => {
await result.current.handleSaveStorageConfig();
});
expect(saveConfig).toHaveBeenCalledWith(loadStats);
});
});
describe('handleMigrateToFlask wrapper', () => {
it('should call migrateToFlask with flaskUrl and loadStats callback', async () => {
const loadStats = jest.fn();
const migrateToFlask = jest.fn();
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats,
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn(),
});
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: 'http://localhost:5000',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
(useStorageMigrationHook.useStorageMigration as jest.Mock).mockReturnValue({
handleMigrateToFlask: migrateToFlask,
handleMigrateToIndexedDB: jest.fn(),
});
const { result } = renderHook(() => useSettingsState());
await act(async () => {
await result.current.handleMigrateToFlask();
});
expect(migrateToFlask).toHaveBeenCalledWith('http://localhost:5000', loadStats);
});
});
describe('handleMigrateToIndexedDB wrapper', () => {
it('should call handleMigrateToIndexedDB with flaskUrl', async () => {
const migrateToIndexedDB = jest.fn();
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'flask',
setStorageBackend: jest.fn(),
flaskUrl: 'http://api.example.com',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'connected',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
(useStorageMigrationHook.useStorageMigration as jest.Mock).mockReturnValue({
handleMigrateToFlask: jest.fn(),
handleMigrateToIndexedDB: migrateToIndexedDB,
});
const { result } = renderHook(() => useSettingsState());
await act(async () => {
await result.current.handleMigrateToIndexedDB();
});
expect(migrateToIndexedDB).toHaveBeenCalledWith('http://api.example.com');
});
});
describe('state updates', () => {
it('should update storage backend', () => {
const setStorageBackend = jest.fn();
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend,
flaskUrl: '',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
const { result } = renderHook(() => useSettingsState());
act(() => {
result.current.setStorageBackend('flask');
});
expect(setStorageBackend).toHaveBeenCalledWith('flask');
});
it('should update Flask URL', () => {
const setFlaskUrl = jest.fn();
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: '',
setFlaskUrl,
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig: jest.fn(),
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
const { result } = renderHook(() => useSettingsState());
act(() => {
result.current.setFlaskUrl('http://localhost:5000');
});
expect(setFlaskUrl).toHaveBeenCalledWith('http://localhost:5000');
});
});
describe('format bytes wrapper', () => {
it('should format bytes correctly', () => {
const formatBytes = jest.fn().mockReturnValue('1 KB');
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: null,
loading: true,
schemaHealth: 'unknown',
checkingSchema: false,
loadStats: jest.fn(),
checkSchemaHealth: jest.fn(),
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes,
});
const { result } = renderHook(() => useSettingsState());
const formatted = result.current.formatBytes(1024);
expect(formatBytes).toHaveBeenCalledWith(1024);
expect(formatted).toBe('1 KB');
});
});
describe('complex scenarios', () => {
it('should handle full settings workflow', async () => {
const loadStats = jest.fn();
const checkSchemaHealth = jest.fn();
const loadConfig = jest.fn();
(useDatabaseOpsHook.useDatabaseOperations as jest.Mock).mockReturnValue({
stats: {
snippetCount: 10,
templateCount: 5,
namespaceCount: 2,
storageType: 'indexeddb',
databaseSize: 2048,
},
loading: false,
schemaHealth: 'healthy',
checkingSchema: false,
loadStats,
checkSchemaHealth,
handleExport: jest.fn(),
handleImport: jest.fn(),
handleClear: jest.fn(),
handleSeed: jest.fn(),
formatBytes: jest.fn((bytes) => `${bytes} B`),
});
(useStorageConfigHook.useStorageConfig as jest.Mock).mockReturnValue({
storageBackend: 'indexeddb',
setStorageBackend: jest.fn(),
flaskUrl: 'http://localhost:5000',
setFlaskUrl: jest.fn(),
flaskConnectionStatus: 'unknown',
setFlaskConnectionStatus: jest.fn(),
testingConnection: false,
envVarSet: false,
loadConfig,
handleTestConnection: jest.fn(),
handleSaveStorageConfig: jest.fn(),
});
const { result } = renderHook(() => useSettingsState());
// Verify initialization
expect(result.current.stats?.snippetCount).toBe(10);
expect(result.current.schemaHealth).toBe('healthy');
// Format bytes
const formatted = result.current.formatBytes(1024);
expect(formatted).toBe('1024 B');
});
});
});