Add VaultConfig infrastructure (store, hook, migration) that persists zoom, view mode, section visibility, tag/status colors, and property display modes to config/ui.config.md in the vault instead of localStorage. - New vaultConfigStore module with subscribe/notify pattern - useVaultConfig hook loads config via Tauri, binds store, runs migration - One-time silent migration from localStorage on first load - Config type excluded from note search and unified search - All hooks/utils updated to read/write through vault config store - Tests updated to use vault config store instead of localStorage mocks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
import { renderHook, act } from '@testing-library/react'
|
|
import { useZoom } from './useZoom'
|
|
import { bindVaultConfigStore, getVaultConfig, resetVaultConfigStore } from '../utils/vaultConfigStore'
|
|
|
|
const DEFAULT_VC = { zoom: null, view_mode: null, tag_colors: null, status_colors: null, property_display_modes: null, hidden_sections: null } as const
|
|
|
|
describe('useZoom', () => {
|
|
beforeEach(() => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC }, vi.fn())
|
|
document.documentElement.style.removeProperty('zoom')
|
|
})
|
|
|
|
it('initializes at 100% by default', () => {
|
|
const { result } = renderHook(() => useZoom())
|
|
expect(result.current.zoomLevel).toBe(100)
|
|
})
|
|
|
|
it('restores persisted zoom level from vault config', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: 1.2 }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
expect(result.current.zoomLevel).toBe(120)
|
|
})
|
|
|
|
it('defaults to 100 when vault config zoom is null', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: null }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
expect(result.current.zoomLevel).toBe(100)
|
|
})
|
|
|
|
it('ignores out-of-range persisted values', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: 2.0 }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
expect(result.current.zoomLevel).toBe(100)
|
|
})
|
|
|
|
it('zoomIn increases level by 10', () => {
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomIn())
|
|
expect(result.current.zoomLevel).toBe(110)
|
|
expect(getVaultConfig().zoom).toBe(1.1)
|
|
})
|
|
|
|
it('zoomOut decreases level by 10', () => {
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomOut())
|
|
expect(result.current.zoomLevel).toBe(90)
|
|
expect(getVaultConfig().zoom).toBe(0.9)
|
|
})
|
|
|
|
it('zoomIn clamps at 150', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: 1.5 }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomIn())
|
|
expect(result.current.zoomLevel).toBe(150)
|
|
})
|
|
|
|
it('zoomOut clamps at 80', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: 0.8 }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomOut())
|
|
expect(result.current.zoomLevel).toBe(80)
|
|
})
|
|
|
|
it('zoomReset returns to 100', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC, zoom: 1.3 }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomReset())
|
|
expect(result.current.zoomLevel).toBe(100)
|
|
expect(getVaultConfig().zoom).toBe(1.0)
|
|
})
|
|
|
|
it('applies CSS zoom property to document element', () => {
|
|
const spy = vi.spyOn(document.documentElement.style, 'setProperty')
|
|
const { result } = renderHook(() => useZoom())
|
|
spy.mockClear() // clear the mount call
|
|
act(() => result.current.zoomIn())
|
|
expect(spy).toHaveBeenCalledWith('zoom', '110%')
|
|
spy.mockRestore()
|
|
})
|
|
|
|
it('zoomIn and zoomOut are stable callbacks', () => {
|
|
const { result, rerender } = renderHook(() => useZoom())
|
|
const { zoomIn: a, zoomOut: b, zoomReset: c } = result.current
|
|
rerender()
|
|
expect(result.current.zoomIn).toBe(a)
|
|
expect(result.current.zoomOut).toBe(b)
|
|
expect(result.current.zoomReset).toBe(c)
|
|
})
|
|
|
|
it('successive zoomIn calls accumulate', () => {
|
|
const { result } = renderHook(() => useZoom())
|
|
act(() => result.current.zoomIn())
|
|
act(() => result.current.zoomIn())
|
|
act(() => result.current.zoomIn())
|
|
expect(result.current.zoomLevel).toBe(130)
|
|
})
|
|
|
|
it('defaults to 100 when vault config store is empty', () => {
|
|
resetVaultConfigStore()
|
|
bindVaultConfigStore({ ...DEFAULT_VC }, vi.fn())
|
|
const { result } = renderHook(() => useZoom())
|
|
expect(result.current.zoomLevel).toBe(100)
|
|
})
|
|
})
|