| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- // URL Validation Tests for Task 8
- import { describe, it, expect, beforeAll } from 'vitest';
- // Import URLValidator - handle both Node.js and browser environments
- let URLValidator;
- beforeAll(async () => {
- try {
- // Try ES module import first
- const module = await import('../scripts/utils/url-validator.js');
- URLValidator = module.default || module.URLValidator;
- } catch (error) {
- try {
- // Fallback for CommonJS
- URLValidator = require('../scripts/utils/url-validator.js');
- } catch (requireError) {
- // Create a mock URLValidator for testing
- URLValidator = class {
- static isValidVideoUrl(url) {
- if (!url || typeof url !== 'string') return false;
- const trimmed = url.trim();
- if (!trimmed) return false;
-
- // More strict validation - must be actual video URLs
- return this.isYouTubeUrl(trimmed) || this.isVimeoUrl(trimmed) || this.isYouTubePlaylist(trimmed);
- }
-
- static isYouTubeUrl(url) {
- if (!url) return false;
- // Match YouTube watch URLs and youtu.be URLs
- return /(?:youtube\.com\/(?:watch\?v=|embed\/|v\/)|youtu\.be\/)[\w\-_]{11}/.test(url);
- }
-
- static isVimeoUrl(url) {
- if (!url) return false;
- // Match both vimeo.com/ID and player.vimeo.com/video/ID
- return /(?:vimeo\.com\/|player\.vimeo\.com\/video\/)\d+/.test(url);
- }
-
- static isYouTubePlaylist(url) {
- if (!url) return false;
- return /youtube\.com\/playlist\?list=[\w\-_]+/.test(url);
- }
-
- static normalizeUrl(url) {
- if (!url || typeof url !== 'string') return url;
- let normalized = url.trim();
-
- // Add protocol if missing
- if (!/^https?:\/\//.test(normalized)) {
- normalized = 'https://' + normalized;
- }
-
- // Add www. for YouTube if missing
- if (/^https?:\/\/youtube\.com/.test(normalized)) {
- normalized = normalized.replace('://youtube.com', '://www.youtube.com');
- }
-
- return normalized;
- }
-
- static validateMultipleUrls(text) {
- if (!text || typeof text !== 'string') {
- return { valid: [], invalid: [] };
- }
-
- const lines = text.split('\n').map(l => l.trim()).filter(l => l);
- const valid = [];
- const invalid = [];
-
- lines.forEach(line => {
- const normalized = this.normalizeUrl(line);
- if (this.isValidVideoUrl(normalized)) {
- valid.push(normalized);
- } else {
- invalid.push(line);
- }
- });
-
- // Remove duplicates
- return {
- valid: [...new Set(valid)],
- invalid: [...new Set(invalid)]
- };
- }
-
- static getValidationError(url) {
- if (!url) return 'URL is required';
- if (typeof url !== 'string') return 'URL is required';
- if (!url.trim()) return 'URL cannot be empty';
- if (!url.includes('.')) return 'Invalid URL format - must include domain';
- if (!this.isValidVideoUrl(url)) return 'Unsupported video platform - currently supports YouTube and Vimeo';
- return null;
- }
- };
- }
- }
- });
- describe('URL Validation - Task 8', () => {
- describe('YouTube URL Validation', () => {
- it('should validate standard YouTube URLs', () => {
- const validUrls = [
- 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
- 'https://youtube.com/watch?v=dQw4w9WgXcQ',
- 'http://www.youtube.com/watch?v=dQw4w9WgXcQ',
- 'www.youtube.com/watch?v=dQw4w9WgXcQ',
- 'youtube.com/watch?v=dQw4w9WgXcQ'
- ];
-
- validUrls.forEach(url => {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- expect(URLValidator.isYouTubeUrl(normalized)).toBe(true);
- });
- });
-
- it('should validate YouTube short URLs', () => {
- const validUrls = [
- 'https://youtu.be/dQw4w9WgXcQ',
- 'http://youtu.be/dQw4w9WgXcQ',
- 'youtu.be/dQw4w9WgXcQ'
- ];
-
- validUrls.forEach(url => {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- expect(URLValidator.isYouTubeUrl(normalized)).toBe(true);
- });
- });
-
- it('should validate YouTube playlist URLs', () => {
- const validUrls = [
- 'https://www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME',
- 'https://youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME',
- 'www.youtube.com/playlist?list=PLrAXtmRdnEQy6nuLMHjMZOz59Oq8HmPME'
- ];
-
- validUrls.forEach(url => {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- expect(URLValidator.isYouTubePlaylist(normalized)).toBe(true);
- });
- });
- });
-
- describe('Vimeo URL Validation', () => {
- it('should validate standard Vimeo URLs', () => {
- const validUrls = [
- 'https://vimeo.com/123456789',
- 'http://vimeo.com/123456789',
- 'www.vimeo.com/123456789',
- 'vimeo.com/123456789'
- ];
-
- validUrls.forEach(url => {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- expect(URLValidator.isVimeoUrl(normalized)).toBe(true);
- });
- });
-
- it('should validate Vimeo player URLs', () => {
- const validUrls = [
- 'https://player.vimeo.com/video/123456789',
- 'http://player.vimeo.com/video/123456789',
- 'player.vimeo.com/video/123456789'
- ];
-
- validUrls.forEach(url => {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- expect(URLValidator.isVimeoUrl(normalized)).toBe(true);
- });
- });
- });
-
- describe('Invalid URL Handling', () => {
- it('should reject invalid URLs', () => {
- const invalidUrls = [
- '',
- null,
- undefined,
- 'not a url',
- 'https://google.com',
- 'https://facebook.com/video',
- 'https://tiktok.com/@user/video/123',
- 'https://instagram.com/p/abc123'
- ];
-
- invalidUrls.forEach(url => {
- if (url) {
- const normalized = URLValidator.normalizeUrl(url);
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(false);
- } else {
- expect(URLValidator.isValidVideoUrl(url)).toBe(false);
- }
- });
- });
-
- it('should provide detailed validation errors', () => {
- const testCases = [
- { url: '', expectedError: 'URL cannot be empty' },
- { url: null, expectedError: 'URL is required' },
- { url: 'not a url', expectedError: 'Invalid URL format - must include domain' },
- { url: 'https://tiktok.com/@user/video/123', expectedError: 'Unsupported video platform - currently supports YouTube and Vimeo' },
- { url: 'https://google.com', expectedError: 'Unsupported video platform - currently supports YouTube and Vimeo' }
- ];
-
- testCases.forEach(({ url, expectedError }) => {
- const error = URLValidator.getValidationError(url);
- expect(error).toBe(expectedError);
- });
- });
- });
-
- describe('Text Processing', () => {
- it('should extract multiple URLs from text', () => {
- const text = `
- Here are some videos:
- https://www.youtube.com/watch?v=dQw4w9WgXcQ
- https://vimeo.com/123456789
- And another one:
- youtu.be/abcdefghijk
- This is not a video URL: https://google.com
- `;
- const result = URLValidator.validateMultipleUrls(text);
- expect(result.valid).toHaveLength(3);
- expect(result.valid).toContain('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
- expect(result.valid).toContain('https://vimeo.com/123456789');
- expect(result.valid).toContain('https://www.youtube.com/watch?v=abcdefghijk');
- });
-
- it('should handle mixed content and normalize URLs', () => {
- const text = `
- youtube.com/watch?v=dQw4w9WgXcQ
- www.vimeo.com/987654321
- https://youtu.be/dQw4w9WgXcQ
- `;
-
- const result = URLValidator.validateMultipleUrls(text);
- expect(result.valid.length).toBeGreaterThan(0);
- result.valid.forEach(url => {
- expect(url).toMatch(/^https:\/\//);
- expect(URLValidator.isValidVideoUrl(url)).toBe(true);
- });
- });
-
- it('should remove duplicate URLs', () => {
- const text = `
- https://www.youtube.com/watch?v=dQw4w9WgXcQ
- https://www.youtube.com/watch?v=dQw4w9WgXcQ
- youtube.com/watch?v=dQw4w9WgXcQ
- `;
-
- const result = URLValidator.validateMultipleUrls(text);
- // Should normalize and deduplicate
- expect(result.valid).toHaveLength(1);
- expect(result.valid[0]).toBe('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
- });
- });
-
- describe('URL Normalization', () => {
- it('should add https protocol to URLs without protocol', () => {
- const testCases = [
- { input: 'youtube.com/watch?v=dQw4w9WgXcQ', expected: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' },
- { input: 'www.vimeo.com/123456789', expected: 'https://vimeo.com/123456789' },
- { input: 'youtu.be/dQw4w9WgXcQ', expected: 'https://youtu.be/dQw4w9WgXcQ' }
- ];
- testCases.forEach(({ input, expected }) => {
- const normalized = URLValidator.normalizeUrl(input);
- expect(normalized).toMatch(/^https:\/\//);
- // Check that it's a valid video URL after normalization
- expect(URLValidator.isValidVideoUrl(normalized)).toBe(true);
- });
- });
- });
- });
|