import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { isSameOrigin, isBlobOrDataURL, validateVideoURL, getCORSErrorMessage, isCORSError, checkVideoCORS, } from './corsHelper'; describe('corsHelper', () => { describe('isSameOrigin', () => { it('returns true for same origin URLs', () => { const sameOriginUrl = `${window.location.origin}/video.mp4`; expect(isSameOrigin(sameOriginUrl)).toBe(true); }); it('returns false for different origin URLs', () => { expect(isSameOrigin('https://example.com/video.mp4')).toBe(false); }); it('returns true for relative URLs', () => { expect(isSameOrigin('/videos/test.mp4')).toBe(true); }); it('returns true for relative path-like strings', () => { // In browsers, "not-a-url" is treated as a relative URL expect(isSameOrigin('not-a-url')).toBe(true); }); }); describe('isBlobOrDataURL', () => { it('returns true for blob URLs', () => { expect(isBlobOrDataURL('blob:http://example.com/123456')).toBe(true); }); it('returns true for data URLs', () => { expect(isBlobOrDataURL('data:video/mp4;base64,AAAA')).toBe(true); }); it('returns false for regular URLs', () => { expect(isBlobOrDataURL('https://example.com/video.mp4')).toBe(false); }); it('returns false for empty string', () => { expect(isBlobOrDataURL('')).toBe(false); }); }); describe('validateVideoURL', () => { it('returns invalid for empty URL', () => { const result = validateVideoURL(''); expect(result.valid).toBe(false); expect(result.error).toBe('Video URL is empty'); }); it('returns invalid for whitespace-only URL', () => { const result = validateVideoURL(' '); expect(result.valid).toBe(false); expect(result.error).toBe('Video URL is empty'); }); it('returns valid for relative path strings', () => { // Browser treats this as a relative URL const result = validateVideoURL('not a valid url'); expect(result.valid).toBe(true); }); it('returns valid for same origin URL without warning', () => { const result = validateVideoURL(`${window.location.origin}/video.mp4`); expect(result.valid).toBe(true); expect(result.warning).toBeUndefined(); }); it('returns valid for blob URL without warning', () => { const result = validateVideoURL('blob:http://example.com/123456'); expect(result.valid).toBe(true); expect(result.warning).toBeUndefined(); }); it('returns valid with warning for external URL', () => { const result = validateVideoURL('https://example.com/video.mp4'); expect(result.valid).toBe(true); expect(result.warning).toContain('CORS'); }); it('returns valid for relative URLs', () => { const result = validateVideoURL('/videos/test.mp4'); expect(result.valid).toBe(true); expect(result.warning).toBeUndefined(); }); }); describe('getCORSErrorMessage', () => { it('returns generic message for same origin', () => { const message = getCORSErrorMessage(`${window.location.origin}/video.mp4`); expect(message).toBe('Failed to load video. Please check the URL.'); }); it('returns generic message for blob URLs', () => { const message = getCORSErrorMessage('blob:http://example.com/123456'); expect(message).toBe('Failed to load video. Please check the URL.'); }); it('returns CORS-specific message for external URLs', () => { const message = getCORSErrorMessage('https://example.com/video.mp4'); expect(message).toContain('CORS Error'); expect(message).toContain('example.com'); expect(message).toContain('Access-Control-Allow-Origin'); }); }); describe('isCORSError', () => { it('returns true for errors containing "cors"', () => { const error = new Error('CORS policy blocked this request'); expect(isCORSError(error)).toBe(true); }); it('returns true for errors containing "cross-origin"', () => { const error = new Error('Cross-origin request blocked'); expect(isCORSError(error)).toBe(true); }); it('returns true for errors containing "blocked by cors policy"', () => { const error = new Error('Request blocked by CORS policy'); expect(isCORSError(error)).toBe(true); }); it('returns true for errors containing "access-control-allow-origin"', () => { const error = new Error('No \'access-control-allow-origin\' header present'); expect(isCORSError(error)).toBe(true); }); it('returns false for non-CORS errors', () => { const error = new Error('Network timeout'); expect(isCORSError(error)).toBe(false); }); it('is case insensitive', () => { const error = new Error('BLOCKED BY CORS POLICY'); expect(isCORSError(error)).toBe(true); }); }); describe('checkVideoCORS', () => { beforeEach(() => { globalThis.fetch = vi.fn(); }); afterEach(() => { vi.restoreAllMocks(); }); it('returns supported when CORS headers are present', async () => { (globalThis.fetch as any).mockResolvedValue({ ok: true, headers: new Map([ ['Access-Control-Allow-Origin', '*'], ['Accept-Ranges', 'bytes'], ]), }); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supported).toBe(true); expect(result.needsProxy).toBe(false); expect(result.supportsRange).toBe(true); }); it('returns not supported when CORS headers are missing', async () => { (globalThis.fetch as any).mockResolvedValue({ ok: false, headers: new Map(), }); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supported).toBe(false); expect(result.needsProxy).toBe(true); expect(result.error).toContain('CORS not enabled'); }); it('detects range support', async () => { (globalThis.fetch as any).mockResolvedValue({ ok: true, headers: new Map([ ['Access-Control-Allow-Origin', '*'], ['Accept-Ranges', 'bytes'], ]), }); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supportsRange).toBe(true); }); it('detects no range support when header is "none"', async () => { (globalThis.fetch as any).mockResolvedValue({ ok: true, headers: new Map([ ['Access-Control-Allow-Origin', '*'], ['Accept-Ranges', 'none'], ]), }); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supportsRange).toBe(false); }); it('handles CORS fetch errors', async () => { (globalThis.fetch as any).mockRejectedValue(new TypeError('Failed to fetch (CORS)')); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supported).toBe(false); expect(result.needsProxy).toBe(true); expect(result.error).toContain('CORS blocked'); }); it('handles general fetch errors', async () => { (globalThis.fetch as any).mockRejectedValue(new Error('Network error')); const result = await checkVideoCORS('https://example.com/video.mp4'); expect(result.supported).toBe(false); expect(result.needsProxy).toBe(true); expect(result.error).toBe('Network error'); }); }); });