Skip to content

Commit

Permalink
Fix: #1142 setting groups toggle does not turn off it's nested settin…
Browse files Browse the repository at this point in the history
…gs (#3681)

* fix: #1142 - Toggle off experimental toggle does not turn off gated features

* test: add tests
  • Loading branch information
louis-menlo authored Sep 17, 2024
1 parent 8e603bd commit 031b351
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 17 deletions.
1 change: 1 addition & 0 deletions web/screens/Settings/Advanced/DataFolder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ const DataFolder = () => {
<div className="flex items-center gap-x-3">
<div className="relative">
<Input
data-testid="jan-data-folder-input"
value={janDataFolderPath}
className="w-full pr-8 sm:w-[240px]"
disabled
Expand Down
6 changes: 5 additions & 1 deletion web/screens/Settings/Advanced/FactoryReset/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const FactoryReset = () => {
recommended only if the application is in a corrupted state.
</p>
</div>
<Button theme="destructive" onClick={() => setModalValidation(true)}>
<Button
data-testid="reset-button"
theme="destructive"
onClick={() => setModalValidation(true)}
>
Reset
</Button>
<ModalValidation />
Expand Down
154 changes: 154 additions & 0 deletions web/screens/Settings/Advanced/index.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Advanced />)
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(<Advanced />)
let experimentalToggle
await waitFor(() => {
experimentalToggle = screen.getByTestId(/experimental-switch/i)
fireEvent.click(experimentalToggle!)
})
expect(experimentalToggle).toBeChecked()
})

it('updates Experimental disabled', async () => {
render(<Advanced />)

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(<Advanced />)
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(<Advanced />)
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(<Advanced />)
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(<Advanced />)
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(<Advanced />)
await waitFor(() => {
expect(screen.getByText('Jan Data Folder')).toBeInTheDocument()
expect(screen.getByTestId(/jan-data-folder-input/i)).toBeInTheDocument()
})
})

it('renders FactoryReset component', async () => {
render(<Advanced />)
await waitFor(() => {
expect(screen.getByText('Reset to Factory Settings')).toBeInTheDocument()
expect(screen.getByTestId(/reset-button/i)).toBeInTheDocument()
})
})
})
70 changes: 54 additions & 16 deletions web/screens/Settings/Advanced/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -69,7 +60,7 @@ const Advanced = () => {

const [partialProxy, setPartialProxy] = useState<string>(proxy)
const [gpuEnabled, setGpuEnabled] = useState<boolean>(false)
const [gpuList, setGpuList] = useState<GPU[]>(test)
const [gpuList, setGpuList] = useState<GPU[]>([])
const [gpusInUse, setGpusInUse] = useState<string[]>([])
const [dropdownOptions, setDropdownOptions] = useState<HTMLDivElement | null>(
null
Expand All @@ -87,6 +78,9 @@ const Advanced = () => {
return y['name']
})

/**
* Handle proxy change
*/
const onProxyChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value || ''
Expand All @@ -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
Expand All @@ -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',
Expand All @@ -123,11 +129,19 @@ const Advanced = () => {
if (relaunch) window.location.reload()
}

/**
* Update Experimental Enabled
* @param e
* @returns
*/
const updateExperimentalEnabled = async (
e: ChangeEvent<HTMLInputElement>
) => {
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
Expand All @@ -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()
Expand All @@ -149,6 +166,10 @@ const Advanced = () => {
setUseGpuIfPossible()
}, [readSettings, setGpuList, setGpuEnabled, setGpusInUse, setVulkanEnabled])

/**
* Clear logs
* @returns
*/
const clearLogs = async () => {
try {
await fs.rm(`file://logs`)
Expand All @@ -163,6 +184,11 @@ const Advanced = () => {
})
}

/**
* Handle GPU Change
* @param gpuId
* @returns
*/
const handleGPUChange = (gpuId: string) => {
let updatedGpusInUse = [...gpusInUse]
if (updatedGpusInUse.includes(gpuId)) {
Expand All @@ -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 (
Expand All @@ -204,6 +233,7 @@ const Advanced = () => {
</p>
</div>
<Switch
data-testid="experimental-switch"
checked={experimentalEnabled}
onChange={updateExperimentalEnabled}
/>
Expand Down Expand Up @@ -401,11 +431,13 @@ const Advanced = () => {

<div className="flex w-full flex-shrink-0 flex-col items-end gap-2 pr-1 sm:w-1/2">
<Switch
data-testid="proxy-switch"
checked={proxyEnabled}
onChange={() => setProxyEnabled(!proxyEnabled)}
/>
<div className="w-full">
<Input
data-testid="proxy-input"
placeholder={'http://<user>:<password>@<domain or IP>:<port>'}
value={partialProxy}
onChange={onProxyChange}
Expand All @@ -428,6 +460,7 @@ const Advanced = () => {
</p>
</div>
<Switch
data-testid="ignore-ssl-switch"
checked={ignoreSSL}
onChange={(e) => setIgnoreSSL(e.target.checked)}
/>
Expand All @@ -448,6 +481,7 @@ const Advanced = () => {
</p>
</div>
<Switch
data-testid="quick-ask-switch"
checked={quickAskEnabled}
onChange={() => {
toaster({
Expand All @@ -471,7 +505,11 @@ const Advanced = () => {
Clear all logs from Jan app.
</p>
</div>
<Button theme="destructive" onClick={clearLogs}>
<Button
data-testid="clear-logs"
theme="destructive"
onClick={clearLogs}
>
Clear
</Button>
</div>
Expand Down

0 comments on commit 031b351

Please sign in to comment.