New web/src/api/languages.ts module (getLanguages) plus channels.ts additions (getMusicVideoCreditsTemplates, getChannelStreamSelectors, createChannel) for the channel-editor gaps in #212. Each has URL-building tests.
167 lines
4.8 KiB
TypeScript
167 lines
4.8 KiB
TypeScript
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/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/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/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/channels');
|
|
expect((init?.method ?? '').toUpperCase()).toBe('POST');
|
|
});
|
|
});
|
|
|
|
describe('getMusicVideoCreditsTemplates', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs /api/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/channels/music-video-credits-templates');
|
|
});
|
|
});
|
|
|
|
describe('getChannelStreamSelectors', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs /api/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/channels/stream-selectors');
|
|
});
|
|
});
|