Files
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

412 lines
17 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { act, createElement as h } from 'react'
import assert from 'node:assert/strict'
import { afterEach, describe, test } from 'vitest'
import {
makeDateRange,
monthLabelOf,
type DateRangeProps,
} from '../../../src/client/components/dateRange'
import type { DayRange } from '../../../src/client/overview'
import { click, hover, keydown, makeKit, mount, query, queryAll, text, type Mounted } from '../helpers/kit'
/** The specs' calendar: a Friday, so the month opens mid-week and ends mid-week. */
const TODAY = '2026-10-09'
const DateRange = makeDateRange(makeKit())
/** Every tree this spec mounted, torn down after each test (the panel portals to <body>). */
const mounted: Mounted[] = []
function panel(): HTMLElement | null {
return document.querySelector<HTMLElement>('.lc-dp-panel')
}
function trigger(m: Mounted): HTMLElement {
return query(m.container, '.lc-dp-trigger')
}
function cell(m: Mounted, key: string): HTMLElement {
const found = queryAll(document.body, '.lc-dp-cell').find(el => el.getAttribute('aria-label') === key)
if (found === undefined) throw new Error(`no day cell: ${key}`)
return found
}
/** Dispatch a raw event on a window listener, act-wrapped like the kit's own relays. */
async function fireWindow(ev: Event): Promise<void> {
await act(async () => { window.dispatchEvent(ev) })
}
interface Harness {
m: Mounted
changes: (DayRange | null)[]
/** Re-render with new props, as a controlled parent would. */
set: (next: Partial<DateRangeProps>) => Promise<void>
}
async function open(over: Partial<DateRangeProps> = {}): Promise<Harness> {
const changes: (DayRange | null)[] = []
let props: DateRangeProps = {
value: null,
picked: false,
onChange: (range) => { changes.push(range) },
today: TODAY,
locale: 'en',
...over,
}
const m = await mount(h(DateRange, props))
mounted.push(m)
return {
m,
changes,
async set(next) {
props = { ...props, ...next }
await m.update(h(DateRange, props))
},
}
}
async function toggle(m: Mounted): Promise<void> {
await click(trigger(m))
}
afterEach(async () => {
while (mounted.length > 0) await mounted.pop()?.unmount()
})
describe('monthLabelOf', () => {
test('the panel header names the month in the active locale', () => {
assert.match(monthLabelOf('2026-10', 'en'), /2026/)
assert.match(monthLabelOf('2026-10', 'zh'), /2026/)
assert.equal(monthLabelOf('not-a-month', 'en'), 'not-a-month', 'a key that will not convert falls back to itself')
})
})
describe('the trigger', () => {
test('carries the scope’s two ends, or the two placeholders', async () => {
const { m, set } = await open()
const halves = () => queryAll(m.container, '.lc-dp-half').map(el => el.textContent)
assert.deepEqual(halves(), ['Start date', 'End date'], 'an unbounded scope has no span to print')
assert.match(query(m.container, '.lc-dp-trigger').getAttribute('aria-label') ?? '', /custom date range/)
assert.equal(trigger(m).getAttribute('aria-expanded'), 'false')
await toggle(m)
assert.equal(trigger(m).getAttribute('aria-expanded'), 'true')
assert.ok(panel() !== null, 'the panel is portaled to the body')
// A preset's window rides the chip too — linked, but never in the brand
// voice and with no way back, because a preset is not a pick.
await set({ value: { from: '2026-09-10', to: '2026-10-09' } })
assert.deepEqual(halves(), ['2026-09-10', '2026-10-09'])
assert.ok(!trigger(m).className.includes('lc-dp-trigger-on'))
assert.equal(queryAll(panel()!, '.lc-dp-clear').length, 0, 'nothing to clear while a preset is in charge')
await set({ picked: true })
assert.match(trigger(m).className, /lc-dp-trigger-on/)
assert.equal(queryAll(panel()!, '.lc-dp-clear').length, 1, 'a pick grows the way back')
})
test('a press while open closes it again', async () => {
const { m } = await open()
await toggle(m)
await toggle(m)
assert.ok(panel() === null)
})
})
describe('picking a range', () => {
test('two clicks in order commit the pair and close the panel', async () => {
const { m, changes } = await open()
await toggle(m)
assert.match(text(query(panel()!, '.lc-dp-hint')), /start date/)
await click(cell(m, '2026-10-03'))
assert.deepEqual(changes, [], 'the first click only anchors')
assert.match(text(query(panel()!, '.lc-dp-hint')), /end date/)
assert.match(cell(m, '2026-10-03').className, /lc-dp-edge/, 'the anchor is the start until the end lands')
assert.match(text(query(m.container, '.lc-dp-half')), /2026-10-03/, 'the head reads the anchor as it lands')
await hover(cell(m, '2026-10-09'))
assert.match(text(queryAll(m.container, '.lc-dp-half')[1]), /2026-10-09/, 'the head previews the end under the cursor')
await click(cell(m, '2026-10-09'))
assert.deepEqual(changes, [{ from: '2026-10-03', to: '2026-10-09' }])
assert.ok(panel() === null, 'the pick answers the page immediately')
})
test('the end may come first: the pair is sorted, never inverted', async () => {
const { m, changes } = await open()
await toggle(m)
await click(cell(m, '2026-10-09'))
await click(cell(m, '2026-10-03'))
assert.deepEqual(changes, [{ from: '2026-10-03', to: '2026-10-09' }])
})
test('the same day twice is a one-day range', async () => {
const { m, changes } = await open()
await toggle(m)
await click(cell(m, '2026-10-05'))
await click(cell(m, '2026-10-05'))
assert.deepEqual(changes, [{ from: '2026-10-05', to: '2026-10-05' }])
})
test('the band previews under the cursor once the start is anchored', async () => {
const { m } = await open()
await toggle(m)
await click(cell(m, '2026-10-03'))
assert.match(cell(m, '2026-10-03').className, /lc-dp-edge/, 'the anchor alone lights up as an edge')
await hover(cell(m, '2026-10-07'))
assert.match(cell(m, '2026-10-05').className, /lc-dp-in/, 'the band runs to the hovered day')
assert.match(cell(m, '2026-10-07').className, /lc-dp-edge/, 'the hovered day previews where the end would land')
await hover(cell(m, '2026-10-01'))
assert.match(cell(m, '2026-10-02').className, /lc-dp-in/, 'hovering back past the anchor previews the swapped band')
assert.match(cell(m, '2026-10-01').className, /lc-dp-edge/, 'the swap moves the previewed end to the near side')
})
test('a hover before any anchor previews nothing', async () => {
const { m } = await open()
await toggle(m)
const before = cell(m, '2026-10-07').className
await hover(cell(m, '2026-10-07'))
assert.equal(cell(m, '2026-10-07').className, before)
})
test('the live scope is highlighted when the panel opens, and a click restarts the pick', async () => {
const { m } = await open({ value: { from: '2026-10-03', to: '2026-10-06' }, picked: true })
await toggle(m)
assert.match(cell(m, '2026-10-04').className, /lc-dp-in/)
assert.match(cell(m, '2026-10-03').className, /lc-dp-edge/)
assert.match(cell(m, '2026-10-06').className, /lc-dp-edge/)
assert.match(cell(m, '2026-10-09').className, /lc-dp-today/)
assert.match(cell(m, '2026-10-07').textContent ?? '', /^7$/, 'each cell prints its own day of the month')
// Clicking anywhere restarts the pick, so the old band releases.
await click(cell(m, '2026-10-08'))
assert.equal(cell(m, '2026-10-04').className.includes('lc-dp-in'), false)
assert.match(text(query(panel()!, '.lc-dp-hint')), /end date/)
})
test('a preset’s window is the band on show, with no second copy of its dates', async () => {
const { m } = await open({ value: { from: '2026-10-03', to: '2026-10-09' } })
await toggle(m)
assert.match(cell(m, '2026-10-05').className, /lc-dp-in/, 'the preset’s own days show as the live band')
assert.equal(queryAll(panel()!, '.lc-dp-box').length, 0, 'the chip above is the only place the ends are printed')
assert.equal(queryAll(panel()!, '.lc-dp-pair').length, 0)
})
test('a future day is inert and a blank pad cell is inert', async () => {
const { m, changes } = await open()
await toggle(m)
const future = cell(m, '2026-10-10')
assert.match(future.className, /lc-dp-future/)
assert.equal(future.getAttribute('aria-disabled'), 'true')
await click(future)
assert.deepEqual(changes, [])
assert.match(text(query(panel()!, '.lc-dp-hint')), /start date/, 'the future day did not anchor')
await hover(future)
assert.ok(queryAll(panel()!, '.lc-dp-blank').length > 0, 'the month pads its weeks')
})
test('the clear button hands the scope back to the presets', async () => {
const { m, changes } = await open({ value: { from: '2026-10-03', to: '2026-10-06' }, picked: true })
await toggle(m)
await click(query(panel()!, '.lc-dp-clear'))
assert.deepEqual(changes, [null])
})
test('no clear button while nothing is picked', async () => {
const { m } = await open()
await toggle(m)
assert.equal(queryAll(panel()!, '.lc-dp-clear').length, 0)
})
})
describe('the month nav', () => {
test('steps a month at a time and stops at this month', async () => {
const { m } = await open()
await toggle(m)
assert.match(query(panel()!, '.lc-dp-month').textContent ?? '', /2026/)
assert.equal(queryAll(panel()!, '.lc-dp-navbtn')[1].hasAttribute('disabled'), true, 'the panel opens on this month, so next is spent')
await click(queryAll(panel()!, '.lc-dp-navbtn')[0])
assert.equal(queryAll(panel()!, '.lc-dp-navbtn')[1].hasAttribute('disabled'), false, 'back a month, next serves again')
await click(queryAll(panel()!, '.lc-dp-navbtn')[1])
assert.equal(queryAll(panel()!, '.lc-dp-navbtn')[1].hasAttribute('disabled'), true, 'no month past this one')
assert.equal(queryAll(panel()!, '.lc-dp-cell').length > 0, true)
})
test('the panel always comes back on this month, not the browsed one', async () => {
const { m } = await open()
await toggle(m)
await click(queryAll(panel()!, '.lc-dp-navbtn')[0])
const away = query(panel()!, '.lc-dp-month').textContent
await toggle(m)
await toggle(m)
assert.notEqual(query(panel()!, '.lc-dp-month').textContent, away, 'the panel came back on today, not the browsed month')
})
test('the earliest representable month cannot step back', async () => {
const { m } = await open({ today: '1970-01-05' })
await toggle(m)
await click(queryAll(panel()!, '.lc-dp-navbtn')[0])
assert.match(query(panel()!, '.lc-dp-month').textContent ?? '', /1970/)
})
test('a malformed today opens no panel and claims no expansion', async () => {
const { m } = await open({ today: 'garbage' })
await toggle(m)
assert.ok(panel() === null)
assert.equal(trigger(m).getAttribute('aria-expanded'), 'false', 'nothing rendered, so nothing reported as open')
})
})
describe('the keyboard cursor', () => {
test('arrows walk the shown month and Enter picks the day under them', async () => {
const { m, changes } = await open()
await toggle(m)
const grid = query(panel()!, '.lc-dp-grid')
assert.equal(grid.getAttribute('aria-activedescendant'), 'lc-dp-day')
assert.match(cell(m, '2026-10-01').className, /lc-dp-cursor/, 'the cursor opens on the month’s first day')
await keydown('ArrowRight', grid)
assert.match(cell(m, '2026-10-02').className, /lc-dp-cursor/)
await keydown('ArrowDown', grid)
assert.match(cell(m, '2026-10-09').className, /lc-dp-cursor/)
await keydown('ArrowUp', grid)
assert.match(cell(m, '2026-10-02').className, /lc-dp-cursor/)
await keydown('ArrowLeft', grid)
assert.match(cell(m, '2026-10-01').className, /lc-dp-cursor/)
await keydown('Enter', grid)
await keydown(' ', grid)
assert.deepEqual(changes, [{ from: '2026-10-01', to: '2026-10-01' }], 'Enter anchors, Space closes the pair')
})
test('the cursor never leaves the shown month or enters the future', async () => {
const { m } = await open()
await toggle(m)
const grid = () => query(panel()!, '.lc-dp-grid')
// The 1st is a Thursday; a week up from it would leave October.
await keydown('ArrowUp', grid())
assert.match(cell(m, '2026-10-01').className, /lc-dp-cursor/, 'the cursor stays inside the shown month')
// Rightward it walks, and today is the last stop — the future is not a seat.
for (let i = 0; i < 40; i++) await keydown('ArrowRight', grid())
assert.match(cell(m, TODAY).className, /lc-dp-cursor/)
await keydown('ArrowRight', grid())
assert.match(cell(m, TODAY).className, /lc-dp-cursor/)
await click(queryAll(panel()!, '.lc-dp-navbtn')[0])
assert.match(cell(m, '2026-09-01').className, /lc-dp-cursor/, 'a month step re-seats the cursor on the 1st')
await keydown('ArrowDown', grid())
assert.match(cell(m, '2026-09-08').className, /lc-dp-cursor/)
await keydown('ArrowDown', grid())
assert.match(cell(m, '2026-09-15').className, /lc-dp-cursor/)
})
test('any other key is left to the page', async () => {
const { m } = await open()
await toggle(m)
const grid = query(panel()!, '.lc-dp-grid')
await keydown('Tab', grid)
await keydown('a', grid)
assert.match(cell(m, '2026-10-01').className, /lc-dp-cursor/)
})
})
describe('the panel seat', () => {
test('sits under its trigger, inside the viewport, and flips when it would fall off', async () => {
// jsdom measures every element as 0; drive the panel's own box, then put
// the prototype's own descriptors back (deleting them would leave every later measurement NaN).
const size = { w: 240, h: 300 }
const own = (name: string): PropertyDescriptor | undefined => Object.getOwnPropertyDescriptor(HTMLElement.prototype, name)
const width = own('offsetWidth')
const height = own('offsetHeight')
const restore = (name: string, was: PropertyDescriptor | undefined): void => {
if (was === undefined) Reflect.deleteProperty(HTMLElement.prototype, name)
else Object.defineProperty(HTMLElement.prototype, name, was)
}
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', { configurable: true, get: () => size.w })
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { configurable: true, get: () => size.h })
try {
const { m } = await open()
// The panel measures its anchor wrapper (the trigger's own box): jsdom's rects are all zeros, so drive the one it reads.
const rect = { left: 800, right: 900, top: 680, bottom: 700 }
query(m.container, '.lc-dp').getBoundingClientRect = () =>
({ ...rect, x: rect.left, y: rect.top, width: 100, height: 20, toJSON: () => '' }) as DOMRect
await toggle(m)
const seated = panel()!
assert.equal(seated.style.left, '660px', 'right-aligned to the trigger')
assert.equal(seated.style.top, '374px', 'flipped above: below the trigger there is no room for 300px')
rect.left = 10
rect.right = 110
await toggle(m)
await toggle(m)
const clamped = panel()!
assert.equal(clamped.style.left, '8px', 'clamped inside the viewport')
assert.equal(clamped.style.top, '374px')
} finally {
restore('offsetWidth', width)
restore('offsetHeight', height)
}
})
test('renders hidden until it has measured itself', async () => {
const { m } = await open()
await toggle(m)
// The layout effect measures before paint, so the seated panel is what a
// reader ever sees; a fresh mount would flash at (0, 0) without it.
assert.equal(panel()!.style.visibility, '')
assert.notEqual(panel()!.style.left, '0px')
})
})
describe('leaving the panel', () => {
test('Escape closes it and puts focus back on the trigger', async () => {
const { m } = await open()
await toggle(m)
trigger(m).focus()
await keydown('Escape')
assert.ok(panel() === null)
assert.equal(document.activeElement?.className, trigger(m).className, 'focus comes back to the trigger')
})
test('a press outside closes it; a press inside does not', async () => {
const { m } = await open()
await toggle(m)
await act(async () => { query(m.container, '.lc-dp').dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) })
assert.ok(panel() !== null, 'the tree the panel was opened from stays open')
await act(async () => { document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) })
assert.ok(panel() === null)
})
test('a press inside the panel is not an outside press', async () => {
const { m } = await open()
await toggle(m)
await act(async () => { cell(m, '2026-10-03').dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) })
assert.ok(panel() !== null)
})
test('a press on the trigger itself does not race the toggle', async () => {
const { m } = await open()
await toggle(m)
await act(async () => { trigger(m).dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) })
assert.ok(panel() !== null)
})
test('an event with no node target is ignored', async () => {
const { m } = await open()
await toggle(m)
// Dispatched at window itself, so the target is not a node at all.
await fireWindow(new MouseEvent('mousedown'))
assert.ok(panel() !== null)
})
test('a scroll or a resize retracts it rather than floating off the anchor', async () => {
const { m } = await open()
await toggle(m)
await fireWindow(new Event('scroll'))
assert.ok(panel() === null)
await toggle(m)
await fireWindow(new Event('resize'))
assert.ok(panel() === null)
})
})