297 lines
10 KiB
TypeScript
297 lines
10 KiB
TypeScript
import {
|
|
embeddableURLValidator,
|
|
getEmbedLink,
|
|
maybeParseEmbedSrc,
|
|
} from "../src/embeddable";
|
|
|
|
describe("YouTube timestamp parsing", () => {
|
|
it("should parse YouTube URLs with timestamp in seconds", () => {
|
|
const testCases = [
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=90",
|
|
expectedStart: 90,
|
|
},
|
|
{
|
|
url: "https://youtu.be/dQw4w9WgXcQ?t=120",
|
|
expectedStart: 120,
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&start=150",
|
|
expectedStart: 150,
|
|
},
|
|
];
|
|
|
|
testCases.forEach(({ url, expectedStart }) => {
|
|
const result = getEmbedLink(url);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toContain(`start=${expectedStart}`);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should parse YouTube URLs with timestamp in time format", () => {
|
|
const testCases = [
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=1m30s",
|
|
expectedStart: 90, // 1*60 + 30
|
|
},
|
|
{
|
|
url: "https://youtu.be/dQw4w9WgXcQ?t=2m45s",
|
|
expectedStart: 165, // 2*60 + 45
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=1h2m3s",
|
|
expectedStart: 3723, // 1*3600 + 2*60 + 3
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=45s",
|
|
expectedStart: 45,
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=5m",
|
|
expectedStart: 300, // 5*60
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=2h",
|
|
expectedStart: 7200, // 2*3600
|
|
},
|
|
];
|
|
|
|
testCases.forEach(({ url, expectedStart }) => {
|
|
const result = getEmbedLink(url);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toContain(`start=${expectedStart}`);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should handle YouTube URLs without timestamps", () => {
|
|
const testCases = [
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"https://youtu.be/dQw4w9WgXcQ",
|
|
"https://www.youtube.com/embed/dQw4w9WgXcQ",
|
|
];
|
|
|
|
testCases.forEach((url) => {
|
|
const result = getEmbedLink(url);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).not.toContain("start=");
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should handle YouTube shorts URLs with timestamps", () => {
|
|
const url = "https://www.youtube.com/shorts/dQw4w9WgXcQ?t=30";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toContain("start=30");
|
|
}
|
|
// Shorts should have portrait aspect ratio
|
|
expect(result?.intrinsicSize).toEqual({ w: 315, h: 560 });
|
|
});
|
|
|
|
it("should handle playlist URLs with timestamps", () => {
|
|
const url =
|
|
"https://www.youtube.com/playlist?list=PLrAXtmRdnEQy1KbG5lbfgQ0-PKQY6FKYZ&t=60";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toContain("start=60");
|
|
expect(result.link).toContain("list=PLrAXtmRdnEQy1KbG5lbfgQ0-PKQY6FKYZ");
|
|
}
|
|
});
|
|
|
|
it("should handle malformed or edge case timestamps", () => {
|
|
const testCases = [
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=abc",
|
|
expectedStart: 0, // Invalid timestamp should default to 0
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=",
|
|
expectedStart: 0, // Empty timestamp should default to 0
|
|
},
|
|
{
|
|
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=0",
|
|
expectedStart: 0, // Zero timestamp should be handled
|
|
},
|
|
];
|
|
|
|
testCases.forEach(({ url, expectedStart }) => {
|
|
const result = getEmbedLink(url);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
if (expectedStart === 0) {
|
|
expect(result.link).not.toContain("start=");
|
|
} else {
|
|
expect(result.link).toContain(`start=${expectedStart}`);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should preserve other URL parameters", () => {
|
|
const url =
|
|
"https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=90&feature=youtu.be&list=PLtest";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toContain("start=90");
|
|
expect(result.link).toContain("enablejsapi=1");
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Google Drive video embedding", () => {
|
|
it.each([
|
|
{
|
|
url: "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?usp=sharing",
|
|
expectedLink:
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
|
|
},
|
|
{
|
|
url: "https://drive.google.com/open?id=1AbCdEfGhIjKlMnOpQrStUvWxYz123456",
|
|
expectedLink:
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
|
|
},
|
|
{
|
|
url: "https://drive.google.com/uc?export=download&id=1AbCdEfGhIjKlMnOpQrStUvWxYz123456",
|
|
expectedLink:
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
|
|
},
|
|
])("should normalize Google Drive link: $url", ({ url, expectedLink }) => {
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toBe(expectedLink);
|
|
}
|
|
expect(result?.intrinsicSize).toEqual({ w: 560, h: 315 });
|
|
});
|
|
|
|
it("should preserve resourcekey when available", () => {
|
|
const url =
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?resourcekey=0-abcdef123456";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toBe(
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?resourcekey=0-abcdef123456",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("should preserve timestamp when available", () => {
|
|
const url =
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?t=9";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toBe(
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?t=9",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("should preserve resourcekey and timestamp together", () => {
|
|
const url =
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?resourcekey=0-abcdef123456&t=9";
|
|
const result = getEmbedLink(url);
|
|
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("video");
|
|
if (result?.type === "video" || result?.type === "generic") {
|
|
expect(result.link).toBe(
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?resourcekey=0-abcdef123456&t=9",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("should validate Google Drive domain by default", () => {
|
|
expect(
|
|
embeddableURLValidator(
|
|
"https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view",
|
|
undefined,
|
|
),
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("Google Maps embedding", () => {
|
|
const regularUrl =
|
|
"https://www.google.com/maps/place/26-432+Jab%C5%82onica,+Poland/@51.356302,20.797168,1921m/data=!3m2!1e3!4b1!4m15!1m8!3m7!1s0x47186c0e0e7578fd:0xe80d19a1ef6ad853!2zMjctMTAwIEnFgsW8YSwgUG9sYW5k!3b1!8m2!3d51.16305!4d21.23991!16zL20vMGM1ZnJ3!3m5!1s0x47184db43a4a5df9:0x6a2b8e648f9dc694!8m2!3d51.3562959!4d20.8023178!16s%2Fm%2F04q6t9r?entry=ttu";
|
|
const officialEmbedSrc =
|
|
"https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d8363.540754033738!2d20.79716795156659!3d51.356301987021546!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47184db43a4a5df9%3A0x6a2b8e648f9dc694!2s26-432%20Jab%C5%82onica%2C%20Poland!5e1!3m2!1sen!2scz!4v1778159513974!5m2!1sen!2scz";
|
|
|
|
it("should preserve official Google Maps embed links", () => {
|
|
const parsedSrc = maybeParseEmbedSrc(
|
|
`<iframe src="${officialEmbedSrc}" width="600" height="450"></iframe>`,
|
|
);
|
|
const result = getEmbedLink(parsedSrc);
|
|
|
|
expect(embeddableURLValidator(parsedSrc, undefined)).toBe(true);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("generic");
|
|
if (result?.type === "generic") {
|
|
expect(result.link).toBe(officialEmbedSrc);
|
|
}
|
|
expect(result?.intrinsicSize).toEqual({ w: 600, h: 450 });
|
|
});
|
|
|
|
it("should normalize regular Google Maps place links", () => {
|
|
const result = getEmbedLink(regularUrl);
|
|
|
|
expect(embeddableURLValidator(regularUrl, undefined)).toBe(true);
|
|
expect(result).toBeTruthy();
|
|
expect(result?.type).toBe("generic");
|
|
if (result?.type !== "generic") {
|
|
return;
|
|
}
|
|
|
|
const embedURL = new URL(result.link);
|
|
expect(embedURL.origin).toBe("https://www.google.com");
|
|
expect(embedURL.pathname).toBe("/maps");
|
|
expect(embedURL.searchParams.get("q")).toBe(
|
|
decodeURIComponent("26-432%20Jab%C5%82onica%2C%20Poland"),
|
|
);
|
|
expect(embedURL.searchParams.get("output")).toBe("embed");
|
|
expect(embedURL.searchParams.get("ll")).toBe("51.356302,20.797168");
|
|
expect(embedURL.searchParams.get("z")).toBe("14");
|
|
expect(result.intrinsicSize).toEqual({ w: 600, h: 450 });
|
|
});
|
|
|
|
it("should reject non-Maps Google pages and fail closed for unsupported Maps pages", () => {
|
|
expect(
|
|
embeddableURLValidator("https://www.google.com/search?q=maps", undefined),
|
|
).toBe(false);
|
|
|
|
const unsupportedMapsUrl = "https://www.google.com/maps/about/";
|
|
expect(embeddableURLValidator(unsupportedMapsUrl, undefined)).toBe(true);
|
|
expect(getEmbedLink(unsupportedMapsUrl)).toBe(null);
|
|
|
|
const malformedMapsUrl = `https://www.google.com/maps/@${"0,0,".repeat(
|
|
1000,
|
|
)}`;
|
|
expect(embeddableURLValidator(malformedMapsUrl, undefined)).toBe(true);
|
|
});
|
|
});
|