import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createChannel, getChannelById, getChannelStreamSelectors, getMusicVideoCreditsTemplates, updateChannel, type CreateChannelRequest, type UpdateChannelRequest } from './channels'; const sampleChannel = { id: 5, number: '5', name: 'Cartoons', group: 'ChicoryTV', categories: '', ffmpegProfileId: 1, slugSeconds: null, logo: { path: '', contentType: '' }, streamSelectorMode: 'Default', streamSelector: null, preferredAudioLanguageCode: null, preferredAudioTitle: null, playoutSource: 'Generated', playoutMode: 'Continuous', mirrorSourceChannelId: null, playoutOffset: null, streamingMode: 'TransportStreamHybrid', watermarkId: null, fallbackFillerId: null, playoutCount: 0, preferredSubtitleLanguageCode: null, subtitleMode: 'None', musicVideoCreditsMode: 'None', musicVideoCreditsTemplate: null, songVideoMode: 'Default', transcodeMode: 'OnDemand', idleBehavior: 'StopOnDisconnect', isEnabled: true, showInEpg: true }; describe('getChannelById', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('GETs the channel by id and returns the view model', async () => { const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify(sampleChannel), { headers: { 'Content-Type': 'application/json' }, status: 200 }) ); await expect(getChannelById(5)).resolves.toMatchObject({ id: 5, name: 'Cartoons' }); const [url, init] = fetchSpy.mock.calls[0]; expect(url).toBe('/api/v1/channels/5'); expect((init?.method ?? 'GET').toUpperCase()).toBe('GET'); }); it('rejects with the ApiError status on 404', async () => { vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify({ status: 404, title: 'Not Found' }), { headers: { 'Content-Type': 'application/json' }, status: 404 }) ); await expect(getChannelById(999)).rejects.toMatchObject({ status: 404 }); }); }); describe('updateChannel', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('PUTs the request body to the channel id and returns the updated view model', async () => { const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify({ ...sampleChannel, name: 'Renamed' }), { headers: { 'Content-Type': 'application/json' }, status: 200 }) ); const body = { ...sampleChannel, name: 'Renamed' } as unknown as UpdateChannelRequest; await expect(updateChannel(5, body)).resolves.toMatchObject({ name: 'Renamed' }); const [url, init] = fetchSpy.mock.calls[0]; expect(url).toBe('/api/v1/channels/5'); expect((init?.method ?? '').toUpperCase()).toBe('PUT'); expect(JSON.parse(init?.body as string)).toMatchObject({ name: 'Renamed', number: '5' }); }); }); describe('createChannel', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('POSTs the request body to /api/v1/channels and returns the created view model', async () => { const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify(sampleChannel), { headers: { 'Content-Type': 'application/json' }, status: 200 }) ); const body = { ...sampleChannel, id: undefined } as unknown as CreateChannelRequest; await expect(createChannel(body)).resolves.toMatchObject({ id: 5 }); const [url, init] = fetchSpy.mock.calls[0]; expect(url).toBe('/api/v1/channels'); expect((init?.method ?? '').toUpperCase()).toBe('POST'); }); }); describe('getMusicVideoCreditsTemplates', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('GETs /api/v1/channels/music-video-credits-templates', async () => { const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify(['default']), { headers: { 'Content-Type': 'application/json' }, status: 200 }) ); await expect(getMusicVideoCreditsTemplates()).resolves.toEqual(['default']); const [url] = fetchSpy.mock.calls[0]; expect(url).toBe('/api/v1/channels/music-video-credits-templates'); }); }); describe('getChannelStreamSelectors', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('GETs /api/v1/channels/stream-selectors', async () => { const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue( new Response(JSON.stringify(['selector.py']), { headers: { 'Content-Type': 'application/json' }, status: 200 }) ); await expect(getChannelStreamSelectors()).resolves.toEqual(['selector.py']); const [url] = fetchSpy.mock.calls[0]; expect(url).toBe('/api/v1/channels/stream-selectors'); }); });