Files
player/src/utils/corsHelper.test.ts
T
2025-10-29 13:13:47 +03:00

226 lines
7.4 KiB
TypeScript

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');
});
});
});