mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 18:26:17 +00:00
* perf(studio): schedule adaptive timeline thumbnails * perf(studio): bound thumbnail decoding resources * perf(studio): virtualize timeline thumbnail media * perf(studio): prioritize timeline thumbnail work * perf(studio-server): coordinate cancelable thumbnail generation --------- Co-authored-by: Codex <codex@local>
99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import React, { act } from "react";
|
|
import { createRoot, type Root } from "react-dom/client";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { thumbnailScheduler } from "../lib/thumbnailScheduler";
|
|
import { decodeVideoThumbnail } from "../lib/thumbnailVideoDecoder";
|
|
import { VideoThumbnail } from "./VideoThumbnail";
|
|
|
|
vi.mock("../lib/thumbnailVideoDecoder", () => ({ decodeVideoThumbnail: vi.fn() }));
|
|
|
|
Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", {
|
|
configurable: true,
|
|
value: true,
|
|
});
|
|
|
|
class MockResizeObserver {
|
|
observe() {}
|
|
disconnect() {}
|
|
unobserve() {}
|
|
}
|
|
|
|
let host: HTMLDivElement;
|
|
let root: Root | null = null;
|
|
|
|
beforeEach(() => {
|
|
globalThis.ResizeObserver = MockResizeObserver as unknown as typeof ResizeObserver;
|
|
host = document.createElement("div");
|
|
document.body.append(host);
|
|
});
|
|
|
|
afterEach(() => {
|
|
act(() => root?.unmount());
|
|
root = null;
|
|
thumbnailScheduler.invalidateProject("p");
|
|
vi.clearAllMocks();
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
async function render(rich = false) {
|
|
root = createRoot(host);
|
|
await act(async () => {
|
|
root!.render(
|
|
<VideoThumbnail
|
|
videoSrc="/api/projects/p/preview/assets/clip.mp4"
|
|
label=""
|
|
labelColor="#fff"
|
|
projectId="p"
|
|
sessionEpoch={1}
|
|
priority="visible"
|
|
rich={rich}
|
|
/>,
|
|
);
|
|
await Promise.resolve();
|
|
});
|
|
}
|
|
|
|
describe("VideoThumbnail", () => {
|
|
it("renders a scheduler-provided sparse poster", async () => {
|
|
vi.mocked(decodeVideoThumbnail).mockResolvedValue({
|
|
value: { kind: "image", url: "blob:poster", aspect: 16 / 9 },
|
|
weight: 128,
|
|
});
|
|
|
|
await render();
|
|
|
|
expect(decodeVideoThumbnail).toHaveBeenCalledWith(
|
|
expect.objectContaining({ frameCount: 1 }),
|
|
expect.any(AbortSignal),
|
|
);
|
|
expect(host.querySelector('img[src="blob:poster"]')).not.toBeNull();
|
|
expect(host.querySelector(".animate-pulse")).toBeNull();
|
|
});
|
|
|
|
it("requests a rich filmstrip only for interaction actors", async () => {
|
|
vi.mocked(decodeVideoThumbnail).mockResolvedValue({
|
|
value: { kind: "filmstrip", urls: ["blob:a", "blob:b"], aspect: 16 / 9 },
|
|
weight: 256,
|
|
});
|
|
|
|
await render(true);
|
|
|
|
expect(decodeVideoThumbnail).toHaveBeenCalledWith(
|
|
expect.objectContaining({ frameCount: 6 }),
|
|
expect.any(AbortSignal),
|
|
);
|
|
expect(host.querySelectorAll("img").length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("clears the loading shimmer when the scheduled decode fails", async () => {
|
|
vi.mocked(decodeVideoThumbnail).mockRejectedValue(new Error("decode failed"));
|
|
|
|
await render();
|
|
await vi.waitFor(() => expect(thumbnailScheduler.getDiagnostics().active).toBe(0));
|
|
|
|
expect(host.querySelector(".animate-pulse")).toBeNull();
|
|
expect(host.querySelector("img")).toBeNull();
|
|
});
|
|
});
|