test: share native chat render cost setup (#29316)

* test: batch lint fixtures and remove repeated runtime scan

* test(cli): consolidate owner coverage and remove false decoding proof

* test(renderer): retain IPC lifecycle behavior without copied inventories

* test: share floating panel behavior suite startup

* test: retire synthetic runtime constructor probes

* test: share saved terminal title runtime setup

* test: share native chat render cost setup

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-16 02:21:29 -07:00
committed by GitHub
co-authored by OrcaWin
parent 2e97d9aa84
commit 9ee7ee79bf
2 changed files with 116 additions and 147 deletions
@@ -3,7 +3,7 @@
import '@testing-library/jest-dom/vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
// Why: these fixtures have neither inline visual owners nor sender navigation.
const unusedChatLeaves = vi.hoisted(() => ({
@@ -24,6 +24,7 @@ afterEach(() => {
expect(unusedChatLeaves.inlineVisual).not.toHaveBeenCalled()
expect(unusedChatLeaves.openSender).not.toHaveBeenCalled()
})
import type * as NativeChatWorkRunModule from '../../../../shared/native-chat-work-run'
import type * as NativeChatProseModule from '../../../../shared/native-chat-prose'
import type { NativeChatMessage } from '../../../../shared/native-chat-types'
import { projectStructuredAgentSessionMessages } from '../../../../shared/structured-agent-session-message-projection'
@@ -62,6 +63,20 @@ vi.mock('../../../../shared/native-chat-unified-patch', async (importOriginal) =
}
})
// Counts each run rebuilding its lines and thoughts: a rebuilt transcript hands every run a fresh
// member list, and only the row's memo keeps a settled run from redoing that work every frame.
const entriesCalls = vi.hoisted(() => ({ count: 0 }))
vi.mock('../../../../shared/native-chat-work-run', async (importOriginal) => {
const actual = await importOriginal<typeof NativeChatWorkRunModule>()
return {
...actual,
nativeChatWorkRunEntries: (...args: Parameters<typeof actual.nativeChatWorkRunEntries>) => {
entriesCalls.count += 1
return actual.nativeChatWorkRunEntries(...args)
}
}
})
const { NativeChatMessageList } = await import('./NativeChatMessageList')
let restoreViewport = (): void => {}
@@ -70,6 +85,12 @@ beforeAll(() => {
})
afterAll(() => restoreViewport())
afterEach(cleanup)
beforeEach(() => {
proseCalls.count = 0
patchCalls.detailed = 0
patchCalls.summary = 0
entriesCalls.count = 0
})
const TRANSCRIPT_LENGTH = 120
@@ -201,3 +222,97 @@ describe('native chat transcript re-render cost during a streaming turn', () =>
expect(screen.getByText('new')).toBeInTheDocument()
})
})
let clock = 0
function row(
id: string,
role: NativeChatMessage['role'],
blocks: NativeChatMessage['blocks'],
extra: Partial<NativeChatMessage> = {}
): NativeChatMessage {
clock += 1
return { id, role, blocks, timestamp: clock, source: 'transcript', ...extra }
}
const thought = (id: string) =>
row(id, 'reasoning', [{ type: 'text', text: `thinking ${id}` }], {
state: 'completed',
completedAt: clock + 1
})
const command = (id: string, output: string) =>
row(id, 'assistant', [
{ type: 'tool-call', name: 'Bash', input: { command: id }, state: 'completed', callId: id },
{ type: 'tool-result', output, callId: id }
])
const prompt = row('user-1', 'user', [{ type: 'text', text: 'go' }])
// Three settled runs, each closed by a reply, then the live run.
const settled = [1, 2, 3].flatMap((turn) => [
thought(`r${turn}a`),
command(`c${turn}a`, 'ok'),
thought(`r${turn}b`),
command(`c${turn}b`, 'ok'),
row(`reply-${turn}`, 'assistant', [{ type: 'text', text: `Step ${turn} done.` }])
])
const liveHead = [thought('r4'), command('c4', 'ok')]
function journal(rows: readonly NativeChatMessage[]): AgentJournalRenderItem[] {
return [
{
itemId: prompt.id,
revision: 1,
sequence: 1,
observedAt: 1,
body: { kind: 'message', role: 'user', blocks: prompt.blocks }
},
{
itemId: 'turn-1',
revision: 1,
sequence: 2,
observedAt: 2,
body: { kind: 'turn', turnId: 'turn-1', state: 'running', userItemId: prompt.id }
},
...rows.map((message, index) => ({
itemId: message.id,
revision: 1,
sequence: index + 3,
observedAt: index + 3,
body: { kind: 'message' as const, role: message.role, blocks: message.blocks }
}))
]
}
function list(tail: NativeChatMessage): React.JSX.Element {
const rows = [...settled, ...liveHead, tail]
const session: NativeChatLiveSession = {
messages: [prompt, ...rows],
status: 'working',
sessionId: 'session-1',
agent: 'codex',
hasMore: false,
loadingEarlier: false,
olderHistoryGeneration: 0,
loadEarlier: vi.fn(),
readPhase: 'ready'
}
return (
<NativeChatMessageList
session={session}
journalItems={journal(rows)}
isWorking
expandSignal={false}
/>
)
}
describe('settled work runs while the live run streams', () => {
it('rebuilds only the live run on each frame', () => {
const tail = (frame: number) => ({ ...command('c5', `line ${frame}`), id: 'c5' })
const { rerender, container } = render(list(tail(0)))
expect(container.querySelectorAll('[data-native-chat-tool-run-state]')).toHaveLength(4)
entriesCalls.count = 0
for (let frame = 1; frame <= 5; frame += 1) {
rerender(list(tail(frame)))
}
expect(entriesCalls.count).toBe(5)
})
})
@@ -1,146 +0,0 @@
// @vitest-environment happy-dom
import { cleanup, render } from '@testing-library/react'
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
// Why: these fixtures have neither inline visual owners nor sender navigation.
const unusedChatLeaves = vi.hoisted(() => ({
inlineVisual: vi.fn(() => {
throw new Error('Unexpected inline visual in transcript fixture')
}),
openSender: vi.fn(() => {
throw new Error('Unexpected sender navigation in transcript fixture')
})
}))
vi.mock('./NativeChatInlineVisual', () => ({
NativeChatInlineVisual: unusedChatLeaves.inlineVisual
}))
vi.mock('@/lib/open-agent-message-sender', () => ({
openAgentMessageSender: unusedChatLeaves.openSender
}))
afterEach(() => {
expect(unusedChatLeaves.inlineVisual).not.toHaveBeenCalled()
expect(unusedChatLeaves.openSender).not.toHaveBeenCalled()
})
import type * as NativeChatWorkRunModule from '../../../../shared/native-chat-work-run'
import type { AgentJournalRenderItem } from '../../../../shared/agent-session-journal-types'
import type { NativeChatMessage } from '../../../../shared/native-chat-types'
import type { NativeChatLiveSession } from './use-native-chat-live-session'
import { installNativeChatMessageListTestViewport } from './native-chat-message-list-test-viewport'
// Counts each run rebuilding its lines and thoughts: a rebuilt transcript hands every run a fresh
// member list, and only the row's memo keeps a settled run from redoing that work every frame.
const entriesCalls = vi.hoisted(() => ({ count: 0 }))
vi.mock('../../../../shared/native-chat-work-run', async (importOriginal) => {
const actual = await importOriginal<typeof NativeChatWorkRunModule>()
return {
...actual,
nativeChatWorkRunEntries: (...args: Parameters<typeof actual.nativeChatWorkRunEntries>) => {
entriesCalls.count += 1
return actual.nativeChatWorkRunEntries(...args)
}
}
})
const { NativeChatMessageList } = await import('./NativeChatMessageList')
let restoreViewport = (): void => {}
beforeAll(() => {
restoreViewport = installNativeChatMessageListTestViewport()
})
afterAll(() => restoreViewport())
afterEach(cleanup)
let clock = 0
function row(
id: string,
role: NativeChatMessage['role'],
blocks: NativeChatMessage['blocks'],
extra: Partial<NativeChatMessage> = {}
): NativeChatMessage {
clock += 1
return { id, role, blocks, timestamp: clock, source: 'transcript', ...extra }
}
const thought = (id: string) =>
row(id, 'reasoning', [{ type: 'text', text: `thinking ${id}` }], {
state: 'completed',
completedAt: clock + 1
})
const command = (id: string, output: string) =>
row(id, 'assistant', [
{ type: 'tool-call', name: 'Bash', input: { command: id }, state: 'completed', callId: id },
{ type: 'tool-result', output, callId: id }
])
const prompt = row('user-1', 'user', [{ type: 'text', text: 'go' }])
// Three settled runs, each closed by a reply, then the live run.
const settled = [1, 2, 3].flatMap((turn) => [
thought(`r${turn}a`),
command(`c${turn}a`, 'ok'),
thought(`r${turn}b`),
command(`c${turn}b`, 'ok'),
row(`reply-${turn}`, 'assistant', [{ type: 'text', text: `Step ${turn} done.` }])
])
const liveHead = [thought('r4'), command('c4', 'ok')]
function journal(rows: readonly NativeChatMessage[]): AgentJournalRenderItem[] {
return [
{
itemId: prompt.id,
revision: 1,
sequence: 1,
observedAt: 1,
body: { kind: 'message', role: 'user', blocks: prompt.blocks }
},
{
itemId: 'turn-1',
revision: 1,
sequence: 2,
observedAt: 2,
body: { kind: 'turn', turnId: 'turn-1', state: 'running', userItemId: prompt.id }
},
...rows.map((message, index) => ({
itemId: message.id,
revision: 1,
sequence: index + 3,
observedAt: index + 3,
body: { kind: 'message' as const, role: message.role, blocks: message.blocks }
}))
]
}
function list(tail: NativeChatMessage): React.JSX.Element {
const rows = [...settled, ...liveHead, tail]
const session: NativeChatLiveSession = {
messages: [prompt, ...rows],
status: 'working',
sessionId: 'session-1',
agent: 'codex',
hasMore: false,
loadingEarlier: false,
olderHistoryGeneration: 0,
loadEarlier: vi.fn(),
readPhase: 'ready'
}
return (
<NativeChatMessageList
session={session}
journalItems={journal(rows)}
isWorking
expandSignal={false}
/>
)
}
describe('settled work runs while the live run streams', () => {
it('rebuilds only the live run on each frame', () => {
const tail = (frame: number) => ({ ...command('c5', `line ${frame}`), id: 'c5' })
const { rerender, container } = render(list(tail(0)))
expect(container.querySelectorAll('[data-native-chat-tool-run-state]')).toHaveLength(4)
entriesCalls.count = 0
for (let frame = 1; frame <= 5; frame += 1) {
rerender(list(tail(frame)))
}
expect(entriesCalls.count).toBe(5)
})
})