修复: 生成质量机制根治——max_tokens截断检测+自动重试/icon白名单机械归一/空image剔除/金句引号伪元素治理/文本溢出扩高+重叠校正/图表y轴自适应防窄幅失真/stat缩字兜底/饼图图例流式排布/卡片icon字体锁定
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
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')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user