Files
Piik/tests/stats-grid.test.ts
T
AAA f81577c4c5 feat(web): observe selected pair responses (#181)
Add local, generation-guarded selected-pair response evidence without granting it routing or recovery authority.
2026-08-22 08:26:41 +08:00

166 lines
5.3 KiB
TypeScript

import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { StatsGrid } from "../src/client/components/StatsGrid.tsx";
import {
EMPTY_METRICS,
type ConnectionMetrics,
} from "../src/client/types.ts";
const metrics = {
...EMPTY_METRICS,
bitrateKbps: 4_200,
framesPerSecond: 59.8,
resolution: "1920x1080",
rttMs: 21,
packetLossPercent: 0.4,
availableOutgoingKbps: 8_100,
iceProtocol: "udp",
localCandidateType: "srflx",
remoteCandidateType: "host",
localCandidateAddress: "192.0.2.10",
localCandidatePort: 50_000,
remoteCandidateAddress: "2001:db8::10",
remoteCandidatePort: 50_001,
selectedCandidatePairId: "candidate-pair-7",
candidatePairResponsesReceived: 18,
intervalCandidatePairResponsesReceived: 1,
candidatePairSampleWindowMs: 2_000,
codec: "video/VP8",
codecParameters: "max-fs=8160",
captureWidth: 1920,
captureHeight: 1080,
captureFramesPerSecond: 60,
mediaSourceFramesPerSecond: 58.5,
rtpRid: "h",
audioBitrateKbps: 128,
audioCodec: "audio/opus",
encoderImplementation: "ExternalEncoder",
intervalFramesEncoded: 116,
intervalEncodeTimeMs: 371.2,
intervalEncodeMs: 3.2,
qualityLimitationReason: "bandwidth",
} satisfies ConnectionMetrics;
describe("StatsGrid progressive disclosure", () => {
it("keeps route-critical metrics above an accessible collapsed deep panel", () => {
const html = renderToStaticMarkup(
createElement(StatsGrid, {
metrics,
direction: "send",
progressive: true,
}),
);
for (const label of [
"发送码率",
"帧率",
"分辨率",
"RTT",
"视频丢包率",
"传输协议",
"候选路径",
"质量状态",
]) {
expect(html).toContain(label);
}
expect(html).toContain('aria-expanded="false"');
expect(html).toContain('title="展开更多连接指标"');
const controlId = html.match(/aria-controls="([^"]+)"/)?.[1];
expect(controlId).toBeTruthy();
const panelStart = html.indexOf(`<dl id="${controlId}"`);
expect(panelStart).toBeGreaterThan(0);
expect(html.slice(panelStart, html.indexOf(">", panelStart))).toContain(
'hidden=""',
);
expect(html.indexOf("质量状态")).toBeLessThan(panelStart);
expect(html.indexOf("本地候选地址")).toBeGreaterThan(panelStart);
expect(html.indexOf("远端候选地址")).toBeGreaterThan(panelStart);
expect(html).toContain("192.0.2.10:50000");
expect(html).toContain("[2001:db8::10]:50001");
expect(html).toContain("候选对 ID");
expect(html).toContain("candidate-pair-7");
expect(html).toContain("STUN 响应");
expect(html).toContain("18 累计 · +1 / 2.0 s");
expect(html.indexOf("视频 Codec")).toBeGreaterThan(panelStart);
expect(html.indexOf("音频发送码率")).toBeGreaterThan(panelStart);
expect(html.indexOf("编码输入帧率")).toBeGreaterThan(panelStart);
expect(html.indexOf("最近区间编码量")).toBeGreaterThan(panelStart);
expect(html.indexOf("最近区间编码/帧")).toBeGreaterThan(panelStart);
expect(html).toContain("58.5 fps");
expect(html).toContain("116 帧 · 371.2 ms");
expect(html).toContain("当前 RID");
});
it("keeps the existing full grid when progressive disclosure is not requested", () => {
const html = renderToStaticMarkup(
createElement(StatsGrid, { metrics, direction: "send" }),
);
expect(html).toContain("视频 Codec");
expect(html).toContain("音频发送码率");
expect(html).not.toContain("更多指标");
expect(html).not.toContain("aria-expanded");
});
it("shows local inbound playout and repair evidence only for receivers", () => {
const receiverMetrics = {
...metrics,
audioVideoPlayoutDeltaMs: -12.5,
videoJitterBufferDelayMs: 24.5,
audioJitterBufferDelayMs: 18.5,
audioConcealedSamplesPercent: 1.25,
intervalAudioConcealmentEvents: 3,
} satisfies ConnectionMetrics;
const receiveHtml = renderToStaticMarkup(
createElement(StatsGrid, {
metrics: receiverMetrics,
direction: "receive",
}),
);
const sendHtml = renderToStaticMarkup(
createElement(StatsGrid, {
metrics: receiverMetrics,
direction: "send",
}),
);
const unavailableReceiveHtml = renderToStaticMarkup(
createElement(StatsGrid, {
metrics,
direction: "receive",
}),
);
for (const label of [
"音视频播放差",
"视频抖动缓冲",
"音频抖动缓冲",
"音频补偿样本率",
"音频补偿事件",
]) {
expect(receiveHtml).toContain(label);
expect(sendHtml).not.toContain(label);
expect(unavailableReceiveHtml).not.toContain(label);
}
expect(receiveHtml).toContain("-12.5 ms");
expect(receiveHtml).toContain("1.3%");
});
it("omits candidate endpoints when the browser withholds either field", () => {
const html = renderToStaticMarkup(
createElement(StatsGrid, {
metrics: {
...metrics,
localCandidatePort: null,
remoteCandidateAddress: null,
},
direction: "receive",
}),
);
expect(html).not.toContain("本地候选地址");
expect(html).not.toContain("远端候选地址");
});
});