fix: restore coding history and quota feedback

This commit is contained in:
inman
2026-09-01 11:46:04 +08:00
parent 38f85f6b5e
commit d523b72cf8
14 changed files with 480 additions and 49 deletions

View File

@@ -631,7 +631,83 @@ describe('CodingConversationTimeline', () => {
.toBeVisible();
});
it('windows long timelines and loads earlier nodes only on demand', async () => {
it('shows an actionable non-retryable quota failure in the process summary', async () => {
const { codingConversationStore } = await import('@/stores/coding-conversations');
const { CodingConversationTimeline } = await import(
'@/pages/Chat/CodingConversationTimeline'
);
const base = createProductSnapshot('conversation-quota-failure', 1);
const snapshot = {
...base,
run: {
status: 'idle' as const,
runId: 'run-quota-failure',
startedAt: 1_000,
settledAt: 6_000,
terminalReason: 'failed' as const,
error: {
code: 'CODING_PROVIDER_QUOTA_EXHAUSTED' as const,
message: '词元点数余额不足,请充值后重试。',
recoverable: false,
},
},
nodes: [{
kind: 'tool' as const,
id: 'tool-quota-failure',
toolCallId: 'call-quota-failure',
toolName: 'bash',
title: '执行命令',
inputText: '',
status: 'error' as const,
output: [],
}],
};
codingConversationStore.getState().applySnapshotEvent({
type: 'snapshot',
conversationId: 'conversation-quota-failure',
workerGeneration: 1,
seq: snapshot.cursor.seq,
snapshot,
});
render(<CodingConversationTimeline conversationId="conversation-quota-failure" />);
expect(within(screen.getByTestId('coding-process-group'))
.getByText('词元点数余额不足,请充值后重试。 · 5 秒')).toBeVisible();
});
it('shows an actionable persisted Provider failure after reopening a Conversation', async () => {
const { codingConversationStore } = await import('@/stores/coding-conversations');
const { CodingConversationTimeline } = await import(
'@/pages/Chat/CodingConversationTimeline'
);
const base = createProductSnapshot('conversation-persisted-quota-failure', 1);
const snapshot = {
...base,
run: { status: 'idle' as const },
nodes: [{
kind: 'notice' as const,
id: 'persisted-quota-notice',
code: 'CODING_PROVIDER_QUOTA_EXHAUSTED',
level: 'error' as const,
message: '词元点数余额不足,请充值后重试。',
}],
};
codingConversationStore.getState().applySnapshotEvent({
type: 'snapshot',
conversationId: 'conversation-persisted-quota-failure',
workerGeneration: 1,
seq: snapshot.cursor.seq,
snapshot,
});
render(<CodingConversationTimeline conversationId="conversation-persisted-quota-failure" />);
expect(screen.getByTestId('coding-process-group').querySelector('summary'))
.toHaveTextContent('词元点数余额不足,请充值后重试。');
});
it('windows long timelines, loads near the top, and preserves the scroll anchor', async () => {
const { codingConversationStore } = await import('@/stores/coding-conversations');
const { CodingConversationTimeline } = await import(
'@/pages/Chat/CodingConversationTimeline'
@@ -659,8 +735,17 @@ describe('CodingConversationTimeline', () => {
expect(screen.queryByText('Notice 0')).not.toBeInTheDocument();
expect(screen.getByText('Notice 30')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '加载更早内容' }));
expect(screen.getByText('Notice 0')).toBeInTheDocument();
const timeline = screen.getByTestId('coding-conversation-timeline');
Object.defineProperty(timeline, 'scrollHeight', {
configurable: true,
get: () => screen.queryByText('Notice 0') ? 1_600 : 1_000,
});
timeline.scrollTop = 0;
fireEvent.scroll(timeline);
await waitFor(() => expect(screen.getByText('Notice 0')).toBeInTheDocument());
expect(timeline.scrollTop).toBe(600);
});
it('does not commit the selected timeline when a hidden Conversation streams', async () => {