test(voices): add useAudioPlayer hook unit test
AI Code Review / AI Code Review (pull_request) Failing after 0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Successful in 35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 36s
AI Code Review / AI Code Review (pull_request) Failing after 0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Successful in 35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 36s
This commit is contained in:
+232
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* useAudioPlayer hook 测试 — VoiceLibrary 版本
|
||||
*
|
||||
* 该 Hook 使用 setInterval 模拟音频播放进度,纯逻辑可测。
|
||||
* 参考 voice-materials/hooks/useAudioPlayer.test.ts 的测试结构。
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"
|
||||
import { renderHook, act } from "@testing-library/react"
|
||||
import { useAudioPlayer } from "@/pages/voices/hooks/useAudioPlayer"
|
||||
|
||||
describe("useAudioPlayer (voices)", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("应该使用初始状态初始化", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
expect(result.current.playingId).toBeNull()
|
||||
expect(result.current.currentTime).toBe(0)
|
||||
})
|
||||
|
||||
it("handlePlay 应该开始播放指定音色", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
expect(result.current.currentTime).toBe(0)
|
||||
})
|
||||
|
||||
it("handlePlay 对同一个音色不应重复启动播放", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
const initialTime = result.current.currentTime
|
||||
|
||||
// 推进一些时间让进度走动
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
const timeAfterAdvance = result.current.currentTime
|
||||
expect(timeAfterAdvance).toBeGreaterThan(initialTime)
|
||||
|
||||
// 对同一个音色再次调用 handlePlay 不应重置
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
expect(result.current.currentTime).toBe(timeAfterAdvance)
|
||||
})
|
||||
|
||||
it("handlePlay 切换音色时应停止上一个并从头开始", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(500)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
expect(result.current.currentTime).toBeGreaterThan(0)
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-2", 15)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-2")
|
||||
expect(result.current.currentTime).toBe(0)
|
||||
})
|
||||
|
||||
it("播放进度应该随时间递增", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
// 每 100ms 增加 0.1
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(300)
|
||||
})
|
||||
|
||||
expect(result.current.currentTime).toBeCloseTo(0.3, 1)
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
})
|
||||
|
||||
it("播放到结尾应自动停止并重置", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 0.5) // 0.5 秒的短音频
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(600) // 超过 0.5 秒
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBeNull()
|
||||
expect(result.current.currentTime).toBe(0)
|
||||
})
|
||||
|
||||
it("handlePause 应该暂停播放", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
|
||||
const timeBeforePause = result.current.currentTime
|
||||
|
||||
act(() => {
|
||||
result.current.handlePause()
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBeNull()
|
||||
|
||||
// 暂停后时间不应再变化
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(500)
|
||||
})
|
||||
|
||||
expect(result.current.currentTime).toBe(timeBeforePause)
|
||||
})
|
||||
|
||||
it("handleSeek 应该跳转到指定时间", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.handleSeek("voice-1", 5, 10)
|
||||
})
|
||||
|
||||
expect(result.current.currentTime).toBe(5)
|
||||
})
|
||||
|
||||
it("handleSeek 对不同音色应该开始播放该音色", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
result.current.handleSeek("voice-2", 3, 15)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-2")
|
||||
expect(result.current.currentTime).toBe(3)
|
||||
})
|
||||
|
||||
it("handleTogglePlay 应该在播放和暂停之间切换", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
// 初始为暂停,调用应开始播放
|
||||
act(() => {
|
||||
result.current.handleTogglePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
|
||||
// 再次调用应暂停
|
||||
act(() => {
|
||||
result.current.handleTogglePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBeNull()
|
||||
})
|
||||
|
||||
it("stopPlayback 应该重置所有播放状态", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
act(() => {
|
||||
result.current.handlePlay("voice-1", 10)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(300)
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBe("voice-1")
|
||||
expect(result.current.currentTime).toBeGreaterThan(0)
|
||||
|
||||
act(() => {
|
||||
result.current.stopPlayback()
|
||||
})
|
||||
|
||||
expect(result.current.playingId).toBeNull()
|
||||
expect(result.current.currentTime).toBe(0)
|
||||
|
||||
// 停止后定时器不应再触发
|
||||
const timeAfterStop = result.current.currentTime
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(500)
|
||||
})
|
||||
expect(result.current.currentTime).toBe(timeAfterStop)
|
||||
})
|
||||
|
||||
it("返回值应该包含所有必要的方法和状态", () => {
|
||||
const { result } = renderHook(() => useAudioPlayer())
|
||||
|
||||
expect(typeof result.current.handlePlay).toBe("function")
|
||||
expect(typeof result.current.handlePause).toBe("function")
|
||||
expect(typeof result.current.handleSeek).toBe("function")
|
||||
expect(typeof result.current.handleTogglePlay).toBe("function")
|
||||
expect(typeof result.current.stopPlayback).toBe("function")
|
||||
expect(typeof result.current.playingId).toBe("object") // string | null
|
||||
expect(typeof result.current.currentTime).toBe("number")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user