Files
dify/web/app/components/app/overview/__tests__/app-chart-utils.spec.ts
T
Stephen ZhouGitHubautofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
eb71e47f3b chore: migrate linting to vp lint with ESLint fallback (#38834)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-13 06:41:53 +00:00

179 lines
5.6 KiB
TypeScript

/* oxlint-disable typescript/no-explicit-any */
import {
buildChartOptions,
getChartValueField,
getDefaultChartData,
getSummaryValue,
getTokenSummary,
hasNonZeroChartData,
} from '../app-chart-utils'
describe('app-chart-utils', () => {
describe('getDefaultChartData', () => {
it('should build fallback rows for the requested date window', () => {
const rows = getDefaultChartData({
start: 'Jan 1, 2024',
end: 'Jan 4, 2024',
key: 'interactions',
})
expect(rows).toHaveLength(3)
expect(rows[0]).toEqual({ date: 'Jan 1, 2024', interactions: 0 })
expect(rows[2]).toEqual({ date: 'Jan 3, 2024', interactions: 0 })
})
})
describe('hasNonZeroChartData', () => {
it('should detect whether rows contain a non-zero chart value', () => {
expect(
hasNonZeroChartData(
[
{ date: 'Jan 1, 2024', count: 0 },
{ date: 'Jan 2, 2024', count: '0' },
],
'count',
),
).toBe(false)
expect(
hasNonZeroChartData(
[
{ date: 'Jan 1, 2024', count: 0 },
{ date: 'Jan 2, 2024', count: 1 },
],
'count',
),
).toBe(true)
})
})
describe('getSummaryValue', () => {
it('should average values when the chart is configured as an average', () => {
const summaryValue = getSummaryValue({
chartType: 'conversations',
statistics: [
{ date: 'Jan 1, 2024', latency: 10 },
{ date: 'Jan 2, 2024', latency: 20 },
],
yField: 'latency',
isAvg: true,
unit: 'ms',
})
expect(summaryValue).toBe('15 ms')
})
it('should compress large cost totals with a k suffix', () => {
const summaryValue = getSummaryValue({
chartType: 'costs',
statistics: [
{ date: 'Jan 1, 2024', count: 700 },
{ date: 'Jan 2, 2024', count: 800 },
],
yField: 'count',
})
expect(summaryValue).toBe('2k')
})
it('should keep small cost totals in their raw form', () => {
const summaryValue = getSummaryValue({
chartType: 'costs',
statistics: [
{ date: 'Jan 1, 2024', count: 250 },
{ date: 'Jan 2, 2024', count: 300 },
],
yField: 'count',
})
expect(summaryValue).toBe('550')
})
})
describe('getChartValueField', () => {
it('should prefer the explicit value key and otherwise fall back to the count-like field', () => {
expect(getChartValueField([{ date: 'Jan 1, 2024', request_count: 2 }], 'total')).toBe('total')
expect(getChartValueField([{ date: 'Jan 1, 2024', request_count: 2 }])).toBe('request_count')
expect(getChartValueField([{ date: 'Jan 1, 2024', latency: 2 }])).toBe('count')
})
})
describe('getTokenSummary', () => {
it('should sum token costs using currency formatting', () => {
const tokenSummary = getTokenSummary([
{ date: 'Jan 1, 2024', count: 1, total_price: '1.25' },
{ date: 'Jan 2, 2024', count: 2, total_price: '2.5' },
])
expect(tokenSummary).toBe('$3.7500')
})
})
describe('buildChartOptions', () => {
it('should build line chart options with dataset and y-axis max', () => {
const options = buildChartOptions({
statistics: [
{ date: 'Jan 1, 2024', count: 5 },
{ date: 'Jan 2, 2024', count: 10 },
],
chartType: 'messages',
yField: 'count',
yMax: 100,
})
const dataset = options.dataset as {
dimensions: string[]
source: Array<Record<string, unknown>>
}
const grid = options.grid as { top: number; left: number; right: number }
const yAxis = options.yAxis as { max: number }
const series = options.series as Array<{ lineStyle: { color: string } }>
expect(dataset.dimensions).toEqual(['date', 'count'])
expect(dataset.source).toHaveLength(2)
expect(grid.top).toBe(16)
expect(grid.left).toBe(0)
expect(grid.right).toBe(0)
expect(yAxis.max).toBe(100)
expect(series[0]!.lineStyle.color).toBe('rgba(6, 148, 162, 1)')
})
it('should build token-aware tooltip content and split-line intervals for cost charts', () => {
const options = buildChartOptions({
statistics: [
{ date: 'Jan 1, 2024', total_cost: 5, total_price: '1.25' },
{ date: 'Jan 2, 2024', total_cost: 10, total_price: '2.50' },
{ date: 'Jan 3, 2024', total_cost: 15, total_price: '3.75' },
],
chartType: 'costs',
yField: 'total_cost',
})
const xAxis = options.xAxis as Array<Record<string, any>>
const formatter = xAxis[0]!.axisLabel.formatter as (value: string) => string
const outerInterval = xAxis[0]!.splitLine.interval as (index: number) => boolean
const innerInterval = xAxis[1]!.splitLine.interval as (
_index: number,
value: string,
) => boolean
const series = options.series as Array<Record<string, any>>
const tooltipFormatter = series[0]!.tooltip.formatter as (params: {
name: string
data: { total_cost: number; total_price: string }
}) => string
expect(formatter('Jan 2, 2024')).toBe('Jan 2, 2024')
expect(outerInterval(0)).toBe(true)
expect(outerInterval(1)).toBe(false)
expect(innerInterval(0, '')).toBe(false)
expect(innerInterval(1, '1')).toBe(true)
expect(
tooltipFormatter({
name: 'Jan 2, 2024',
data: { total_cost: 10, total_price: '2.50' },
}),
).toContain('~$2.50')
})
})
})