mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { buildFrameCaptureUrl } from "./frameCapture";
|
|
import {
|
|
buildProjectApiPath,
|
|
buildProjectHash,
|
|
encodeProjectId,
|
|
parseProjectHashRoute,
|
|
parseProjectIdFromHash,
|
|
} from "./projectRouting";
|
|
|
|
describe("project routing utilities", () => {
|
|
it("decodes project ids from hash routes before building capture URLs", () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date("2026-05-01T12:00:00Z"));
|
|
|
|
const projectId = parseProjectIdFromHash("#project/Notion%20Showcase");
|
|
|
|
expect(projectId).toBe("Notion Showcase");
|
|
expect(
|
|
buildFrameCaptureUrl({
|
|
projectId: projectId ?? "",
|
|
compositionPath: null,
|
|
currentTime: 1.809,
|
|
origin: "http://localhost:3002",
|
|
}),
|
|
).toBe(
|
|
"http://localhost:3002/api/projects/Notion%20Showcase/thumbnail/index.html?t=1.809&format=png&v=1777636800000",
|
|
);
|
|
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("accepts legacy raw-space hash routes", () => {
|
|
expect(parseProjectIdFromHash("#project/Notion Showcase")).toBe("Notion Showcase");
|
|
});
|
|
|
|
it("decodes reserved characters when the hash route is encoded", () => {
|
|
expect(parseProjectIdFromHash("#project/Launch%20%231%3F%20v2")).toBe("Launch #1? v2");
|
|
});
|
|
|
|
it("does not throw on malformed percent escapes in hash routes", () => {
|
|
expect(parseProjectIdFromHash("#project/Broken%ZZName")).toBe("Broken%ZZName");
|
|
});
|
|
|
|
it("ignores non-project hash routes", () => {
|
|
expect(parseProjectIdFromHash("")).toBeNull();
|
|
expect(parseProjectIdFromHash("#settings")).toBeNull();
|
|
expect(parseProjectIdFromHash("#project/")).toBeNull();
|
|
expect(parseProjectIdFromHash("#project/foo/bar")).toBeNull();
|
|
});
|
|
|
|
it("encodes project ids when writing hash routes", () => {
|
|
expect(buildProjectHash("Notion Showcase")).toBe("#project/Notion%20Showcase");
|
|
expect(buildProjectHash("Notion%20Showcase")).toBe("#project/Notion%2520Showcase");
|
|
expect(buildProjectHash("Launch #1? v2")).toBe("#project/Launch%20%231%3F%20v2");
|
|
});
|
|
|
|
it("round-trips unicode project ids through hash routes", () => {
|
|
const hash = buildProjectHash("Mañana demo");
|
|
|
|
expect(hash).toBe("#project/Ma%C3%B1ana%20demo");
|
|
expect(parseProjectIdFromHash(hash)).toBe("Mañana demo");
|
|
});
|
|
|
|
it("parses project hash routes with query params", () => {
|
|
const route = parseProjectHashRoute("#project/Notion%20Showcase?tab=design&t=4.2");
|
|
|
|
expect(route?.projectId).toBe("Notion Showcase");
|
|
expect(route?.params.get("tab")).toBe("design");
|
|
expect(route?.params.get("t")).toBe("4.2");
|
|
});
|
|
|
|
it("builds hash routes with query params", () => {
|
|
expect(buildProjectHash("Notion Showcase", { tab: "design", t: "4.2" })).toBe(
|
|
"#project/Notion%20Showcase?tab=design&t=4.2",
|
|
);
|
|
});
|
|
|
|
it("encodes project ids as one API path segment", () => {
|
|
expect(encodeProjectId("Notion Showcase")).toBe("Notion%20Showcase");
|
|
expect(encodeProjectId("Notion%20Showcase")).toBe("Notion%2520Showcase");
|
|
expect(encodeProjectId("Launch #1? v2")).toBe("Launch%20%231%3F%20v2");
|
|
});
|
|
|
|
it("builds API paths without double encoding decoded project ids", () => {
|
|
expect(buildProjectApiPath("Notion Showcase", "/thumbnail/index.html")).toBe(
|
|
"/api/projects/Notion%20Showcase/thumbnail/index.html",
|
|
);
|
|
});
|
|
|
|
it("keeps literal percent signs safe in API paths", () => {
|
|
expect(buildProjectApiPath("Percent%20Name", "/preview")).toBe(
|
|
"/api/projects/Percent%2520Name/preview",
|
|
);
|
|
});
|
|
|
|
it("keeps unicode project ids safe in API paths", () => {
|
|
expect(buildProjectApiPath("Mañana demo", "/preview")).toBe(
|
|
"/api/projects/Ma%C3%B1ana%20demo/preview",
|
|
);
|
|
});
|
|
});
|