Adds thin client wrappers (+ URL-building tests) for GET
/api/troubleshoot/playback/{stream-selectors,subtitles/{id},status}, used by
the playback troubleshooting screen (#145).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
getTroubleshootingInfo,
|
|
getTroubleshootingPlaybackStatus,
|
|
getTroubleshootingStreamSelectors,
|
|
getTroubleshootingSubtitles,
|
|
validateSequentialSchedule
|
|
} from './troubleshoot';
|
|
|
|
function jsonResponse(body: unknown, status = 200): Response {
|
|
return new Response(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' }, status });
|
|
}
|
|
|
|
const sampleInfo = {
|
|
generalJson: '{"Version":"1.2.3"}',
|
|
nvidiaCapabilities: 'nvidia output',
|
|
qsvCapabilities: null,
|
|
vaapiCapabilities: null,
|
|
videoToolboxCapabilities: null
|
|
};
|
|
|
|
describe('getTroubleshootingInfo', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs /api/troubleshoot/info and returns the info payload', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify(sampleInfo), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 200
|
|
})
|
|
);
|
|
|
|
await expect(getTroubleshootingInfo()).resolves.toMatchObject({ nvidiaCapabilities: 'nvidia output' });
|
|
|
|
const [url, init] = fetchSpy.mock.calls[0];
|
|
expect(url).toBe('/api/troubleshoot/info');
|
|
expect((init?.method ?? 'GET').toUpperCase()).toBe('GET');
|
|
});
|
|
|
|
it('rejects with the ApiError status on failure', async () => {
|
|
vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify({ status: 500, title: 'Server Error' }), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 500
|
|
})
|
|
);
|
|
|
|
await expect(getTroubleshootingInfo()).rejects.toMatchObject({ status: 500 });
|
|
});
|
|
});
|
|
|
|
describe('validateSequentialSchedule', () => {
|
|
const sampleResult = { isValid: true, messages: [], json: '{}' };
|
|
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('POSTs /api/troubleshoot/validate-schedule with the yaml and isImport body', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify(sampleResult), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 200
|
|
})
|
|
);
|
|
|
|
await expect(validateSequentialSchedule('content: []', true)).resolves.toMatchObject({ isValid: true });
|
|
|
|
const [url, init] = fetchSpy.mock.calls[0];
|
|
expect(url).toBe('/api/troubleshoot/validate-schedule');
|
|
expect((init?.method ?? 'GET').toUpperCase()).toBe('POST');
|
|
expect(JSON.parse(String(init?.body))).toEqual({ yaml: 'content: []', isImport: true });
|
|
});
|
|
|
|
it('rejects with the ApiError status on failure', async () => {
|
|
vi.spyOn(window, 'fetch').mockResolvedValue(
|
|
new Response(JSON.stringify({ status: 400, title: 'Validation failed' }), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
status: 400
|
|
})
|
|
);
|
|
|
|
await expect(validateSequentialSchedule('', false)).rejects.toMatchObject({ status: 400 });
|
|
});
|
|
});
|
|
|
|
describe('troubleshooting playback wrappers', () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('GETs the stream selectors list', async () => {
|
|
const fetchSpy = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse(['a', 'b']));
|
|
|
|
await expect(getTroubleshootingStreamSelectors()).resolves.toEqual(['a', 'b']);
|
|
expect(fetchSpy.mock.calls[0][0]).toBe('/api/troubleshoot/playback/stream-selectors');
|
|
});
|
|
|
|
it('GETs the subtitle list for a media item id', async () => {
|
|
const fetchSpy = vi
|
|
.spyOn(window, 'fetch')
|
|
.mockResolvedValue(jsonResponse([{ id: 3, language: 'eng', title: 'English', codec: 'subrip' }]));
|
|
|
|
await expect(getTroubleshootingSubtitles(42)).resolves.toMatchObject([{ id: 3 }]);
|
|
expect(fetchSpy.mock.calls[0][0]).toBe('/api/troubleshoot/playback/subtitles/42');
|
|
});
|
|
|
|
it('GETs the playback status', async () => {
|
|
const fetchSpy = vi
|
|
.spyOn(window, 'fetch')
|
|
.mockResolvedValue(jsonResponse({ state: 'completed', exitCode: 0, speed: 1.2, logs: 'ok' }));
|
|
|
|
await expect(getTroubleshootingPlaybackStatus()).resolves.toMatchObject({ state: 'completed', speed: 1.2 });
|
|
expect(fetchSpy.mock.calls[0][0]).toBe('/api/troubleshoot/playback/status');
|
|
});
|
|
});
|