Files
dsh-context/tests/client/viewFocus.spec.ts
T
bowenliang123 0c8312763e feat(client): jump from a chat reply to its turn in the Context tab
Add an icon to each finalized reply's assistant-actions row that opens
the Context tab pinned to that reply's turn: the click resolves the
reply's request seq off the served node seat, records it in a per-session
relay, and activates the tab; the view consumes the relay once its
projection data is in, flips the trend chart to turn bars, pins and
centers the turn's aggregate, and resets the shared scroller. Every hop
degrades quietly (unknown seat, unresolvable seq, missing tab, hostile
nodes, aged-out turn).
2026-08-29 01:44:51 +08:00

68 lines
2.7 KiB
TypeScript

// viewFocus (src/client/viewFocus.ts) — the chat→Context jump relay and the
// Context-tab activation, driven against a real jsdom tab bar.
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { activateContextTab, requestContextFocus, takeContextFocus } from '../../src/client/viewFocus'
describe('context focus relay', () => {
test('one request survives until taken, then the map entry is consumed', () => {
requestContextFocus('sv-relay', 42)
assert.equal(takeContextFocus('sv-relay'), 42)
assert.equal(takeContextFocus('sv-relay'), null, 'one-shot: a second take finds nothing')
})
test('a fresh request replaces an unconsumed one; sessions are isolated', () => {
requestContextFocus('sv-race', 7)
requestContextFocus('sv-race', 9)
assert.equal(takeContextFocus('sv-race'), 9, 'the latest click wins')
requestContextFocus('sv-other', 1)
assert.equal(takeContextFocus('sv-other'), 1)
assert.equal(takeContextFocus('sv-unknown'), null)
})
})
describe('activateContextTab', () => {
test('clicks the inactive tab matching the label, skips the active one, and reports misses', () => {
type Counted = HTMLElement & { __clicks: () => number }
const bar = document.createElement('div')
const mk = (label: string, selected: boolean): Counted => {
const b = document.createElement('button')
b.setAttribute('role', 'tab')
if (selected) b.setAttribute('aria-selected', 'true')
b.textContent = label
let clicks = 0
b.addEventListener('click', () => { clicks++ })
const counted = Object.assign(b, { __clicks: () => clicks })
bar.appendChild(counted)
return counted
}
const chat = mk('Chat', true)
const context = mk('Context', false)
const trajectory = mk('Trajectory', false)
document.body.appendChild(bar)
try {
assert.equal(activateContextTab('Context'), true)
assert.equal(context.__clicks(), 1)
assert.equal(chat.__clicks(), 0)
// Already-active: reported success without a redundant click.
context.setAttribute('aria-selected', 'true')
assert.equal(activateContextTab('Context'), true)
assert.equal(context.__clicks(), 1)
// Whitespace-padded label text still matches the trimmed comparison.
context.setAttribute('aria-selected', 'false')
context.textContent = ' Context '
assert.equal(activateContextTab('Context'), true)
assert.equal(context.__clicks(), 2)
// No tab carries the label → nothing clicked, false.
assert.equal(activateContextTab('Missing'), false)
assert.equal(trajectory.__clicks(), 0)
} finally {
bar.remove()
}
})
})