import { describe, it, expect } from 'vitest' import { niceDomain, fmtTick } from '../src/components/editor/chart-domain' describe('niceDomain', () => { // 已确诊 bug 场景:数据全挤 97~98,旧实现映射到 [0,98] 导致折线贴顶 it('高基线小波动:line 值域收紧为数据带,折线不贴顶', () => { const d = niceDomain([97, 98, 97.6, 97, 98]) // 波动 <10% → domain = [97 - 1.5, 98 + 1.5],step 0.5 → [95.5, 99.5] expect(d.min).toBeLessThan(97) expect(d.max).toBeGreaterThan(98) expect(d.max - d.min).toBeLessThan(10) }) it('高基线小波动:ticks 4~5 个且为整数步长', () => { const d = niceDomain([97, 98, 97.6, 97, 98]) expect(d.ticks.length).toBeGreaterThanOrEqual(4) expect(d.ticks.length).toBeLessThanOrEqual(6) const step = d.ticks[1] - d.ticks[0] expect(step).toBe(1) // 首尾与 domain 对齐 expect(d.ticks[0]).toBe(d.min) expect(d.ticks[d.ticks.length - 1]).toBe(d.max) }) it('bar 类保持 0 基线(不因数据全大而从数据 min 起)', () => { const d = niceDomain([97, 98, 97.6, 97, 98], { zeroBase: true }) expect(d.min).toBe(0) expect(d.ticks[0]).toBe(0) expect(d.max).toBeGreaterThanOrEqual(98) }) it('常规整数数据 [1,2,3] → nice 步长(0.5 或 1),刻度覆盖 max', () => { const d = niceDomain([1, 2, 3]) const step = d.ticks[1] - d.ticks[0] expect([0.5, 1]).toContain(step) expect(d.ticks).toContain(3) }) it('小数值数据 [0.1, 0.2] → 步长 0.5,下限 0.5 生效', () => { const d = niceDomain([0.1, 0.2]) expect(d.ticks[1] - d.ticks[0]).toBe(0.5) expect(d.min).toBeLessThanOrEqual(0.1) expect(d.max).toBeGreaterThanOrEqual(0.2) }) it('单值数据不产生除零/NaN', () => { const d = niceDomain([5]) expect(d.min).toBeLessThan(d.max) expect(Number.isFinite(d.min)).toBe(true) expect(Number.isFinite(d.max)).toBe(true) expect(d.ticks.length).toBeGreaterThanOrEqual(3) }) it('全等值数据 line 值域仍为区间而非单点', () => { const d = niceDomain([42, 42, 42, 42]) expect(d.max).toBeGreaterThan(d.min) expect(d.ticks.length).toBeGreaterThanOrEqual(3) }) it('全等值数据 bar 类 → [0, ≥value]', () => { const d = niceDomain([42, 42, 42], { zeroBase: true }) expect(d.min).toBe(0) expect(d.max).toBeGreaterThanOrEqual(42) }) it('maxCap 作为上限覆盖(仅当 > 数据 max)', () => { const d = niceDomain([1, 2, 3], { zeroBase: true, maxCap: 100 }) expect(d.max).toBeGreaterThanOrEqual(100) }) it('maxCap 小于数据 max 时不收紧值域', () => { const d = niceDomain([1, 2, 3], { zeroBase: true, maxCap: 1 }) expect(d.max).toBeGreaterThanOrEqual(3) }) it('空数据返回安全默认值域', () => { const d = niceDomain([]) expect(d.max).toBeGreaterThan(d.min) expect(d.ticks.length).toBeGreaterThanOrEqual(2) }) it('过滤 NaN/Infinity', () => { const d = niceDomain([NaN, Infinity, -Infinity, 5, 10]) expect(d.min).toBeLessThanOrEqual(5) expect(d.max).toBeGreaterThanOrEqual(10) }) it('大整数数据步长取 5×10^k', () => { const d = niceDomain([1000, 1200, 1500, 2000], { zeroBase: true }) const step = d.ticks[1] - d.ticks[0] expect(step).toBeGreaterThan(0) // 2000 跨度 → 步长 500 或 1000 之类 nice 数 expect([100, 200, 500, 1000].includes(step)).toBe(true) }) }) describe('fmtTick', () => { it('去尾零', () => { expect(fmtTick(2.5)).toBe('2.5') expect(fmtTick(20)).toBe('20') expect(fmtTick(0)).toBe('0') expect(fmtTick(97.6)).toBe('97.6') }) it('消浮点误差', () => { expect(fmtTick(0.1 + 0.2)).toBe('0.3') expect(fmtTick(2.55)).toBe('2.55') }) })