Files
dsh-context/tests/client/components/detailNote.spec.ts
T
Bowen Liang 4d144147a0 refactor(client): import React members directly, dropping the central react module
Every client file reached hooks and element types through the central
require seam (src/client/react.ts) and spelled them React.useState /
ReactNS.ReactElement. Import the members directly instead — named value
imports for hooks, inline type-only imports for element types — and let
the bundler own the single require('react'), which is exactly what the
platform module table answers.

This needs JSX off the classic runtime (it required a React identifier
in every tsx scope): tsconfig moves to jsx: react-jsx, so the bundle
sources jsx from 'react/jsx-runtime', already seeded by every supported
harness (verified against the dsh-v0.1.2-rc.1 platform.ts). The bundle
smoke's fake module table gains the specifier to stay a faithful shell
stand-in. h() survives as a local createElement alias at the slot
render-prop seams and the lightbox portal imports createPortal from
react-dom directly.

Verified: the built client.js requires react, react/jsx-runtime, and
react-dom exactly once each; the compat lane (bundle smoke + the
real-baseline matrix) passes against the fresh artifacts; the full gate
stays green with per-file coverage at 100%.
2026-09-07 08:59:03 +08:00

49 lines
1.9 KiB
TypeScript

// DetailNote (src/client/components/detailNote.tsx): the split generation's
// detail-pending note — loading strip, the failed state's retry button, and
// the inert plain-text failure when no retry is wired.
import { createElement as h } from 'react'
import assert from 'node:assert/strict'
import { describe, test } from 'vitest'
import { makeDetailNote } from '../../../src/client/components/detailNote'
import { DICT_EN, DICT_ZH } from '../../../src/client/i18n'
import { click, makeKit, mount, query, text } from '../helpers/kit'
const DetailNote = makeDetailNote(makeKit())
const DetailNoteZh = makeDetailNote(makeKit('zh'))
describe('DetailNote', () => {
test('the loading strip names the pending read', async () => {
const m = await mount(h(DetailNote, { state: 'loading' as const }))
assert.equal(text(m.container), DICT_EN['detail.loading'])
assert.equal(query(m.container, 'div').className, 'lc-empty', 'the default well class')
await m.unmount()
})
test('the failed strip arms the retry button; the caller class applies', async () => {
let retries = 0
const m = await mount(h(DetailNote, {
state: 'failed' as const,
onRetry: () => { retries++ },
className: 'lc-br-note',
}))
assert.equal(query(m.container, 'div').className, 'lc-br-note')
await click(query(m.container, '.lc-br-retry'))
assert.equal(retries, 1)
await m.unmount()
})
test('a failed note without a retry callback renders the inert text', async () => {
const m = await mount(h(DetailNote, { state: 'failed' as const }))
assert.equal(text(m.container), DICT_EN['detail.loadFailed'])
assert.equal(m.container.querySelector('button'), null)
await m.unmount()
})
test('the zh locale localizes both states', async () => {
const m = await mount(h(DetailNoteZh, { state: 'loading' as const }))
assert.equal(text(m.container), DICT_ZH['detail.loading'])
await m.unmount()
})
})