Files
tolaria/src/hooks/useSettings.test.ts

139 lines
3.9 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act, waitFor } from '@testing-library/react'
import type { Settings } from '../types'
import { useSettings } from './useSettings'
const defaultSettings: Settings = {
openai_key: null,
google_key: null,
github_token: null,
github_username: null,
feat: auto-pull vault changes from Git (background sync + conflict handling) (#79) * feat: add auto-pull vault sync with conflict handling - Add git_pull, has_remote, get_conflict_files to Rust backend - Add GitPullResult type and auto_pull_interval_minutes to Settings - Create useAutoSync hook (pull on launch, focus, periodic interval) - Update StatusBar with real sync status indicator - Add pull interval setting to SettingsPanel - Add mock handler for git_pull - Update existing tests for new Settings field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for git pull, settings, and useAutoSync hook - Add Rust tests: has_remote, git_pull (no_remote, up_to_date, updated), get_conflict_files, parse_updated_files, GitPullResult serialization - Add frontend tests: useAutoSync (mount pull, focus pull, conflict, error, manual trigger, concurrent prevention, no_remote) - Fix existing settings tests for new auto_pull_interval_minutes field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add auto-pull-vault wireframes Frames showing: sync idle, syncing, conflict indicator, settings sync section, and conflict toast notification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add auto_pull_interval_minutes to all Settings literals Fix build error and test fixtures missing the new field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: cargo fmt * ci: re-trigger CI after flaky test * ci: retrigger after disk space cleanup --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:55:16 +01:00
auto_pull_interval_minutes: null,
telemetry_consent: null,
crash_reporting_enabled: null,
analytics_enabled: null,
anonymous_id: null,
update_channel: null,
}
const savedSettings: Settings = {
openai_key: null,
google_key: 'AIza-test',
github_token: null,
github_username: null,
feat: auto-pull vault changes from Git (background sync + conflict handling) (#79) * feat: add auto-pull vault sync with conflict handling - Add git_pull, has_remote, get_conflict_files to Rust backend - Add GitPullResult type and auto_pull_interval_minutes to Settings - Create useAutoSync hook (pull on launch, focus, periodic interval) - Update StatusBar with real sync status indicator - Add pull interval setting to SettingsPanel - Add mock handler for git_pull - Update existing tests for new Settings field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for git pull, settings, and useAutoSync hook - Add Rust tests: has_remote, git_pull (no_remote, up_to_date, updated), get_conflict_files, parse_updated_files, GitPullResult serialization - Add frontend tests: useAutoSync (mount pull, focus pull, conflict, error, manual trigger, concurrent prevention, no_remote) - Fix existing settings tests for new auto_pull_interval_minutes field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add auto-pull-vault wireframes Frames showing: sync idle, syncing, conflict indicator, settings sync section, and conflict toast notification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add auto_pull_interval_minutes to all Settings literals Fix build error and test fixtures missing the new field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: cargo fmt * ci: re-trigger CI after flaky test * ci: retrigger after disk space cleanup --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:55:16 +01:00
auto_pull_interval_minutes: null,
telemetry_consent: null,
crash_reporting_enabled: null,
analytics_enabled: null,
anonymous_id: null,
update_channel: null,
}
let mockSettingsStore: Settings = { ...defaultSettings }
const mockInvokeFn = vi.fn((cmd: string, args?: Record<string, unknown>): Promise<unknown> => {
if (cmd === 'get_settings') return Promise.resolve({ ...mockSettingsStore })
if (cmd === 'save_settings') {
mockSettingsStore = { ...(args as { settings: Settings }).settings }
return Promise.resolve(null)
}
return Promise.resolve(null)
})
vi.mock('@tauri-apps/api/core', () => ({
invoke: vi.fn(),
}))
vi.mock('../mock-tauri', () => ({
isTauri: () => false,
mockInvoke: (cmd: string, args?: Record<string, unknown>) => mockInvokeFn(cmd, args),
}))
describe('useSettings', () => {
beforeEach(() => {
vi.clearAllMocks()
mockSettingsStore = { ...defaultSettings }
})
it('returns empty settings initially', () => {
const { result } = renderHook(() => useSettings())
expect(result.current.settings).toEqual(defaultSettings)
expect(result.current.loaded).toBe(false)
})
it('loads settings from backend on mount', async () => {
mockSettingsStore = { ...savedSettings }
const { result } = renderHook(() => useSettings())
await waitFor(() => {
expect(result.current.loaded).toBe(true)
})
expect(result.current.settings.google_key).toBe('AIza-test')
expect(mockInvokeFn).toHaveBeenCalledWith('get_settings', {})
})
it('saves settings via backend', async () => {
const { result } = renderHook(() => useSettings())
await waitFor(() => {
expect(result.current.loaded).toBe(true)
})
const newSettings: Settings = {
openai_key: 'sk-openai-new',
google_key: null,
github_token: null,
github_username: null,
feat: auto-pull vault changes from Git (background sync + conflict handling) (#79) * feat: add auto-pull vault sync with conflict handling - Add git_pull, has_remote, get_conflict_files to Rust backend - Add GitPullResult type and auto_pull_interval_minutes to Settings - Create useAutoSync hook (pull on launch, focus, periodic interval) - Update StatusBar with real sync status indicator - Add pull interval setting to SettingsPanel - Add mock handler for git_pull - Update existing tests for new Settings field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for git pull, settings, and useAutoSync hook - Add Rust tests: has_remote, git_pull (no_remote, up_to_date, updated), get_conflict_files, parse_updated_files, GitPullResult serialization - Add frontend tests: useAutoSync (mount pull, focus pull, conflict, error, manual trigger, concurrent prevention, no_remote) - Fix existing settings tests for new auto_pull_interval_minutes field Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * design: add auto-pull-vault wireframes Frames showing: sync idle, syncing, conflict indicator, settings sync section, and conflict toast notification. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add auto_pull_interval_minutes to all Settings literals Fix build error and test fixtures missing the new field. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: cargo fmt * ci: re-trigger CI after flaky test * ci: retrigger after disk space cleanup --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 01:55:16 +01:00
auto_pull_interval_minutes: null,
telemetry_consent: null,
crash_reporting_enabled: null,
analytics_enabled: null,
anonymous_id: null,
update_channel: null,
}
await act(async () => {
await result.current.saveSettings(newSettings)
})
expect(mockInvokeFn).toHaveBeenCalledWith('save_settings', { settings: newSettings })
expect(result.current.settings).toEqual(newSettings)
})
it('handles load error gracefully', async () => {
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
mockInvokeFn.mockImplementationOnce(() => Promise.reject(new Error('no config')))
const { result } = renderHook(() => useSettings())
await waitFor(() => {
expect(result.current.loaded).toBe(true)
})
// Should fall back to empty settings
expect(result.current.settings).toEqual(defaultSettings)
warnSpy.mockRestore()
})
it('handles save error gracefully', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { result } = renderHook(() => useSettings())
await waitFor(() => {
expect(result.current.loaded).toBe(true)
})
mockInvokeFn.mockImplementationOnce(() => Promise.reject(new Error('write failed')))
await act(async () => {
await result.current.saveSettings(savedSettings)
})
// Settings should not have changed on error
expect(result.current.settings).toEqual(defaultSettings)
errorSpy.mockRestore()
})
})