import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' import { afterEach, describe, expect, it, vi } from 'vitest' import type { HermesConfigRecord } from '@/hermes' import { type I18nConfigClient, I18nProvider, useI18n } from './context' import type { Locale } from './types' function LanguageProbe({ target = 'zh' }: { target?: Locale }) { const { isLoadingConfig, isSavingLocale, locale, saveError, setLocale, t } = useI18n() return (

{locale}

{t.language.label}

{String(isLoadingConfig)}

{String(isSavingLocale)}

{saveError?.message ?? ''}

) } describe('I18nProvider', () => { afterEach(() => { cleanup() vi.restoreAllMocks() }) it('defaults to English without a config client', () => { render( ) expect(screen.getByTestId('locale').textContent).toBe('en') expect(screen.getByTestId('label').textContent).toBe('Language') }) it('normalizes an initial locale alias and switches translations', async () => { render( ) expect(screen.getByTestId('locale').textContent).toBe('zh') expect(screen.getByTestId('label').textContent).toBe('语言') fireEvent.click(screen.getByRole('button', { name: 'switch' })) await waitFor(() => expect(screen.getByTestId('locale').textContent).toBe('en')) expect(screen.getByTestId('label').textContent).toBe('Language') }) it('loads the initial locale from display.language config', async () => { const configClient: I18nConfigClient = { getConfig: vi.fn().mockResolvedValue({ display: { language: 'zh-Hans' } }), saveConfig: vi.fn() } render( ) await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false')) expect(screen.getByTestId('locale').textContent).toBe('zh') expect(screen.getByTestId('label').textContent).toBe('语言') expect(configClient.saveConfig).not.toHaveBeenCalled() }) it('keeps English usable when config loading fails', async () => { const configClient: I18nConfigClient = { getConfig: vi.fn().mockRejectedValue(new Error('config unavailable')), saveConfig: vi.fn() } render( ) await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false')) expect(screen.getByTestId('locale').textContent).toBe('en') expect(screen.getByTestId('label').textContent).toBe('Language') expect(configClient.saveConfig).not.toHaveBeenCalled() }) it('does not overwrite unsupported configured languages', async () => { const configClient: I18nConfigClient = { getConfig: vi.fn().mockResolvedValue({ display: { language: 'ja' } }), saveConfig: vi.fn() } render( ) await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false')) expect(screen.getByTestId('locale').textContent).toBe('en') expect(screen.getByTestId('label').textContent).toBe('Language') expect(configClient.saveConfig).not.toHaveBeenCalled() }) it('reads latest config before saving language and preserves unrelated values', async () => { const saveConfig = vi.fn().mockResolvedValue({ ok: true }) const latestConfig: HermesConfigRecord = { display: { language: 'en', skin: 'slate' }, terminal: { cwd: '/new' } } const configClient: I18nConfigClient = { getConfig: vi .fn() .mockResolvedValueOnce({ display: { language: 'en', skin: 'mono' }, terminal: { cwd: '/old' } }) .mockResolvedValueOnce(latestConfig), saveConfig } render( ) await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false')) fireEvent.click(screen.getByRole('button', { name: 'switch' })) await waitFor(() => expect(saveConfig).toHaveBeenCalledTimes(1)) expect(saveConfig).toHaveBeenCalledWith({ display: { language: 'zh', skin: 'slate' }, terminal: { cwd: '/new' } }) }) it('rolls back the visible locale when saving fails', async () => { const configClient: I18nConfigClient = { getConfig: vi.fn().mockResolvedValue({ display: { language: 'en' } }), saveConfig: vi.fn().mockRejectedValue(new Error('save failed')) } render( ) await waitFor(() => expect(screen.getByTestId('loading').textContent).toBe('false')) fireEvent.click(screen.getByRole('button', { name: 'switch' })) await waitFor(() => expect(screen.getByTestId('save-error').textContent).toBe('save failed')) expect(screen.getByTestId('locale').textContent).toBe('en') expect(screen.getByTestId('label').textContent).toBe('Language') }) })