mirror of
https://github.com/langgenius/dify.git
synced 2026-03-28 15:51:00 +08:00
Signed-off-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: majiayu000 <1835304752@qq.com> Co-authored-by: Poojan <poojan@infocusp.com> Co-authored-by: sahil-infocusp <73810410+sahil-infocusp@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Pandaaaa906 <ye.pandaaaa906@gmail.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: heyszt <270985384@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Ijas <ijas.ahmd.ap@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 木之本澪 <kinomotomiovo@gmail.com> Co-authored-by: KinomotoMio <200703522+KinomotoMio@users.noreply.github.com> Co-authored-by: 不做了睡大觉 <64798754+stakeswky@users.noreply.github.com> Co-authored-by: User <user@example.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Leilei <138381132+Inlei@users.noreply.github.com> Co-authored-by: HaKu <104669497+haku-ink@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: wangxiaolei <fatelei@gmail.com> Co-authored-by: Varun Chawla <34209028+veeceey@users.noreply.github.com> Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: tda <95275462+tda1017@users.noreply.github.com> Co-authored-by: root <root@DESKTOP-KQLO90N> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Niels Kaspers <153818647+nielskaspers@users.noreply.github.com> Co-authored-by: hj24 <mambahj24@gmail.com> Co-authored-by: Tyson Cung <45380903+tysoncung@users.noreply.github.com> Co-authored-by: Stephen Zhou <hi@hyoban.cc> Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com> Co-authored-by: slegarraga <64795732+slegarraga@users.noreply.github.com> Co-authored-by: 99 <wh2099@pm.me> Co-authored-by: Br1an <932039080@qq.com> Co-authored-by: L1nSn0w <l1nsn0w@qq.com> Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai> Co-authored-by: akkoaya <151345394+akkoaya@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai> Co-authored-by: lif <1835304752@qq.com> Co-authored-by: weiguang li <codingpunk@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: HanWenbo <124024253+hwb96@users.noreply.github.com> Co-authored-by: Coding On Star <447357187@qq.com> Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: Stable Genius <stablegenius043@gmail.com> Co-authored-by: Stable Genius <259448942+stablegenius49@users.noreply.github.com> Co-authored-by: ふるい <46769295+Echo0ff@users.noreply.github.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
175 lines
6.4 KiB
TypeScript
175 lines
6.4 KiB
TypeScript
import { fireEvent, render, screen } from '@testing-library/react'
|
|
import * as React from 'react'
|
|
import { createReactI18nextMock } from '@/test/i18n-mock'
|
|
import Input, { inputVariants } from '../index'
|
|
|
|
// Mock the i18n hook with custom translations for test assertions
|
|
vi.mock('react-i18next', () => createReactI18nextMock({
|
|
'operation.search': 'Search',
|
|
'placeholder.input': 'Please input',
|
|
}))
|
|
|
|
describe('Input component', () => {
|
|
describe('Variants', () => {
|
|
it('should return correct classes for regular size', () => {
|
|
const result = inputVariants({ size: 'regular' })
|
|
expect(result).toContain('px-3')
|
|
expect(result).toContain('radius-md')
|
|
expect(result).toContain('system-sm-regular')
|
|
})
|
|
|
|
it('should return correct classes for large size', () => {
|
|
const result = inputVariants({ size: 'large' })
|
|
expect(result).toContain('px-4')
|
|
expect(result).toContain('radius-lg')
|
|
expect(result).toContain('system-md-regular')
|
|
})
|
|
|
|
it('should use regular size as default', () => {
|
|
const result = inputVariants({})
|
|
expect(result).toContain('px-3')
|
|
expect(result).toContain('radius-md')
|
|
expect(result).toContain('system-sm-regular')
|
|
})
|
|
})
|
|
|
|
it('renders correctly with default props', () => {
|
|
render(<Input />)
|
|
const input = screen.getByPlaceholderText(/input/i)
|
|
expect(input).toBeInTheDocument()
|
|
expect(input).not.toBeDisabled()
|
|
expect(input).not.toHaveClass('cursor-not-allowed')
|
|
})
|
|
|
|
it('shows left icon when showLeftIcon is true', () => {
|
|
render(<Input showLeftIcon />)
|
|
const searchIcon = document.querySelector('.i-ri-search-line')
|
|
expect(searchIcon).toBeInTheDocument()
|
|
const input = screen.getByPlaceholderText(/search/i)
|
|
expect(input).toHaveClass('pl-[26px]')
|
|
})
|
|
|
|
it('shows clear icon when showClearIcon is true and has value', () => {
|
|
render(<Input showClearIcon value="test" />)
|
|
const clearIcon = document.querySelector('.i-ri-close-circle-fill')
|
|
expect(clearIcon).toBeInTheDocument()
|
|
const input = screen.getByDisplayValue('test')
|
|
expect(input).toHaveClass('pr-[26px]')
|
|
})
|
|
|
|
it('does not show clear icon when disabled, even with value', () => {
|
|
render(<Input showClearIcon value="test" disabled />)
|
|
const clearIcon = document.querySelector('.i-ri-close-circle-fill')
|
|
expect(clearIcon).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('calls onClear when clear icon is clicked', () => {
|
|
const onClear = vi.fn()
|
|
render(<Input showClearIcon value="test" onClear={onClear} />)
|
|
const clearIconContainer = screen.getByTestId('input-clear')
|
|
fireEvent.click(clearIconContainer!)
|
|
expect(onClear).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('shows warning icon when destructive is true', () => {
|
|
render(<Input destructive />)
|
|
const warningIcon = document.querySelector('.i-ri-error-warning-line')
|
|
expect(warningIcon).toBeInTheDocument()
|
|
const input = screen.getByPlaceholderText(/input/i)
|
|
expect(input).toHaveClass('border-components-input-border-destructive')
|
|
})
|
|
|
|
it('applies disabled styles when disabled', () => {
|
|
render(<Input disabled />)
|
|
const input = screen.getByPlaceholderText(/input/i)
|
|
expect(input).toBeDisabled()
|
|
expect(input).toHaveClass('cursor-not-allowed')
|
|
expect(input).toHaveClass('bg-components-input-bg-disabled')
|
|
})
|
|
|
|
it('displays custom unit when provided', () => {
|
|
render(<Input unit="km" />)
|
|
const unitElement = screen.getByText('km')
|
|
expect(unitElement).toBeInTheDocument()
|
|
})
|
|
|
|
it('applies custom className and style', () => {
|
|
const customClass = 'test-class'
|
|
const customStyle = { color: 'red' }
|
|
render(<Input className={customClass} styleCss={customStyle} />)
|
|
const input = screen.getByPlaceholderText(/input/i)
|
|
expect(input).toHaveClass(customClass)
|
|
expect(input).toHaveStyle({ color: 'rgb(255, 0, 0)' })
|
|
})
|
|
|
|
it('applies large size variant correctly', () => {
|
|
render(<Input size="large" />)
|
|
const input = screen.getByPlaceholderText('Please input')
|
|
expect(input.className).toContain(inputVariants({ size: 'large' }))
|
|
})
|
|
|
|
it('uses custom placeholder when provided', () => {
|
|
const placeholder = 'Custom placeholder'
|
|
render(<Input placeholder={placeholder} />)
|
|
const input = screen.getByPlaceholderText(placeholder)
|
|
expect(input).toBeInTheDocument()
|
|
})
|
|
|
|
describe('Number Input Formatting', () => {
|
|
it('removes leading zeros on change when current value is zero', () => {
|
|
let changedValue = ''
|
|
const onChange = vi.fn((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
changedValue = e.target.value
|
|
})
|
|
render(<Input type="number" value={0} onChange={onChange} />)
|
|
|
|
const input = screen.getByRole('spinbutton') as HTMLInputElement
|
|
fireEvent.change(input, { target: { value: '00042' } })
|
|
|
|
expect(onChange).toHaveBeenCalledTimes(1)
|
|
expect(changedValue).toBe('42')
|
|
})
|
|
|
|
it('keeps typed value on change when current value is not zero', () => {
|
|
let changedValue = ''
|
|
const onChange = vi.fn((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
changedValue = e.target.value
|
|
})
|
|
render(<Input type="number" value={1} onChange={onChange} />)
|
|
|
|
const input = screen.getByRole('spinbutton') as HTMLInputElement
|
|
fireEvent.change(input, { target: { value: '00042' } })
|
|
expect(onChange).toHaveBeenCalledTimes(1)
|
|
expect(changedValue).toBe('00042')
|
|
})
|
|
|
|
it('normalizes value and triggers change on blur when leading zeros exist', () => {
|
|
const onChange = vi.fn()
|
|
const onBlur = vi.fn()
|
|
render(<Input type="number" defaultValue="0012" onChange={onChange} onBlur={onBlur} />)
|
|
|
|
const input = screen.getByRole('spinbutton')
|
|
fireEvent.blur(input)
|
|
|
|
expect(onChange).toHaveBeenCalledTimes(1)
|
|
expect(onChange.mock.calls[0][0].type).toBe('change')
|
|
expect(onChange.mock.calls[0][0].target.value).toBe('12')
|
|
expect(onBlur).toHaveBeenCalledTimes(1)
|
|
expect(onBlur.mock.calls[0][0].target.value).toBe('12')
|
|
})
|
|
|
|
it('does not trigger change on blur when value is already normalized', () => {
|
|
const onChange = vi.fn()
|
|
const onBlur = vi.fn()
|
|
render(<Input type="number" defaultValue="12" onChange={onChange} onBlur={onBlur} />)
|
|
|
|
const input = screen.getByRole('spinbutton')
|
|
fireEvent.blur(input)
|
|
|
|
expect(onChange).not.toHaveBeenCalled()
|
|
expect(onBlur).toHaveBeenCalledTimes(1)
|
|
expect(onBlur.mock.calls[0][0].target.value).toBe('12')
|
|
})
|
|
})
|
|
})
|