优化
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { GaugeSeriesOption, BarSeriesOption } from 'echarts'
|
||||
import { adminWorkbenchChartTheme } from '@/styles/adminWorkbenchChartTheme'
|
||||
import { barChartOption, progressGaugeOption } from './dashboardChartOptions'
|
||||
|
||||
describe('self-operated ECharts configurations', () => {
|
||||
it('wraps a concise tooltip and escapes source text rather than inserting markup', () => {
|
||||
const option = barChartOption([{ key: 'sku', label: '<img src=x>', secondary: 'A&B', value: 12, detail: 'very long detail belongs in the dialog' }], '件', '热销', true)
|
||||
const option = barChartOption([{ key: 'sku', label: '<img src=x>', secondary: 'A&B "\'货主', value: '12000.123456', detail: 'very long detail belongs in the dialog' }], '<件>', '热销', true)
|
||||
const tooltip = option.tooltip as { formatter: (value: { dataIndex: number }) => string; extraCssText: string }
|
||||
const content = tooltip.formatter({ dataIndex: 0 })
|
||||
expect(content).toContain('<img src=x>')
|
||||
expect(content).toContain('A&B')
|
||||
expect(content).toContain('A&B "'货主')
|
||||
expect(content).toContain('12,000.123456')
|
||||
expect(content).toContain('<件>')
|
||||
expect(content).not.toContain('<img')
|
||||
expect(content).not.toContain('very long detail')
|
||||
expect(tooltip.extraCssText).toContain('white-space: normal')
|
||||
expect(option.tooltip).toMatchObject({ backgroundColor: adminWorkbenchChartTheme.colors.surface, borderColor: adminWorkbenchChartTheme.colors.border })
|
||||
})
|
||||
it('renders unknown progress as an empty track without a zero progress value', () => {
|
||||
const unknown = (progressGaugeOption(null).series as GaugeSeriesOption[])[0]!
|
||||
@@ -19,6 +24,30 @@ describe('self-operated ECharts configurations', () => {
|
||||
const known = (progressGaugeOption(12.1).series as GaugeSeriesOption[])[0]!
|
||||
expect(known.progress?.show).toBe(true)
|
||||
expect(known.data).toEqual([{ value: 12.1 }])
|
||||
expect(known.itemStyle).toMatchObject({ color: adminWorkbenchChartTheme.colors.primary })
|
||||
expect(known.axisLine?.lineStyle?.color).toEqual([[1, adminWorkbenchChartTheme.colors.primarySoft]])
|
||||
const zero = (progressGaugeOption(0).series as GaugeSeriesOption[])[0]!
|
||||
expect(zero.progress?.show).toBe(true)
|
||||
expect(zero.data).toEqual([{ value: 0 }])
|
||||
})
|
||||
it('keeps indeterminate activity distinct from known completion and honors reduced motion', () => {
|
||||
const matchMedia = vi.fn(() => ({ matches: true }))
|
||||
vi.stubGlobal('window', { matchMedia })
|
||||
try {
|
||||
const running = progressGaugeOption(null, true)
|
||||
expect(running.animation).toBe(false)
|
||||
expect((running.series as GaugeSeriesOption[])[0]).toMatchObject({
|
||||
progress: { show: true, roundCap: true },
|
||||
data: [{ value: 18 }],
|
||||
})
|
||||
expect(progressGaugeOption(12.1).animation).toBe(false)
|
||||
expect(matchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)')
|
||||
matchMedia.mockReturnValue({ matches: false })
|
||||
expect(progressGaugeOption(12.1).animation).toBe(true)
|
||||
expect(progressGaugeOption(null, true).animation).toBe(false)
|
||||
} finally {
|
||||
vi.unstubAllGlobals()
|
||||
}
|
||||
})
|
||||
it('keeps the seven bucket order, zero, null and common numeric scale', () => {
|
||||
const rows = Array.from({ length: 7 }, (_, index) => ({ key: `bucket-${index}`, label: `分组${index}`, value: index === 6 ? null : index, detail: '' }))
|
||||
@@ -27,5 +56,64 @@ describe('self-operated ECharts configurations', () => {
|
||||
expect(option.xAxis).toMatchObject({ type: 'value', min: 0 })
|
||||
expect((option.series as BarSeriesOption[])[0]?.data?.[0]).toMatchObject({ value: 0 })
|
||||
expect((option.series as BarSeriesOption[])[0]?.data?.[6]).toMatchObject({ value: null })
|
||||
expect((option.series as BarSeriesOption[])[0]).not.toHaveProperty('barMinHeight')
|
||||
expect((option.series as BarSeriesOption[])[0]).not.toHaveProperty('showBackground')
|
||||
})
|
||||
it('assigns bucket colors by semantic key even when the rows are reordered', () => {
|
||||
const expected: Array<[string, string]> = [
|
||||
['UNKNOWN', adminWorkbenchChartTheme.colors.unknown],
|
||||
['DAYS_GT_90', adminWorkbenchChartTheme.colors.teal],
|
||||
['NO_AVAILABLE', adminWorkbenchChartTheme.colors.rose],
|
||||
['DAYS_30_60', adminWorkbenchChartTheme.colors.primaryLight],
|
||||
['NO_OUTBOUND_IN_WINDOW', adminWorkbenchChartTheme.colors.amber],
|
||||
['DAYS_0_30', adminWorkbenchChartTheme.colors.primary],
|
||||
['DAYS_60_90', adminWorkbenchChartTheme.colors.tealLight],
|
||||
]
|
||||
const rows = expected.map(([key]) => ({ key, label: key, value: 1, detail: '' }))
|
||||
for (const orderedRows of [rows, [...rows].reverse()]) {
|
||||
const option = barChartOption(orderedRows, 'SKU', '库存分布')
|
||||
const data = (option.series as BarSeriesOption[])[0]!.data!
|
||||
for (const [index, row] of orderedRows.entries()) {
|
||||
expect(data[index]).toMatchObject({ itemStyle: { color: expected.find(([key]) => key === row.key)?.[1] } })
|
||||
}
|
||||
}
|
||||
})
|
||||
it('uses one blue for ranking and preserves real values instead of normalizing bar lengths', () => {
|
||||
const option = barChartOption([
|
||||
{ key: 'large', label: 'SKU-LARGE', value: '15000', detail: '' },
|
||||
{ key: 'small', label: 'SKU-SMALL', value: '150', detail: '' },
|
||||
{ key: 'zero', label: 'SKU-ZERO', value: 0, detail: '' },
|
||||
{ key: 'unknown', label: 'SKU-UNKNOWN', value: null, detail: '' },
|
||||
], '件', '热销', true)
|
||||
const series = (option.series as BarSeriesOption[])[0]!
|
||||
expect(series.data).toEqual([15000, 150, 0, null].map(value => ({
|
||||
value, itemStyle: { color: adminWorkbenchChartTheme.colors.primary, borderRadius: [0, 6, 6, 0] },
|
||||
})))
|
||||
expect(series).not.toHaveProperty('barMinHeight')
|
||||
expect(series).not.toHaveProperty('showBackground')
|
||||
expect(option.xAxis).toMatchObject({ min: 0 })
|
||||
const label = series.label as { formatter: (value: { dataIndex: number }) => string }
|
||||
expect(label.formatter({ dataIndex: 0 })).toBe('15,000')
|
||||
expect(label.formatter({ dataIndex: 2 })).toBe('0')
|
||||
expect(label.formatter({ dataIndex: 3 })).toBe('—')
|
||||
})
|
||||
it('rounds day labels and tooltips to two decimals while retaining the full chart value', () => {
|
||||
const option = barChartOption([{ key: 'sku', label: 'SKU', value: '1234.56789', detail: '' }], '天', '预计消化时间', true)
|
||||
const series = (option.series as BarSeriesOption[])[0]!
|
||||
expect(series.data?.[0]).toMatchObject({ value: 1234.56789 })
|
||||
const label = series.label as { formatter: (value: { dataIndex: number }) => string }
|
||||
const tooltip = option.tooltip as { formatter: (value: { dataIndex: number }) => string }
|
||||
expect(label.formatter({ dataIndex: 0 })).toBe('1,234.57')
|
||||
expect(tooltip.formatter({ dataIndex: 0 })).toContain('1,234.57 天')
|
||||
})
|
||||
it('retains every quantity digit in chart labels and tooltips for values beyond safe integers', () => {
|
||||
const value = '12345678901234567890.123450000001'
|
||||
const expected = '12,345,678,901,234,567,890.123450000001'
|
||||
const option = barChartOption([{ key: 'sku', label: 'SKU', value, detail: '' }], '件', '热销', true)
|
||||
const series = (option.series as BarSeriesOption[])[0]!
|
||||
const label = series.label as { formatter: (value: { dataIndex: number }) => string }
|
||||
const tooltip = option.tooltip as { formatter: (value: { dataIndex: number }) => string }
|
||||
expect(label.formatter({ dataIndex: 0 })).toBe(expected)
|
||||
expect(tooltip.formatter({ dataIndex: 0 })).toContain(`${expected} 件`)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user