mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
test: improve coverage for some files (#33218)
This commit is contained in:
@ -1,6 +1,4 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { createReactI18nextMock } from '@/test/i18n-mock'
|
||||
import InputWithCopy from '../index'
|
||||
|
||||
// Create a controllable mock for useClipboard
|
||||
@ -16,14 +14,6 @@ vi.mock('foxact/use-clipboard', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock the i18n hook with custom translations for test assertions
|
||||
vi.mock('react-i18next', () => createReactI18nextMock({
|
||||
'operation.copy': 'Copy',
|
||||
'operation.copied': 'Copied',
|
||||
'overview.appInfo.embedded.copy': 'Copy',
|
||||
'overview.appInfo.embedded.copied': 'Copied',
|
||||
}))
|
||||
|
||||
describe('InputWithCopy component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@ -145,4 +135,98 @@ describe('InputWithCopy component', () => {
|
||||
// Input should maintain focus after copy
|
||||
expect(input).toHaveFocus()
|
||||
})
|
||||
|
||||
it('converts non-string value to string for copying', () => {
|
||||
const mockOnChange = vi.fn()
|
||||
// number value triggers String(value || '') branch where typeof value !== 'string'
|
||||
render(<InputWithCopy value={12345} onChange={mockOnChange} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(mockCopy).toHaveBeenCalledWith('12345')
|
||||
})
|
||||
|
||||
it('handles undefined value by converting to empty string', () => {
|
||||
const mockOnChange = vi.fn()
|
||||
// undefined value triggers String(value || '') where value is falsy
|
||||
render(<InputWithCopy value={undefined} onChange={mockOnChange} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(mockCopy).toHaveBeenCalledWith('')
|
||||
})
|
||||
|
||||
it('shows copied tooltip text when copied state is true', () => {
|
||||
mockCopied = true
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
// The tooltip content should use the 'copied' translation
|
||||
const copyButton = screen.getByRole('button')
|
||||
expect(copyButton).toBeInTheDocument()
|
||||
|
||||
// Verify the filled clipboard icon is rendered (not the line variant)
|
||||
const filledIcon = screen.getByTestId('copied-icon')
|
||||
expect(filledIcon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows copy tooltip text when copied state is false', () => {
|
||||
mockCopied = false
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
expect(copyButton).toBeInTheDocument()
|
||||
|
||||
const lineIcon = screen.getByTestId('copy-icon')
|
||||
expect(lineIcon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls reset on mouse leave from copy button wrapper', () => {
|
||||
const mockOnChange = vi.fn()
|
||||
render(<InputWithCopy value="test value" onChange={mockOnChange} />)
|
||||
|
||||
const wrapper = screen.getByTestId('copy-button-wrapper')
|
||||
expect(wrapper).toBeInTheDocument()
|
||||
fireEvent.mouseLeave(wrapper)
|
||||
|
||||
expect(mockReset).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('applies wrapperClassName to the outer container', () => {
|
||||
const mockOnChange = vi.fn()
|
||||
const { container } = render(
|
||||
<InputWithCopy value="test" onChange={mockOnChange} wrapperClassName="my-wrapper" />,
|
||||
)
|
||||
|
||||
const outerDiv = container.firstChild as HTMLElement
|
||||
expect(outerDiv).toHaveClass('my-wrapper')
|
||||
})
|
||||
|
||||
it('copies copyValue over non-string input value when both provided', () => {
|
||||
const mockOnChange = vi.fn()
|
||||
render(
|
||||
<InputWithCopy value={42} onChange={mockOnChange} copyValue="override-copy" />,
|
||||
)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(mockCopy).toHaveBeenCalledWith('override-copy')
|
||||
})
|
||||
|
||||
it('invokes onCopy with copyValue when copyValue is provided', () => {
|
||||
const onCopyMock = vi.fn()
|
||||
const mockOnChange = vi.fn()
|
||||
render(
|
||||
<InputWithCopy value="display" onChange={mockOnChange} copyValue="custom" onCopy={onCopyMock} />,
|
||||
)
|
||||
|
||||
const copyButton = screen.getByRole('button')
|
||||
fireEvent.click(copyButton)
|
||||
|
||||
expect(onCopyMock).toHaveBeenCalledWith('custom')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
import type { InputProps } from '../input'
|
||||
import { RiClipboardFill, RiClipboardLine } from '@remixicon/react'
|
||||
import { useClipboard } from 'foxact/use-clipboard'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -39,13 +38,19 @@ const InputWithCopy = React.forwardRef<HTMLInputElement, InputWithCopyProps>((
|
||||
onCopy?.(finalCopyValue)
|
||||
}
|
||||
|
||||
const tooltipText = copied
|
||||
? t(`${prefixEmbedded}.copied`, { ns: 'appOverview' })
|
||||
: t(`${prefixEmbedded}.copy`, { ns: 'appOverview' })
|
||||
/* v8 ignore next -- i18n test mock always returns a non-empty string; runtime fallback is defensive. -- @preserve */
|
||||
const safeTooltipText = tooltipText || ''
|
||||
|
||||
return (
|
||||
<div className={cn('relative w-full', wrapperClassName)}>
|
||||
<input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'w-full appearance-none border border-transparent bg-components-input-bg-normal py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs',
|
||||
'radius-md system-sm-regular px-3',
|
||||
'px-3 system-sm-regular radius-md',
|
||||
showCopyButton && 'pr-8',
|
||||
inputProps.disabled && 'cursor-not-allowed border-transparent bg-components-input-bg-disabled text-components-input-text-filled-disabled hover:border-transparent hover:bg-components-input-bg-disabled',
|
||||
inputProps.className,
|
||||
@ -57,13 +62,10 @@ const InputWithCopy = React.forwardRef<HTMLInputElement, InputWithCopyProps>((
|
||||
<div
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2"
|
||||
onMouseLeave={reset}
|
||||
data-testid="copy-button-wrapper"
|
||||
>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
(copied
|
||||
? t(`${prefixEmbedded}.copied`, { ns: 'appOverview' })
|
||||
: t(`${prefixEmbedded}.copy`, { ns: 'appOverview' })) || ''
|
||||
}
|
||||
popupContent={safeTooltipText}
|
||||
>
|
||||
<ActionButton
|
||||
size="xs"
|
||||
@ -71,12 +73,8 @@ const InputWithCopy = React.forwardRef<HTMLInputElement, InputWithCopyProps>((
|
||||
className="hover:bg-components-button-ghost-bg-hover"
|
||||
>
|
||||
{copied
|
||||
? (
|
||||
<RiClipboardFill className="h-3.5 w-3.5 text-text-tertiary" />
|
||||
)
|
||||
: (
|
||||
<RiClipboardLine className="h-3.5 w-3.5 text-text-tertiary" />
|
||||
)}
|
||||
? (<span className="i-ri-clipboard-fill h-3.5 w-3.5 text-text-tertiary" data-testid="copied-icon" />)
|
||||
: (<span className="i-ri-clipboard-line h-3.5 w-3.5 text-text-tertiary" data-testid="copy-icon" />)}
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user