import { describe, it, expect, beforeEach, vi } from 'vitest';
import { parseXMLTV, parseXMLTVDate, fetchAndParseXMLTV } from '../parsers/xmltv';
// Mock DOMParser for Node environment
if (typeof DOMParser === 'undefined') {
global.DOMParser = class {
parseFromString(xml: string, type: string): Document {
const { JSDOM } = require('jsdom');
const dom = new JSDOM(xml, { contentType: 'text/xml' });
return dom.window.document;
}
} as any;
}
describe('XMLTV Parser', () => {
describe('parseXMLTVDate', () => {
it('should parse XMLTV date with timezone', () => {
const dateStr = '20240101120000 +0000';
const result = parseXMLTVDate(dateStr);
expect(result).toBe('2024-01-01T12:00:00.000Z');
});
it('should parse XMLTV date without timezone', () => {
const dateStr = '20240101120000';
const result = parseXMLTVDate(dateStr);
expect(result).toBe('2024-01-01T12:00:00.000Z');
});
it('should parse XMLTV date with custom timezone', () => {
const dateStr = '20240101120000';
const result = parseXMLTVDate(dateStr, '+0500');
// The result will be converted to UTC, so 12:00 +05:00 = 07:00 UTC
expect(result).toBe('2024-01-01T07:00:00.000Z');
});
it('should throw error for invalid date format', () => {
expect(() => parseXMLTVDate('invalid')).toThrow();
});
});
describe('parseXMLTV', () => {
it('should parse basic XMLTV format', () => {
const xml = `
HBO
Test Show
Test Description
Movies
`;
const result = parseXMLTV(xml);
expect(result.channels).toHaveLength(1);
expect(result.channels[0].id).toBe('hbo');
expect(result.channels[0].displayName).toBe('HBO');
expect(result.channels[0].icon).toBe('http://example.com/hbo.png');
expect(result.programs).toHaveLength(1);
expect(result.programs[0].channelId).toBe('hbo');
expect(result.programs[0].title).toBe('Test Show');
expect(result.programs[0].description).toBe('Test Description');
expect(result.programs[0].category).toBe('Movies');
});
it('should parse multiple channels and programmes', () => {
const xml = `
HBO
ESPN
Show 1
Show 2
`;
const result = parseXMLTV(xml);
expect(result.channels).toHaveLength(2);
expect(result.programs).toHaveLength(2);
});
it('should handle missing optional fields', () => {
const xml = `
HBO
Test Show
`;
const result = parseXMLTV(xml);
expect(result.programs[0].description).toBeUndefined();
expect(result.programs[0].category).toBeUndefined();
expect(result.programs[0].image).toBeUndefined();
});
it('should handle channel without display-name', () => {
const xml = `
`;
const result = parseXMLTV(xml);
expect(result.channels).toHaveLength(1);
expect(result.channels[0].displayName).toBe('hbo');
});
it('should skip programmes without required attributes', () => {
const xml = `
HBO
Valid Show
Invalid Show
`;
const result = parseXMLTV(xml);
expect(result.programs).toHaveLength(1);
expect(result.programs[0].title).toBe('Valid Show');
});
it('should skip programmes with invalid dates', () => {
const xml = `
HBO
Invalid Date Show
Valid Show
`;
const result = parseXMLTV(xml);
expect(result.programs).toHaveLength(1);
expect(result.programs[0].title).toBe('Valid Show');
});
it('should parse episode information', () => {
const xml = `
HBO
Test Show
S01E01
`;
const result = parseXMLTV(xml);
expect(result.programs[0].episode).toBeDefined();
expect(result.programs[0].episode?.season).toBe(1);
expect(result.programs[0].episode?.episode).toBe(1);
});
it('should parse programme icon', () => {
const xml = `
HBO
Test Show
`;
const result = parseXMLTV(xml);
expect(result.programs[0].image).toBe('http://example.com/show.png');
});
});
describe('fetchAndParseXMLTV', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should fetch and parse XMLTV from URL', async () => {
const mockXml = `
HBO
Test Show
`;
global.fetch = vi.fn().mockResolvedValue({
ok: true,
text: async () => mockXml,
});
const result = await fetchAndParseXMLTV('https://example.com/epg.xml');
expect(result.channels).toHaveLength(1);
expect(result.programs).toHaveLength(1);
expect(global.fetch).toHaveBeenCalledWith('https://example.com/epg.xml', {
headers: {
'Accept': 'application/xml, text/xml',
},
});
});
it('should use proxy URL if provided', async () => {
const mockXml = `
HBO
`;
global.fetch = vi.fn().mockResolvedValue({
ok: true,
text: async () => mockXml,
});
await fetchAndParseXMLTV(
'https://example.com/epg.xml',
undefined,
'https://proxy.com'
);
expect(global.fetch).toHaveBeenCalledWith(
'https://proxy.com?url=' + encodeURIComponent('https://example.com/epg.xml'),
expect.any(Object)
);
});
it('should throw error on fetch failure', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: false,
statusText: 'Not Found',
});
await expect(
fetchAndParseXMLTV('https://example.com/epg.xml')
).rejects.toThrow('Failed to fetch XMLTV: Not Found');
});
});
});