furniture-3d-system-Wu/tests/lib/playback-progress-manager.test.ts

280 lines
8.2 KiB
TypeScript

/**
* PlaybackProgressManager 单元测试
*/
import PlaybackProgressManager from '@/lib/3d/playback-progress-manager'
// Mock localStorage
const mockLocalStorage = (() => {
let store: Record<string, string> = {}
return {
getItem: jest.fn((key: string) => store[key] || null),
setItem: jest.fn((key: string, value: string) => { store[key] = value }),
removeItem: jest.fn((key: string) => { delete store[key] }),
clear: jest.fn(() => { store = {} }),
}
})()
Object.defineProperty(global, 'localStorage', { value: mockLocalStorage })
describe('PlaybackProgressManager', () => {
let manager: PlaybackProgressManager
beforeEach(() => {
mockLocalStorage.clear()
manager = new PlaybackProgressManager({
expirationTime: 7 * 24 * 60 * 60 * 1000,
maxSavedProgress: 50,
autoCleanup: false,
})
})
describe('保存和获取进度', () => {
it('应该正确保存播放进度', () => {
manager.saveProgress('product-1', 5, 10, 1)
const progress = manager.getProgress('product-1')
expect(progress).not.toBeNull()
expect(progress?.productId).toBe('product-1')
expect(progress?.currentStepIndex).toBe(5)
expect(progress?.totalSteps).toBe(10)
expect(progress?.playbackSpeed).toBe(1)
expect(progress?.isCompleted).toBe(false)
})
it('应该在步骤到达最后时标记为完成', () => {
manager.saveProgress('product-1', 9, 10, 1)
const progress = manager.getProgress('product-1')
expect(progress?.isCompleted).toBe(true)
})
it('获取不存在的进度应该返回 null', () => {
const progress = manager.getProgress('non-existent')
expect(progress).toBeNull()
})
it('应该正确更新已存在的进度', () => {
manager.saveProgress('product-1', 3, 10, 1)
manager.saveProgress('product-1', 7, 10, 2)
const progress = manager.getProgress('product-1')
expect(progress?.currentStepIndex).toBe(7)
expect(progress?.playbackSpeed).toBe(2)
})
})
describe('检查进度', () => {
it('hasProgress 应该正确检测', () => {
expect(manager.hasProgress('product-1')).toBe(false)
manager.saveProgress('product-1', 5, 10)
expect(manager.hasProgress('product-1')).toBe(true)
})
it('hasValidProgress 应该排除已完成的进度', () => {
manager.saveProgress('product-1', 9, 10) // 完成
manager.saveProgress('product-2', 5, 10) // 未完成
manager.saveProgress('product-3', 0, 10) // 第0步
expect(manager.hasValidProgress('product-1')).toBe(false)
expect(manager.hasValidProgress('product-2')).toBe(true)
expect(manager.hasValidProgress('product-3')).toBe(false)
})
})
describe('删除进度', () => {
it('应该正确删除进度', () => {
manager.saveProgress('product-1', 5, 10)
const result = manager.deleteProgress('product-1')
expect(result).toBe(true)
expect(manager.hasProgress('product-1')).toBe(false)
})
it('删除不存在的进度应该返回 false', () => {
const result = manager.deleteProgress('non-existent')
expect(result).toBe(false)
})
it('clearAll 应该清除所有进度', () => {
manager.saveProgress('product-1', 5, 10)
manager.saveProgress('product-2', 3, 8)
manager.clearAll()
expect(manager.getAllProgress().length).toBe(0)
})
})
describe('标记完成', () => {
it('应该正确标记进度为完成', () => {
manager.saveProgress('product-1', 5, 10)
manager.markCompleted('product-1')
const progress = manager.getProgress('product-1')
expect(progress?.isCompleted).toBe(true)
})
})
describe('进度百分比', () => {
it('应该正确计算进度百分比', () => {
manager.saveProgress('product-1', 5, 10)
expect(manager.getProgressPercent('product-1')).toBe(50)
})
it('步骤为0时应该返回0', () => {
manager.saveProgress('product-1', 0, 10)
expect(manager.getProgressPercent('product-1')).toBe(0)
})
it('不存在的产品应该返回0', () => {
expect(manager.getProgressPercent('non-existent')).toBe(0)
})
})
describe('获取所有进度', () => {
it('应该返回所有保存的进度', () => {
manager.saveProgress('product-1', 5, 10)
manager.saveProgress('product-2', 3, 8)
manager.saveProgress('product-3', 7, 15)
const allProgress = manager.getAllProgress()
expect(allProgress.length).toBe(3)
})
it('应该按时间戳降序排序', async () => {
manager.saveProgress('product-1', 5, 10)
await new Promise(resolve => setTimeout(resolve, 10))
manager.saveProgress('product-2', 3, 8)
await new Promise(resolve => setTimeout(resolve, 10))
manager.saveProgress('product-3', 7, 15)
const allProgress = manager.getAllProgress()
// 最后保存的应该在最前面
expect(allProgress[0].productId).toBe('product-3')
})
})
describe('恢复提示信息', () => {
it('应该生成正确的恢复提示信息', () => {
manager.saveProgress('product-1', 5, 10)
const info = manager.getResumePromptInfo('product-1')
expect(info).not.toBeNull()
expect(info?.hasProgress).toBe(true)
expect(info?.stepInfo).toBe('第 6/10 步')
expect(info?.percent).toBe(50)
})
it('已完成的进度应该返回 null', () => {
manager.saveProgress('product-1', 9, 10)
const info = manager.getResumePromptInfo('product-1')
expect(info).toBeNull()
})
it('第0步的进度应该返回 null', () => {
manager.saveProgress('product-1', 0, 10)
const info = manager.getResumePromptInfo('product-1')
expect(info).toBeNull()
})
})
describe('时间格式化', () => {
it('应该格式化"刚刚"', () => {
manager.saveProgress('product-1', 5, 10)
const timeStr = manager.formatLastPlayedTime('product-1')
expect(timeStr).toBe('刚刚')
})
it('不存在的产品应该返回空字符串', () => {
const timeStr = manager.formatLastPlayedTime('non-existent')
expect(timeStr).toBe('')
})
})
describe('清理过期进度', () => {
it('应该清理过期的进度', () => {
// 创建一个带短过期时间的管理器
const shortExpirationManager = new PlaybackProgressManager({
expirationTime: 1, // 1ms
autoCleanup: false,
})
shortExpirationManager.saveProgress('product-1', 5, 10)
// 等待进度过期
return new Promise<void>((resolve) => {
setTimeout(() => {
const cleaned = shortExpirationManager.cleanupExpired()
expect(cleaned).toBeGreaterThanOrEqual(0)
resolve()
}, 10)
})
})
it('应该限制最大保存数量', () => {
const limitedManager = new PlaybackProgressManager({
maxSavedProgress: 3,
autoCleanup: false,
})
limitedManager.saveProgress('product-1', 1, 10)
limitedManager.saveProgress('product-2', 2, 10)
limitedManager.saveProgress('product-3', 3, 10)
limitedManager.saveProgress('product-4', 4, 10)
limitedManager.saveProgress('product-5', 5, 10)
limitedManager.cleanupExpired()
// 应该只保留最新的3个
expect(limitedManager.getAllProgress().length).toBeLessThanOrEqual(3)
})
})
describe('存储', () => {
it('应该持久化到 localStorage', () => {
manager.saveProgress('product-1', 5, 10)
expect(mockLocalStorage.setItem).toHaveBeenCalled()
})
it('应该从 localStorage 加载', () => {
// 预设数据
const presetData = [{
productId: 'product-1',
currentStepIndex: 5,
totalSteps: 10,
timestamp: Date.now(),
playbackSpeed: 1,
isCompleted: false,
}]
mockLocalStorage.getItem.mockReturnValueOnce(JSON.stringify(presetData))
const newManager = new PlaybackProgressManager({ autoCleanup: false })
expect(newManager.hasProgress('product-1')).toBe(true)
})
})
})