Skip to content

Commit 2d05134

Browse files
authored
test: add tests to model settings's Slider Input (#3615)
* test: add tests to model settings's Slider Input * test: add slider test * test: fix expectation * test: correct slider test
1 parent ac67247 commit 2d05134

File tree

9 files changed

+128
-11
lines changed

9 files changed

+128
-11
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@ electron/test-data
4343
electron/test-results
4444
core/test_results.html
4545
coverage
46+
.yarn
47+
.yarnrc

electron/.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ module.exports = {
3434
{ name: 'Link', linkAttribute: 'to' },
3535
],
3636
},
37-
ignorePatterns: ['build', 'renderer', 'node_modules', '@global'],
37+
ignorePatterns: ['build', 'renderer', 'node_modules', '@global', 'playwright-report'],
3838
}

web/.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
'**/*.test.tsx',
1515
'**/*.test.ts',
1616
'testRunner.js',
17+
'jest.config.js',
1718
],
1819
extends: [
1920
'next/core-web-vitals',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React from 'react'
2+
import { render } from '@testing-library/react'
3+
import { fireEvent } from '@testing-library/dom'
4+
import SliderRightPanel from './index'
5+
import '@testing-library/jest-dom'
6+
7+
class ResizeObserverMock {
8+
observe() {}
9+
unobserve() {}
10+
disconnect() {}
11+
}
12+
13+
global.ResizeObserver = ResizeObserverMock
14+
15+
jest.mock('@janhq/joi', () => ({
16+
...jest.requireActual('@janhq/joi'),
17+
Slider: ({ children, onValueChange, ...props }: any) => (
18+
<div data-testid="slider-root" {...props}>
19+
<input
20+
data-testid="slider-input"
21+
type="number"
22+
{...props}
23+
onChange={(e: any) =>
24+
onValueChange && onValueChange([parseInt(e.target.value)])
25+
}
26+
/>
27+
{children}
28+
</div>
29+
),
30+
}))
31+
32+
describe('SliderRightPanel', () => {
33+
const defaultProps = {
34+
title: 'Test Slider',
35+
disabled: false,
36+
min: 0,
37+
max: 100,
38+
step: 1,
39+
description: 'This is a test slider',
40+
value: 50,
41+
onValueChanged: jest.fn(),
42+
}
43+
44+
it('renders correctly with given props', () => {
45+
const { getByText } = render(<SliderRightPanel {...defaultProps} />)
46+
expect(getByText('Test Slider')).toBeInTheDocument()
47+
})
48+
49+
it('calls onValueChanged with correct value when input is changed', () => {
50+
defaultProps.onValueChanged = jest.fn()
51+
const { getByRole } = render(<SliderRightPanel {...defaultProps} />)
52+
53+
const input = getByRole('textbox')
54+
fireEvent.change(input, { target: { value: '75' } })
55+
expect(defaultProps.onValueChanged).toHaveBeenCalledWith(75)
56+
})
57+
58+
it('calls onValueChanged with correct value when slider is changed', () => {
59+
defaultProps.onValueChanged = jest.fn()
60+
const { getByTestId } = render(<SliderRightPanel {...defaultProps} />)
61+
62+
const input = getByTestId('slider-input')
63+
fireEvent.change(input, { target: { value: '75' } })
64+
expect(defaultProps.onValueChanged).toHaveBeenCalledWith(75)
65+
})
66+
67+
it('calls onValueChanged with max value when input exceeds max', () => {
68+
defaultProps.onValueChanged = jest.fn()
69+
const { getByRole } = render(<SliderRightPanel {...defaultProps} />)
70+
const input = getByRole('textbox')
71+
fireEvent.change(input, { target: { value: '150' } })
72+
fireEvent.focusOut(input)
73+
expect(defaultProps.onValueChanged).toHaveBeenCalledWith(100)
74+
})
75+
76+
it('calls onValueChanged with min value when input is below min', () => {
77+
defaultProps.onValueChanged = jest.fn()
78+
const { getByRole } = render(<SliderRightPanel {...defaultProps} />)
79+
const input = getByRole('textbox')
80+
fireEvent.change(input, { target: { value: '0' } })
81+
fireEvent.focusOut(input)
82+
expect(defaultProps.onValueChanged).toHaveBeenCalledWith(0)
83+
})
84+
85+
it('does not call onValueChanged when input is invalid', () => {
86+
defaultProps.onValueChanged = jest.fn()
87+
const { getByRole } = render(<SliderRightPanel {...defaultProps} />)
88+
const input = getByRole('textbox')
89+
fireEvent.change(input, { target: { value: 'invalid' } })
90+
expect(defaultProps.onValueChanged).not.toHaveBeenCalledWith(0)
91+
})
92+
})

web/containers/SliderRightPanel/index.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useClickOutside } from '@janhq/joi'
66
import { InfoIcon } from 'lucide-react'
77

