修复: 生成质量机制根治——max_tokens截断检测+自动重试/icon白名单机械归一/空image剔除/金句引号伪元素治理/文本溢出扩高+重叠校正/图表y轴自适应防窄幅失真/stat缩字兜底/饼图图例流式排布/卡片icon字体锁定

This commit is contained in:
lxy
2026-08-30 17:27:58 +08:00
parent f7d994842a
commit 4e68773642
11 changed files with 1204 additions and 102 deletions
+109
View File
@@ -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')
})
})