url-validation-simple.test.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /**
  2. * @fileoverview Simple URL Validation Tests
  3. * @author GrabZilla Development Team
  4. * @version 2.1.0
  5. * @since 2024-01-01
  6. */
  7. import { describe, it, expect } from 'vitest';
  8. // Simple URL validation functions for testing
  9. const URLValidationUtils = {
  10. isYouTubeUrl(url) {
  11. if (!url) return false;
  12. return /(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)[\w\-_]{11}/.test(url);
  13. },
  14. isVimeoUrl(url) {
  15. if (!url) return false;
  16. return /(?:vimeo\.com\/|player\.vimeo\.com\/video\/)\d+/.test(url);
  17. },
  18. isYouTubePlaylist(url) {
  19. if (!url) return false;
  20. return /youtube\.com\/playlist\?list=[\w\-_]+/.test(url);
  21. },
  22. isValidVideoUrl(url) {
  23. if (!url || typeof url !== 'string') return false;
  24. const trimmed = url.trim();
  25. if (!trimmed) return false;
  26. return this.isYouTubeUrl(trimmed) || this.isVimeoUrl(trimmed) || this.isYouTubePlaylist(trimmed);
  27. },
  28. normalizeUrl(url) {
  29. if (!url || typeof url !== 'string') return url;
  30. let normalized = url.trim();
  31. // Add protocol if missing
  32. if (!/^https?:\/\//.test(normalized)) {
  33. normalized = 'https://' + normalized;
  34. }
  35. // Add www. for YouTube if missing
  36. if (/^https?:\/\/youtube\.com/.test(normalized)) {
  37. normalized = normalized.replace('://youtube.com', '://www.youtube.com');
  38. }
  39. return normalized;
  40. },
  41. validateMultipleUrls(text) {
  42. if (!text || typeof text !== 'string') {
  43. return { valid: [], invalid: [] };
  44. }
  45. const lines = text.split('\n').map(l => l.trim()).filter(l => l);
  46. const valid = [];
  47. const invalid = [];
  48. lines.forEach(line => {
  49. const normalized = this.normalizeUrl(line);
  50. if (this.isValidVideoUrl(normalized)) {
  51. valid.push(normalized);
  52. } else {
  53. invalid.push(line);
  54. }
  55. });
  56. // Remove duplicates
  57. return {
  58. valid: [...new Set(valid)],
  59. invalid: [...new Set(invalid)]
  60. };
  61. },
  62. getValidationError(url) {
  63. if (url === null || url === undefined) return 'URL is required';
  64. if (typeof url !== 'string') return 'URL is required';
  65. if (url === '') return 'URL cannot be empty';
  66. if (!url.trim()) return 'URL cannot be empty';
  67. if (!url.includes('.')) return 'Invalid URL format - must include domain';
  68. if (!this.isValidVideoUrl(url)) return 'Unsupported video platform - currently supports YouTube and Vimeo';
  69. return null;
  70. }
  71. };
  72. describe('URL Validation Utils', () => {
  73. describe('YouTube URL Validation', () => {
  74. it('should validate standard YouTube URLs', () => {
  75. const validUrls = [
  76. 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
  77. 'https://youtube.com/watch?v=dQw4w9WgXcQ',
  78. 'http://www.youtube.com/watch?v=dQw4w9WgXcQ',
  79. 'www.youtube.com/watch?v=dQw4w9WgXcQ',
  80. 'youtube.com/watch?v=dQw4w9WgXcQ'
  81. ];
  82. validUrls.forEach(url => {
  83. const normalized = URLValidationUtils.normalizeUrl(url);
  84. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  85. expect(URLValidationUtils.isYouTubeUrl(normalized)).toBe(true);
  86. });
  87. });
  88. it('should validate YouTube short URLs', () => {
  89. const validUrls = [
  90. 'https://youtu.be/dQw4w9WgXcQ',
  91. 'http://youtu.be/dQw4w9WgXcQ',
  92. 'youtu.be/dQw4w9WgXcQ'
  93. ];
  94. validUrls.forEach(url => {
  95. const normalized = URLValidationUtils.normalizeUrl(url);
  96. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  97. expect(URLValidationUtils.isYouTubeUrl(normalized)).toBe(true);
  98. });
  99. });
  100. it('should validate YouTube playlist URLs', () => {
  101. const validUrls = [
  102. 'https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME',
  103. 'https://youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME',
  104. 'www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME'
  105. ];
  106. validUrls.forEach(url => {
  107. const normalized = URLValidationUtils.normalizeUrl(url);
  108. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  109. expect(URLValidationUtils.isYouTubePlaylist(normalized)).toBe(true);
  110. });
  111. });
  112. });
  113. describe('Vimeo URL Validation', () => {
  114. it('should validate standard Vimeo URLs', () => {
  115. const validUrls = [
  116. 'https://vimeo.com/123456789',
  117. 'http://vimeo.com/123456789',
  118. 'www.vimeo.com/123456789',
  119. 'vimeo.com/123456789'
  120. ];
  121. validUrls.forEach(url => {
  122. const normalized = URLValidationUtils.normalizeUrl(url);
  123. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  124. expect(URLValidationUtils.isVimeoUrl(normalized)).toBe(true);
  125. });
  126. });
  127. it('should validate Vimeo player URLs', () => {
  128. const validUrls = [
  129. 'https://player.vimeo.com/video/123456789',
  130. 'http://player.vimeo.com/video/123456789',
  131. 'player.vimeo.com/video/123456789'
  132. ];
  133. validUrls.forEach(url => {
  134. const normalized = URLValidationUtils.normalizeUrl(url);
  135. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  136. expect(URLValidationUtils.isVimeoUrl(normalized)).toBe(true);
  137. });
  138. });
  139. });
  140. describe('Invalid URL Handling', () => {
  141. it('should reject invalid URLs', () => {
  142. const invalidUrls = [
  143. '',
  144. null,
  145. undefined,
  146. 'not a url',
  147. 'https://google.com',
  148. 'https://facebook.com/video',
  149. 'https://tiktok.com/@user/video/123',
  150. 'https://instagram.com/p/abc123'
  151. ];
  152. invalidUrls.forEach(url => {
  153. expect(URLValidationUtils.isValidVideoUrl(url)).toBe(false);
  154. });
  155. });
  156. it('should provide detailed validation errors', () => {
  157. const testCases = [
  158. { url: '', expectedError: 'URL cannot be empty' },
  159. { url: null, expectedError: 'URL is required' },
  160. { url: 'not a url', expectedError: 'Invalid URL format - must include domain' },
  161. { url: 'https://tiktok.com/@user/video/123', expectedError: 'Unsupported video platform - currently supports YouTube and Vimeo' },
  162. { url: 'https://google.com', expectedError: 'Unsupported video platform - currently supports YouTube and Vimeo' }
  163. ];
  164. testCases.forEach(({ url, expectedError }) => {
  165. const error = URLValidationUtils.getValidationError(url);
  166. expect(error).toBe(expectedError);
  167. });
  168. });
  169. });
  170. describe('Text Processing', () => {
  171. it('should extract multiple URLs from text', () => {
  172. const text = `
  173. Here are some videos:
  174. https://www.youtube.com/watch?v=dQw4w9WgXcQ
  175. https://vimeo.com/123456789
  176. And another one:
  177. youtu.be/dQw4w9WgXcQ
  178. This is not a video URL: https://google.com
  179. `;
  180. const result = URLValidationUtils.validateMultipleUrls(text);
  181. expect(result.valid).toHaveLength(3);
  182. expect(result.valid).toContain('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
  183. expect(result.valid).toContain('https://vimeo.com/123456789');
  184. expect(result.valid).toContain('https://youtu.be/dQw4w9WgXcQ');
  185. });
  186. it('should handle mixed content and normalize URLs', () => {
  187. const text = `
  188. youtube.com/watch?v=dQw4w9WgXcQ
  189. www.vimeo.com/987654321
  190. https://youtu.be/dQw4w9WgXcQ
  191. `;
  192. const result = URLValidationUtils.validateMultipleUrls(text);
  193. expect(result.valid.length).toBeGreaterThan(0);
  194. result.valid.forEach(url => {
  195. expect(url).toMatch(/^https:\/\//);
  196. expect(URLValidationUtils.isValidVideoUrl(url)).toBe(true);
  197. });
  198. });
  199. it('should remove duplicate URLs', () => {
  200. const text = `
  201. https://www.youtube.com/watch?v=dQw4w9WgXcQ
  202. https://www.youtube.com/watch?v=dQw4w9WgXcQ
  203. youtube.com/watch?v=dQw4w9WgXcQ
  204. `;
  205. const result = URLValidationUtils.validateMultipleUrls(text);
  206. // Should normalize and deduplicate
  207. expect(result.valid).toHaveLength(1);
  208. expect(result.valid[0]).toBe('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
  209. });
  210. });
  211. describe('URL Normalization', () => {
  212. it('should add https protocol to URLs without protocol', () => {
  213. const testCases = [
  214. { input: 'youtube.com/watch?v=dQw4w9WgXcQ', expected: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' },
  215. { input: 'www.vimeo.com/123456', expected: 'https://www.vimeo.com/123456' },
  216. { input: 'https://youtu.be/dQw4w9WgXcQ', expected: 'https://youtu.be/dQw4w9WgXcQ' }
  217. ];
  218. testCases.forEach(({ input, expected }) => {
  219. const normalized = URLValidationUtils.normalizeUrl(input);
  220. expect(normalized).toMatch(/^https:\/\//);
  221. // Check that it's a valid video URL after normalization
  222. expect(URLValidationUtils.isValidVideoUrl(normalized)).toBe(true);
  223. });
  224. });
  225. });
  226. });