jan/web-app/src/hooks/__tests__/useGeneralSetting.test.ts
2025-08-21 12:01:45 +07:00

290 lines
8.3 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { useGeneralSetting } from '../useGeneralSetting'
// Mock constants
vi.mock('@/constants/localStorage', () => ({
localStorageKey: {
settingGeneral: 'general-settings',
},
}))
// Mock zustand persist
vi.mock('zustand/middleware', () => ({
persist: (fn: any) => fn,
createJSONStorage: () => ({
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
}),
}))
// Mock ExtensionManager
vi.mock('@/lib/extension', () => ({
ExtensionManager: {
getInstance: vi.fn(),
},
}))
describe('useGeneralSetting', () => {
let mockExtensionManager: any
beforeEach(async () => {
vi.clearAllMocks()
// Get the mocked ExtensionManager
const { ExtensionManager } = await import('@/lib/extension')
mockExtensionManager = ExtensionManager
// Reset store state to defaults
useGeneralSetting.setState({
currentLanguage: 'en',
spellCheckChatInput: true,
huggingfaceToken: undefined,
})
// Setup default mock behavior to prevent errors
const mockGetByName = vi.fn().mockReturnValue({
getSettings: vi.fn().mockResolvedValue(null),
updateSettings: vi.fn(),
})
mockExtensionManager.getInstance.mockReturnValue({
getByName: mockGetByName,
})
})
it('should initialize with default values', () => {
const { result } = renderHook(() => useGeneralSetting())
expect(result.current.currentLanguage).toBe('en')
expect(result.current.spellCheckChatInput).toBe(true)
expect(result.current.huggingfaceToken).toBeUndefined()
expect(typeof result.current.setCurrentLanguage).toBe('function')
expect(typeof result.current.setSpellCheckChatInput).toBe('function')
expect(typeof result.current.setHuggingfaceToken).toBe('function')
})
describe('setCurrentLanguage', () => {
it('should set language to English', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setCurrentLanguage('en')
})
expect(result.current.currentLanguage).toBe('en')
})
it('should set language to Indonesian', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setCurrentLanguage('id')
})
expect(result.current.currentLanguage).toBe('id')
})
it('should set language to Vietnamese', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setCurrentLanguage('vn')
})
expect(result.current.currentLanguage).toBe('vn')
})
it('should change language multiple times', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setCurrentLanguage('id')
})
expect(result.current.currentLanguage).toBe('id')
act(() => {
result.current.setCurrentLanguage('vn')
})
expect(result.current.currentLanguage).toBe('vn')
act(() => {
result.current.setCurrentLanguage('en')
})
expect(result.current.currentLanguage).toBe('en')
})
})
describe('setSpellCheckChatInput', () => {
it('should enable spell check', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setSpellCheckChatInput(true)
})
expect(result.current.spellCheckChatInput).toBe(true)
})
it('should disable spell check', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setSpellCheckChatInput(false)
})
expect(result.current.spellCheckChatInput).toBe(false)
})
it('should toggle spell check multiple times', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setSpellCheckChatInput(false)
})
expect(result.current.spellCheckChatInput).toBe(false)
act(() => {
result.current.setSpellCheckChatInput(true)
})
expect(result.current.spellCheckChatInput).toBe(true)
})
})
describe('setHuggingfaceToken', () => {
it('should set huggingface token', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setHuggingfaceToken('test-token-123')
})
expect(result.current.huggingfaceToken).toBe('test-token-123')
})
it('should update huggingface token', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setHuggingfaceToken('old-token')
})
expect(result.current.huggingfaceToken).toBe('old-token')
act(() => {
result.current.setHuggingfaceToken('new-token')
})
expect(result.current.huggingfaceToken).toBe('new-token')
})
it('should handle empty token', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setHuggingfaceToken('')
})
expect(result.current.huggingfaceToken).toBe('')
})
it('should call ExtensionManager when setting token', async () => {
const mockSettings = [
{ key: 'hf-token', controllerProps: { value: 'old-value' } },
{ key: 'other-setting', controllerProps: { value: 'other-value' } },
]
const mockGetByName = vi.fn()
const mockGetSettings = vi.fn().mockResolvedValue(mockSettings)
const mockUpdateSettings = vi.fn()
mockExtensionManager.getInstance.mockReturnValue({
getByName: mockGetByName,
})
mockGetByName.mockReturnValue({
getSettings: mockGetSettings,
updateSettings: mockUpdateSettings,
})
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setHuggingfaceToken('new-token')
})
expect(mockExtensionManager.getInstance).toHaveBeenCalled()
expect(mockGetByName).toHaveBeenCalledWith('@janhq/download-extension')
// Wait for async operations
await new Promise((resolve) => setTimeout(resolve, 0))
expect(mockGetSettings).toHaveBeenCalled()
expect(mockUpdateSettings).toHaveBeenCalledWith([
{ key: 'hf-token', controllerProps: { value: 'new-token' } },
{ key: 'other-setting', controllerProps: { value: 'other-value' } },
])
})
})
describe('state management', () => {
it('should maintain state across multiple hook instances', () => {
const { result: result1 } = renderHook(() => useGeneralSetting())
const { result: result2 } = renderHook(() => useGeneralSetting())
act(() => {
result1.current.setCurrentLanguage('id')
result1.current.setSpellCheckChatInput(false)
result1.current.setHuggingfaceToken('shared-token')
})
expect(result2.current.currentLanguage).toBe('id')
expect(result2.current.spellCheckChatInput).toBe(false)
expect(result2.current.huggingfaceToken).toBe('shared-token')
})
})
describe('complex scenarios', () => {
it('should handle complete settings configuration', () => {
const { result } = renderHook(() => useGeneralSetting())
act(() => {
result.current.setCurrentLanguage('vn')
result.current.setSpellCheckChatInput(false)
result.current.setHuggingfaceToken('complex-token-123')
})
expect(result.current.currentLanguage).toBe('vn')
expect(result.current.spellCheckChatInput).toBe(false)
expect(result.current.huggingfaceToken).toBe('complex-token-123')
})
it('should handle multiple sequential updates', () => {
const { result } = renderHook(() => useGeneralSetting())
// First update
act(() => {
result.current.setCurrentLanguage('id')
result.current.setSpellCheckChatInput(false)
})
expect(result.current.currentLanguage).toBe('id')
expect(result.current.spellCheckChatInput).toBe(false)
// Second update
act(() => {
result.current.setHuggingfaceToken('sequential-token')
})
expect(result.current.huggingfaceToken).toBe('sequential-token')
// Third update
act(() => {
result.current.setCurrentLanguage('en')
result.current.setSpellCheckChatInput(true)
})
expect(result.current.currentLanguage).toBe('en')
expect(result.current.spellCheckChatInput).toBe(true)
})
})
})