diff --git a/web-app/src/containers/__tests__/SettingsMenu.test.tsx b/web-app/src/containers/__tests__/SettingsMenu.test.tsx index 14b7bfca7..56a73fbb8 100644 --- a/web-app/src/containers/__tests__/SettingsMenu.test.tsx +++ b/web-app/src/containers/__tests__/SettingsMenu.test.tsx @@ -5,7 +5,6 @@ import SettingsMenu from '../SettingsMenu' import { useNavigate, useMatches } from '@tanstack/react-router' import { useGeneralSetting } from '@/hooks/useGeneralSetting' import { useModelProvider } from '@/hooks/useModelProvider' -import { useAppState } from '@/hooks/useAppState' // Mock dependencies vi.mock('@tanstack/react-router', () => ({ @@ -25,9 +24,7 @@ vi.mock('@/i18n/react-i18next-compat', () => ({ })) vi.mock('@/hooks/useGeneralSetting', () => ({ - useGeneralSetting: vi.fn(() => ({ - experimentalFeatures: false, - })), + useGeneralSetting: vi.fn(() => ({})), })) vi.mock('@/hooks/useModelProvider', () => ({ @@ -71,14 +68,14 @@ describe('SettingsMenu', () => { beforeEach(() => { vi.clearAllMocks() - + vi.mocked(useNavigate).mockReturnValue(mockNavigate) vi.mocked(useMatches).mockReturnValue(mockMatches) }) it('renders all menu items', () => { render() - + expect(screen.getByText('common:general')).toBeInTheDocument() expect(screen.getByText('common:appearance')).toBeInTheDocument() expect(screen.getByText('common:privacy')).toBeInTheDocument() @@ -88,29 +85,14 @@ describe('SettingsMenu', () => { expect(screen.getByText('common:local_api_server')).toBeInTheDocument() expect(screen.getByText('common:https_proxy')).toBeInTheDocument() expect(screen.getByText('common:extensions')).toBeInTheDocument() - }) - - it('does not show MCP Servers when experimental features disabled', () => { - render() - - expect(screen.queryByText('common:mcp-servers')).not.toBeInTheDocument() - }) - - it('shows MCP Servers when experimental features enabled', () => { - vi.mocked(useGeneralSetting).mockReturnValue({ - experimentalFeatures: true, - }) - - render() - expect(screen.getByText('common:mcp-servers')).toBeInTheDocument() }) it('shows provider expansion chevron when providers are active', () => { render() - + const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) expect(chevron).toBeInTheDocument() @@ -119,14 +101,14 @@ describe('SettingsMenu', () => { it('expands providers submenu when chevron is clicked', async () => { const user = userEvent.setup() render() - + const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) if (!chevron) throw new Error('Chevron button not found') await user.click(chevron) - + expect(screen.getByTestId('provider-avatar-openai')).toBeInTheDocument() expect(screen.getByTestId('provider-avatar-llama.cpp')).toBeInTheDocument() }) @@ -138,52 +120,56 @@ describe('SettingsMenu', () => { params: { providerName: 'openai' }, }, ]) - + render() - + expect(screen.getByTestId('provider-avatar-openai')).toBeInTheDocument() expect(screen.getByTestId('provider-avatar-llama.cpp')).toBeInTheDocument() }) it('highlights active provider in submenu', async () => { const user = userEvent.setup() - + vi.mocked(useMatches).mockReturnValue([ { routeId: '/settings/providers/$providerName', params: { providerName: 'openai' }, }, ]) - + render() - + // First expand the providers submenu const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) if (chevron) await user.click(chevron) - - const openaiProvider = screen.getByTestId('provider-avatar-openai').closest('div') + + const openaiProvider = screen + .getByTestId('provider-avatar-openai') + .closest('div') expect(openaiProvider).toBeInTheDocument() }) it('navigates to provider when provider is clicked', async () => { const user = userEvent.setup() render() - + // First expand the providers const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) if (!chevron) throw new Error('Chevron button not found') await user.click(chevron) - + // Then click on a provider - const openaiProvider = screen.getByTestId('provider-avatar-openai').closest('div') + const openaiProvider = screen + .getByTestId('provider-avatar-openai') + .closest('div') await user.click(openaiProvider!) - + expect(mockNavigate).toHaveBeenCalledWith({ to: '/settings/providers/$providerName', params: { providerName: 'openai' }, @@ -192,18 +178,22 @@ describe('SettingsMenu', () => { it('shows mobile menu toggle button', () => { render() - - const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' }) + + const menuToggle = screen.getByRole('button', { + name: 'Toggle settings menu', + }) expect(menuToggle).toBeInTheDocument() }) it('opens mobile menu when toggle is clicked', async () => { const user = userEvent.setup() render() - - const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' }) + + const menuToggle = screen.getByRole('button', { + name: 'Toggle settings menu', + }) await user.click(menuToggle) - + // Menu should now be visible const menu = screen.getByText('common:general').closest('div') expect(menu).toHaveClass('flex') @@ -212,21 +202,23 @@ describe('SettingsMenu', () => { it('closes mobile menu when X is clicked', async () => { const user = userEvent.setup() render() - + // Open menu first - const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' }) + const menuToggle = screen.getByRole('button', { + name: 'Toggle settings menu', + }) await user.click(menuToggle) - + // Then close it await user.click(menuToggle) - + // Just verify the toggle button is still there after clicking twice expect(menuToggle).toBeInTheDocument() }) it('hides llamacpp provider during setup remote provider step', async () => { const user = userEvent.setup() - + vi.mocked(useMatches).mockReturnValue([ { routeId: '/settings/providers/', @@ -234,16 +226,16 @@ describe('SettingsMenu', () => { search: { step: 'setup_remote_provider' }, }, ]) - + render() - + // First expand the providers submenu const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) if (chevron) await user.click(chevron) - + // llamacpp provider div should have hidden class const llamacppElement = screen.getByTestId('provider-avatar-llama.cpp') expect(llamacppElement.parentElement).toHaveClass('hidden') @@ -253,7 +245,7 @@ describe('SettingsMenu', () => { it('filters out inactive providers from submenu', async () => { const user = userEvent.setup() - + vi.mocked(useModelProvider).mockReturnValue({ providers: [ { @@ -268,17 +260,19 @@ describe('SettingsMenu', () => { }, ], }) - + render() - + // Expand providers const chevronButtons = screen.getAllByRole('button') - const chevron = chevronButtons.find(button => + const chevron = chevronButtons.find((button) => button.querySelector('svg.tabler-icon-chevron-right') ) if (chevron) await user.click(chevron) - + expect(screen.getByTestId('provider-avatar-openai')).toBeInTheDocument() - expect(screen.queryByTestId('provider-avatar-anthropic')).not.toBeInTheDocument() + expect( + screen.queryByTestId('provider-avatar-anthropic') + ).not.toBeInTheDocument() }) -}) \ No newline at end of file +}) diff --git a/web-app/src/hooks/__tests__/useGeneralSetting.test.ts b/web-app/src/hooks/__tests__/useGeneralSetting.test.ts index 19e71c4fa..f55835c0f 100644 --- a/web-app/src/hooks/__tests__/useGeneralSetting.test.ts +++ b/web-app/src/hooks/__tests__/useGeneralSetting.test.ts @@ -31,16 +31,15 @@ describe('useGeneralSetting', () => { beforeEach(async () => { vi.clearAllMocks() - + // Get the mocked ExtensionManager const { ExtensionManager } = await import('@/lib/extension') mockExtensionManager = ExtensionManager - + // Reset store state to defaults useGeneralSetting.setState({ currentLanguage: 'en', spellCheckChatInput: true, - experimentalFeatures: false, huggingfaceToken: undefined, }) @@ -49,7 +48,7 @@ describe('useGeneralSetting', () => { getSettings: vi.fn().mockResolvedValue(null), updateSettings: vi.fn(), }) - + mockExtensionManager.getInstance.mockReturnValue({ getByName: mockGetByName, }) @@ -60,11 +59,9 @@ describe('useGeneralSetting', () => { expect(result.current.currentLanguage).toBe('en') expect(result.current.spellCheckChatInput).toBe(true) - expect(result.current.experimentalFeatures).toBe(false) expect(result.current.huggingfaceToken).toBeUndefined() expect(typeof result.current.setCurrentLanguage).toBe('function') expect(typeof result.current.setSpellCheckChatInput).toBe('function') - expect(typeof result.current.setExperimentalFeatures).toBe('function') expect(typeof result.current.setHuggingfaceToken).toBe('function') }) @@ -155,42 +152,6 @@ describe('useGeneralSetting', () => { }) }) - describe('setExperimentalFeatures', () => { - it('should enable experimental features', () => { - const { result } = renderHook(() => useGeneralSetting()) - - act(() => { - result.current.setExperimentalFeatures(true) - }) - - expect(result.current.experimentalFeatures).toBe(true) - }) - - it('should disable experimental features', () => { - const { result } = renderHook(() => useGeneralSetting()) - - act(() => { - result.current.setExperimentalFeatures(false) - }) - - expect(result.current.experimentalFeatures).toBe(false) - }) - - it('should toggle experimental features multiple times', () => { - const { result } = renderHook(() => useGeneralSetting()) - - act(() => { - result.current.setExperimentalFeatures(true) - }) - expect(result.current.experimentalFeatures).toBe(true) - - act(() => { - result.current.setExperimentalFeatures(false) - }) - expect(result.current.experimentalFeatures).toBe(false) - }) - }) - describe('setHuggingfaceToken', () => { it('should set huggingface token', () => { const { result } = renderHook(() => useGeneralSetting()) @@ -235,7 +196,7 @@ describe('useGeneralSetting', () => { const mockGetByName = vi.fn() const mockGetSettings = vi.fn().mockResolvedValue(mockSettings) const mockUpdateSettings = vi.fn() - + mockExtensionManager.getInstance.mockReturnValue({ getByName: mockGetByName, }) @@ -252,9 +213,9 @@ describe('useGeneralSetting', () => { expect(mockExtensionManager.getInstance).toHaveBeenCalled() expect(mockGetByName).toHaveBeenCalledWith('@janhq/download-extension') - + // Wait for async operations - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise((resolve) => setTimeout(resolve, 0)) expect(mockGetSettings).toHaveBeenCalled() expect(mockUpdateSettings).toHaveBeenCalledWith([ @@ -272,13 +233,11 @@ describe('useGeneralSetting', () => { act(() => { result1.current.setCurrentLanguage('id') result1.current.setSpellCheckChatInput(false) - result1.current.setExperimentalFeatures(true) result1.current.setHuggingfaceToken('shared-token') }) expect(result2.current.currentLanguage).toBe('id') expect(result2.current.spellCheckChatInput).toBe(false) - expect(result2.current.experimentalFeatures).toBe(true) expect(result2.current.huggingfaceToken).toBe('shared-token') }) }) @@ -290,13 +249,11 @@ describe('useGeneralSetting', () => { act(() => { result.current.setCurrentLanguage('vn') result.current.setSpellCheckChatInput(false) - result.current.setExperimentalFeatures(true) result.current.setHuggingfaceToken('complex-token-123') }) expect(result.current.currentLanguage).toBe('vn') expect(result.current.spellCheckChatInput).toBe(false) - expect(result.current.experimentalFeatures).toBe(true) expect(result.current.huggingfaceToken).toBe('complex-token-123') }) @@ -314,11 +271,9 @@ describe('useGeneralSetting', () => { // Second update act(() => { - result.current.setExperimentalFeatures(true) result.current.setHuggingfaceToken('sequential-token') }) - expect(result.current.experimentalFeatures).toBe(true) expect(result.current.huggingfaceToken).toBe('sequential-token') // Third update @@ -331,4 +286,4 @@ describe('useGeneralSetting', () => { expect(result.current.spellCheckChatInput).toBe(true) }) }) -}) \ No newline at end of file +}) diff --git a/web-app/src/routes/settings/__tests__/general.test.tsx b/web-app/src/routes/settings/__tests__/general.test.tsx index 96388b0fb..e21a28dcf 100644 --- a/web-app/src/routes/settings/__tests__/general.test.tsx +++ b/web-app/src/routes/settings/__tests__/general.test.tsx @@ -61,8 +61,6 @@ vi.mock('@/hooks/useGeneralSetting', () => ({ useGeneralSetting: () => ({ spellCheckChatInput: true, setSpellCheckChatInput: vi.fn(), - experimentalFeatures: false, - setExperimentalFeatures: vi.fn(), huggingfaceToken: 'test-token', setHuggingfaceToken: vi.fn(), }), @@ -188,12 +186,14 @@ vi.mock('@tauri-apps/plugin-opener', () => ({ })) vi.mock('@tauri-apps/api/webviewWindow', () => { - const MockWebviewWindow = vi.fn().mockImplementation((label: string, options: any) => ({ - once: vi.fn(), - setFocus: vi.fn(), - })) + const MockWebviewWindow = vi + .fn() + .mockImplementation((label: string, options: any) => ({ + once: vi.fn(), + setFocus: vi.fn(), + })) MockWebviewWindow.getByLabel = vi.fn().mockReturnValue(null) - + return { WebviewWindow: MockWebviewWindow, } @@ -299,16 +299,6 @@ describe('General Settings Route', () => { // expect(screen.getByTestId('language-switcher')).toBeInTheDocument() // }) - it('should render switches for experimental features and spell check', async () => { - const Component = GeneralRoute.component as React.ComponentType - await act(async () => { - render() - }) - - const switches = screen.getAllByTestId('switch') - expect(switches.length).toBeGreaterThanOrEqual(2) - }) - it('should render huggingface token input', async () => { const Component = GeneralRoute.component as React.ComponentType await act(async () => { @@ -336,24 +326,6 @@ describe('General Settings Route', () => { expect(switches[0]).toBeInTheDocument() }) - it('should handle experimental features toggle', async () => { - const Component = GeneralRoute.component as React.ComponentType - await act(async () => { - render() - }) - - const switches = screen.getAllByTestId('switch') - expect(switches.length).toBeGreaterThan(0) - - // Test that switches are interactive - if (switches.length > 1) { - await act(async () => { - fireEvent.click(switches[1]) - }) - expect(switches[1]).toBeInTheDocument() - } - }) - it('should handle huggingface token change', async () => { const Component = GeneralRoute.component as React.ComponentType await act(async () => { @@ -514,16 +486,16 @@ describe('General Settings Route', () => { act(() => { fireEvent.click(checkUpdateButton) }) - + // Now the button should be disabled while checking expect(checkUpdateButton).toBeDisabled() - + // Resolve the promise to finish the update check await act(async () => { resolveUpdate!(null) await updatePromise }) - + // Button should be enabled again expect(checkUpdateButton).not.toBeDisabled() }