88
type Props = {
9-
name: string
9+
name?: string
1010
title: string
1111
disabled: boolean
1212
description: string
@@ -87,7 +87,17 @@ const SliderRightPanel = ({
8787
}
8888
}}
8989
onChange={(e) => {
90+
// Should not accept invalid value or NaN
91+
// E.g. anything changes that trigger onValueChanged
92+
// Which is incorrect
93+
if (Number(e.target.value) > Number(max)) {
94+
setVal(max.toString())
95+
} else if (Number(e.target.value) < Number(min)) {
96+
setVal(min.toString())
97+
} else if (Number.isNaN(Number(e.target.value))) return
98+
9099
onValueChanged?.(Number(e.target.value))
100+
// TODO: How to support negative number input?
91101
if (/^\d*\.?\d*$/.test(e.target.value)) {
92102
setVal(e.target.value)
93103
}

web/hooks/useCreateNewThread.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,8 @@ export const useCreateNewThread = () => {
135135
tools: experimentalEnabled ? [assistantTools] : assistant.tools,
136136
model: {
137137
id: defaultModel?.id ?? '*',
138-
settings: { ...defaultModel?.settings, ...overriddenSettings } ?? {},
139-
parameters:
140-
{ ...defaultModel?.parameters, ...overriddenParameters } ?? {},
138+
settings: { ...defaultModel?.settings, ...overriddenSettings },
139+
parameters: { ...defaultModel?.parameters, ...overriddenParameters },
141140
engine: defaultModel?.engine,
142141
},
143142
instructions,

web/jest.config.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
module.exports = {
2-
preset: 'ts-jest',
3-
testEnvironment: 'node',
4-
runner: './testRunner.js',
1+
const nextJest = require('next/jest')
2+
3+
/** @type {import('jest').Config} */
4+
const createJestConfig = nextJest({})
5+
6+
// Add any custom config to be passed to Jest
7+
const config = {
8+
coverageProvider: 'v8',
9+
testEnvironment: 'jsdom',
10+
// Add more setup options before each test is run
11+
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
512
}
13+
14+
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
15+
module.exports = createJestConfig(config)

web/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
},
5555
"devDependencies": {
5656
"@next/eslint-plugin-next": "^14.0.1",
57+
"@testing-library/react": "^16.0.1",
5758
"@types/jest": "^29.5.12",
5859
"@types/lodash": "^4.14.200",
5960
"@types/node": "20.8.10",
@@ -74,6 +75,7 @@
7475
"eslint-plugin-prettier": "^5.0.1",
7576
"eslint-plugin-react": "^7.34.0",
7677
"eslint-plugin-react-hooks": "^4.6.0",
78+
"jest-environment-jsdom": "^29.7.0",
7779
"jest-runner": "^29.7.0",
7880
"prettier": "^3.0.3",
7981
"prettier-plugin-tailwindcss": "^0.5.6",

web/tsconfig.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"typeRoots": [
66
"./node_modules/@types",
77
"./src/types",
8-
"../node_modules/@types/jest"
8+
"../node_modules/@types/jest",
9+
"@testing-library/jest-dom"
910
],
1011
"allowJs": true,
1112
"skipLibCheck": true,
@@ -29,5 +30,5 @@
2930
}
3031
},
3132
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
32-
"exclude": ["node_modules", "**/*.test.ts"]
33+
"exclude": ["node_modules"]
3334
}

0 commit comments

Comments
 (0)