fix: persist native language settings
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { renderHook, act, waitFor } from '@testing-library/react'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import type { Settings } from '../types'
|
||||
import { useSettings } from './useSettings'
|
||||
|
||||
@@ -46,6 +47,8 @@ const mockInvokeFn = vi.fn((cmd: string, args?: Record<string, unknown>): Promis
|
||||
return Promise.resolve(null)
|
||||
})
|
||||
|
||||
const nativeInvoke = vi.mocked(invoke)
|
||||
|
||||
vi.mock('@tauri-apps/api/core', () => ({
|
||||
invoke: vi.fn(),
|
||||
}))
|
||||
@@ -65,10 +68,29 @@ async function renderLoadedSettings(): Promise<Settings> {
|
||||
return result.current.settings
|
||||
}
|
||||
|
||||
function changedSettings(): Settings {
|
||||
return {
|
||||
auto_pull_interval_minutes: null,
|
||||
autogit_enabled: false,
|
||||
autogit_idle_threshold_seconds: 120,
|
||||
autogit_inactive_threshold_seconds: 45,
|
||||
auto_advance_inbox_after_organize: false,
|
||||
telemetry_consent: null,
|
||||
crash_reporting_enabled: null,
|
||||
analytics_enabled: null,
|
||||
anonymous_id: null,
|
||||
release_channel: null,
|
||||
theme_mode: null,
|
||||
ui_language: 'zh-Hans',
|
||||
default_ai_agent: null,
|
||||
}
|
||||
}
|
||||
|
||||
describe('useSettings', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockSettingsStore = { ...defaultSettings }
|
||||
nativeInvoke.mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it('returns empty settings initially', () => {
|
||||
@@ -92,6 +114,15 @@ describe('useSettings', () => {
|
||||
expect(mockInvokeFn).toHaveBeenCalledWith('get_settings', {})
|
||||
})
|
||||
|
||||
it('loads settings from native invoke when Tauri globals are not detectable', async () => {
|
||||
nativeInvoke.mockResolvedValueOnce({ ...savedSettings, ui_language: 'zh-Hans' })
|
||||
|
||||
const settings = await renderLoadedSettings()
|
||||
|
||||
expect(settings.ui_language).toBe('zh-Hans')
|
||||
expect(mockInvokeFn).not.toHaveBeenCalledWith('get_settings', {})
|
||||
})
|
||||
|
||||
it('normalizes a legacy beta release channel back to stable on load', async () => {
|
||||
mockSettingsStore = {
|
||||
...savedSettings,
|
||||
@@ -119,21 +150,7 @@ describe('useSettings', () => {
|
||||
expect(result.current.loaded).toBe(true)
|
||||
})
|
||||
|
||||
const newSettings: Settings = {
|
||||
auto_pull_interval_minutes: null,
|
||||
autogit_enabled: false,
|
||||
autogit_idle_threshold_seconds: 120,
|
||||
autogit_inactive_threshold_seconds: 45,
|
||||
auto_advance_inbox_after_organize: false,
|
||||
telemetry_consent: null,
|
||||
crash_reporting_enabled: null,
|
||||
analytics_enabled: null,
|
||||
anonymous_id: null,
|
||||
release_channel: null,
|
||||
theme_mode: null,
|
||||
ui_language: 'zh-Hans',
|
||||
default_ai_agent: null,
|
||||
}
|
||||
const newSettings = changedSettings()
|
||||
|
||||
await act(async () => {
|
||||
await result.current.saveSettings(newSettings)
|
||||
@@ -143,6 +160,27 @@ describe('useSettings', () => {
|
||||
expect(result.current.settings).toEqual(newSettings)
|
||||
})
|
||||
|
||||
it('saves settings through native invoke when Tauri globals are not detectable', async () => {
|
||||
const { result } = renderHook(() => useSettings())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.loaded).toBe(true)
|
||||
})
|
||||
|
||||
const newSettings = changedSettings()
|
||||
|
||||
vi.clearAllMocks()
|
||||
nativeInvoke.mockResolvedValueOnce(null)
|
||||
|
||||
await act(async () => {
|
||||
await result.current.saveSettings(newSettings)
|
||||
})
|
||||
|
||||
expect(nativeInvoke).toHaveBeenCalledWith('save_settings', { settings: newSettings })
|
||||
expect(mockInvokeFn).not.toHaveBeenCalled()
|
||||
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')))
|
||||
|
||||
@@ -7,8 +7,22 @@ import { normalizeReleaseChannel, serializeReleaseChannel } from '../lib/release
|
||||
import { normalizeThemeMode } from '../lib/themeMode'
|
||||
import type { Settings } from '../types'
|
||||
|
||||
function tauriCall<T>(command: string, tauriArgs: Record<string, unknown>, mockArgs?: Record<string, unknown>): Promise<T> {
|
||||
return isTauri() ? invoke<T>(command, tauriArgs) : mockInvoke<T>(command, mockArgs ?? tauriArgs)
|
||||
async function invokeNativeIfAvailable<T>(command: string, tauriArgs: Record<string, unknown>): Promise<T | undefined> {
|
||||
try {
|
||||
return await invoke<T>(command, tauriArgs)
|
||||
} catch (err) {
|
||||
if (isTauri()) throw err
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function tauriCall<T>(command: string, tauriArgs: Record<string, unknown>, mockArgs?: Record<string, unknown>): Promise<T> {
|
||||
if (isTauri()) return invoke<T>(command, tauriArgs)
|
||||
|
||||
const nativeResult = await invokeNativeIfAvailable<T>(command, tauriArgs)
|
||||
if (nativeResult !== undefined) return nativeResult
|
||||
|
||||
return mockInvoke<T>(command, mockArgs ?? tauriArgs)
|
||||
}
|
||||
|
||||
const EMPTY_SETTINGS: Settings = {
|
||||
|
||||
Reference in New Issue
Block a user