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

186 lines
8.4 KiB
TypeScript

import { createElement as h } from 'react'
import assert from 'node:assert/strict'
import { afterEach, describe, test, vi } from 'vitest'
import { makeBalanceCapsule } from '../../../src/client/components/balanceCapsule'
import { PLATFORM_BALANCE_STORAGE_KEY, resetPlatformBalance } from '../../../src/client/balance'
import { asClientCtx, TestClientCtx } from '../helpers/harness'
import { flush, makeKit, mount, query, text } from '../helpers/kit'
const kit = makeKit()
const WIRE_BALANCE = {
isAvailable: true,
balances: [
{ currency: 'CNY', total: 110, granted: 10, toppedUp: 100 },
{ currency: 'USD', total: 12.5, granted: 2.5, toppedUp: 10 },
],
}
function stubRoute(body: unknown): void {
vi.stubGlobal('fetch', async () => ({ ok: true, json: async () => body }))
}
/** Leave the previous open's figure in storage, as that open would have. */
function remember(balance: unknown): void {
globalThis.localStorage.setItem(PLATFORM_BALANCE_STORAGE_KEY, JSON.stringify(balance))
}
afterEach(() => {
resetPlatformBalance()
globalThis.localStorage.clear()
vi.unstubAllGlobals()
})
describe('BalanceCapsule', () => {
test('absent, failed, and still-pending reads render nothing', async () => {
stubRoute({ ok: true, value: null })
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
await flush()
assert.equal(text(m.container), '')
await m.unmount()
stubRoute({ ok: false })
const failed = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m2 = await mount(h(failed, {}))
await flush()
assert.equal(text(m2.container), '')
await m2.unmount()
let release!: () => void
const gate = new Promise<void>((resolve) => { release = resolve })
vi.stubGlobal('fetch', async () => {
await gate
return { ok: true, json: async () => ({ ok: true, value: WIRE_BALANCE }) }
})
const pending = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m3 = await mount(h(pending, {}))
assert.equal(text(m3.container), '', 'nothing while the first read is pending')
await m3.unmount()
release()
await flush()
})
test('the en locale shows USD with the total and the breakdown tooltip', async () => {
stubRoute({ ok: true, value: WIRE_BALANCE })
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
await flush()
const pill = query(m.container, '.lc-ov-balance')
assert.equal(pill?.tagName, 'A')
assert.equal(pill?.getAttribute('href'), 'https://platform.deepseek.com/usage')
assert.equal(pill?.getAttribute('target'), '_blank')
assert.equal(pill?.getAttribute('rel'), 'noreferrer')
assert.equal(query(m.container, '.lc-ov-balance-label')?.textContent, 'DeepSeek balance')
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$12.50')
// The breakdown lives in the pill's data attributes for the page's hover
// tip (no native `title`): the pill already carries the total, so only
// the non-zero parts ride the bubble — the topped-up line leads the granted one, and the tip drops below the pill.
assert.equal(pill?.getAttribute('title'), null)
assert.equal(pill?.getAttribute('data-lc-tip'), 'Topped-up balance: $10.00\nGranted balance: $2.50')
assert.equal(pill?.getAttribute('data-lc-tip-side'), 'bottom')
await m.unmount()
})
test('zero sub-items drop from the tooltip; an all-zero account rides bare', async () => {
stubRoute({
ok: true,
value: { isAvailable: true, balances: [{ currency: 'USD', total: 9, granted: 0, toppedUp: 9 }] },
})
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
await flush()
const pill = query(m.container, '.lc-ov-balance')
assert.equal(pill.getAttribute('data-lc-tip'), 'Topped-up balance: $9.00', 'the zero granted line is gone')
await m.unmount()
stubRoute({
ok: true,
value: { isAvailable: false, balances: [{ currency: 'USD', total: 0, granted: 0, toppedUp: 0 }] },
})
const bare = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m2 = await mount(h(bare, {}))
await flush()
assert.equal(query(m2.container, '.lc-ov-balance-value')?.textContent, '$0.00', 'the pill still shows the figure')
assert.equal(query(m2.container, '.lc-ov-balance').getAttribute('data-lc-tip'), null, 'nothing to break down — no tip')
await m2.unmount()
})
test('the zh locale shows CNY with localized breakdown labels', async () => {
stubRoute({ ok: true, value: WIRE_BALANCE })
// The kit's `t` is the harness's locale-bound translate (zh), while the ctx's locale service drives the currency pick.
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'zh' })), makeKit('zh'))
const m = await mount(h(Capsule, {}))
await flush()
assert.equal(query(m.container, '.lc-ov-balance-label')?.textContent, 'DeepSeek 余额')
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '¥110.00')
assert.equal(query(m.container, '.lc-ov-balance').getAttribute('data-lc-tip'), '充值余额: ¥100.00\n赠送余额: ¥10.00')
await m.unmount()
})
test('the remembered figure paints first, then the revalidated one replaces it', async () => {
remember(WIRE_BALANCE)
const fresh = { isAvailable: true, balances: [{ currency: 'USD', total: 9.99, granted: 0.99, toppedUp: 9 }] }
let release!: () => void
const gate = new Promise<void>((resolve) => { release = resolve })
vi.stubGlobal('fetch', async () => {
await gate
return { ok: true, json: async () => ({ ok: true, value: fresh }) }
})
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$12.50', 'the remembered total shows at once')
release()
await flush()
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$9.99', 'the live figure takes over')
await m.unmount()
})
test('a failed revalidation keeps the remembered figure instead of blanking the pill', async () => {
remember(WIRE_BALANCE)
stubRoute({ ok: false })
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$12.50')
await flush()
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, '$12.50', 'the miss changes nothing')
await m.unmount()
})
test('an account without the display currency falls back to its first entry, code-prefixed', async () => {
stubRoute({
ok: true,
value: { isAvailable: true, balances: [{ currency: 'EUR', total: 5, granted: 0, toppedUp: 5 }] },
})
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const m = await mount(h(Capsule, {}))
await flush()
assert.equal(query(m.container, '.lc-ov-balance-value')?.textContent, 'EUR 5.00')
await m.unmount()
})
})
describe('BalanceCapsule — the fast close-reopen window (issue #82)', () => {
test('a reopen inside the read window joins it and settles on the landing figure', async () => {
let release!: () => void
const gate = new Promise<void>((resolve) => { release = resolve })
vi.stubGlobal('fetch', async () => {
await gate
return { ok: true, json: async () => ({ ok: true, value: WIRE_BALANCE }) }
})
const Capsule = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const first = await mount(h(Capsule, {}))
assert.equal(text(first.container), '', 'nothing remembered yet')
await first.unmount()
// The reopen lands inside the first read's window: it joins the read —
// and settles on the landing figure exactly like the first open would.
const Reopened = makeBalanceCapsule(asClientCtx(new TestClientCtx({ locale: 'en' })), kit)
const second = await mount(h(Reopened, {}))
assert.equal(text(second.container), '', 'the joined read has not landed yet')
release()
await flush()
assert.equal(query(second.container, '.lc-ov-balance-value')?.textContent, '$12.50', 'the landing reaches the reopen')
await second.unmount()
})
})