/** * 阶段进度 —— 进度条的刻度。判据是「这一阶段的计划项**全收完**才算过」。 */ import { describe, expect, it } from 'vitest'; import type { CasePhase } from '@contracts'; import { CLOSING_PHASE, phaseProgress } from '../internal/domain/phase.ts'; import type { ItemRuntime, ItemState } from '../internal/domain/lifecycle.ts'; /** A1 的三段:定位客户 / 归一受理 / 判定去向;放行那一格由进度条自己加 */ const PHASES: CasePhase[] = [ { name: '定位客户', items: ['S1_customer'] }, { name: '归一受理', items: ['S2_normalize'] }, { name: '判定去向', items: ['S4_history', 'S5_classify', 'S6_route'] }, ]; const items = (states: Record): ItemRuntime[] => Object.entries(states).map(([id, state]) => ({ id, state, instance: 1 })); const ALL = ['S1_customer', 'S2_normalize', 'S4_history', 'S5_classify', 'S6_route', 'T_release']; const at = (done: readonly string[]): ItemRuntime[] => items(Object.fromEntries(ALL.map((id) => [id, done.includes(id) ? 'Completed' : 'Available']))); describe('阶段进度 —— 三段激活态 + 一格收口 = x/4', () => { it('一格都没收:停在第一阶段', () => { expect(phaseProgress(PHASES, at([]), false)).toEqual({ index: 1, total: 4, name: '定位客户' }); }); it('逐段推进:收完一段才进下一段', () => { expect(phaseProgress(PHASES, at(['S1_customer']), false)).toMatchObject({ index: 2, name: '归一受理' }); expect(phaseProgress(PHASES, at(['S1_customer', 'S2_normalize']), false)).toMatchObject({ index: 3, name: '判定去向', }); }); it('一段里的项没收齐就不算过 —— 三项收两项仍停在这一段', () => { const done = ['S1_customer', 'S2_normalize', 'S4_history', 'S5_classify']; expect(phaseProgress(PHASES, at(done), false)).toMatchObject({ index: 3, name: '判定去向' }); }); it('三段都过、还没收口:进最后一格(等放行),但不是终态', () => { const done = ['S1_customer', 'S2_normalize', 'S4_history', 'S5_classify', 'S6_route']; expect(phaseProgress(PHASES, at(done), false)).toEqual({ index: 4, total: 4, name: CLOSING_PHASE }); }); it('终态直接给最后一格 —— 不去数计划项', () => { // 放行完成那一瞬 T_release 还没收:去数项的话会显示「还差一格」,而单已经收口了 expect(phaseProgress(PHASES, at([]), true)).toEqual({ index: 4, total: 4, name: CLOSING_PHASE }); }); it('被跳过的裁量项(Terminated)算过 —— 它不会再收,等它等到天亮', () => { const mixed = items({ S1_customer: 'Completed', S2_normalize: 'Completed', S4_history: 'Completed', S5_classify: 'Terminated', S6_route: 'Completed', T_release: 'Available', }); expect(phaseProgress(PHASES, mixed, false)).toMatchObject({ index: 4, name: CLOSING_PHASE }); }); it('没声明阶段就不给刻度 —— 界面照此不画进度条,不编一个出来', () => { expect(phaseProgress(undefined, at([]), false)).toBeUndefined(); expect(phaseProgress([], at([]), false)).toBeUndefined(); }); });