import { describe, it, expect } from 'vitest' import { createTheme, type Theme } from '@mui/material' import { addZoom, createAddZoom, ZOOM_LAYOUT } from './transforms' const theme = createTheme() as unknown as Theme describe('addZoom', () => { it('appends an inside + slider dataZoom pair to the option', () => { const out = addZoom({ xAxis: { type: 'value' } }) as { xAxis: { type: string } dataZoom: { type: string }[] } expect(out.xAxis).toEqual({ type: 'value' }) expect(out.dataZoom).toHaveLength(2) expect(out.dataZoom[0]?.type).toBe('inside') expect(out.dataZoom[1]?.type).toBe('slider') }) it('replaces an existing dataZoom array (no append/stack across runs)', () => { const seeded = { dataZoom: [{ type: 'inside' }, { type: 'inside' }] } const out = addZoom(seeded) as { dataZoom: { type: string }[] } expect(out.dataZoom).toHaveLength(2) expect(out.dataZoom.map((z) => z.type)).toEqual(['inside', 'slider']) }) it('omits start/end so ECharts preserves the user-dragged range across re-runs', () => { const out = addZoom({}) as { dataZoom: { start?: number; end?: number }[] } for (const dz of out.dataZoom) { expect(dz.start).toBeUndefined() expect(dz.end).toBeUndefined() } }) it('returns the input unchanged when not an object', () => { expect(addZoom(null)).toBe(null) expect(addZoom(undefined)).toBe(undefined) expect(addZoom('not an object')).toBe('not an object') }) }) describe('createAddZoom (theme-bound, v1-styled slider)', () => { it('produces an inside zoom + a styled slider with the v1 chrome', () => { const transform = createAddZoom(theme) const out = transform({}) as { dataZoom: { type: string height?: number handleIcon?: string fillerColor?: string borderColor?: string dataBackground?: { areaStyle?: { color?: string } } textStyle?: { fontSize?: number } }[] } expect(out.dataZoom).toHaveLength(2) expect(out.dataZoom[0]?.type).toBe('inside') const slider = out.dataZoom[1]! expect(slider.type).toBe('slider') // v1 hallmarks the new slider must carry. expect(slider.height).toBe(ZOOM_LAYOUT.sliderHeight) expect(slider.handleIcon).toMatch(/^image:\/\/data:image\/svg\+xml/) expect(slider.fillerColor).toBeTruthy() expect(slider.borderColor).toBeTruthy() expect(slider.textStyle?.fontSize).toBeGreaterThan(0) }) it('omits start/end so the dragged range survives re-runs', () => { const transform = createAddZoom(theme) const out = transform({}) as { dataZoom: { start?: number; end?: number }[] } for (const dz of out.dataZoom) { expect(dz.start).toBeUndefined() expect(dz.end).toBeUndefined() } }) it('returns non-object input unchanged', () => { const transform = createAddZoom(theme) expect(transform(null)).toBe(null) expect(transform(undefined)).toBe(undefined) }) it('defaults to x-axis only (matches bar/histogram/timeseries semantics)', () => { const transform = createAddZoom(theme) const out = transform({}) as { dataZoom: { type: string; xAxisIndex?: unknown; yAxisIndex?: unknown }[] } expect(out.dataZoom).toHaveLength(2) for (const dz of out.dataZoom) { expect(dz.xAxisIndex).toEqual([0]) expect(dz.yAxisIndex).toBeUndefined() } }) it('emits inside + slider per axis when axes=["x","y"] (2D zoom for scatter)', () => { const transform = createAddZoom(theme, { axes: ['x', 'y'] }) const out = transform({}) as { dataZoom: { type: string xAxisIndex?: unknown yAxisIndex?: unknown width?: number right?: number height?: number bottom?: number }[] } expect(out.dataZoom).toHaveLength(4) const insides = out.dataZoom.filter((dz) => dz.type === 'inside') const sliders = out.dataZoom.filter((dz) => dz.type === 'slider') expect(insides).toHaveLength(2) expect(sliders).toHaveLength(2) // One inside per axis. expect(insides.some((dz) => dz.xAxisIndex !== undefined)).toBe(true) expect(insides.some((dz) => dz.yAxisIndex !== undefined)).toBe(true) // X-slider: horizontal at the bottom (has height + bottom). const xSlider = sliders.find((dz) => dz.xAxisIndex !== undefined) expect(xSlider?.height).toBe(ZOOM_LAYOUT.sliderHeight) expect(xSlider?.bottom).toBeDefined() // Y-slider: vertical on the right (has width + right, no handleIcon). const ySlider = sliders.find((dz) => dz.yAxisIndex !== undefined) expect(ySlider?.width).toBe(ZOOM_LAYOUT.sliderHeight) expect(ySlider?.right).toBeDefined() }) it('emits y-only when axes=["y"]', () => { const transform = createAddZoom(theme, { axes: ['y'] }) const out = transform({}) as { dataZoom: { type: string; xAxisIndex?: unknown; yAxisIndex?: unknown }[] } expect(out.dataZoom).toHaveLength(2) for (const dz of out.dataZoom) { expect(dz.yAxisIndex).toEqual([0]) expect(dz.xAxisIndex).toBeUndefined() } }) }) describe('ZOOM_LAYOUT', () => { it('exposes the layout constants used by data-fusion mergers', () => { expect(ZOOM_LAYOUT.sliderHeight).toBe(32) expect(ZOOM_LAYOUT.sliderGap).toBe(8) expect(ZOOM_LAYOUT.sliderBottomWithLegend).toBe(28) }) })