Files
zyt/TUICallKit-Vue3/training/pose/__tests__/session.test.ts
T
long 4932ed7651 refactor: 移除 AI 跟练中没有作用的代码
- 删除从未显示的 idle 手势帧(小程序包内 png 和视频用高清帧),训练分包减少 17KB
- 删除骨架连线 BONES、镜像函数 mirrorPose(骨架已去掉)和未使用的 Bgm 类型
- 只给单测用的 sampleMotion 移到测试目录
- 删除训练菜单里未使用的 .menu-icon-text 样式
- 补全 history 单测数据缺少的 adviceKind 字段(严格类型检查报错)
2026-10-31 10:33:25 +08:00

97 lines
3.5 KiB
TypeScript

import { test } from 'node:test'
import assert from 'node:assert/strict'
import { TrainingSession, summarize, type SessionEvent } from '../session.ts'
import { MOTIONS } from '../motions.ts'
import { sampleMotion } from './sampleMotion.ts'
import type { Pose } from '../keypoints.ts'
const standing = (t: number): Pose => {
const points = MOTIONS.squat.poseAt(0)
return { points, scores: points.map(() => 0.9), t }
}
/** 站好 → 倒计时 → 开始训练,返回训练开始时刻 */
function warmUp(s: TrainingSession, events: SessionEvent[]) {
let t = 0
for (; t <= 5000; t += 100) events.push(...s.feed(standing(t), t))
return t
}
test('站好保持 1.5 秒后进入倒计时,3 秒后开始训练', () => {
const s = new TrainingSession('squat', 5)
const events: SessionEvent[] = []
warmUp(s, events)
const phases = events.filter((e) => e.type === 'phase').map((e) => (e as any).phase)
assert.deepEqual(phases, ['countdown', 'training'])
const counts = events.filter((e) => e.type === 'countdown').map((e) => (e as any).n)
assert.deepEqual(counts, [3, 2, 1])
})
test('没站进画面不会开始', () => {
const s = new TrainingSession('squat', 5)
for (let t = 0; t < 5000; t += 100) s.feed(null, t)
assert.equal(s.phase, 'framing')
assert.equal(s.framingIssue, 'no_body')
})
test('完成目标次数后结束,并统计时长与连击', () => {
const s = new TrainingSession('squat', 4)
const events: SessionEvent[] = []
const t0 = warmUp(s, events)
for (const f of sampleMotion('squat', MOTIONS.squat.cycleSeconds * 5, 12, t0)) {
events.push(...s.feed(f, f.t))
if (s.phase === 'done') break
}
assert.equal(s.phase, 'done')
assert.equal(s.count, 4)
assert.equal(s.bestCombo, 4)
assert.ok(events.some((e) => e.type === 'combo' && e.combo === 3))
const sum = summarize(s, 4)
assert.equal(sum.stars, 3)
assert.equal(sum.adviceKind, 'praise')
assert.ok(sum.durationSec >= 9 && sum.durationSec <= 14, `duration ${sum.durationSec}`)
assert.ok(sum.kcal > 0)
})
test('离开画面自动进入 lost,回来后继续,离开期间不计时', () => {
const s = new TrainingSession('squat', 10)
const t0 = warmUp(s, [])
s.feed(standing(t0 + 100), t0 + 100)
const before = s.activeMs
let t = t0 + 200
for (; t < t0 + 6000; t += 100) s.feed(null, t)
assert.equal(s.phase, 'lost')
assert.ok(s.activeMs - before < 2000, `active grew ${s.activeMs - before}`)
s.feed(standing(t), t)
assert.equal(s.phase, 'training')
})
test('暂停与恢复', () => {
const s = new TrainingSession('raise', 10)
const t0 = warmUp(s, [])
s.pause()
assert.equal(s.phase, 'paused')
const before = s.activeMs
s.tick(t0 + 10000)
assert.equal(s.activeMs, before)
s.resume(t0 + 10000)
assert.equal(s.phase, 'training')
})
test('领练模式:跳过站位、按节奏计数、不会因无人而暂停', () => {
const s = new TrainingSession('march', 3, { detectLost: false })
s.startCountdown(0)
assert.equal(s.phase, 'countdown')
for (let t = 0; t <= 3200; t += 200) s.tick(t)
assert.equal(s.phase, 'training')
for (let t = 3400; t < 9000; t += 200) s.tick(t)
assert.equal(s.phase, 'training')
s.addGuidedRep()
s.addGuidedRep()
const last = s.addGuidedRep()
assert.equal(s.count, 3)
assert.equal(s.phase, 'done')
assert.ok(last.some((e) => e.type === 'phase' && e.phase === 'done'))
assert.equal(s.reps.length, 0)
})