test(session-turn-outline): cover fold edges and deflake settle assertion
Edge-branch coverage for the preview reading bound, repeated and empty drafts, draftless turn ends, orphan-draft clearing, and same-response recommits (the CI per-file gate exercises them); the jump-settle spec now asserts only the busy lifecycle after settlement — jsdom's zero geometry made the rAF active-turn resync timing-dependent under coverage instrumentation, and the landing position contract lives in the browser e2e.
This commit is contained in:
parent
0e63841189
commit
7c58a95ebb
2 changed files with 48 additions and 1 deletions
|
|
@ -720,8 +720,11 @@ describe('ChatView', () => {
|
|||
// runs the final landing correction after the load-earlier button leaves.
|
||||
expect(first.getAttribute('aria-busy')).toBe('true')
|
||||
await act(async () => { releaseJump?.() })
|
||||
// Only the busy lifecycle is asserted after settlement: jsdom's zero
|
||||
// geometry makes the rAF active-turn resync read "at bottom" and hand the
|
||||
// mark to the last turn, so aria-current here is timing-dependent under
|
||||
// instrumentation; the landing position contract lives in the browser e2e.
|
||||
expect(first.getAttribute('aria-busy')).toBeNull()
|
||||
expect(first.getAttribute('aria-current')).toBe('true')
|
||||
})
|
||||
|
||||
it('hands a windowless tool result to the Tool seat with an empty tool name', () => {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,50 @@ describe('turn outline projection unit', () => {
|
|||
expect(changes.at(-1)?.last?.response).toBe('draft two')
|
||||
})
|
||||
|
||||
it('keeps quiet on a draftless turn end and an empty in-turn prompt', async () => {
|
||||
const { ctx, session } = await harness(true)
|
||||
session.append('turn/start', { turn: 1 })
|
||||
// Whitespace-only prompt text normalizes to nothing: the entry stays unlabeled.
|
||||
appendPrompt(session, ' \t ')
|
||||
endTurn(session, 1)
|
||||
expect(outlineOf(ctx, session)).toEqual([{ turn: 1, seq: 0, prompt: '', response: '' }])
|
||||
})
|
||||
|
||||
it('bounds preview reading and keeps repeated or empty drafts quiet (fabricated envelopes)', () => {
|
||||
const def = turnOutlineProjectionDefinition
|
||||
const assistant = (blocks: readonly unknown[]): SessionEvent => ({
|
||||
type: 'assistant/message',
|
||||
seq: 9,
|
||||
time: 0,
|
||||
data: { message: { content: blocks } },
|
||||
}) as unknown as SessionEvent
|
||||
const base: TurnOutlineState = { turns: [{ turn: 1, seq: 0, prompt: 'p', response: '' }], draft: '' }
|
||||
// Non-text blocks are skipped; whitespace-heavy short blocks cross the raw
|
||||
// reading bound early, so the collapsed (short) draft still marks the
|
||||
// unread remainder with an ellipsis.
|
||||
const airy = Array.from({ length: 40 }, (_, index) => ({ type: 'text', text: `w${String(index)}${' '.repeat(20)}` }))
|
||||
const buffered = def.apply(base, assistant([{ type: 'tool-call' }, ...airy]))
|
||||
expect(buffered.draft.startsWith('w0 w1 ')).toBe(true)
|
||||
expect(buffered.draft.endsWith('…')).toBe(true)
|
||||
expect(buffered.draft.length).toBeLessThan(120)
|
||||
// The same draft again, or a text-free message, changes nothing.
|
||||
expect(def.apply(buffered, assistant([{ type: 'tool-call' }, ...airy]))).toBe(buffered)
|
||||
expect(def.apply(buffered, assistant([{ type: 'text', text: ' ' }]))).toBe(buffered)
|
||||
// A draft with no entry to commit into clears itself at the boundary…
|
||||
const end = {
|
||||
type: 'turn/end',
|
||||
seq: 11,
|
||||
time: 0,
|
||||
data: { turn: 1, reason: { kind: 'completed' } },
|
||||
} as unknown as SessionEvent
|
||||
expect(def.apply({ turns: [], draft: 'orphan' }, end)).toEqual({ turns: [], draft: '' })
|
||||
// …and a re-settled identical response keeps the entries' identity.
|
||||
const settled: TurnOutlineState = { turns: [{ turn: 1, seq: 0, prompt: 'p', response: 'done' }], draft: 'done' }
|
||||
const recommitted = def.apply(settled, end)
|
||||
expect(recommitted.turns).toBe(settled.turns)
|
||||
expect(recommitted.draft).toBe('')
|
||||
})
|
||||
|
||||
it('skips a boundary that does not advance the turn number (fabricated envelope)', () => {
|
||||
const state: TurnOutlineState = { turns: [{ turn: 2, seq: 5, prompt: 'kept', response: '' }], draft: '' }
|
||||
const regressive = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue