1
0
forked from boos/BoosAPI

[codex] refine proxy logs debug UI follow-ups (#312)

* Refine proxy logs debug page layout

* Align proxy logs debug UI with page patterns

* Prevent proxy trace detail refresh flicker

* Polish proxy trace detail modal
This commit is contained in:
Cita
2026-03-29 13:39:25 +08:00
committed by GitHub
parent ab8640c8c3
commit 7092e2f129
2 changed files with 587 additions and 337 deletions
@@ -323,6 +323,42 @@ describe('ProxyLogs server-driven page', () => {
} }
}); });
it('opens debug trace detail on demand instead of preloading the first trace inline', async () => {
let root!: WebTestRenderer;
try {
await act(async () => {
root = create(
<MemoryRouter initialEntries={['/logs']}>
<ToastProvider>
<ProxyLogs />
</ToastProvider>
</MemoryRouter>,
);
});
await flushMicrotasks();
expect(apiMock.getProxyDebugTraceDetail).not.toHaveBeenCalled();
const viewDetailButton = root.root.find((node) => (
node.type === 'button'
&& typeof node.props.onClick === 'function'
&& collectText(node).trim() === '查看详情'
));
await act(async () => {
viewDetailButton.props.onClick();
});
await flushMicrotasks();
expect(apiMock.getProxyDebugTraceDetail).toHaveBeenCalledWith(701);
expect(collectText(root.root)).toContain('原始下游请求头');
expect(collectText(root.root)).toContain('Attempt 记录');
} finally {
root?.unmount();
}
});
it('polls debug traces after tracing is enabled so new results are not hidden behind the settings modal', async () => { it('polls debug traces after tracing is enabled so new results are not hidden behind the settings modal', async () => {
vi.useFakeTimers(); vi.useFakeTimers();
apiMock.getRuntimeSettings.mockResolvedValue({ apiMock.getRuntimeSettings.mockResolvedValue({
@@ -368,6 +404,95 @@ describe('ProxyLogs server-driven page', () => {
} }
}); });
it('keeps debug trace detail visible during polling refresh instead of flashing back to loading', async () => {
vi.useFakeTimers();
apiMock.getRuntimeSettings.mockResolvedValue({
proxyDebugTraceEnabled: true,
proxyDebugCaptureHeaders: true,
proxyDebugCaptureBodies: false,
proxyDebugCaptureStreamChunks: false,
proxyDebugTargetSessionId: '',
proxyDebugTargetClientKind: '',
proxyDebugTargetModel: '',
proxyDebugRetentionHours: 24,
proxyDebugMaxBodyBytes: 262144,
});
let resolveDetail!: (value: any) => void;
apiMock.getProxyDebugTraceDetail
.mockResolvedValueOnce({
trace: {
id: 701,
requestedModel: 'gpt-4o',
sessionId: 'sess-debug-1',
requestHeadersJson: '{\"before\":true}',
},
attempts: [],
})
.mockImplementationOnce(() => new Promise((resolve) => {
resolveDetail = resolve;
}));
let root!: WebTestRenderer;
try {
await act(async () => {
root = create(
<MemoryRouter initialEntries={['/logs']}>
<ToastProvider>
<ProxyLogs />
</ToastProvider>
</MemoryRouter>,
);
});
await flushMicrotasks();
const viewDetailButton = root.root.find((node) => (
node.type === 'button'
&& typeof node.props.onClick === 'function'
&& collectText(node).trim() === '查看详情'
));
await act(async () => {
viewDetailButton.props.onClick();
});
await flushMicrotasks();
expect(collectText(root.root)).toContain('原始下游请求头');
expect(collectText(root.root)).not.toContain('加载追踪详情中...');
await act(async () => {
vi.advanceTimersByTime(2100);
});
await flushMicrotasks();
expect(collectText(root.root)).toContain('原始下游请求头');
expect(collectText(root.root)).not.toContain('加载追踪详情中...');
await act(async () => {
resolveDetail({
trace: {
id: 701,
requestedModel: 'gpt-4o',
sessionId: 'sess-debug-1',
requestHeadersJson: '{\"after\":true}',
},
attempts: [],
});
});
await flushMicrotasks();
expect(collectText(root.root)).toContain('原始下游请求头');
expect(collectText(root.root)).not.toContain('加载追踪详情中...');
} finally {
await act(async () => {
root?.unmount();
});
vi.runOnlyPendingTimers();
vi.useRealTimers();
}
});
it('keeps the model badge sized to the model name in desktop rows', async () => { it('keeps the model badge sized to the model name in desktop rows', async () => {
let root!: WebTestRenderer; let root!: WebTestRenderer;
File diff suppressed because it is too large Load Diff