flnxi-Wu/lib/__tests__/audio-processor.test.ts
WuYongJie 63ec6b7dfb fix: 修复排行榜按钮消失、tag排序和输入框浅色模式问题
1. 排行榜悬浮窗关闭后小按钮消失问题
   - 修复面板居中模式关闭时按钮位置错误
   - 保存并恢复按钮的居中状态

2. 首页tag按钮按素材数量排序
   - 添加按素材数量降序排序逻辑

3. 上传弹窗输入框浅色模式文字不可见
   - 添加 text-slate-900 确保浅色模式下文字可见
   - 添加 placeholder 样式确保占位符正确显示
2025-12-14 21:41:29 +08:00

226 lines
6.6 KiB
TypeScript

/**
* 音频处理模块测试
* 测试 lib/audio-processor.ts 的核心功能
*/
// Get the mocked parseBuffer from jest.setup.js
const mockParseBuffer = jest.requireMock('music-metadata').parseBuffer as jest.Mock
import {
formatDuration,
isAudioFile,
parseAudioMetadata,
SUPPORTED_AUDIO_FORMATS,
AudioMetadata,
} from '../audio-processor'
describe('音频处理模块', () => {
describe('formatDuration', () => {
it('应该正确格式化0秒', () => {
expect(formatDuration(0)).toBe('00:00')
})
it('应该正确格式化小于1分钟的时长', () => {
expect(formatDuration(30)).toBe('00:30')
expect(formatDuration(59)).toBe('00:59')
})
it('应该正确格式化整分钟', () => {
expect(formatDuration(60)).toBe('01:00')
expect(formatDuration(120)).toBe('02:00')
expect(formatDuration(600)).toBe('10:00')
})
it('应该正确格式化分钟和秒的组合', () => {
expect(formatDuration(90)).toBe('01:30')
expect(formatDuration(125)).toBe('02:05')
expect(formatDuration(3661)).toBe('61:01') // 超过1小时
})
it('应该正确处理小数秒(向下取整)', () => {
expect(formatDuration(30.5)).toBe('00:30')
expect(formatDuration(59.9)).toBe('00:59')
expect(formatDuration(90.7)).toBe('01:30')
})
it('应该正确处理负数(边界情况)', () => {
// Math.floor 对负数的行为
expect(formatDuration(-1)).toBe('-1:-1')
})
})
describe('isAudioFile', () => {
it('应该识别常见音频MIME类型', () => {
expect(isAudioFile('audio/mpeg')).toBe(true)
expect(isAudioFile('audio/wav')).toBe(true)
expect(isAudioFile('audio/flac')).toBe(true)
expect(isAudioFile('audio/aac')).toBe(true)
expect(isAudioFile('audio/ogg')).toBe(true)
expect(isAudioFile('audio/mp4')).toBe(true)
expect(isAudioFile('audio/webm')).toBe(true)
})
it('应该拒绝非音频MIME类型', () => {
expect(isAudioFile('video/mp4')).toBe(false)
expect(isAudioFile('image/png')).toBe(false)
expect(isAudioFile('application/pdf')).toBe(false)
expect(isAudioFile('text/plain')).toBe(false)
})
it('应该处理空字符串', () => {
expect(isAudioFile('')).toBe(false)
})
it('应该处理不完整的MIME类型', () => {
expect(isAudioFile('audio')).toBe(false)
expect(isAudioFile('audio/')).toBe(true) // 以 audio/ 开头
})
})
describe('SUPPORTED_AUDIO_FORMATS', () => {
it('应该包含MP3格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/mpeg')
})
it('应该包含WAV格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/wav')
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/x-wav')
})
it('应该包含FLAC格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/flac')
})
it('应该包含AAC格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/aac')
})
it('应该包含M4A格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/mp4')
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/x-m4a')
})
it('应该包含OGG格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/ogg')
})
it('应该包含WebM格式', () => {
expect(SUPPORTED_AUDIO_FORMATS).toContain('audio/webm')
})
it('应该有正确的格式数量', () => {
expect(SUPPORTED_AUDIO_FORMATS.length).toBe(9)
})
})
describe('parseAudioMetadata', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('应该正确解析音频元数据', async () => {
mockParseBuffer.mockResolvedValue({
format: {
duration: 180.5,
container: 'MPEG',
bitrate: 320000,
sampleRate: 44100,
numberOfChannels: 2,
},
} as any)
const buffer = Buffer.from('test audio data')
const result = await parseAudioMetadata(buffer, 'audio/mpeg')
expect(result).not.toBeNull()
expect(result?.duration).toBe(180.5)
expect(result?.durationFormatted).toBe('03:00')
expect(result?.format).toBe('MPEG')
expect(result?.bitrate).toBe(320000)
expect(result?.sampleRate).toBe(44100)
expect(result?.channels).toBe(2)
})
it('应该处理缺少duration的情况', async () => {
mockParseBuffer.mockResolvedValue({
format: {
container: 'WAV',
},
} as any)
const buffer = Buffer.from('test')
const result = await parseAudioMetadata(buffer)
expect(result).not.toBeNull()
expect(result?.duration).toBe(0)
expect(result?.durationFormatted).toBe('00:00')
})
it('应该处理缺少container的情况', async () => {
mockParseBuffer.mockResolvedValue({
format: {
duration: 60,
},
} as any)
const buffer = Buffer.from('test')
const result = await parseAudioMetadata(buffer)
expect(result).not.toBeNull()
expect(result?.format).toBe('unknown')
})
it('应该优雅处理解析错误', async () => {
mockParseBuffer.mockRejectedValue(new Error('Invalid audio format'))
const buffer = Buffer.from('invalid data')
const result = await parseAudioMetadata(buffer, 'audio/mpeg')
expect(result).toBeNull()
})
it('应该传递正确的mimeType参数', async () => {
mockParseBuffer.mockResolvedValue({
format: { duration: 30, container: 'MP3' },
} as any)
const buffer = Buffer.from('test')
await parseAudioMetadata(buffer, 'audio/mpeg')
expect(mockParseBuffer).toHaveBeenCalledWith(buffer, { mimeType: 'audio/mpeg' })
})
})
describe('AudioMetadata 类型验证', () => {
it('应该有正确的类型结构', () => {
const mockMetadata: AudioMetadata = {
duration: 180,
durationFormatted: '03:00',
format: 'mp3',
bitrate: 320000,
sampleRate: 44100,
channels: 2,
}
expect(mockMetadata.duration).toBe(180)
expect(mockMetadata.durationFormatted).toBe('03:00')
expect(mockMetadata.format).toBe('mp3')
expect(mockMetadata.bitrate).toBe(320000)
expect(mockMetadata.sampleRate).toBe(44100)
expect(mockMetadata.channels).toBe(2)
})
it('可选字段应该可以为undefined', () => {
const minimalMetadata: AudioMetadata = {
duration: 60,
durationFormatted: '01:00',
format: 'wav',
}
expect(minimalMetadata.bitrate).toBeUndefined()
expect(minimalMetadata.sampleRate).toBeUndefined()
expect(minimalMetadata.channels).toBeUndefined()
})
})
})