Files
dsh-context/tests/client/components/contextModal.spec.ts
T
bowenliang123 87a2893190 refactor(comments): clean up the test-suite comments to the guideline
Apply the AGENTS.md comments guideline to every .ts under tests/: remove
what the code already expresses, keep only what it cannot.

Removed: spec-header coverage inventories the describe/test tree already
names; comments restating the next line's assertion or its message; helper
JSDoc whose body already says it; within-file duplicates; five stale
claims (trendChart's "six aggregated turns" against agg.length === 8,
agentGraph's "six categories" against CATS' seven, fold-timing's "cheapest
call LAST" against a loop that feeds it first, fold-surface's unreachable
plain-JSON note, contextView's "two turns" for a one-turn fixture).

Kept and condensed: the projection-freeze class, strict wire/state-schema
hazards, retention and pricing windows, pairing rules, hostile-input
rationale, fixture and expectation-array ground truth, jsdom mechanics,
and issue-numbered regressions.

Comment lines 2604 -> 2014 (-22%); 85 module headers kept, 30 coverage
inventories dropped; every surviving comment line is within the 140-column
limit, with 36 wrapped lines joined onto fuller ones.

No logic, string, identifier, import or directive changed: a comment-strip
gate proves each file's comment-free code is byte-identical to HEAD, and a
line-level proof pairs all 845 changed lines with an unchanged code part.
pnpm run lint && pnpm run test (106 files / 2061 tests, per-file 100%
coverage) && pnpm run build all pass.
2026-10-10 00:50:55 +08:00

523 lines
22 KiB
TypeScript

import { act, createElement as h, useSyncExternalStore } from 'react'
import assert from 'node:assert/strict'
import { afterEach, describe, test, vi } from 'vitest'
import { makeContextModal } from '../../../src/client/components/contextModal'
import { resetTimelineDetailStores } from '../../../src/client/timelineSource'
import { modalStoreOf, setPendingConsume, takePendingConsume } from '../../../src/client/modalStore'
import { watchHistoryFaces } from '../../../src/client/historyPage'
import { createContextSettings } from '../../../src/client/settings'
import type { ContextTimeline } from '../../../src/shared/types'
import { DICT_EN } from '../../../src/client/i18n'
import { TestClientCtx, TestSessions, asClientCtx } from '../helpers/harness'
import { click, flush, hover, keydown, makeKit, mount, query, queryAll, text, unhover, until } from '../helpers/kit'
const kit = makeKit()
const settings = createContextSettings()
function timeline(over: Record<string, unknown> = {}): ContextTimeline {
return {
ok: true,
current: { system: 1, tools: 2, user: 3, inject: 4, skill: 0, assistant: 5, tool: 6, total: 21 },
requests: [],
events: [],
nodes: [],
droppedNodes: 0,
archive: [],
...over,
} as ContextTimeline
}
function boundModalHook(sessionId: string) {
const store = modalStoreOf(sessionId)
return (sel: (open: boolean) => boolean): boolean =>
useSyncExternalStore(store.subscribe, () => sel(store.getSnapshot()))
}
const OPEN = (): boolean => true
afterEach(() => {
vi.restoreAllMocks()
vi.unstubAllGlobals()
// The cold-start share (timelineSource.ts) is page-lifetime per session:
// drop it so one test's cold read never leaks its state into a later test that reuses the same session id.
resetTimelineDetailStores()
})
describe('ContextModal', () => {
test('renders null without a useContextModal hook or while closed', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m1 = await mount(h(ContextModal, { sessionId: 'sm-none', useProjection: () => timeline() }))
assert.equal(m1.container.childElementCount, 0)
await m1.unmount()
const m2 = await mount(h(ContextModal, {
sessionId: 'sm-closed',
useContextModal: () => false,
useProjection: () => timeline(),
}))
assert.equal(m2.container.childElementCount, 0)
await m2.unmount()
})
test('open with no projection hook (or an empty one) shows the loading view', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m1 = await mount(h(ContextModal, { sessionId: 'sm-load1', useContextModal: OPEN }))
assert.ok(text(m1.container).includes(DICT_EN.loading))
assert.ok(m1.container.querySelector('.lc-modal-card') !== null)
await m1.unmount()
const m2 = await mount(h(ContextModal, {
sessionId: 'sm-load2',
useContextModal: OPEN,
useProjection: () => undefined,
}))
assert.ok(text(m2.container).includes(DICT_EN.loading))
await m2.unmount()
})
test('a cold read that settles without data surfaces the retryable failure, not a stall', async () => {
vi.stubGlobal('fetch', () => Promise.resolve({ ok: false, status: 500, json: async () => null } as Response))
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m = await mount(h(ContextModal, {
sessionId: 'sm-cold-fail',
useContextModal: OPEN,
useProjection: () => undefined,
}))
await until(() => text(m.container).includes(DICT_EN['detail.loadFailed']), 'the failure note never surfaced')
assert.ok(!text(m.container).includes(DICT_EN.loading), 'no spinner remains once failed')
await act(async () => {
const hit = queryAll(m.container, 'button').find(b => text(b) === DICT_EN['detail.loadFailed'])
if (hit === undefined) throw new Error('retry button not found')
hit.click()
})
await m.unmount()
})
test('a corrupt timeline projection is sanitized and still renders the composition', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m = await mount(h(ContextModal, {
sessionId: 'sm-garbage',
useContextModal: OPEN,
useProjection: (key: string) =>
key === 'contextTimeline' ? { current: 'junk', requests: 'nope', nodes: 7, archive: null } : undefined,
}))
assert.ok(!text(m.container).includes(DICT_EN.loading))
assert.ok(text(m.container).includes(DICT_EN['overview.title']))
assert.ok(m.container.querySelector('.lc-br-cats') !== null)
await m.unmount()
})
test('full render: subtitle variants and hover link through the composed browser', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const projections: Record<string, unknown> = {
contextTimeline: timeline({
model: 'deepseek-v4-flash',
provider: 'deepseek',
}),
contextPressure: { projectedTokens: 100, contextWindow: 128000 },
contextBreakdown: { systemTokens: 1, toolsTokens: 2, messageTokens: 18 },
contextHeaders: {
headers: [{ seq: 1, time: 0, system: 'SYS', tools: [{ name: 'bash', tokens: 12, description: 'run' }] }],
},
}
const m = await mount(h(ContextModal, {
sessionId: 'sm-full',
useContextModal: OPEN,
useProjection: (key: string) => projections[key],
}))
assert.ok(text(m.container).includes('deepseek-v4-flash · deepseek'))
// Hovering a composition segment flows into hoverCat (dim + legend chip).
const compCard = query(m.container, '.lc-modal-card .lc-card')
const seg = query(compCard, '.lc-stacked-seg')
await hover(seg)
assert.ok(query(compCard, '.lc-stacked').className.includes('lc-stacked-dim'))
assert.ok(queryAll(compCard, '.lc-chip').some(c => c.className.includes('lc-chip-on')))
await unhover(query(compCard, '.lc-stacked'))
assert.ok(!query(compCard, '.lc-stacked').className.includes('lc-stacked-dim'))
await m.unmount()
})
test('subtitle degrades to the model alone, then to nothing', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m1 = await mount(h(ContextModal, {
sessionId: 'sm-sub1',
useContextModal: OPEN,
useProjection: (key: string) => key === 'contextTimeline' ? timeline({ model: 'only-model' }) : undefined,
}))
assert.ok(text(m1.container).includes('only-model'))
assert.ok(!text(m1.container).includes('only-model ·'))
await m1.unmount()
const m2 = await mount(h(ContextModal, {
sessionId: 'sm-sub2',
useContextModal: OPEN,
useProjection: (key: string) => key === 'contextTimeline' ? timeline() : undefined,
}))
const card = query(m2.container, '.lc-modal-card .lc-card')
assert.equal(card.querySelector('.lc-card-sub'), null)
await m2.unmount()
})
test('backdrop click without a sessionId is a no-op', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m = await mount(h(ContextModal, { useContextModal: OPEN, useProjection: () => undefined }))
await click(query(m.container, '.lc-modal-backdrop'))
assert.ok(m.container.querySelector('.lc-modal-backdrop') !== null)
assert.equal(sessions.bails.length, 0)
await m.unmount()
})
test('clicking the card itself does not close (stopPropagation)', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m = await mount(h(ContextModal, { sessionId: 'sm-card', useContextModal: OPEN, useProjection: () => undefined }))
await click(query(m.container, '.lc-modal-card'))
assert.ok(m.container.querySelector('.lc-modal-backdrop') !== null)
assert.equal(sessions.bails.length, 0)
await m.unmount()
})
test('close flips the store and bails a pending consume through the session scope', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-close'
modalStoreOf(sid).set(true)
setPendingConsume(sid, { kind: 'bare-token', token: '/context' })
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
assert.ok(m.container.querySelector('.lc-modal-backdrop') !== null)
await click(query(m.container, '.lc-modal-close'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.equal(m.container.querySelector('.lc-modal-backdrop'), null)
assert.equal(sessions.bails.length, 1)
assert.equal(sessions.bails[0].event, 'slash/input-consume-token')
assert.deepEqual(sessions.bails[0].payload, { guard: { kind: 'bare-token', token: '/context' } })
assert.equal(takePendingConsume(sid), undefined)
await m.unmount()
})
test('close without a pending consume sets the store but bails nothing', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-noguard'
modalStoreOf(sid).set(true)
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await click(query(m.container, '.lc-modal-backdrop'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.equal(sessions.bails.length, 0)
await m.unmount()
})
test('a pending consume without the sessions service closes quietly', async () => {
const ctx = new TestClientCtx()
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-nosvc'
modalStoreOf(sid).set(true)
setPendingConsume(sid, { kind: 'bare-token', token: '/context' })
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await click(query(m.container, '.lc-modal-backdrop'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.equal(m.container.querySelector('.lc-modal-backdrop'), null)
await m.unmount()
})
test('a pending consume whose scope is gone closes quietly', async () => {
const scoped: string[] = []
const sessions = {
scope: (id: string) => {
scoped.push(id)
return undefined
},
}
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-noscope'
modalStoreOf(sid).set(true)
setPendingConsume(sid, { kind: 'span', span: { start: 0, end: 8, draftRev: 3 } })
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await click(query(m.container, '.lc-modal-backdrop'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.deepEqual(scoped, [sid])
await m.unmount()
})
test('the mask docks between the frame sidebars, follows template rewrites, and disconnects on close', async () => {
vi.stubGlobal('innerWidth', 1280)
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-dock'
// The shell frame: the plugin matches its INLINE grid template, the one
// host anchor carried by every supported baseline (dockMeasure.ts).
const frame = document.createElement('div')
frame.style.gridTemplateColumns = '280px minmax(400px, 1fr) minmax(0px, 360px)'
const seat = document.createElement('div')
frame.appendChild(seat)
document.body.appendChild(frame)
const disconnectSpy = vi.spyOn(MutationObserver.prototype, 'disconnect')
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
// Relocate the mount container under the frame before opening, so the measure walks the real ancestor chain.
seat.appendChild(m.container)
await act(async () => {
modalStoreOf(sid).set(true)
})
const backdrop = query(m.container, '.lc-modal-backdrop')
assert.equal(backdrop.style.left, '280px')
assert.equal(backdrop.style.right, '360px')
// A sidebar rewrite while open (the right panel closed here) is followed through the observer on both edges.
await act(async () => {
frame.style.gridTemplateColumns = '56px minmax(0px, 1fr) minmax(0px, 0px)'
})
await flush()
const rewritten = query(m.container, '.lc-modal-backdrop')
assert.equal(rewritten.style.left, '56px')
assert.equal(rewritten.style.right, '0px')
disconnectSpy.mockClear()
await click(query(m.container, '.lc-modal-backdrop'))
assert.equal(m.container.querySelector('.lc-modal-backdrop'), null)
assert.equal(disconnectSpy.mock.calls.length, 1)
frame.remove()
await m.unmount()
})
test('without a frame ancestor the mask stays full-viewport', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const m = await mount(h(ContextModal, {
sessionId: 'sm-noframe',
useContextModal: OPEN,
useProjection: () => undefined,
}))
const backdrop = query(m.container, '.lc-modal-backdrop')
assert.equal(backdrop.style.left, '0px')
assert.equal(backdrop.style.right, '0px')
await m.unmount()
})
test('Escape closes; other keys do not; the listener is removed on close', async () => {
const sessions = new TestSessions()
const ctx = new TestClientCtx({ services: { sessions } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-esc'
modalStoreOf(sid).set(true)
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await keydown('a')
assert.ok(m.container.querySelector('.lc-modal-backdrop') !== null)
await keydown('Escape')
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.equal(m.container.querySelector('.lc-modal-backdrop'), null)
// Listener removed on close: another Escape must not run close() again —
// with a pending consume armed, a live listener would bail.
setPendingConsume(sid, { kind: 'bare-token', token: '/context' })
await keydown('Escape')
assert.equal(sessions.bails.length, 0)
assert.notEqual(takePendingConsume(sid), undefined)
await m.unmount()
})
test('closing restores focus to the previously focused element', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-focus'
const btn = document.createElement('button')
document.body.appendChild(btn)
btn.focus()
assert.equal(document.activeElement, btn)
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await act(async () => {
modalStoreOf(sid).set(true)
})
const closeBtn = query(m.container, '.lc-modal-close')
closeBtn.focus()
assert.equal(document.activeElement, closeBtn)
await click(closeBtn)
assert.equal(document.activeElement, btn)
btn.remove()
await m.unmount()
})
test('closing skips the focus restore when the previous element left the document', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-focus-gone'
const btn = document.createElement('button')
document.body.appendChild(btn)
btn.focus()
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
await act(async () => {
modalStoreOf(sid).set(true)
})
btn.remove()
await click(query(m.container, '.lc-modal-close'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.ok(!document.contains(btn))
await m.unmount()
})
test('no focus bookkeeping when nothing was focused (activeElement not an element)', async () => {
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const sid = 'sm-focus-null'
modalStoreOf(sid).set(true)
// Environment control (not a plugin fake): jsdom always reports body;
// the harness browser can report null before the body exists.
vi.spyOn(document, 'activeElement', 'get').mockReturnValue(null)
const m = await mount(h(ContextModal, {
sessionId: sid,
useContextModal: boundModalHook(sid),
useProjection: () => undefined,
}))
vi.restoreAllMocks()
await click(query(m.container, '.lc-modal-close'))
assert.equal(modalStoreOf(sid).getSnapshot(), false)
assert.equal(document.activeElement, document.body)
await m.unmount()
})
})
describe('ContextModal — the split generation', () => {
test('the modal shares the detail route: the browser lands the collections on open', async () => {
vi.stubGlobal('fetch', async () => ({
ok: true,
status: 200,
json: async () => ({
ok: true,
value: {
rev: 1,
requests: [{ seq: 1, turn: 1, step: 1, time: 1, system: 1, tools: 2, user: 3, inject: 4, assistant: 5, tool: 6, total: 21 }],
events: [],
nodes: [{ seq: 1, cat: 'assistant', tokens: 5, text: 'modal reply' }],
droppedNodes: 0,
archive: [],
},
}),
}))
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const head = timeline({
counts: { turns: 1, steps: 1, injects: 0, compactions: 0, prunes: 0 },
last: { seq: 1, total: 21, prompt: 20 },
detailRev: 1,
})
const m = await mount(h(ContextModal, {
sessionId: 'sm-slim',
useContextModal: OPEN,
useProjection: (key: string) => (key === 'contextTimeline' ? head : undefined),
}))
// The composition card paints off the head while the browser's detail note shows.
assert.ok(text(m.container).includes(DICT_EN['overview.title']))
assert.ok(text(m.container).includes(DICT_EN['detail.loading']))
await until(() => !text(m.container).includes(DICT_EN['detail.loading']), 'the modal detail never landed')
assert.equal(queryAll(m.container, '.lc-br-pick option').length, 2, 'live + the one served step')
assert.ok(text(m.container).includes('1 Item'), 'the assistant section counts the served node')
await m.unmount()
})
test('a mount that raced the declared inject heals when the face lands mid-open', async () => {
// The race: the modal mounts (and its fetchers snapshot the face) BEFORE
// `remote` composes — the degraded note shows — then the inject fires and
// the OPEN system section must auto-fetch without any user action.
const calls: { sessionId: string; throughSeq: number; beforeSeq: number }[] = []
const ctx = new TestClientCtx({ services: { sessions: new TestSessions() } })
// Armed before either service exists: the inject stays pending, the race.
watchHistoryFaces(asClientCtx(ctx))
const ContextModal = makeContextModal(asClientCtx(ctx), kit, settings)
const projections: Record<string, unknown> = {
contextTimeline: timeline(),
contextHeaders: { headers: [{ seq: 1, time: 0, systemTokens: 5, tools: [] }] },
}
const m = await mount(h(ContextModal, {
sessionId: 'sm-race',
useContextModal: OPEN,
useProjection: (key: string) => projections[key],
}))
const sysRow = queryAll(m.container, '.lc-br-cat-row').find(r => text(r).includes(DICT_EN['cat.system']))
assert.ok(sysRow !== undefined)
await click(sysRow)
assert.ok(text(m.container).includes(DICT_EN['browser.headerMetaOnly']), 'the faceless mount shows the static note')
// The face lands mid-open (cordis replays the pending inject): the modal
// re-renders, the fetcher builds, and the open epoch fetches by itself.
// The face rides INSIDE the `remote` facade; the `remote.session` service key stays hostile, as on the real host.
await act(async () => {
ctx.setService('remote.session', { get page() { throw new Error('cannot get property "remote.session" without inject') } })
ctx.setService('remote', {
session: {
page: (request: { address: { sessionId: string }; throughSeq: number; beforeSeq: number }) => {
calls.push({ sessionId: request.address.sessionId, throughSeq: request.throughSeq, beforeSeq: request.beforeSeq })
return Promise.resolve({
ok: true,
value: {
records: [{ type: 'event', event: { type: 'system/message', seq: 1, time: 1, data: { message: { content: [{ type: 'text', text: 'HEALED SYSTEM PROMPT' }] } } } }],
},
})
},
},
})
})
await until(() => text(m.container).includes('HEALED SYSTEM PROMPT'), 'the open epoch never healed after the face landed')
assert.ok(!text(m.container).includes(DICT_EN['browser.headerMetaOnly']))
assert.deepEqual(calls, [{ sessionId: 'sm-race', throughSeq: 1, beforeSeq: 2 }])
await m.unmount()
// Unload the declared slot so no face stales into the next test.
ctx.dispose()
})
})