/** * The free CSV / TSV / JSON export + copy-to-clipboard now on the grid API. * Mounts a real and drives api.exportCsv/exportTsv/exportJson/ * copyToClipboard with download:false so nothing hits the DOM download path. */ import { afterEach, describe, expect, it, vi } from 'vitest' import { mount, unmount } from 'svelte' import SvGrid from './SvGrid.svelte' import { columnFilteringFeature, rowSelectionFeature, rowSortingFeature, tableFeatures, } from './index' import type { ColumnDef, SvGridApi } from './index' type Row = { id: number; name: string; price: number; when: string } const rows: Row[] = [ { id: 1, name: 'ACME, Inc', price: 1234.5, when: '2026-03-04' }, { id: 2, name: 'Globex', price: 20, when: '2026-06-01' }, ] const columns: ColumnDef[] = [ { field: 'name', header: 'Company', width: 180 }, { field: 'price', header: 'Price', width: 120, align: 'right', format: { type: 'currency', currency: 'USD' } }, { field: 'when', header: 'Date', width: 120 }, // plain string, no format (deterministic) ] const features = tableFeatures({ columnFilteringFeature, rowSortingFeature, rowSelectionFeature }) function mountGrid(): Promise<{ api: SvGridApi; destroy: () => void }> { return new Promise((resolve, reject) => { const target = document.createElement('div') document.body.appendChild(target) let done = false const app = mount(SvGrid, { target, props: { data: rows.map((r) => ({ ...r })), columns: columns.map((c) => ({ ...c })), features, getRowId: (r: Row) => String(r.id), rowHeight: 34, containerHeight: 400, virtualization: false, onApiReady(api: SvGridApi) { done = true resolve({ api, destroy: () => { unmount(app); target.remove() } }) }, } as any, }) queueMicrotask(() => { if (!done) reject(new Error('onApiReady never fired')) }) }) } let cleanup: (() => void) | null = null afterEach(() => { cleanup?.(); cleanup = null; vi.restoreAllMocks() }) describe('free grid export: exportCsv', () => { it('serializes visible rows with faithful formatted values + a BOM header', async () => { const { api, destroy } = await mountGrid() cleanup = destroy const csv = await api.exportCsv({ download: false }) const lines = csv.replace(/^/, '').split('\r\n') expect(lines[0]).toBe('Company,Price,Date') // Formatted as on screen; the comma in "ACME, Inc" AND in "$1,234.50" both // force RFC-4180 quoting. expect(lines[1]).toBe('"ACME, Inc","$1,234.50",2026-03-04') expect(csv.startsWith('')).toBe(true) }) it('rawValues:true writes the underlying numbers/strings', async () => { const { api, destroy } = await mountGrid() cleanup = destroy const csv = await api.exportCsv({ download: false, rawValues: true, bom: false }) const lines = csv.split('\r\n') expect(lines[1]).toBe('"ACME, Inc",1234.5,2026-03-04') }) it('honors a column subset in the given order', async () => { const { api, destroy } = await mountGrid() cleanup = destroy const csv = await api.exportCsv({ download: false, bom: false, columns: ['when', 'name'] }) expect(csv.split('\r\n')[0]).toBe('Date,Company') }) }) describe('free grid export: exportTsv / exportJson', () => { it('exportTsv is tab-delimited', async () => { const { api, destroy } = await mountGrid() cleanup = destroy const tsv = await api.exportTsv({ download: false, bom: false }) expect(tsv.split('\r\n')[0]).toBe('Company\tPrice\tDate') }) it('exportJson emits an array of formatted records', async () => { const { api, destroy } = await mountGrid() cleanup = destroy const json = JSON.parse(await api.exportJson({ download: false })) expect(json).toHaveLength(2) expect(json[0]).toEqual({ name: 'ACME, Inc', price: '$1,234.50', when: '2026-03-04' }) }) }) describe('free grid export: copyToClipboard', () => { it('writes TSV to the clipboard by default', async () => { const writeText = vi.fn().mockResolvedValue(undefined) vi.stubGlobal('navigator', { clipboard: { writeText } }) const { api, destroy } = await mountGrid() cleanup = destroy const text = await api.copyToClipboard() expect(writeText).toHaveBeenCalledOnce() expect(text.split('\r\n')[0]).toBe('Company\tPrice\tDate') expect(text.startsWith('')).toBe(false) // no BOM on clipboard }) it('can copy a Markdown table', async () => { const writeText = vi.fn().mockResolvedValue(undefined) vi.stubGlobal('navigator', { clipboard: { writeText } }) const { api, destroy } = await mountGrid() cleanup = destroy const md = await api.copyToClipboard({ format: 'markdown' }) const lines = md.split('\n') expect(lines[0]).toBe('| Company | Price | Date |') expect(lines[1]).toBe('| --- | ---: | --- |') // Price is right-aligned }) })