import { describe, it, expect, beforeEach, vi } from 'vitest' import { createBarDownloadConfig } from './download' import type { BarWidgetData } from './types' const sample: BarWidgetData = [[{ name: 'a', value: 1 }]] const multi: BarWidgetData = [ [ { name: 'a', value: 1 }, { name: 'b', value: 2 }, ], [{ name: 'c', value: 3 }], ] let revokeSpy: ReturnType beforeEach(() => { // jsdom/happy-dom often miss URL.* — stub deterministically. vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock') revokeSpy = vi .spyOn(URL, 'revokeObjectURL') .mockImplementation(() => undefined) }) describe('createBarDownloadConfig', () => { it('returns CSV-only items when getCaptureEl is not provided', () => { const items = createBarDownloadConfig({ filename: 'demo', getData: () => sample, }) expect(items.map((i) => i.id)).toEqual(['csv']) }) it('prepends a PNG item when getCaptureEl is provided', () => { const items = createBarDownloadConfig({ filename: 'demo', getData: () => sample, getCaptureEl: () => document.createElement('div'), }) expect(items.map((i) => i.id)).toEqual(['png', 'csv']) const pngItem = items[0] expect(pngItem?.label).toBe('PNG') expect(pngItem?.icon).toBeTruthy() }) it('the PNG item rejects when the captureEl getter returns null', async () => { const items = createBarDownloadConfig({ filename: 'demo', getData: () => sample, getCaptureEl: () => null, }) const png = items.find((i) => i.id === 'png') expect(png).toBeTruthy() await expect(png!.resolve()).rejects.toThrow(/No PNG capture element/) }) it('CSV resolve returns a download handle with the filename + revoke fn (single series)', async () => { const items = createBarDownloadConfig({ filename: 'sales', getData: () => sample, }) const csv = items.find((i) => i.id === 'csv')! const handle = await csv.resolve() expect(handle.url).toBe('blob:mock') expect(handle.filename).toBe('sales.csv') expect(typeof handle.revoke).toBe('function') handle.revoke?.() expect(revokeSpy).toHaveBeenCalledWith('blob:mock') }) it('CSV resolve emits empty-row separators between multiple series', async () => { let csvText = '' // Intercept the Blob payload so we can assert on the serialised CSV. const RealBlob = global.Blob vi.stubGlobal( 'Blob', class extends RealBlob { constructor(parts: BlobPart[], opts?: BlobPropertyBag) { csvText = typeof parts[0] === 'string' ? parts[0] : '' super(parts, opts) } }, ) const items = createBarDownloadConfig({ filename: 'multi', getData: () => multi, }) await items.find((i) => i.id === 'csv')!.resolve() expect(csvText).toBe('name,value\na,1\nb,2\n\nname,value\nc,3') vi.unstubAllGlobals() }) })