3862996045
CI/CD Pipeline / Production Browser E2E (push) Failing after 1312h43m40s
CI/CD Pipeline / Deploy Production (push) Failing after 1312h43m42s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 1312h48m16s
CI/CD Pipeline / Build Production API Image (push) Failing after 1312h48m20s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 1312h49m4s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 1312h51m34s
CI/CD Pipeline / PR Build API Image (push) Failing after 1312h49m8s
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m4s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m5s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m56s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 58s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m0s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m5s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m12s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 21s
CI/CD Pipeline / Unit Tests (push) Successful in 6m21s
CI/CD Pipeline / Integration Tests (push) Successful in 2m38s
CI/CD Pipeline / Frontend Lint (push) Successful in 46s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m39s
CI/CD Pipeline / PR Build Web Image (push) Failing after 1313h21m29s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m40s
CI/CD Pipeline / Build Production Web Image (push) Failing after 1313h20m41s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { describe, expect, it, vi, beforeEach } from "vitest"
|
|
import { renderHook, act } from "@testing-library/react"
|
|
import { useCloneProgress } from "@/hooks/useCloneProgress"
|
|
|
|
// mock API
|
|
const mockGetVoiceClones = vi.fn()
|
|
vi.mock("@/api/voice-clone", () => ({
|
|
getVoiceClones: (...args: unknown[]) => mockGetVoiceClones(...args),
|
|
VoiceCloneStatus: { READY: "ready" },
|
|
}))
|
|
|
|
vi.useFakeTimers()
|
|
|
|
describe("useCloneProgress", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.clearAllTimers()
|
|
})
|
|
|
|
it("should initial load clones", async () => {
|
|
const mockClones = [{ id: "1", name: "克隆1", status: "ready" }]
|
|
mockGetVoiceClones.mockResolvedValue(mockClones)
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
// 等待初始加载
|
|
await vi.waitFor(() => {
|
|
expect(result.current.clones).toEqual(mockClones)
|
|
})
|
|
expect(result.current.loading).toBe(false)
|
|
expect(result.current.hasProcessing).toBe(false)
|
|
})
|
|
|
|
it("should have loading state during fetch", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([])
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
expect(result.current.loading).toBe(true)
|
|
|
|
await vi.waitFor(() => {
|
|
expect(result.current.loading).toBe(false)
|
|
})
|
|
})
|
|
|
|
it("should add clone via addClone", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([])
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => expect(result.current.loading).toBe(false))
|
|
|
|
act(() => {
|
|
result.current.addClone({ id: "new-1", name: "新克隆", status: "processing" })
|
|
})
|
|
|
|
expect(result.current.clones.length).toBe(1)
|
|
expect(result.current.clones[0].id).toBe("new-1")
|
|
})
|
|
|
|
it("should remove clone via removeClone", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([
|
|
{ id: "1", name: "克隆1", status: "ready" },
|
|
{ id: "2", name: "克隆2", status: "ready" },
|
|
])
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => expect(result.current.clones.length).toBe(2))
|
|
|
|
act(() => {
|
|
result.current.removeClone("1")
|
|
})
|
|
|
|
expect(result.current.clones.length).toBe(1)
|
|
expect(result.current.clones[0].id).toBe("2")
|
|
})
|
|
|
|
it("should update clone via updateClone", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([{ id: "1", name: "旧名字", status: "processing" }])
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => expect(result.current.clones.length).toBe(1))
|
|
|
|
act(() => {
|
|
result.current.updateClone({ id: "1", name: "新名字", status: "ready" })
|
|
})
|
|
|
|
expect(result.current.clones[0].name).toBe("新名字")
|
|
expect(result.current.clones[0].status).toBe("ready")
|
|
})
|
|
|
|
it("should refresh clones manually", async () => {
|
|
let callCount = 0
|
|
mockGetVoiceClones.mockImplementation(() => {
|
|
callCount++
|
|
return Promise.resolve([{ id: `${callCount}`, name: `克隆${callCount}`, status: "ready" }])
|
|
})
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => expect(result.current.clones[0]?.id).toBe("1"))
|
|
|
|
await act(async () => {
|
|
await result.current.refresh()
|
|
})
|
|
|
|
expect(mockGetVoiceClones).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
it("should not start polling when all clones are ready", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([{ id: "1", name: "克隆1", status: "ready" }])
|
|
|
|
renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => expect(mockGetVoiceClones).toHaveBeenCalledTimes(1))
|
|
|
|
// 快进 10 秒,ready 状态不应该轮询
|
|
vi.advanceTimersByTime(10000)
|
|
|
|
// 应该只调用了初始的那一次
|
|
expect(mockGetVoiceClones).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it("hasProcessing should be true when there are processing clones", async () => {
|
|
mockGetVoiceClones.mockResolvedValue([{ id: "1", name: "克隆1", status: "processing" }])
|
|
|
|
const { result } = renderHook(() => useCloneProgress())
|
|
|
|
await vi.waitFor(() => {
|
|
expect(result.current.hasProcessing).toBe(true)
|
|
})
|
|
})
|
|
})
|