| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /**
- * @fileoverview Tests for YouTube Playlist Extraction
- * @description Test suite for playlist URL detection and data parsing
- */
- import { describe, it, expect } from 'vitest'
- /**
- * PLAYLIST EXTRACTION TESTS
- *
- * Tests playlist functionality:
- * - Playlist URL detection
- * - Playlist ID extraction
- * - JSON response parsing
- * - Error handling
- */
- describe('Playlist Extraction', () => {
- describe('Playlist URL Detection', () => {
- it('should detect valid playlist URLs', () => {
- const playlistUrls = [
- 'https://www.youtube.com/playlist?list=PLtest123',
- 'https://youtube.com/playlist?list=PLtest456',
- 'https://www.youtube.com/watch?v=abc12345678&list=PLtest789'
- ]
- playlistUrls.forEach(url => {
- expect(url).toMatch(/[?&]list=[\w\-]+/)
- })
- })
- it('should extract playlist ID from URL', () => {
- const testCases = [
- { url: 'https://www.youtube.com/playlist?list=PLtest12345', expected: 'PLtest12345' },
- { url: 'https://youtube.com/playlist?list=PLabc-xyz_123', expected: 'PLabc-xyz_123' },
- { url: 'https://www.youtube.com/watch?v=test&list=PLmixed123', expected: 'PLmixed123' }
- ]
- testCases.forEach(({ url, expected }) => {
- const match = url.match(/[?&]list=([\w\-]+)/)
- expect(match).not.toBeNull()
- expect(match[1]).toBe(expected)
- })
- })
- it('should reject non-playlist URLs', () => {
- const nonPlaylistUrls = [
- 'https://www.youtube.com/watch?v=abc12345678',
- 'https://youtu.be/xyz98765432',
- 'https://vimeo.com/123456789',
- 'https://youtube.com/shorts/test1234567'
- ]
- nonPlaylistUrls.forEach(url => {
- const match = url.match(/[?&]list=([\w\-]+)/)
- expect(match).toBeNull()
- })
- })
- })
- describe('Playlist Data Parsing', () => {
- it('should parse playlist JSON response with all fields', () => {
- const mockJsonLine = JSON.stringify({
- id: 'abc12345678',
- title: 'Test Video',
- url: 'https://www.youtube.com/watch?v=abc12345678',
- duration: 300,
- thumbnail: 'https://i.ytimg.com/vi/abc12345678/default.jpg',
- uploader: 'Test Channel'
- })
- const parsed = JSON.parse(mockJsonLine)
- expect(parsed.id).toBe('abc12345678')
- expect(parsed.title).toBe('Test Video')
- expect(parsed.url).toBe('https://www.youtube.com/watch?v=abc12345678')
- expect(parsed.duration).toBe(300)
- expect(parsed.thumbnail).toBe('https://i.ytimg.com/vi/abc12345678/default.jpg')
- expect(parsed.uploader).toBe('Test Channel')
- })
- it('should handle missing optional fields gracefully', () => {
- const mockJsonLine = JSON.stringify({
- id: 'abc12345678',
- title: 'Test Video'
- })
- const parsed = JSON.parse(mockJsonLine)
- expect(parsed.id).toBe('abc12345678')
- expect(parsed.title).toBe('Test Video')
- expect(parsed.duration).toBeUndefined()
- expect(parsed.thumbnail).toBeUndefined()
- expect(parsed.uploader).toBeUndefined()
- })
- it('should parse multiple JSON lines from playlist response', () => {
- const mockResponse = `{"id":"video1","title":"First Video"}
- {"id":"video2","title":"Second Video"}
- {"id":"video3","title":"Third Video"}`
- const lines = mockResponse.trim().split('\n')
- const videos = lines.map(line => JSON.parse(line))
- expect(videos).toHaveLength(3)
- expect(videos[0].id).toBe('video1')
- expect(videos[1].id).toBe('video2')
- expect(videos[2].id).toBe('video3')
- })
- })
- describe('Playlist Response Structure', () => {
- it('should create proper video objects from parsed data', () => {
- const mockData = {
- id: 'abc12345678',
- title: 'Test Video',
- url: 'https://www.youtube.com/watch?v=abc12345678',
- duration: 300,
- thumbnail: 'https://i.ytimg.com/vi/abc12345678/default.jpg',
- uploader: 'Test Channel'
- }
- const video = {
- id: mockData.id,
- title: mockData.title || 'Unknown Title',
- url: mockData.url || `https://www.youtube.com/watch?v=${mockData.id}`,
- duration: mockData.duration || null,
- thumbnail: mockData.thumbnail || null,
- uploader: mockData.uploader || mockData.channel || null
- }
- expect(video.id).toBe('abc12345678')
- expect(video.title).toBe('Test Video')
- expect(video.url).toBe('https://www.youtube.com/watch?v=abc12345678')
- expect(video.duration).toBe(300)
- expect(video.thumbnail).toBe('https://i.ytimg.com/vi/abc12345678/default.jpg')
- expect(video.uploader).toBe('Test Channel')
- })
- it('should use fallback values when fields are missing', () => {
- const mockData = {
- id: 'abc12345678'
- }
- const video = {
- id: mockData.id,
- title: mockData.title || 'Unknown Title',
- url: mockData.url || `https://www.youtube.com/watch?v=${mockData.id}`,
- duration: mockData.duration || null,
- thumbnail: mockData.thumbnail || null,
- uploader: mockData.uploader || mockData.channel || null
- }
- expect(video.id).toBe('abc12345678')
- expect(video.title).toBe('Unknown Title')
- expect(video.url).toBe('https://www.youtube.com/watch?v=abc12345678')
- expect(video.duration).toBeNull()
- expect(video.thumbnail).toBeNull()
- expect(video.uploader).toBeNull()
- })
- it('should handle channel field as fallback for uploader', () => {
- const mockData = {
- id: 'abc12345678',
- title: 'Test Video',
- channel: 'Channel Name'
- }
- const video = {
- id: mockData.id,
- title: mockData.title || 'Unknown Title',
- url: mockData.url || `https://www.youtube.com/watch?v=${mockData.id}`,
- duration: mockData.duration || null,
- thumbnail: mockData.thumbnail || null,
- uploader: mockData.uploader || mockData.channel || null
- }
- expect(video.uploader).toBe('Channel Name')
- })
- })
- describe('Playlist Response Validation', () => {
- it('should create valid success response structure', () => {
- const playlistId = 'PLtest12345'
- const videos = [
- { id: 'video1', title: 'First' },
- { id: 'video2', title: 'Second' }
- ]
- const response = {
- success: true,
- playlistId: playlistId,
- videoCount: videos.length,
- videos: videos
- }
- expect(response.success).toBe(true)
- expect(response.playlistId).toBe('PLtest12345')
- expect(response.videoCount).toBe(2)
- expect(response.videos).toHaveLength(2)
- })
- it('should count videos correctly', () => {
- const videos = []
- for (let i = 0; i < 50; i++) {
- videos.push({ id: `video${i}`, title: `Video ${i}` })
- }
- const response = {
- success: true,
- playlistId: 'PLtest',
- videoCount: videos.length,
- videos: videos
- }
- expect(response.videoCount).toBe(50)
- expect(response.videos).toHaveLength(50)
- })
- })
- describe('Error Handling', () => {
- it('should validate playlist URL format', () => {
- const invalidUrls = [
- '',
- null,
- undefined,
- 'https://www.youtube.com/watch?v=abc12345678',
- 'https://vimeo.com/123456789'
- ]
- invalidUrls.forEach(url => {
- if (url) {
- const match = url.match(/[?&]list=([\w\-]+)/)
- expect(match).toBeNull()
- }
- })
- })
- it('should handle JSON parse errors gracefully', () => {
- const invalidJson = 'not valid json'
- expect(() => {
- JSON.parse(invalidJson)
- }).toThrow()
- })
- it('should continue parsing when one line fails', () => {
- const mockResponse = `{"id":"video1","title":"First Video"}
- invalid json line here
- {"id":"video3","title":"Third Video"}`
- const lines = mockResponse.trim().split('\n')
- const videos = []
- lines.forEach(line => {
- try {
- const videoData = JSON.parse(line)
- videos.push(videoData)
- } catch (error) {
- // Skip invalid lines
- }
- })
- expect(videos).toHaveLength(2)
- expect(videos[0].id).toBe('video1')
- expect(videos[1].id).toBe('video3')
- })
- })
- })
|