Files
dsh-context/tests/client/components/timingStrip.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

171 lines
8.6 KiB
TypeScript

import { createElement as h } from 'react'
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import type { TimingSpan } from '../../../src/shared/types'
import { makeTimingStrip } from '../../../src/client/components/timingStrip'
import { makeKit, mount, query, queryAll, hover, unhover } from '../helpers/kit'
const kit = makeKit()
const kitZh = makeKit('zh')
const Strip = makeTimingStrip(kit)
const StripZh = makeTimingStrip(kitZh)
/** Local HH:MM:SS exactly as the kit's fmtTime renders it (a timezone-free assertion). */
const timeOf = (ms: number): string => new Date(ms).toLocaleTimeString('en-GB', { hour12: false })
// A 50-second session window (epoch 1s → 51s) holding 40s of ACTIVE time: the three decode slices, then tools and overhead — with 5s idle
// gaps between them (21s → 26s and 41s → 46s) that must take NO track on the strip.
const SPANS: TimingSpan[] = [
{ kind: 'ttft', start: 1_000, end: 6_000 },
{ kind: 'reasoning', start: 6_000, end: 16_000 },
{ kind: 'text', start: 16_000, end: 21_000 },
{ kind: 'tools', start: 26_000, end: 41_000 },
{ kind: 'other', start: 46_000, end: 51_000 },
]
const segsOf = (container: HTMLElement): HTMLElement[] => queryAll(container, '.lc-tstrip-seg')
const leftsOf = (container: HTMLElement): number[] => segsOf(container).map(s => parseFloat(s.style.left))
const widthsOf = (container: HTMLElement): number[] => segsOf(container).map(s => parseFloat(s.style.width))
describe('TimingStrip', () => {
test('every span packs by its share of the active time in occurrence order; the axis ticks the quartiles', async () => {
const m = await mount(h(Strip, { spans: SPANS }))
const segs = segsOf(m.container)
assert.equal(segs.length, 5)
// Width reads HOW LONG (5/10/5/15/5s of the 40s active total), position
// reads the running share — the idle gaps (21s→26s, 41s→46s) take no track: tools sits at 50%, not at the wall-clock 62.5%.
assert.deepEqual(leftsOf(m.container), [0, 12.5, 37.5, 50, 87.5])
assert.deepEqual(widthsOf(m.container), [12.5, 25, 12.5, 37.5, 12.5])
assert.deepEqual(segs.map(s => s.style.background), [
'var(--color-blue-500)',
'var(--color-violet-500)',
'var(--color-pink-500)',
'var(--color-teal-500)',
'var(--color-slate-400)',
])
// The axis: zero, the three quartiles, and the full span of the 40s ACTIVE total (not the 50s wall window).
const ticks = queryAll(m.container, '.lc-tstrip-tick')
assert.deepEqual(ticks.map(t => t.textContent), ['0', '10.0s', '20.0s', '30.0s', '40.0s'])
assert.deepEqual(ticks.map(t => (t as HTMLElement).style.left), ['0%', '25%', '50%', '75%', '100%'])
assert.ok(ticks[0].className.includes('lc-tstrip-tick-first'))
assert.ok(!ticks[2].className.includes('lc-tstrip-tick-first') && !ticks[2].className.includes('lc-tstrip-tick-last'))
assert.ok(ticks[4].className.includes('lc-tstrip-tick-last'))
assert.equal(query(m.container, '.lc-tstrip').getAttribute('role'), 'img')
assert.equal(query(m.container, '.lc-bar-tip').className, 'lc-tip lc-bar-tip')
await m.unmount()
})
test('the grow-in rides ONE wrapper, never a per-band animation (a packed strip must not storm the compositor)', async () => {
const spans: TimingSpan[] = []
for (let i = 0; i < 300; i++) spans.push({ kind: 'tools', start: i * 1_000, end: i * 1_000 + 500 })
const m = await mount(h(Strip, { spans }))
const wrapper = queryAll(m.container, '.lc-tstrip-fill')
assert.equal(wrapper.length, 1)
assert.ok(wrapper[0].className.includes('animate-lc-stacked-in'))
assert.ok(wrapper[0].className.includes('motion-reduce:animate-none'))
// Every band sits INSIDE the wrapper (their percent offsets keep the bar's own box) and carries no animation of its own.
assert.equal(wrapper[0].querySelectorAll('.lc-tstrip-seg').length, 300)
assert.deepEqual(queryAll(m.container, '.lc-tstrip-seg').filter(s => /animate-/.test(s.className)), [])
await m.unmount()
})
test('a span hover floats its tip — slice label, true duration, start instant — and relays the kind; leaving clears both', async () => {
let relayed: string | null = 'unset'
const m = await mount(h(Strip, { spans: SPANS, hoverKey: null, onHoverKey: (k) => { relayed = k } }))
await hover(segsOf(m.container)[1])
assert.equal(relayed, 'reasoning')
const tip = query(m.container, '.lc-bar-tip')
assert.ok(tip.className.includes('lc-bar-tip-on'))
assert.equal(tip.textContent, `Thinking 10.0s · ${timeOf(6_000)}`)
// Centered on the band's rendered span: 12.5% + 25%/2 = 25%.
assert.equal((tip as HTMLElement).style.left, '25%')
await unhover(query(m.container, '.lc-tstrip-bar'))
assert.equal(relayed, null)
assert.equal(query(m.container, '.lc-bar-tip').className, 'lc-tip lc-bar-tip')
await m.unmount()
})
test('the tip pins 10% off both edges for hairline end spans', async () => {
const m = await mount(h(Strip, { spans: SPANS }))
const segs = segsOf(m.container)
await hover(segs[0])
// 6.25% center clamps to the 10% floor.
assert.equal((query(m.container, '.lc-bar-tip') as HTMLElement).style.left, '10%')
await unhover(query(m.container, '.lc-tstrip-bar'))
await hover(segs[4])
// 93.75% center clamps to the 90% ceiling.
assert.equal((query(m.container, '.lc-bar-tip') as HTMLElement).style.left, '90%')
await m.unmount()
})
test('every span of the hovered KIND lights together and dims the rest, WITHOUT floating the tip (the mirror rule)', async () => {
// A second ttft span (the next step's wait, sitting inside the wall-clock idle window — it packs all the same).
const spans: TimingSpan[] = [...SPANS, { kind: 'ttft', start: 21_000, end: 26_000 }]
const m = await mount(h(Strip, { spans, hoverKey: 'ttft' }))
const on = queryAll(m.container, '.lc-tstrip-seg-on')
assert.equal(on.length, 2)
assert.equal(on[0], segsOf(m.container)[0])
assert.equal(on[1], segsOf(m.container)[5])
assert.ok(query(m.container, '.lc-tstrip-bar').className.includes('lc-tstrip-dim'))
assert.equal(query(m.container, '.lc-bar-tip').className, 'lc-tip lc-bar-tip')
await m.unmount()
})
test('a hover key naming no painted kind leaves the strip at rest', async () => {
for (const hoverKey of ['toolarg', 'zz']) {
const m = await mount(h(Strip, { spans: SPANS, hoverKey }))
assert.ok(!query(m.container, '.lc-tstrip-bar').className.includes('lc-tstrip-dim'), hoverKey)
assert.equal(queryAll(m.container, '.lc-tstrip-seg-on').length, 0)
await m.unmount()
}
})
test('an inert strip (no relay) still floats its own tip', async () => {
const m = await mount(h(Strip, { spans: SPANS }))
await hover(segsOf(m.container)[3])
assert.equal(query(m.container, '.lc-bar-tip').textContent, `Tool runs 15.0s · ${timeOf(26_000)}`)
await unhover(query(m.container, '.lc-tstrip-bar'))
assert.equal(query(m.container, '.lc-bar-tip').className, 'lc-tip lc-bar-tip')
await m.unmount()
})
test('a hostile span — unknown kind, NaN or inverted instant — drops out whole', async () => {
const hostile: TimingSpan[] = [
{ kind: 'nope', start: 2_000, end: 4_000 } as unknown as TimingSpan,
{ kind: 'ttft', start: Number.NaN, end: 4_000 },
{ kind: 'ttft', start: 2_000, end: Number.NaN },
{ kind: 'ttft', start: 4_000, end: 2_000 },
{ kind: 'ttft', start: 3_000, end: 3_000 },
...SPANS,
]
const m = await mount(h(Strip, { spans: hostile }))
assert.equal(segsOf(m.container).length, 5)
assert.deepEqual(leftsOf(m.container), [0, 12.5, 37.5, 50, 87.5])
await m.unmount()
})
test('an empty or all-hostile collection renders nothing', async () => {
const collections: TimingSpan[][] = [
[],
[
{ kind: 'nope', start: 1, end: 2 } as unknown as TimingSpan,
{ kind: 'ttft', start: Number.NaN, end: 2 },
{ kind: 'ttft', start: 2, end: 1 },
],
]
for (const spans of collections) {
const m = await mount(h(Strip, { spans }))
assert.equal(queryAll(m.container, '.lc-tstrip').length, 0, JSON.stringify(spans.length))
await m.unmount()
}
})
test('the aria label and the tip label localize', async () => {
const m = await mount(h(StripZh, { spans: SPANS }))
assert.equal(query(m.container, '.lc-tstrip').getAttribute('aria-label'), '耗时时间轴:各段按发生次序紧凑铺排,宽度为耗时占比,刻度为零、四分位与全程')
await hover(segsOf(m.container)[0])
assert.equal(query(m.container, '.lc-bar-tip').textContent, `模型等待 5.0s · ${timeOf(1_000)}`)
await m.unmount()
})
})