mirror of
https://github.com/langgenius/dify.git
synced 2026-11-10 21:35:45 +08:00
test(web): verify parameter controls in Browser Mode (#45485)
This commit is contained in:
1 parent
6f0ee4bd1e
commit
b112b28585
9 files changed
+298
-327
No files matched your search
@@ -0,0 +1,30 @@
|
||||
import { page, userEvent } from 'vite-plus/test/browser'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { ParamItemExample } from './param-item-example'
|
||||
|
||||
beforeEach(async () => {
|
||||
await page.viewport(800, 600)
|
||||
})
|
||||
|
||||
// Native Tab/arrow input must update the caller and numeric field, not just emit a callback.
|
||||
it.each([
|
||||
{ max: 1, min: 0, value: 0.5, sliderValue: '50', next: 0.51, nextSliderValue: '51' },
|
||||
{ max: 10, min: 1, value: 5, sliderValue: '5', next: 6, nextSliderValue: '6' },
|
||||
])(
|
||||
'keeps keyboard slider edits in parameter units with max=$max',
|
||||
async ({ max, min, value, sliderValue, next, nextSliderValue }) => {
|
||||
await render(<ParamItemExample value={value} min={min} max={max} />)
|
||||
const slider = page.getByRole('slider', { name: 'Test Param' })
|
||||
const input = page.getByRole('textbox', { name: 'Test Param' })
|
||||
await expect.element(slider).toHaveValue(sliderValue)
|
||||
await input.click()
|
||||
await userEvent.tab()
|
||||
await expect.element(slider).toHaveFocus()
|
||||
await userEvent.keyboard('{ArrowRight}')
|
||||
await expect.element(input).toHaveValue(String(next))
|
||||
await expect.element(slider).toHaveValue(nextSliderValue)
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent(JSON.stringify({ test_param: next }))
|
||||
},
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import ParamItem from '..'
|
||||
|
||||
describe('ParamItem Slider onChange', () => {
|
||||
const defaultProps = {
|
||||
id: 'test_param',
|
||||
name: 'Test Param',
|
||||
enable: true,
|
||||
onChange: vi.fn(),
|
||||
}
|
||||
|
||||
const getSlider = () => screen.getByRole('slider', { name: 'Test Param' })
|
||||
|
||||
it('should divide slider value by 100 when max < 5', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<ParamItem {...defaultProps} value={0.5} min={0} max={1} />)
|
||||
const slider = getSlider()
|
||||
|
||||
expect(slider).toHaveAttribute('aria-valuenow', '50')
|
||||
expect(slider).toHaveAttribute('aria-valuetext', '0.5')
|
||||
|
||||
slider.focus()
|
||||
await user.keyboard('{ArrowRight}')
|
||||
|
||||
// max=1 < 5, so slider value change (50->51) becomes 0.51
|
||||
expect(defaultProps.onChange).toHaveBeenLastCalledWith('test_param', 0.51)
|
||||
})
|
||||
|
||||
it('should not divide slider value when max >= 5', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<ParamItem {...defaultProps} value={5} min={1} max={10} />)
|
||||
const slider = getSlider()
|
||||
|
||||
slider.focus()
|
||||
await user.keyboard('{ArrowRight}')
|
||||
|
||||
// max=10 >= 5, so value remains raw (5->6)
|
||||
expect(defaultProps.onChange).toHaveBeenLastCalledWith('test_param', 6)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,87 @@
|
||||
import { page, userEvent } from 'vite-plus/test/browser'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { ParamItemExample } from './param-item-example'
|
||||
|
||||
beforeEach(async () => {
|
||||
await page.viewport(800, 600)
|
||||
})
|
||||
|
||||
describe('ParamItem', () => {
|
||||
it('omits the tooltip trigger when tooltips are disabled', async () => {
|
||||
await render(<ParamItemExample noTooltip tip="Hidden tip" />)
|
||||
await expect.element(page.getByRole('button', { name: 'Test Param' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('disables both numeric and slider edits when the parameter is off', async () => {
|
||||
await render(<ParamItemExample enable={false} />)
|
||||
await expect.element(page.getByRole('textbox', { name: 'Test Param' })).toBeDisabled()
|
||||
await expect.element(page.getByRole('slider', { name: 'Test Param' })).toBeDisabled()
|
||||
})
|
||||
|
||||
it('applies text edits to the named parameter and its slider', async () => {
|
||||
await render(<ParamItemExample />)
|
||||
const input = page.getByRole('textbox', { name: 'Test Param' })
|
||||
await input.clear()
|
||||
await userEvent.keyboard('0.8')
|
||||
await expect.element(input).toHaveValue('0.8')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"test_param":0.8}')
|
||||
await expect
|
||||
.element(page.getByRole('slider', { name: 'Test Param' }))
|
||||
.toHaveAttribute('aria-valuetext', '0.8')
|
||||
})
|
||||
|
||||
it('allows replacing the minimum with a multi-digit value without saving the empty draft', async () => {
|
||||
await render(<ParamItemExample value={1} min={1} max={100} />)
|
||||
const input = page.getByRole('textbox', { name: 'Test Param' })
|
||||
await input.clear()
|
||||
await expect.element(input).toHaveValue('')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"test_param":1}')
|
||||
await userEvent.keyboard('20')
|
||||
await expect.element(input).toHaveValue('20')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"test_param":20}')
|
||||
await expect.element(page.getByRole('slider', { name: 'Test Param' })).toHaveValue('20')
|
||||
})
|
||||
|
||||
it('clamps text edits before saving the parameter and updating the slider', async () => {
|
||||
await render(<ParamItemExample />)
|
||||
const input = page.getByRole('textbox', { name: 'Test Param' })
|
||||
await input.clear()
|
||||
await userEvent.keyboard('1.5')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"test_param":1}')
|
||||
await expect
|
||||
.element(page.getByRole('slider', { name: 'Test Param' }))
|
||||
.toHaveAttribute('aria-valuetext', '1')
|
||||
})
|
||||
|
||||
it('applies the named switch change and disables the parameter controls', async () => {
|
||||
await render(<ParamItemExample hasSwitch />)
|
||||
const toggle = page.getByRole('switch', { name: 'Test Param' })
|
||||
await toggle.click()
|
||||
await expect.element(toggle).not.toBeChecked()
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Enabled parameters' }))
|
||||
.toHaveTextContent('{"test_param":false}')
|
||||
await expect.element(page.getByRole('textbox', { name: 'Test Param' })).toBeDisabled()
|
||||
await expect.element(page.getByRole('slider', { name: 'Test Param' })).toBeDisabled()
|
||||
})
|
||||
|
||||
it('applies the increment step to the saved parameter and both controls', async () => {
|
||||
await render(<ParamItemExample step={0.1} />)
|
||||
await page.getByRole('button', { name: 'Increment value' }).click()
|
||||
await expect.element(page.getByRole('textbox', { name: 'Test Param' })).toHaveValue('0.6')
|
||||
await expect
|
||||
.element(page.getByRole('slider', { name: 'Test Param' }))
|
||||
.toHaveAttribute('aria-valuetext', '0.6')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"test_param":0.6}')
|
||||
})
|
||||
})
|
||||
@@ -1,169 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { useState } from 'react'
|
||||
import ParamItem from '..'
|
||||
|
||||
describe('ParamItem', () => {
|
||||
const defaultProps = {
|
||||
id: 'test_param',
|
||||
name: 'Test Param',
|
||||
value: 0.5,
|
||||
enable: true,
|
||||
max: 1,
|
||||
onChange: vi.fn(),
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
const getSlider = () =>
|
||||
screen.getByLabelText('Test Param', {
|
||||
selector: 'input[type="range"]',
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('should not render tooltip trigger when noTooltip is true', () => {
|
||||
render(<ParamItem {...defaultProps} noTooltip tip="Hidden tip" />)
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Test Param' })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Props', () => {
|
||||
it('should disable InputNumber when enable is false', () => {
|
||||
render(<ParamItem {...defaultProps} enable={false} />)
|
||||
|
||||
expect(screen.getByRole('textbox')).toBeDisabled()
|
||||
expect(getSlider()).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('User Interactions', () => {
|
||||
it('should call onChange with id and value when InputNumber changes', async () => {
|
||||
const user = userEvent.setup()
|
||||
const StatefulParamItem = () => {
|
||||
const [value, setValue] = useState(defaultProps.value)
|
||||
|
||||
return (
|
||||
<ParamItem
|
||||
{...defaultProps}
|
||||
value={value}
|
||||
onChange={(key: string, nextValue: number) => {
|
||||
defaultProps.onChange(key, nextValue)
|
||||
setValue(nextValue)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render(<StatefulParamItem />)
|
||||
const input = screen.getByRole('textbox')
|
||||
|
||||
await user.clear(input)
|
||||
await user.type(input, '0.8')
|
||||
|
||||
expect(defaultProps.onChange).toHaveBeenLastCalledWith('test_param', 0.8)
|
||||
})
|
||||
|
||||
it('should allow users to replace the minimum value with a multi-digit value', async () => {
|
||||
const user = userEvent.setup()
|
||||
const StatefulParamItem = () => {
|
||||
const [value, setValue] = useState(1)
|
||||
|
||||
return (
|
||||
<ParamItem
|
||||
{...defaultProps}
|
||||
value={value}
|
||||
min={1}
|
||||
max={100}
|
||||
onChange={(key: string, nextValue: number) => {
|
||||
defaultProps.onChange(key, nextValue)
|
||||
setValue(nextValue)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render(<StatefulParamItem />)
|
||||
|
||||
const input = screen.getByRole('textbox')
|
||||
await user.clear(input)
|
||||
|
||||
expect(input).toHaveValue('')
|
||||
expect(defaultProps.onChange).not.toHaveBeenCalled()
|
||||
|
||||
await user.type(input, '20')
|
||||
|
||||
expect(input).toHaveValue('20')
|
||||
expect(defaultProps.onChange).toHaveBeenLastCalledWith('test_param', 20)
|
||||
expect(getSlider()).toHaveAttribute('aria-valuenow', '20')
|
||||
})
|
||||
|
||||
it('should clamp out-of-range text edits before updating state', async () => {
|
||||
const user = userEvent.setup()
|
||||
const StatefulParamItem = () => {
|
||||
const [value, setValue] = useState(defaultProps.value)
|
||||
|
||||
return (
|
||||
<ParamItem
|
||||
{...defaultProps}
|
||||
value={value}
|
||||
onChange={(key: string, nextValue: number) => {
|
||||
defaultProps.onChange(key, nextValue)
|
||||
setValue(nextValue)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render(<StatefulParamItem />)
|
||||
|
||||
const input = screen.getByRole('textbox')
|
||||
await user.clear(input)
|
||||
await user.type(input, '1.5')
|
||||
|
||||
expect(defaultProps.onChange).toHaveBeenLastCalledWith('test_param', 1)
|
||||
expect(getSlider()).toHaveAttribute('aria-valuenow', '100')
|
||||
})
|
||||
|
||||
it('should pass scaled value to slider when max < 5', () => {
|
||||
render(<ParamItem {...defaultProps} value={0.5} />)
|
||||
const slider = getSlider()
|
||||
|
||||
// When max < 5, slider value = value * 100 = 50
|
||||
expect(slider).toHaveAttribute('aria-valuenow', '50')
|
||||
expect(slider).toHaveAttribute('max', '100')
|
||||
})
|
||||
|
||||
it('should pass raw value to slider when max >= 5', () => {
|
||||
render(<ParamItem {...defaultProps} value={5} max={10} />)
|
||||
const slider = getSlider()
|
||||
|
||||
// When max >= 5, slider value = value = 5
|
||||
expect(slider).toHaveAttribute('aria-valuenow', '5')
|
||||
expect(slider).toHaveAttribute('max', '10')
|
||||
})
|
||||
|
||||
it('should call onSwitchChange with id and value when switch is toggled', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onSwitchChange = vi.fn()
|
||||
render(<ParamItem {...defaultProps} hasSwitch onSwitchChange={onSwitchChange} />)
|
||||
|
||||
await user.click(screen.getByRole('switch'))
|
||||
|
||||
expect(onSwitchChange).toHaveBeenCalledWith('test_param', false)
|
||||
})
|
||||
|
||||
it('should call onChange with id when increment button is clicked', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<ParamItem {...defaultProps} value={0.5} step={0.1} />)
|
||||
const incrementBtn = screen.getByRole('button', { name: /increment/i })
|
||||
|
||||
await user.click(incrementBtn)
|
||||
|
||||
// step=0.1, so 0.5 + 0.1 = 0.6, clamped to [0,1] → 0.6
|
||||
expect(defaultProps.onChange).toHaveBeenCalledWith('test_param', 0.6)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { ComponentProps } from 'react'
|
||||
import { useState } from 'react'
|
||||
import ParamItem from '..'
|
||||
|
||||
type Props = Omit<
|
||||
Partial<ComponentProps<typeof ParamItem>>,
|
||||
'id' | 'name' | 'onChange' | 'onSwitchChange'
|
||||
>
|
||||
|
||||
// A caller applies the public value/key payloads; no owner transformation is copied here.
|
||||
export function ParamItemExample({ value = 0.5, enable = true, max = 1, ...props }: Props) {
|
||||
const [values, setValues] = useState({ test_param: value })
|
||||
const [enabled, setEnabled] = useState({ test_param: enable })
|
||||
return (
|
||||
<>
|
||||
<ParamItem
|
||||
{...props}
|
||||
id="test_param"
|
||||
name="Test Param"
|
||||
max={max}
|
||||
value={values.test_param}
|
||||
enable={enabled.test_param}
|
||||
onChange={(key, next) => setValues((previous) => ({ ...previous, [key]: next }))}
|
||||
onSwitchChange={(key, next) => setEnabled((previous) => ({ ...previous, [key]: next }))}
|
||||
/>
|
||||
<output aria-label="Saved parameters">{JSON.stringify(values)}</output>
|
||||
<output aria-label="Enabled parameters">{JSON.stringify(enabled)}</output>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { useState } from 'react'
|
||||
import { page, userEvent } from 'vite-plus/test/browser'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import ScoreThresholdItem from '../score-threshold-item'
|
||||
|
||||
beforeEach(async () => {
|
||||
await page.viewport(800, 600)
|
||||
})
|
||||
|
||||
it.each([
|
||||
[undefined, '0.7'],
|
||||
[-0.1, '0'],
|
||||
[1.5, '1'],
|
||||
[0.556, '0.56'],
|
||||
])('normalizes configured threshold %s to %s in both editors', async (value, expected) => {
|
||||
await render(<ScoreThresholdItem value={value} enable onChange={vi.fn()} />)
|
||||
await expect
|
||||
.element(page.getByRole('textbox', { name: 'appDebug.datasetConfig.score_threshold' }))
|
||||
.toHaveValue(expected)
|
||||
await expect
|
||||
.element(page.getByRole('slider', { name: 'appDebug.datasetConfig.score_threshold' }))
|
||||
.toHaveAttribute('aria-valuetext', expected)
|
||||
})
|
||||
|
||||
it('saves threshold edits in hundredths and makes both editors unavailable when switched off', async () => {
|
||||
function Settings() {
|
||||
const [values, setValues] = useState({ score_threshold: 0.5 })
|
||||
const [enabled, setEnabled] = useState({ score_threshold: true })
|
||||
return (
|
||||
<>
|
||||
<ScoreThresholdItem
|
||||
value={values.score_threshold}
|
||||
enable={enabled.score_threshold}
|
||||
hasSwitch
|
||||
onChange={(key, next) => setValues((previous) => ({ ...previous, [key]: next }))}
|
||||
onSwitchChange={(key, next) => setEnabled((previous) => ({ ...previous, [key]: next }))}
|
||||
/>
|
||||
<output aria-label="Saved parameters">{JSON.stringify(values)}</output>
|
||||
<output aria-label="Enabled parameters">{JSON.stringify(enabled)}</output>
|
||||
</>
|
||||
)
|
||||
}
|
||||
await render(<Settings />)
|
||||
const name = 'appDebug.datasetConfig.score_threshold'
|
||||
const input = page.getByRole('textbox', { name })
|
||||
const slider = page.getByRole('slider', { name })
|
||||
const saved = page.getByRole('status', { name: 'Saved parameters' })
|
||||
await page.getByRole('button', { name: 'Increment value' }).click()
|
||||
await expect.element(input).toHaveValue('0.51')
|
||||
await expect.element(slider).toHaveAttribute('aria-valuetext', '0.51')
|
||||
await expect.element(saved).toHaveTextContent('{"score_threshold":0.51}')
|
||||
await input.clear()
|
||||
await userEvent.keyboard('0.55')
|
||||
await expect.element(input).toHaveValue('0.55')
|
||||
await expect.element(slider).toHaveAttribute('aria-valuetext', '0.55')
|
||||
await expect.element(saved).toHaveTextContent('{"score_threshold":0.55}')
|
||||
// Return through the real tab order rather than imperatively focusing the switch.
|
||||
await userEvent.tab({ shift: true })
|
||||
await expect.element(page.getByRole('button', { name })).toHaveFocus()
|
||||
await userEvent.tab({ shift: true })
|
||||
const toggle = page.getByRole('switch', { name })
|
||||
await expect.element(toggle).toHaveFocus()
|
||||
await userEvent.keyboard(' ')
|
||||
await expect.element(toggle).not.toBeChecked()
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Enabled parameters' }))
|
||||
.toHaveTextContent('{"score_threshold":false}')
|
||||
await expect.element(input).toBeDisabled()
|
||||
await expect.element(slider).toBeDisabled()
|
||||
await expect.element(saved).toHaveTextContent('{"score_threshold":0.55}')
|
||||
})
|
||||
@@ -1,55 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { useState } from 'react'
|
||||
import ScoreThresholdItem from '../score-threshold-item'
|
||||
|
||||
it.each([
|
||||
[undefined, '0.7'],
|
||||
[-0.1, '0'],
|
||||
[1.5, '1'],
|
||||
[0.556, '0.56'],
|
||||
])('normalizes configured threshold %s to %s', (value, expected) => {
|
||||
render(<ScoreThresholdItem value={value} enable onChange={vi.fn()} />)
|
||||
expect(screen.getByRole('textbox')).toHaveValue(expected)
|
||||
})
|
||||
|
||||
it('edits the score threshold in hundredths and disables its controls when switched off', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
const onSwitchChange = vi.fn()
|
||||
function Settings() {
|
||||
const [value, setValue] = useState(0.5)
|
||||
const [enable, setEnable] = useState(true)
|
||||
return (
|
||||
<ScoreThresholdItem
|
||||
value={value}
|
||||
enable={enable}
|
||||
hasSwitch
|
||||
onChange={(key, next) => {
|
||||
onChange(key, next)
|
||||
setValue(next)
|
||||
}}
|
||||
onSwitchChange={(key, next) => {
|
||||
onSwitchChange(key, next)
|
||||
setEnable(next)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
render(<Settings />)
|
||||
const input = screen.getByRole('textbox')
|
||||
await user.click(screen.getByRole('button', { name: /increment/i }))
|
||||
expect(input).toHaveValue('0.51')
|
||||
expect(onChange).toHaveBeenLastCalledWith('score_threshold', 0.51)
|
||||
await user.clear(input)
|
||||
await user.type(input, '0.55')
|
||||
expect(onChange).toHaveBeenLastCalledWith('score_threshold', 0.55)
|
||||
await user.click(screen.getByRole('switch'))
|
||||
expect(onSwitchChange).toHaveBeenLastCalledWith('score_threshold', false)
|
||||
expect(input).toBeDisabled()
|
||||
expect(
|
||||
screen.getByLabelText('appDebug.datasetConfig.score_threshold', {
|
||||
selector: 'input[type="range"]',
|
||||
}),
|
||||
).toBeDisabled()
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import { useState } from 'react'
|
||||
import { page, userEvent } from 'vite-plus/test/browser'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import TopKItem from '../top-k-item'
|
||||
|
||||
vi.mock('@/env', () => ({ env: { NEXT_PUBLIC_TOP_K_MAX_VALUE: 10 } }))
|
||||
|
||||
beforeEach(async () => {
|
||||
await page.viewport(800, 600)
|
||||
})
|
||||
|
||||
function Settings({ max }: { max?: number }) {
|
||||
const [values, setValues] = useState({ top_k: 2 })
|
||||
return (
|
||||
<>
|
||||
<TopKItem
|
||||
enable
|
||||
value={values.top_k}
|
||||
max={max}
|
||||
onChange={(key, next) => setValues((previous) => ({ ...previous, [key]: next }))}
|
||||
/>
|
||||
<output aria-label="Saved parameters">{JSON.stringify(values)}</output>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
it('keeps numeric edits and native slider keys synchronized within configured top-k bounds', async () => {
|
||||
await render(<Settings />)
|
||||
const input = page.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' })
|
||||
const slider = page.getByRole('slider', { name: 'appDebug.datasetConfig.top_k' })
|
||||
const saved = page.getByRole('status', { name: 'Saved parameters' })
|
||||
await page.getByRole('button', { name: 'Increment value' }).click()
|
||||
await expect.element(input).toHaveValue('3')
|
||||
await expect.element(slider).toHaveValue('3')
|
||||
await expect.element(saved).toHaveTextContent('{"top_k":3}')
|
||||
await input.click()
|
||||
await userEvent.tab()
|
||||
await expect.element(slider).toHaveFocus()
|
||||
await userEvent.keyboard('{End}')
|
||||
await expect.element(input).toHaveValue('10')
|
||||
await expect.element(saved).toHaveTextContent('{"top_k":10}')
|
||||
await userEvent.keyboard('{ArrowRight}')
|
||||
await expect.element(input).toHaveValue('10')
|
||||
await expect.element(saved).toHaveTextContent('{"top_k":10}')
|
||||
await userEvent.keyboard('{Home}')
|
||||
await expect.element(input).toHaveValue('1')
|
||||
await expect.element(saved).toHaveTextContent('{"top_k":1}')
|
||||
await userEvent.keyboard('{ArrowLeft}')
|
||||
await expect.element(input).toHaveValue('1')
|
||||
await expect.element(saved).toHaveTextContent('{"top_k":1}')
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ enable: false, disabled: false },
|
||||
{ enable: true, disabled: true },
|
||||
])('makes both top-k editors unavailable with %j', async (props) => {
|
||||
await render(<TopKItem {...props} value={2} onChange={vi.fn()} />)
|
||||
await expect
|
||||
.element(page.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' }))
|
||||
.toBeDisabled()
|
||||
await expect
|
||||
.element(page.getByRole('slider', { name: 'appDebug.datasetConfig.top_k' }))
|
||||
.toBeDisabled()
|
||||
})
|
||||
|
||||
it('applies a feature-specific maximum to native slider edits and the saved top-k', async () => {
|
||||
await render(<Settings max={100} />)
|
||||
const slider = page.getByRole('slider', { name: 'appDebug.datasetConfig.top_k' })
|
||||
await page.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' }).click()
|
||||
await userEvent.tab()
|
||||
await expect.element(slider).toHaveFocus()
|
||||
await userEvent.keyboard('{End}')
|
||||
await expect.element(slider).toHaveValue('100')
|
||||
await expect
|
||||
.element(page.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' }))
|
||||
.toHaveValue('100')
|
||||
await expect
|
||||
.element(page.getByRole('status', { name: 'Saved parameters' }))
|
||||
.toHaveTextContent('{"top_k":100}')
|
||||
})
|
||||
@@ -1,62 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { useState } from 'react'
|
||||
import TopKItem from '../top-k-item'
|
||||
|
||||
vi.mock('@/env', () => ({ env: { NEXT_PUBLIC_TOP_K_MAX_VALUE: 10 } }))
|
||||
|
||||
it('keeps the numeric field and slider synchronized within the configured top-k bounds', async () => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
function Harness() {
|
||||
const [value, setValue] = useState(2)
|
||||
return (
|
||||
<TopKItem
|
||||
enable
|
||||
value={value}
|
||||
onChange={(key, next) => {
|
||||
onChange(key, next)
|
||||
setValue(next)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
render(<Harness />)
|
||||
const input = screen.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' })
|
||||
const slider = screen.getByLabelText('appDebug.datasetConfig.top_k', {
|
||||
selector: 'input[type="range"]',
|
||||
})
|
||||
await user.click(screen.getByRole('button', { name: /increment/i }))
|
||||
expect(input).toHaveValue('3')
|
||||
expect(slider).toHaveValue('3')
|
||||
slider.focus()
|
||||
await user.keyboard('{End}{ArrowRight}')
|
||||
expect(input).toHaveValue('10')
|
||||
expect(onChange).toHaveBeenLastCalledWith('top_k', 10)
|
||||
await user.keyboard('{Home}{ArrowLeft}')
|
||||
expect(input).toHaveValue('1')
|
||||
expect(onChange).toHaveBeenLastCalledWith('top_k', 1)
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ enable: false, disabled: false },
|
||||
{ enable: true, disabled: true },
|
||||
])('prevents edits with %j', async (props) => {
|
||||
const user = userEvent.setup()
|
||||
const onChange = vi.fn()
|
||||
render(<TopKItem {...props} value={2} onChange={onChange} />)
|
||||
const input = screen.getByRole('textbox', { name: 'appDebug.datasetConfig.top_k' })
|
||||
expect(input).toBeDisabled()
|
||||
expect(
|
||||
screen.getByLabelText('appDebug.datasetConfig.top_k', { selector: 'input[type="range"]' }),
|
||||
).toBeDisabled()
|
||||
await user.type(input, '9')
|
||||
expect(onChange).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('allows a feature-specific maximum', () => {
|
||||
render(<TopKItem enable value={2} max={100} onChange={vi.fn()} />)
|
||||
expect(
|
||||
screen.getByLabelText('appDebug.datasetConfig.top_k', { selector: 'input[type="range"]' }),
|
||||
).toHaveAttribute('max', '100')
|
||||
})
|
||||
Reference in new issue
Block a user