* feat(dashboard): add traffic flow sankey chart Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests. * feat: build flow chart from quota data --------- Co-authored-by: CaIon <i@caion.me>
116 lines
2.7 KiB
TypeScript
Vendored
116 lines
2.7 KiB
TypeScript
Vendored
import assert from 'node:assert/strict'
|
|
import { describe, test } from 'node:test'
|
|
import type { FlowUserFilterOption } from '../types'
|
|
import {
|
|
compactFlowSelectionLabel,
|
|
flowDisplayState,
|
|
requireSuccessfulFlowRows,
|
|
visibleFlowUsers,
|
|
} from './flow-selection'
|
|
|
|
const users: FlowUserFilterOption[] = [
|
|
{
|
|
value: 'user:1',
|
|
label: 'dry',
|
|
valueLabel: '100',
|
|
valueRaw: 100,
|
|
color: '#1664ff',
|
|
},
|
|
{
|
|
value: 'user:2',
|
|
label: 'jrc',
|
|
valueLabel: '70',
|
|
valueRaw: 70,
|
|
color: '#1ac6ff',
|
|
},
|
|
]
|
|
|
|
describe('dashboard flow selection helpers', () => {
|
|
test('limits user chips to currently visible users', () => {
|
|
assert.deepEqual(
|
|
visibleFlowUsers(users, []).map((user) => user.value),
|
|
['user:1', 'user:2']
|
|
)
|
|
assert.deepEqual(
|
|
visibleFlowUsers(users, ['user:2']).map((user) => user.value),
|
|
['user:2']
|
|
)
|
|
})
|
|
|
|
test('filters visible users without mutating the source options', () => {
|
|
const visible = visibleFlowUsers(users, ['user:1'])
|
|
|
|
assert.deepEqual(
|
|
visible.map((user) => user.value),
|
|
['user:1']
|
|
)
|
|
assert.deepEqual(
|
|
users.map((user) => user.value),
|
|
['user:1', 'user:2']
|
|
)
|
|
})
|
|
|
|
test('formats compact selected counts for flow multiselect summaries', () => {
|
|
assert.equal(compactFlowSelectionLabel(0), '*')
|
|
assert.equal(compactFlowSelectionLabel(1), '1')
|
|
assert.equal(compactFlowSelectionLabel(23), '23')
|
|
})
|
|
|
|
test('prioritizes loading and error states before empty flow data', () => {
|
|
assert.equal(
|
|
flowDisplayState({
|
|
isLoading: true,
|
|
isError: true,
|
|
linkCount: 0,
|
|
themeReady: true,
|
|
}),
|
|
'loading'
|
|
)
|
|
assert.equal(
|
|
flowDisplayState({
|
|
isLoading: false,
|
|
isError: true,
|
|
linkCount: 0,
|
|
themeReady: true,
|
|
}),
|
|
'error'
|
|
)
|
|
assert.equal(
|
|
flowDisplayState({
|
|
isLoading: false,
|
|
isError: false,
|
|
linkCount: 0,
|
|
themeReady: true,
|
|
}),
|
|
'empty'
|
|
)
|
|
assert.equal(
|
|
flowDisplayState({
|
|
isLoading: false,
|
|
isError: false,
|
|
linkCount: 1,
|
|
themeReady: false,
|
|
}),
|
|
'loading'
|
|
)
|
|
})
|
|
|
|
test('throws unsuccessful flow responses instead of treating them as empty data', () => {
|
|
assert.throws(
|
|
() =>
|
|
requireSuccessfulFlowRows(
|
|
{ success: false, data: [], message: 'database unavailable' },
|
|
'Failed to load'
|
|
),
|
|
/database unavailable/
|
|
)
|
|
assert.deepEqual(
|
|
requireSuccessfulFlowRows(
|
|
{ success: true, data: [{ user_id: 1, quota: 10 }] },
|
|
'Failed to load'
|
|
),
|
|
[{ user_id: 1, quota: 10 }]
|
|
)
|
|
})
|
|
})
|