dify/web/utils/clipboard.spec.ts

149 lines
4.7 KiB
TypeScript
Raw Normal View History

/**
* Test suite for clipboard utilities
*
* This module provides cross-browser clipboard functionality with automatic fallback:
* 1. Modern Clipboard API (navigator.clipboard.writeText) - preferred method
* 2. Legacy execCommand('copy') - fallback for older browsers
*
* The implementation ensures clipboard operations work across all supported browsers
* while gracefully handling permissions and API availability.
*/
2025-11-04 05:06:44 -08:00
import { writeTextToClipboard } from './clipboard'
describe('Clipboard Utilities', () => {
describe('writeTextToClipboard', () => {
afterEach(() => {
jest.restoreAllMocks()
})
/**
* Test modern Clipboard API usage
* When navigator.clipboard is available, should use the modern API
*/
2025-11-04 05:06:44 -08:00
it('should use navigator.clipboard.writeText when available', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
value: { writeText: mockWriteText },
writable: true,
configurable: true,
})
await writeTextToClipboard('test text')
expect(mockWriteText).toHaveBeenCalledWith('test text')
})
/**
* Test fallback to legacy execCommand method
* When Clipboard API is unavailable, should use document.execCommand('copy')
* This involves creating a temporary textarea element
*/
2025-11-04 05:06:44 -08:00
it('should fallback to execCommand when clipboard API not available', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
writable: true,
configurable: true,
})
const mockExecCommand = jest.fn().mockReturnValue(true)
document.execCommand = mockExecCommand
const appendChildSpy = jest.spyOn(document.body, 'appendChild')
const removeChildSpy = jest.spyOn(document.body, 'removeChild')
await writeTextToClipboard('fallback text')
expect(appendChildSpy).toHaveBeenCalled()
expect(mockExecCommand).toHaveBeenCalledWith('copy')
expect(removeChildSpy).toHaveBeenCalled()
})
/**
* Test error handling when execCommand returns false
* execCommand returns false when the operation fails
*/
2025-11-04 05:06:44 -08:00
it('should handle execCommand failure', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
writable: true,
configurable: true,
})
const mockExecCommand = jest.fn().mockReturnValue(false)
document.execCommand = mockExecCommand
await expect(writeTextToClipboard('fail text')).rejects.toThrow()
})
/**
* Test error handling when execCommand throws an exception
* Should propagate the error to the caller
*/
2025-11-04 05:06:44 -08:00
it('should handle execCommand exception', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
writable: true,
configurable: true,
})
const mockExecCommand = jest.fn().mockImplementation(() => {
throw new Error('execCommand error')
})
document.execCommand = mockExecCommand
await expect(writeTextToClipboard('error text')).rejects.toThrow('execCommand error')
})
/**
* Test proper cleanup of temporary DOM elements
* The temporary textarea should be removed after copying
*/
2025-11-04 05:06:44 -08:00
it('should clean up textarea after fallback', async () => {
Object.defineProperty(navigator, 'clipboard', {
value: undefined,
writable: true,
configurable: true,
})
document.execCommand = jest.fn().mockReturnValue(true)
const removeChildSpy = jest.spyOn(document.body, 'removeChild')
await writeTextToClipboard('cleanup test')
expect(removeChildSpy).toHaveBeenCalled()
})
/**
* Test copying empty strings
* Should handle edge case of empty clipboard content
*/
2025-11-04 05:06:44 -08:00
it('should handle empty string', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
value: { writeText: mockWriteText },
writable: true,
configurable: true,
})
await writeTextToClipboard('')
expect(mockWriteText).toHaveBeenCalledWith('')
})
/**
* Test copying text with special characters
* Should preserve newlines, tabs, quotes, unicode, and emojis
*/
2025-11-04 05:06:44 -08:00
it('should handle special characters', async () => {
const mockWriteText = jest.fn().mockResolvedValue(undefined)
Object.defineProperty(navigator, 'clipboard', {
value: { writeText: mockWriteText },
writable: true,
configurable: true,
})
const specialText = 'Test\n\t"quotes"\n中文\n😀'
await writeTextToClipboard(specialText)
expect(mockWriteText).toHaveBeenCalledWith(specialText)
})
})
})