embeddable.test.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { embeddableURLValidator, getEmbedLink } from "../src/embeddable";
  2. describe("YouTube timestamp parsing", () => {
  3. it("should parse YouTube URLs with timestamp in seconds", () => {
  4. const testCases = [
  5. {
  6. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=90",
  7. expectedStart: 90,
  8. },
  9. {
  10. url: "https://youtu.be/dQw4w9WgXcQ?t=120",
  11. expectedStart: 120,
  12. },
  13. {
  14. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&start=150",
  15. expectedStart: 150,
  16. },
  17. ];
  18. testCases.forEach(({ url, expectedStart }) => {
  19. const result = getEmbedLink(url);
  20. expect(result).toBeTruthy();
  21. expect(result?.type).toBe("video");
  22. if (result?.type === "video" || result?.type === "generic") {
  23. expect(result.link).toContain(`start=${expectedStart}`);
  24. }
  25. });
  26. });
  27. it("should parse YouTube URLs with timestamp in time format", () => {
  28. const testCases = [
  29. {
  30. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=1m30s",
  31. expectedStart: 90, // 1*60 + 30
  32. },
  33. {
  34. url: "https://youtu.be/dQw4w9WgXcQ?t=2m45s",
  35. expectedStart: 165, // 2*60 + 45
  36. },
  37. {
  38. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=1h2m3s",
  39. expectedStart: 3723, // 1*3600 + 2*60 + 3
  40. },
  41. {
  42. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=45s",
  43. expectedStart: 45,
  44. },
  45. {
  46. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=5m",
  47. expectedStart: 300, // 5*60
  48. },
  49. {
  50. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=2h",
  51. expectedStart: 7200, // 2*3600
  52. },
  53. ];
  54. testCases.forEach(({ url, expectedStart }) => {
  55. const result = getEmbedLink(url);
  56. expect(result).toBeTruthy();
  57. expect(result?.type).toBe("video");
  58. if (result?.type === "video" || result?.type === "generic") {
  59. expect(result.link).toContain(`start=${expectedStart}`);
  60. }
  61. });
  62. });
  63. it("should handle YouTube URLs without timestamps", () => {
  64. const testCases = [
  65. "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  66. "https://youtu.be/dQw4w9WgXcQ",
  67. "https://www.youtube.com/embed/dQw4w9WgXcQ",
  68. ];
  69. testCases.forEach((url) => {
  70. const result = getEmbedLink(url);
  71. expect(result).toBeTruthy();
  72. expect(result?.type).toBe("video");
  73. if (result?.type === "video" || result?.type === "generic") {
  74. expect(result.link).not.toContain("start=");
  75. }
  76. });
  77. });
  78. it("should handle YouTube shorts URLs with timestamps", () => {
  79. const url = "https://www.youtube.com/shorts/dQw4w9WgXcQ?t=30";
  80. const result = getEmbedLink(url);
  81. expect(result).toBeTruthy();
  82. expect(result?.type).toBe("video");
  83. if (result?.type === "video" || result?.type === "generic") {
  84. expect(result.link).toContain("start=30");
  85. }
  86. // Shorts should have portrait aspect ratio
  87. expect(result?.intrinsicSize).toEqual({ w: 315, h: 560 });
  88. });
  89. it("should handle playlist URLs with timestamps", () => {
  90. const url =
  91. "https://www.youtube.com/playlist?list=PLrAXtmRdnEQy1KbG5lbfgQ0-PKQY6FKYZ&t=60";
  92. const result = getEmbedLink(url);
  93. expect(result).toBeTruthy();
  94. expect(result?.type).toBe("video");
  95. if (result?.type === "video" || result?.type === "generic") {
  96. expect(result.link).toContain("start=60");
  97. expect(result.link).toContain("list=PLrAXtmRdnEQy1KbG5lbfgQ0-PKQY6FKYZ");
  98. }
  99. });
  100. it("should handle malformed or edge case timestamps", () => {
  101. const testCases = [
  102. {
  103. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=abc",
  104. expectedStart: 0, // Invalid timestamp should default to 0
  105. },
  106. {
  107. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=",
  108. expectedStart: 0, // Empty timestamp should default to 0
  109. },
  110. {
  111. url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=0",
  112. expectedStart: 0, // Zero timestamp should be handled
  113. },
  114. ];
  115. testCases.forEach(({ url, expectedStart }) => {
  116. const result = getEmbedLink(url);
  117. expect(result).toBeTruthy();
  118. expect(result?.type).toBe("video");
  119. if (result?.type === "video" || result?.type === "generic") {
  120. if (expectedStart === 0) {
  121. expect(result.link).not.toContain("start=");
  122. } else {
  123. expect(result.link).toContain(`start=${expectedStart}`);
  124. }
  125. }
  126. });
  127. });
  128. it("should preserve other URL parameters", () => {
  129. const url =
  130. "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=90&feature=youtu.be&list=PLtest";
  131. const result = getEmbedLink(url);
  132. expect(result).toBeTruthy();
  133. expect(result?.type).toBe("video");
  134. if (result?.type === "video" || result?.type === "generic") {
  135. expect(result.link).toContain("start=90");
  136. expect(result.link).toContain("enablejsapi=1");
  137. }
  138. });
  139. });
  140. describe("Google Drive video embedding", () => {
  141. it.each([
  142. {
  143. url: "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?usp=sharing",
  144. expectedLink:
  145. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
  146. },
  147. {
  148. url: "https://drive.google.com/open?id=1AbCdEfGhIjKlMnOpQrStUvWxYz123456",
  149. expectedLink:
  150. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
  151. },
  152. {
  153. url: "https://drive.google.com/uc?export=download&id=1AbCdEfGhIjKlMnOpQrStUvWxYz123456",
  154. expectedLink:
  155. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview",
  156. },
  157. ])("should normalize Google Drive link: $url", ({ url, expectedLink }) => {
  158. const result = getEmbedLink(url);
  159. expect(result).toBeTruthy();
  160. expect(result?.type).toBe("video");
  161. if (result?.type === "video" || result?.type === "generic") {
  162. expect(result.link).toBe(expectedLink);
  163. }
  164. expect(result?.intrinsicSize).toEqual({ w: 560, h: 315 });
  165. });
  166. it("should preserve resourcekey when available", () => {
  167. const url =
  168. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?resourcekey=0-abcdef123456";
  169. const result = getEmbedLink(url);
  170. expect(result).toBeTruthy();
  171. expect(result?.type).toBe("video");
  172. if (result?.type === "video" || result?.type === "generic") {
  173. expect(result.link).toBe(
  174. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?resourcekey=0-abcdef123456",
  175. );
  176. }
  177. });
  178. it("should preserve timestamp when available", () => {
  179. const url =
  180. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?t=9";
  181. const result = getEmbedLink(url);
  182. expect(result).toBeTruthy();
  183. expect(result?.type).toBe("video");
  184. if (result?.type === "video" || result?.type === "generic") {
  185. expect(result.link).toBe(
  186. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?t=9",
  187. );
  188. }
  189. });
  190. it("should preserve resourcekey and timestamp together", () => {
  191. const url =
  192. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view?resourcekey=0-abcdef123456&t=9";
  193. const result = getEmbedLink(url);
  194. expect(result).toBeTruthy();
  195. expect(result?.type).toBe("video");
  196. if (result?.type === "video" || result?.type === "generic") {
  197. expect(result.link).toBe(
  198. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/preview?resourcekey=0-abcdef123456&t=9",
  199. );
  200. }
  201. });
  202. it("should validate Google Drive domain by default", () => {
  203. expect(
  204. embeddableURLValidator(
  205. "https://drive.google.com/file/d/1AbCdEfGhIjKlMnOpQrStUvWxYz123456/view",
  206. undefined,
  207. ),
  208. ).toBe(true);
  209. });
  210. });