diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index 3bb059a87c..985dc65c3d 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -100,6 +100,7 @@ const DataFolder = () => {
{ recommended only if the application is in a corrupted state.

- diff --git a/web/screens/Settings/Advanced/index.test.tsx b/web/screens/Settings/Advanced/index.test.tsx new file mode 100644 index 0000000000..10ea810b11 --- /dev/null +++ b/web/screens/Settings/Advanced/index.test.tsx @@ -0,0 +1,154 @@ +import React from 'react' +import { render, screen, fireEvent, waitFor } from '@testing-library/react' +import '@testing-library/jest-dom' +import Advanced from '.' + +class ResizeObserverMock { + observe() {} + unobserve() {} + disconnect() {} +} + +global.ResizeObserver = ResizeObserverMock +// @ts-ignore +global.window.core = { + api: { + getAppConfigurations: () => jest.fn(), + updateAppConfiguration: () => jest.fn(), + relaunch: () => jest.fn(), + }, +} + +const setSettingsMock = jest.fn() + +// Mock useSettings hook +jest.mock('@/hooks/useSettings', () => ({ + __esModule: true, + useSettings: () => ({ + readSettings: () => ({ + run_mode: 'gpu', + experimental: false, + proxy: false, + gpus: [{ name: 'gpu-1' }, { name: 'gpu-2' }], + gpus_in_use: ['0'], + quick_ask: false, + }), + setSettings: setSettingsMock, + }), +})) + +import * as toast from '@/containers/Toast' + +jest.mock('@/containers/Toast') + +jest.mock('@janhq/core', () => ({ + __esModule: true, + ...jest.requireActual('@janhq/core'), + fs: { + rm: jest.fn(), + }, +})) + +// Simulate a full advanced settings screen +// @ts-ignore +global.isMac = false +// @ts-ignore +global.isWindows = true + +describe('Advanced', () => { + it('renders the component', async () => { + render() + await waitFor(() => { + expect(screen.getByText('Experimental Mode')).toBeInTheDocument() + expect(screen.getByText('HTTPS Proxy')).toBeInTheDocument() + expect(screen.getByText('Ignore SSL certificates')).toBeInTheDocument() + expect(screen.getByText('Jan Data Folder')).toBeInTheDocument() + expect(screen.getByText('Reset to Factory Settings')).toBeInTheDocument() + }) + }) + + it('updates Experimental enabled', async () => { + render() + let experimentalToggle + await waitFor(() => { + experimentalToggle = screen.getByTestId(/experimental-switch/i) + fireEvent.click(experimentalToggle!) + }) + expect(experimentalToggle).toBeChecked() + }) + + it('updates Experimental disabled', async () => { + render() + + let experimentalToggle + await waitFor(() => { + experimentalToggle = screen.getByTestId(/experimental-switch/i) + fireEvent.click(experimentalToggle!) + }) + expect(experimentalToggle).not.toBeChecked() + }) + + it('clears logs', async () => { + const jestMock = jest.fn() + jest.spyOn(toast, 'toaster').mockImplementation(jestMock) + + render() + let clearLogsButton + await waitFor(() => { + clearLogsButton = screen.getByTestId(/clear-logs/i) + fireEvent.click(clearLogsButton) + }) + expect(clearLogsButton).toBeInTheDocument() + expect(jestMock).toHaveBeenCalled() + }) + + it('toggles proxy enabled', async () => { + render() + let proxyToggle + await waitFor(() => { + expect(screen.getByText('HTTPS Proxy')).toBeInTheDocument() + proxyToggle = screen.getByTestId(/proxy-switch/i) + fireEvent.click(proxyToggle) + }) + expect(proxyToggle).toBeChecked() + }) + + it('updates proxy settings', async () => { + render() + let proxyInput + await waitFor(() => { + const proxyToggle = screen.getByTestId(/proxy-switch/i) + fireEvent.click(proxyToggle) + proxyInput = screen.getByTestId(/proxy-input/i) + fireEvent.change(proxyInput, { target: { value: 'http://proxy.com' } }) + }) + expect(proxyInput).toHaveValue('http://proxy.com') + }) + + it('toggles ignore SSL certificates', async () => { + render() + let ignoreSslToggle + await waitFor(() => { + expect(screen.getByText('Ignore SSL certificates')).toBeInTheDocument() + ignoreSslToggle = screen.getByTestId(/ignore-ssl-switch/i) + fireEvent.click(ignoreSslToggle) + }) + expect(ignoreSslToggle).toBeChecked() + }) + + it('renders DataFolder component', async () => { + render() + await waitFor(() => { + expect(screen.getByText('Jan Data Folder')).toBeInTheDocument() + expect(screen.getByTestId(/jan-data-folder-input/i)).toBeInTheDocument() + }) + }) + + it('renders FactoryReset component', async () => { + render() + await waitFor(() => { + expect(screen.getByText('Reset to Factory Settings')).toBeInTheDocument() + expect(screen.getByTestId(/reset-button/i)).toBeInTheDocument() + }) + }) +}) diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index f132f81e77..1384f5688e 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -43,19 +43,10 @@ type GPU = { name: string } -const test = [ - { - id: 'test a', - vram: 2, - name: 'nvidia A', - }, - { - id: 'test', - vram: 2, - name: 'nvidia B', - }, -] - +/** + * Advanced Settings Screen + * @returns + */ const Advanced = () => { const [experimentalEnabled, setExperimentalEnabled] = useAtom( experimentalFeatureEnabledAtom @@ -69,7 +60,7 @@ const Advanced = () => { const [partialProxy, setPartialProxy] = useState(proxy) const [gpuEnabled, setGpuEnabled] = useState(false) - const [gpuList, setGpuList] = useState(test) + const [gpuList, setGpuList] = useState([]) const [gpusInUse, setGpusInUse] = useState([]) const [dropdownOptions, setDropdownOptions] = useState( null @@ -87,6 +78,9 @@ const Advanced = () => { return y['name'] }) + /** + * Handle proxy change + */ const onProxyChange = useCallback( (event: ChangeEvent) => { const value = event.target.value || '' @@ -100,6 +94,12 @@ const Advanced = () => { [setPartialProxy, setProxy] ) + /** + * Update Quick Ask Enabled + * @param e + * @param relaunch + * @returns void + */ const updateQuickAskEnabled = async ( e: boolean, relaunch: boolean = true @@ -111,6 +111,12 @@ const Advanced = () => { if (relaunch) window.core?.api?.relaunch() } + /** + * Update Vulkan Enabled + * @param e + * @param relaunch + * @returns void + */ const updateVulkanEnabled = async (e: boolean, relaunch: boolean = true) => { toaster({ title: 'Reload', @@ -123,11 +129,19 @@ const Advanced = () => { if (relaunch) window.location.reload() } + /** + * Update Experimental Enabled + * @param e + * @returns + */ const updateExperimentalEnabled = async ( e: ChangeEvent ) => { setExperimentalEnabled(e.target.checked) - if (e) return + + // If it checked, we don't need to do anything else + // Otherwise have to reset other settings + if (e.target.checked) return // It affects other settings, so we need to reset them const isRelaunch = quickAskEnabled || vulkanEnabled @@ -136,6 +150,9 @@ const Advanced = () => { if (isRelaunch) window.core?.api?.relaunch() } + /** + * useEffect to set GPU enabled if possible + */ useEffect(() => { const setUseGpuIfPossible = async () => { const settings = await readSettings() @@ -149,6 +166,10 @@ const Advanced = () => { setUseGpuIfPossible() }, [readSettings, setGpuList, setGpuEnabled, setGpusInUse, setVulkanEnabled]) + /** + * Clear logs + * @returns + */ const clearLogs = async () => { try { await fs.rm(`file://logs`) @@ -163,6 +184,11 @@ const Advanced = () => { }) } + /** + * Handle GPU Change + * @param gpuId + * @returns + */ const handleGPUChange = (gpuId: string) => { let updatedGpusInUse = [...gpusInUse] if (updatedGpusInUse.includes(gpuId)) { @@ -188,6 +214,9 @@ const Advanced = () => { const gpuSelectionPlaceHolder = gpuList.length > 0 ? 'Select GPU' : "You don't have any compatible GPU" + /** + * Handle click outside + */ useClickOutside(() => setOpen(false), null, [dropdownOptions, toggle]) return ( @@ -204,6 +233,7 @@ const Advanced = () => {

@@ -401,11 +431,13 @@ const Advanced = () => {
setProxyEnabled(!proxyEnabled)} />
:@:'} value={partialProxy} onChange={onProxyChange} @@ -428,6 +460,7 @@ const Advanced = () => {

setIgnoreSSL(e.target.checked)} /> @@ -448,6 +481,7 @@ const Advanced = () => {

{ toaster({ @@ -471,7 +505,11 @@ const Advanced = () => { Clear all logs from Jan app.

-