import { describe, test, expect, vi, beforeEach, afterEach, type MockInstance, } from 'vitest' import { downloadToCSV, downloadToPNG } from './exports' import { createRef } from 'react' import type { RefObject } from 'react' // Mock html2canvas vi.mock('html2canvas', () => ({ default: vi.fn(), })) describe('downloadToCSV', () => { test('has correct label', () => { expect(downloadToCSV.label).toBe('CSV') }) test('has icon', () => { expect(downloadToCSV.icon).toBeTruthy() }) test('converts data to CSV format', async () => { const data: string[][] = [ ['Name', 'Age', 'City'], ['John', '30', 'New York'], ['Jane', '25', 'Los Angeles'], ] const result = await downloadToCSV.modifier(data) expect(result).toBeTruthy() expect(result).toContain('blob:') }) test('creates blob URL from CSV data', async () => { const data: string[][] = [ ['a', 'b'], ['c', 'd'], ] const result = await downloadToCSV.modifier(data) expect(result).toBeTruthy() expect(typeof result).toBe('string') expect(result!.startsWith('blob:')).toBe(true) }) test('callback revokes object URL', () => { const revokeObjectURLSpy = vi.spyOn(URL, 'revokeObjectURL') const testUrl = 'blob:test-url' downloadToCSV.callback?.(testUrl) expect(revokeObjectURLSpy).toHaveBeenCalledWith(testUrl) revokeObjectURLSpy.mockRestore() }) test('handles empty data array', async () => { const data: unknown[][] = [] const result = await downloadToCSV.modifier(data) expect(result).toBeTruthy() }) test('handles single row data', async () => { const data: string[][] = [['single', 'row']] const result = await downloadToCSV.modifier(data) expect(result).toBeTruthy() expect(result).toContain('blob:') }) test('handles data with special characters', async () => { const data: string[][] = [ ['Name', 'Description'], ['Test', 'Hello, World!'], ] const result = await downloadToCSV.modifier(data) expect(result).toBeTruthy() }) }) describe('downloadToPNG', () => { let mockRef: RefObject let mockElement: HTMLDivElement let warnSpy: MockInstance beforeEach(() => { warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined) // Create a real DOM element mockElement = document.createElement('div') mockElement.style.width = '800px' mockElement.style.height = '600px' document.body.appendChild(mockElement) mockRef = createRef() as RefObject // Manually set the current property Object.defineProperty(mockRef, 'current', { writable: true, value: mockElement, }) }) afterEach(() => { document.body.removeChild(mockElement) warnSpy.mockRestore() vi.clearAllMocks() }) test('returns download item with correct label', () => { expect(downloadToPNG.label).toBe('PNG') }) test('has icon', () => { expect(downloadToPNG.icon).toBeTruthy() }) test('modifier returns undefined when ref is null', async () => { const nullRef = createRef() as RefObject const result = await downloadToPNG.modifier(nullRef) expect(result).toBeUndefined() expect(warnSpy).toHaveBeenCalled() }) test('modifier returns undefined when ref is a function', async () => { const functionRef = vi.fn() as unknown as RefObject const result = await downloadToPNG.modifier(functionRef) expect(result).toBeUndefined() expect(warnSpy).toHaveBeenCalled() }) test('removes toolbar and actions containers from clone', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const toolbar = document.createElement('div') toolbar.className = 'widget-toolbar-container' mockElement.appendChild(toolbar) const actions = document.createElement('div') actions.className = 'widget-wrapper-actions' mockElement.appendChild(actions) const mockCanvas = document.createElement('canvas') mockCanvas.toDataURL = vi.fn().mockReturnValue('data:image/png;base64,test') let cloneElement: HTMLElement | undefined html2canvas.mockImplementation((element: HTMLElement) => { cloneElement = element return mockCanvas }) await downloadToPNG.modifier(mockRef) // The clone should not have toolbar or actions expect(cloneElement?.querySelector('.widget-toolbar-container')).toBeNull() expect(cloneElement?.querySelector('.widget-wrapper-actions')).toBeNull() }) test('does not modify original element', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const toolbar = document.createElement('div') toolbar.className = 'widget-toolbar-container' mockElement.appendChild(toolbar) const actions = document.createElement('div') actions.className = 'widget-wrapper-actions' mockElement.appendChild(actions) const mockCanvas = document.createElement('canvas') mockCanvas.toDataURL = vi.fn().mockReturnValue('data:image/png;base64,test') html2canvas.mockResolvedValue(mockCanvas) await downloadToPNG.modifier(mockRef) // Original element should still have toolbar and actions expect(mockElement.querySelector('.widget-toolbar-container')).toBe(toolbar) expect(mockElement.querySelector('.widget-wrapper-actions')).toBe(actions) }) test('calls html2canvas with correct options', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const mockCanvas = document.createElement('canvas') mockCanvas.toDataURL = vi.fn().mockReturnValue('data:image/png;base64,test') html2canvas.mockResolvedValue(mockCanvas) await downloadToPNG.modifier(mockRef) expect(html2canvas).toHaveBeenCalledWith( mockElement, expect.objectContaining({ useCORS: true, scale: 2, backgroundColor: '#fff', }), ) }) test('returns PNG data URL', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const mockCanvas = document.createElement('canvas') const testDataUrl = 'data:image/png;base64,testdata' mockCanvas.toDataURL = vi.fn().mockReturnValue(testDataUrl) html2canvas.mockResolvedValue(mockCanvas) const result = await downloadToPNG.modifier(mockRef) expect(result).toBe(testDataUrl) }) test('calculates dimensions from clone element', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const toolbar = document.createElement('div') toolbar.className = 'widget-toolbar-container' toolbar.style.height = '50px' mockElement.appendChild(toolbar) const mockCanvas = document.createElement('canvas') mockCanvas.toDataURL = vi.fn().mockReturnValue('data:image/png;base64,test') let calledElement: HTMLElement | null = null html2canvas.mockImplementation((element) => { calledElement = element as HTMLElement return mockCanvas }) await downloadToPNG.modifier(mockRef) // Should be called with a clone (different element) expect(calledElement).not.toBe(mockElement) expect(html2canvas).toHaveBeenCalledWith( calledElement, expect.objectContaining({ height: expect.any(Number) as number, width: expect.any(Number) as number, }), ) }) test('handles element without toolbar or actions', async () => { const html2canvas = (await import('html2canvas')).default as ReturnType< typeof vi.fn > const mockCanvas = document.createElement('canvas') mockCanvas.toDataURL = vi.fn().mockReturnValue('data:image/png;base64,test') html2canvas.mockResolvedValue(mockCanvas) const result = await downloadToPNG.modifier(mockRef) expect(result).toBeTruthy() expect(result).toContain('data:image/png') }) })