Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: #1142 setting groups toggle does not turn off it's nested settings #3681

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
louis-jan marked this conversation as resolved.
Show resolved Hide resolved

// 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
Loading