322 lines
9.0 KiB
TypeScript
322 lines
9.0 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
import { renderHook, act } from '@testing-library/react'
|
|
import { useAppearance } from '../useAppearance'
|
|
|
|
// Mock constants
|
|
vi.mock('@/constants/localStorage', () => ({
|
|
localStorageKey: {
|
|
appearance: 'appearance',
|
|
},
|
|
}))
|
|
|
|
vi.mock('../useTheme', () => ({
|
|
useTheme: {
|
|
getState: vi.fn(() => ({ isDark: false })),
|
|
setState: vi.fn(),
|
|
subscribe: vi.fn(),
|
|
destroy: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
// Mock zustand persist
|
|
vi.mock('zustand/middleware', () => ({
|
|
persist: (fn: any) => fn,
|
|
createJSONStorage: () => ({
|
|
getItem: vi.fn(),
|
|
setItem: vi.fn(),
|
|
removeItem: vi.fn(),
|
|
}),
|
|
}))
|
|
|
|
// Mock global constants
|
|
Object.defineProperty(global, 'IS_WINDOWS', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_LINUX', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_MACOS', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_TAURI', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_WEB_APP', { value: false, writable: true })
|
|
|
|
describe('useAppearance', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should initialize with default values', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
expect(result.current.fontSize).toBe('15px')
|
|
expect(result.current.chatWidth).toBe('compact')
|
|
expect(result.current.appBgColor).toEqual({
|
|
r: 25,
|
|
g: 25,
|
|
b: 25,
|
|
a: 1,
|
|
})
|
|
})
|
|
|
|
it('should update font size', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
act(() => {
|
|
result.current.setFontSize('18px')
|
|
})
|
|
|
|
expect(result.current.fontSize).toBe('18px')
|
|
})
|
|
|
|
it('should update chat width', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
act(() => {
|
|
result.current.setChatWidth('full')
|
|
})
|
|
|
|
expect(result.current.chatWidth).toBe('full')
|
|
})
|
|
|
|
it('should update app background color', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const newColor = { r: 100, g: 100, b: 100, a: 1 }
|
|
|
|
act(() => {
|
|
result.current.setAppBgColor(newColor)
|
|
})
|
|
|
|
expect(result.current.appBgColor).toEqual(newColor)
|
|
})
|
|
|
|
it('should update main view background color', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const newColor = { r: 200, g: 200, b: 200, a: 1 }
|
|
|
|
act(() => {
|
|
result.current.setAppMainViewBgColor(newColor)
|
|
})
|
|
|
|
expect(result.current.appMainViewBgColor).toEqual(newColor)
|
|
})
|
|
|
|
it('should update primary background color', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const newColor = { r: 50, g: 100, b: 150, a: 1 }
|
|
|
|
act(() => {
|
|
result.current.setAppPrimaryBgColor(newColor)
|
|
})
|
|
|
|
expect(result.current.appPrimaryBgColor).toEqual(newColor)
|
|
})
|
|
|
|
it('should update accent background color', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const newColor = { r: 255, g: 100, b: 50, a: 1 }
|
|
|
|
act(() => {
|
|
result.current.setAppAccentBgColor(newColor)
|
|
})
|
|
|
|
expect(result.current.appAccentBgColor).toEqual(newColor)
|
|
})
|
|
|
|
it('should update destructive background color', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const newColor = { r: 255, g: 0, b: 0, a: 1 }
|
|
|
|
act(() => {
|
|
result.current.setAppDestructiveBgColor(newColor)
|
|
})
|
|
|
|
expect(result.current.appDestructiveBgColor).toEqual(newColor)
|
|
})
|
|
|
|
it('should reset appearance to defaults', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
// Change some values first
|
|
act(() => {
|
|
result.current.setFontSize('18px')
|
|
result.current.setChatWidth('full')
|
|
result.current.setAppBgColor({ r: 100, g: 100, b: 100, a: 1 })
|
|
})
|
|
|
|
// Reset
|
|
act(() => {
|
|
result.current.resetAppearance()
|
|
})
|
|
|
|
expect(result.current.fontSize).toBe('15px')
|
|
// Note: resetAppearance doesn't reset chatWidth, only visual properties
|
|
expect(result.current.chatWidth).toBe('full')
|
|
expect(result.current.appBgColor).toEqual({
|
|
r: 255,
|
|
g: 255,
|
|
b: 255,
|
|
a: 1,
|
|
})
|
|
})
|
|
|
|
|
|
describe('Platform-specific behavior', () => {
|
|
it('should use alpha 1 for web environments', () => {
|
|
Object.defineProperty(global, 'IS_WEB_APP', { value: false })
|
|
Object.defineProperty(global, 'IS_WINDOWS', { value: true })
|
|
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
expect(result.current.appBgColor.a).toBe(1)
|
|
})
|
|
})
|
|
|
|
|
|
describe('Reset appearance functionality', () => {
|
|
beforeEach(() => {
|
|
// Mock document.documentElement.style.setProperty
|
|
Object.defineProperty(document.documentElement, 'style', {
|
|
value: {
|
|
setProperty: vi.fn(),
|
|
},
|
|
writable: true,
|
|
})
|
|
})
|
|
|
|
it('should reset CSS variables when resetAppearance is called', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
act(() => {
|
|
result.current.resetAppearance()
|
|
})
|
|
|
|
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
|
|
'--font-size-base',
|
|
'15px'
|
|
)
|
|
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
|
|
'--app-bg',
|
|
expect.any(String)
|
|
)
|
|
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
|
|
'--app-main-view',
|
|
expect.any(String)
|
|
)
|
|
})
|
|
})
|
|
|
|
|
|
describe('Color manipulation', () => {
|
|
it('should handle color updates with CSS variable setting', () => {
|
|
// Mock document.documentElement.style.setProperty
|
|
const setPropertySpy = vi.fn()
|
|
Object.defineProperty(document.documentElement, 'style', {
|
|
value: {
|
|
setProperty: setPropertySpy,
|
|
},
|
|
writable: true,
|
|
})
|
|
|
|
const { result } = renderHook(() => useAppearance())
|
|
const testColor = { r: 128, g: 64, b: 192, a: 0.8 }
|
|
|
|
act(() => {
|
|
result.current.setAppBgColor(testColor)
|
|
})
|
|
|
|
// In web environment (IS_TAURI=false), alpha is forced to 1
|
|
expect(result.current.appBgColor).toEqual({ ...testColor, a: 1 })
|
|
})
|
|
|
|
it('should handle transparent colors', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const transparentColor = { r: 100, g: 100, b: 100, a: 0 }
|
|
|
|
act(() => {
|
|
result.current.setAppAccentBgColor(transparentColor)
|
|
})
|
|
|
|
expect(result.current.appAccentBgColor).toEqual(transparentColor)
|
|
})
|
|
|
|
it('should preserve alpha when blur is supported (macOS)', () => {
|
|
// Mock macOS environment
|
|
Object.defineProperty(global, 'IS_MACOS', { value: true, writable: true })
|
|
Object.defineProperty(global, 'IS_TAURI', { value: true, writable: true })
|
|
Object.defineProperty(global, 'IS_WINDOWS', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_LINUX', { value: false, writable: true })
|
|
|
|
const setPropertySpy = vi.fn()
|
|
Object.defineProperty(document.documentElement, 'style', {
|
|
value: {
|
|
setProperty: setPropertySpy,
|
|
},
|
|
writable: true,
|
|
})
|
|
|
|
const { result } = renderHook(() => useAppearance())
|
|
const testColor = { r: 128, g: 64, b: 192, a: 0.5 }
|
|
|
|
act(() => {
|
|
result.current.setAppBgColor(testColor)
|
|
})
|
|
|
|
// On macOS with Tauri, alpha should be preserved
|
|
expect(result.current.appBgColor).toEqual(testColor)
|
|
|
|
// Reset for other tests
|
|
Object.defineProperty(global, 'IS_MACOS', { value: false, writable: true })
|
|
Object.defineProperty(global, 'IS_TAURI', { value: false, writable: true })
|
|
})
|
|
})
|
|
|
|
describe('Edge cases', () => {
|
|
it('should handle invalid color values gracefully', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
const invalidColor = { r: -10, g: 300, b: 128, a: 2 }
|
|
|
|
act(() => {
|
|
result.current.setAppPrimaryBgColor(invalidColor)
|
|
})
|
|
|
|
expect(result.current.appPrimaryBgColor).toEqual(invalidColor)
|
|
})
|
|
})
|
|
|
|
describe('Type checking', () => {
|
|
it('should only accept valid font sizes', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
// These should work
|
|
act(() => {
|
|
result.current.setFontSize('14px')
|
|
})
|
|
expect(result.current.fontSize).toBe('14px')
|
|
|
|
act(() => {
|
|
result.current.setFontSize('15px')
|
|
})
|
|
expect(result.current.fontSize).toBe('15px')
|
|
|
|
act(() => {
|
|
result.current.setFontSize('16px')
|
|
})
|
|
expect(result.current.fontSize).toBe('16px')
|
|
|
|
act(() => {
|
|
result.current.setFontSize('18px')
|
|
})
|
|
expect(result.current.fontSize).toBe('18px')
|
|
})
|
|
|
|
it('should only accept valid chat widths', () => {
|
|
const { result } = renderHook(() => useAppearance())
|
|
|
|
act(() => {
|
|
result.current.setChatWidth('full')
|
|
})
|
|
expect(result.current.chatWidth).toBe('full')
|
|
|
|
act(() => {
|
|
result.current.setChatWidth('compact')
|
|
})
|
|
expect(result.current.chatWidth).toBe('compact')
|
|
})
|
|
})
|
|
})
|