import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createPlayout, getAlternateSchedules, getPlayoutTemplates, replaceAlternateSchedules, replacePlayoutTemplates, updatePlayoutDefaultDeco, updatePlayoutDetails, type CreatePlayoutRequest } from './playouts'; function jsonResponse(body: unknown, status = 200): Response { return new Response(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' }, status }); } describe('playouts api client', () => { beforeEach(() => { window.localStorage.clear(); vi.restoreAllMocks(); }); it('createPlayout POSTs a classic playout request', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 7 }, 201)); const body: CreatePlayoutRequest = { channelId: 1, programScheduleId: 2, scheduleFile: null, scheduleKind: 'Classic' }; await expect(createPlayout(body)).resolves.toMatchObject({ id: 7 }); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts'); expect(init).toMatchObject({ method: 'POST' }); expect(JSON.parse(String(init?.body))).toMatchObject({ channelId: 1, programScheduleId: 2, scheduleKind: 'Classic' }); }); it('createPlayout POSTs a sequential playout request with a schedule file', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 9 }, 201)); const body: CreatePlayoutRequest = { channelId: 3, programScheduleId: null, scheduleFile: '/config/schedule.yml', scheduleKind: 'Sequential' }; await createPlayout(body); const [, init] = fetchMock.mock.calls[0]; expect(JSON.parse(String(init?.body))).toMatchObject({ channelId: 3, scheduleFile: '/config/schedule.yml', scheduleKind: 'Sequential' }); }); it('updatePlayoutDetails PUTs to the id route', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 3 })); await updatePlayoutDetails(3, { dailyRebuildTime: '04:00:00', scheduleFile: null }); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/3'); expect(init).toMatchObject({ method: 'PUT' }); expect(JSON.parse(String(init?.body))).toMatchObject({ dailyRebuildTime: '04:00:00' }); }); it('updatePlayoutDetails sends null dailyRebuildTime to clear the daily reset', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 3 })); await updatePlayoutDetails(3, { dailyRebuildTime: null, scheduleFile: null }); const [, init] = fetchMock.mock.calls[0]; expect(JSON.parse(String(init?.body))).toMatchObject({ dailyRebuildTime: null }); }); it('updatePlayoutDefaultDeco PUTs the deco id to the deco route', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 3 })); await updatePlayoutDefaultDeco(3, 7); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/3/deco'); expect(init).toMatchObject({ method: 'PUT' }); expect(JSON.parse(String(init?.body))).toMatchObject({ decoId: 7 }); }); it('getAlternateSchedules GETs the alternate-schedules route', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse([])); await getAlternateSchedules(5); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/5/alternate-schedules'); expect(init?.method ?? 'GET').toBe('GET'); }); it('replaceAlternateSchedules PUTs items with day-name strings', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse([])); await replaceAlternateSchedules(5, { items: [ { id: 0, programScheduleId: 2, daysOfWeek: ['Monday', 'Tuesday'], daysOfMonth: [1, 2], monthsOfYear: [1], limitToDateRange: false, startMonth: 1, startDay: 1, startYear: null, endMonth: 12, endDay: 31, endYear: null } ] }); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/5/alternate-schedules'); expect(init).toMatchObject({ method: 'PUT' }); const body = JSON.parse(String(init?.body)); expect(body.items[0].daysOfWeek).toEqual(['Monday', 'Tuesday']); expect(body.items[0].programScheduleId).toBe(2); }); it('getPlayoutTemplates GETs the templates route', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse([])); await getPlayoutTemplates(5); const [url] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/5/templates'); }); it('replacePlayoutTemplates PUTs template items', async () => { const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse([])); await replacePlayoutTemplates(5, { items: [ { id: 0, templateId: 3, decoTemplateId: null, daysOfWeek: ['Sunday'], daysOfMonth: [1], monthsOfYear: [1], limitToDateRange: false, startMonth: 1, startDay: 1, startYear: null, endMonth: 12, endDay: 31, endYear: null } ] }); const [url, init] = fetchMock.mock.calls[0]; expect(url).toBe('/api/playouts/5/templates'); expect(init).toMatchObject({ method: 'PUT' }); const body = JSON.parse(String(init?.body)); expect(body.items[0].templateId).toBe(3); expect(body.items[0].decoTemplateId).toBeNull(); }); });