import { describe, it, expect } from 'vitest' import { toRelativeScatterplotData } from './transforms' describe('toRelativeScatterplotData', () => { it('rewrites each tuple y as its share of the series total in 0-100, x stays raw', () => { expect( toRelativeScatterplotData([ [ [1, 25], [2, 75], ], ]), ).toEqual([ [ [1, 25], [2, 75], ], ]) }) it('handles non-100 totals (5/20 = 25%)', () => { expect( toRelativeScatterplotData([ [ [10, 5], [20, 15], ], ]), ).toEqual([ [ [10, 25], [20, 75], ], ]) }) it('handles multiple series independently', () => { expect( toRelativeScatterplotData([ [ [1, 3], [2, 7], ], [ [10, 1], [20, 1], ], ]), ).toEqual([ [ [1, 30], [2, 70], ], [ [10, 50], [20, 50], ], ]) }) it('returns a series unchanged when its total y is zero', () => { const input = [ [ [1, 0], [2, 0], ], ] expect(toRelativeScatterplotData(input)).toEqual(input) }) it('produces signed share-of-magnitude percentages for mixed-sign y values', () => { // Denominator is |1000| + |-990| = 1990 const out = toRelativeScatterplotData([ [ [1, 1000], [2, -990], ], ]) as [number, number][][] expect(out[0]?.[0]?.[0]).toBe(1) expect(out[0]?.[0]?.[1]).toBeCloseTo(50.25126, 4) expect(out[0]?.[1]?.[0]).toBe(2) expect(out[0]?.[1]?.[1]).toBeCloseTo(-49.74874, 4) }) it('returns the input unchanged when shape does not match [number, number][]', () => { expect(toRelativeScatterplotData(null)).toBe(null) expect(toRelativeScatterplotData('not an array')).toBe('not an array') // Flat number array (histogram shape) isn't a tuple array — pass // through unchanged so attaching the wrong transform to the wrong // widget is a no-op rather than data corruption. expect(toRelativeScatterplotData([[10, 20, 70]])).toEqual([[10, 20, 70]]) // Named-value (bar shape) — pass through. expect(toRelativeScatterplotData([[{ name: 'a', value: 10 }]])).toEqual([ [{ name: 'a', value: 10 }], ]) }) })