Files
mihomo-patchbay/web/dashboard.test.ts
T
im0o 97c3943403 新增仪表盘页:流量曲线、流量与内存数字、配置概览、内核状态
流量曲线手画 SVG polyline 而不引图表库:60 个点两条线,一个图表库几百 KB,
决策记录 0006 留的例外用在这里不划算。/traffic 和 /memory 是永不结束的流,
api.request() 会把整个响应读完等于挂死,所以新增 api.openStream() 返回响应体,
交给 web/stream.ts 的 readLines 逐行读,离开页面中止 fetch 断流。
导航入口要在 App.vue 里挂,按约定留给集成者统一改。
2026-08-11 18:36:00 +08:00

88 lines
2.6 KiB
TypeScript

import { describe, expect, it } from 'bun:test'
import { WINDOW, chartPeak, kernelPorts, linePoints, modeLabel, pushSample } from './dashboard.ts'
import type { TrafficSample } from './dashboard.ts'
const sample = (up: number, down: number): TrafficSample => ({
up,
down,
upTotal: 0,
downTotal: 0,
})
describe('流量样本窗口', () => {
it('追加样本', () => {
expect(pushSample([sample(1, 2)], sample(3, 4))).toHaveLength(2)
})
it('窗口满了挤掉最旧的', () => {
const full = Array.from({ length: WINDOW }, (_, i) => sample(i, i))
const next = pushSample(full, sample(999, 999))
expect(next).toHaveLength(WINDOW)
expect(next.at(-1)?.up).toBe(999)
expect(next[0]?.up).toBe(1)
})
})
describe('纵轴上限', () => {
it('空窗口和全 0 都有下限 1,不会除出 NaN', () => {
expect(chartPeak([])).toBe(1)
expect(chartPeak([sample(0, 0)])).toBe(1)
})
it('取上行下行的共同峰值', () => {
expect(chartPeak([sample(10, 500), sample(800, 20)])).toBe(800)
})
})
describe('折线坐标', () => {
it('最新样本钉在右缘,最旧的往左排', () => {
const points = linePoints([5, 10], 10, 590, 100).split(' ')
expect(points).toHaveLength(2)
// 窗口 60,步长 10:两条样本占最右边两格
expect(points[0]).toBe('580.0,50.0')
expect(points[1]).toBe('590.0,0.0')
})
it('窗口不满不拉伸,左边留空', () => {
const points = linePoints([7], 10, 590, 100).split(' ')
expect(points).toEqual(['590.0,30.0'])
})
it('全是 0 时整条贴在底线上', () => {
for (const point of linePoints([0, 0, 0], 1, 590, 100).split(' ')) {
expect(point.endsWith(',100.0')).toBe(true)
}
})
})
describe('运行模式', () => {
it('三个已知模式有中文名', () => {
expect(modeLabel('rule')).toBe('规则')
expect(modeLabel('global')).toBe('全局')
expect(modeLabel('direct')).toBe('直连')
})
it('没见过的值原样显示,缺省是破折号', () => {
expect(modeLabel('script')).toBe('script')
expect(modeLabel(undefined)).toBe('—')
expect(modeLabel('')).toBe('—')
})
})
describe('代理端口', () => {
it('挑出大于 0 的端口,按固定顺序', () => {
expect(kernelPorts({ 'mixed-port': 7890, port: 7891, 'socks-port': 0 })).toEqual([
{ name: 'port', port: 7891 },
{ name: 'mixed-port', port: 7890 },
])
})
it('0、缺省和非数字都不算启用', () => {
expect(kernelPorts({ port: 0, 'socks-port': 'abc' })).toEqual([])
})
it('字符串端口号也认得', () => {
expect(kernelPorts({ port: '7890' })).toEqual([{ name: 'port', port: 7890 }])
})
})