task-122(记录与版本中心): 实现历史记录筛选条件

新增 records/history-filter.ts:筛选草稿/已应用快照与变更回首页。

TDD: task-122.test.ts 8 用例 RED→GREEN。
This commit is contained in:
2026-09-05 17:30:35 +08:00
parent 467c72efb4
commit 103cb28a1f
2 changed files with 120 additions and 0 deletions
@@ -0,0 +1,57 @@
/** 历史记录筛选栏状态(任务 122):筛选草稿/已应用快照与“变更回首页”,纯逻辑。 */
export interface HistoryFilterDraft {
userId: number | null
timeStart: string
timeEnd: string
}
export interface HistoryFilterBarState {
applied: HistoryFilterDraft | null
returnFirstPage: boolean
}
export function createHistoryFilterBarState(): HistoryFilterBarState {
return { applied: null, returnFirstPage: false }
}
function positiveIdOrNull(value: unknown): number | null {
if (typeof value === 'number') return Number.isFinite(value) && value >= 1 ? Math.floor(value) : null
if (typeof value === 'string' && /^\d+$/.test(value.trim())) {
const parsed = Number(value.trim())
return parsed >= 1 ? Math.floor(parsed) : null
}
return null
}
function textOf(value: unknown): string {
return typeof value === 'string' ? value.trim() : ''
}
/** 由界面输入(userId/起止时间)生成筛选草稿。 */
export function historyDraftFromInput(input: { userId?: string | number | null; timeStart?: string; timeEnd?: string }): HistoryFilterDraft {
return {
userId: positiveIdOrNull(input.userId),
timeStart: textOf(input.timeStart),
timeEnd: textOf(input.timeEnd),
}
}
function clone(draft: HistoryFilterDraft): HistoryFilterDraft {
return { userId: draft.userId, timeStart: textOf(draft.timeStart), timeEnd: textOf(draft.timeEnd) }
}
export function commitHistoryFilter(_state: HistoryFilterBarState, draft: HistoryFilterDraft): HistoryFilterBarState {
return { applied: clone(draft), returnFirstPage: true }
}
export function clearHistoryFilter(_state: HistoryFilterBarState): HistoryFilterBarState {
return { applied: null, returnFirstPage: true }
}
/** 草稿是否与已应用一致(无差异则无需重新加载)。 */
export function isHistoryFilterApplied(draft: HistoryFilterDraft, applied: HistoryFilterDraft | null): boolean {
if (!applied) return false
const a = clone(draft)
return a.userId === applied.userId && a.timeStart === applied.timeStart && a.timeEnd === applied.timeEnd
}
+63
View File
@@ -0,0 +1,63 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import {
clearHistoryFilter,
commitHistoryFilter,
createHistoryFilterBarState,
historyDraftFromInput,
isHistoryFilterApplied,
} from '../src/pages/records/history-filter.ts'
test('test_task_122_history_filter_normal_primary_path', () => {
// 正常主路径:筛选草稿提交后成为已应用并回到首页加载。
const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '3', timeStart: '2026-01-01 00:00:00', timeEnd: '' }))
assert.equal(state.applied!.userId, 3)
assert.equal(state.applied!.timeStart, '2026-01-01 00:00:00')
assert.equal(state.returnFirstPage, true)
})
test('test_task_122_history_filter_normal_variant_input', () => {
// 正常变体:仅时间区间筛选。
const draft = historyDraftFromInput({ userId: '', timeStart: '2026-02-01', timeEnd: '2026-02-28' })
assert.equal(draft.userId, null)
assert.equal(draft.timeEnd, '2026-02-28')
})
test('test_task_122_history_filter_repeated_is_idempotent', () => {
// 正常重复:提交同草稿稳定。
const a = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '1' }))
const b = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '1' }))
assert.deepEqual(a.applied, b.applied)
})
test('test_task_122_history_filter_boundary_empty_input', () => {
// 边界空值:重置后无已应用条件。
const cleared = clearHistoryFilter(createHistoryFilterBarState())
assert.equal(cleared.applied, null)
assert.equal(cleared.returnFirstPage, true)
})
test('test_task_122_history_filter_boundary_single_item', () => {
// 边界单元素:单 userId 筛选已应用判定。
const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ userId: '9' }))
assert.equal(isHistoryFilterApplied({ userId: 9, timeStart: '', timeEnd: '' }, state.applied), true)
})
test('test_task_122_history_filter_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:非法 userId 回 null。
assert.equal(historyDraftFromInput({ userId: 'abc' }).userId, null)
})
test('test_task_122_history_filter_invalid_input_rejected', () => {
// 异常输入:不同条件视为有变更。
const state = commitHistoryFilter(createHistoryFilterBarState(), historyDraftFromInput({ timeStart: '2026-01-01' }))
assert.equal(isHistoryFilterApplied({ userId: null, timeStart: '2026-02-01', timeEnd: '' }, state.applied), false)
})
test('test_task_122_history_filter_dependency_failure_returns_actionable_message', () => {
// 依赖失败/可操作:筛选状态纯逻辑、无框架/http。
const mod = readSource('src/pages/records/history-filter.ts')
assert.equal(/axios|http\.|vue/.test(mod), false, '筛选栏状态保持纯逻辑')
assert.match(mod, /returnFirstPage/)
})