mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-05 20:41:30 +08:00
test: add comprehensive tests for CopyButton component (#7719)
* test: add comprehensive tests for CopyButton component - Add tests for basic rendering and functionality - Add clipboard API mocking and error handling - Add tests for custom props (size, tooltip, label) - Add edge case testing (empty text, special characters) - Improve component test coverage Signed-off-by: Jason Young <farion1231@gmail.com> * fix: resolve linting issues in CopyButton tests - Sort imports alphabetically - Remove trailing whitespace - Add final newline Signed-off-by: Jason Young <farion1231@gmail.com> * refactor: consolidate similar test cases in CopyButton tests - Merge 'should render copy icon' and 'should render with basic structure' - Merge 'should apply custom size to icon' and 'should apply custom size to label' - Reduce test duplication while maintaining full coverage - Address maintainer feedback for better test organization Signed-off-by: Jason Young <farion1231@gmail.com> --------- Signed-off-by: Jason Young <farion1231@gmail.com>
This commit is contained in:
parent
ba21a2c5fa
commit
f58378daa0
164
src/renderer/src/components/__tests__/CopyButton.test.tsx
Normal file
164
src/renderer/src/components/__tests__/CopyButton.test.tsx
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import userEvent from '@testing-library/user-event'
|
||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
|
import CopyButton from '../CopyButton'
|
||||||
|
|
||||||
|
// Mock navigator.clipboard
|
||||||
|
const mockWriteText = vi.fn()
|
||||||
|
const mockClipboard = {
|
||||||
|
writeText: mockWriteText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock window.message
|
||||||
|
const mockMessage = {
|
||||||
|
success: vi.fn(),
|
||||||
|
error: vi.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock useTranslation
|
||||||
|
vi.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (key: string) => {
|
||||||
|
const translations: Record<string, string> = {
|
||||||
|
'message.copy.success': '复制成功',
|
||||||
|
'message.copy.failed': '复制失败'
|
||||||
|
}
|
||||||
|
return translations[key] || key
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('CopyButton', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Setup mocks
|
||||||
|
Object.assign(navigator, { clipboard: mockClipboard })
|
||||||
|
Object.assign(window, { message: mockMessage })
|
||||||
|
|
||||||
|
// Clear all mocks
|
||||||
|
vi.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render with basic structure and copy icon', () => {
|
||||||
|
render(<CopyButton textToCopy="test text" />)
|
||||||
|
|
||||||
|
// Should have basic clickable container
|
||||||
|
const container = document.querySelector('div')
|
||||||
|
expect(container).toBeInTheDocument()
|
||||||
|
|
||||||
|
// Should render copy icon
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
expect(copyIcon).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render label when provided', () => {
|
||||||
|
const labelText = 'Copy to clipboard'
|
||||||
|
render(<CopyButton textToCopy="test text" label={labelText} />)
|
||||||
|
|
||||||
|
expect(screen.getByText(labelText)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render tooltip when provided', async () => {
|
||||||
|
const tooltipText = 'Click to copy'
|
||||||
|
render(<CopyButton textToCopy="test text" tooltip={tooltipText} />)
|
||||||
|
|
||||||
|
// Check that the component structure includes tooltip
|
||||||
|
const container = document.querySelector('div')
|
||||||
|
expect(container).toBeInTheDocument()
|
||||||
|
|
||||||
|
// The tooltip should be rendered when hovered
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
expect(copyIcon).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not render tooltip when not provided', () => {
|
||||||
|
render(<CopyButton textToCopy="test text" />)
|
||||||
|
|
||||||
|
// Should not have tooltip wrapper
|
||||||
|
expect(document.querySelector('.ant-tooltip')).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should copy text to clipboard on click', async () => {
|
||||||
|
const textToCopy = 'Hello World'
|
||||||
|
mockWriteText.mockResolvedValue(undefined)
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy={textToCopy} />)
|
||||||
|
|
||||||
|
// Find the clickable element by using the copy icon as reference
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
const clickableElement = copyIcon?.parentElement
|
||||||
|
expect(clickableElement).toBeInTheDocument()
|
||||||
|
|
||||||
|
await userEvent.click(clickableElement!)
|
||||||
|
|
||||||
|
expect(mockWriteText).toHaveBeenCalledWith(textToCopy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show success message when copy succeeds', async () => {
|
||||||
|
mockWriteText.mockResolvedValue(undefined)
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy="test text" />)
|
||||||
|
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
const clickableElement = copyIcon?.parentElement
|
||||||
|
await userEvent.click(clickableElement!)
|
||||||
|
|
||||||
|
expect(mockMessage.success).toHaveBeenCalledWith('复制成功')
|
||||||
|
expect(mockMessage.error).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should show error message when copy fails', async () => {
|
||||||
|
mockWriteText.mockRejectedValue(new Error('Clipboard access denied'))
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy="test text" />)
|
||||||
|
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
const clickableElement = copyIcon?.parentElement
|
||||||
|
await userEvent.click(clickableElement!)
|
||||||
|
|
||||||
|
expect(mockMessage.error).toHaveBeenCalledWith('复制失败')
|
||||||
|
expect(mockMessage.success).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should apply custom size to icon and label', () => {
|
||||||
|
const customSize = 20
|
||||||
|
const labelText = 'Copy'
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy="test text" size={customSize} label={labelText} />)
|
||||||
|
|
||||||
|
// Should apply custom size to icon
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
expect(copyIcon).toHaveAttribute('width', customSize.toString())
|
||||||
|
expect(copyIcon).toHaveAttribute('height', customSize.toString())
|
||||||
|
|
||||||
|
// Should apply custom size to label
|
||||||
|
const label = screen.getByText(labelText)
|
||||||
|
expect(label).toHaveStyle({ fontSize: `${customSize}px` })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty text', async () => {
|
||||||
|
const emptyText = ''
|
||||||
|
mockWriteText.mockResolvedValue(undefined)
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy={emptyText} />)
|
||||||
|
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
const clickableElement = copyIcon?.parentElement
|
||||||
|
await userEvent.click(clickableElement!)
|
||||||
|
|
||||||
|
expect(mockWriteText).toHaveBeenCalledWith(emptyText)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle special characters', async () => {
|
||||||
|
const specialText = '特殊字符 🎉 @#$%^&*()'
|
||||||
|
mockWriteText.mockResolvedValue(undefined)
|
||||||
|
|
||||||
|
render(<CopyButton textToCopy={specialText} />)
|
||||||
|
|
||||||
|
const copyIcon = document.querySelector('.copy-icon')
|
||||||
|
const clickableElement = copyIcon?.parentElement
|
||||||
|
await userEvent.click(clickableElement!)
|
||||||
|
|
||||||
|
expect(mockWriteText).toHaveBeenCalledWith(specialText)
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user