Added tests for utils

This commit is contained in:
Nick Sandstrom 2026-07-14 14:17:25 -07:00
parent 6d5a5a549a
commit f6ad115a1f
3 changed files with 409 additions and 0 deletions

View file

@ -0,0 +1,210 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
// ── Mocks ──────────────────────────────────────────────────────────────────────
vi.mock('../../../api', () => ({
default: {
addOutputProfile: vi.fn(),
updateOutputProfile: vi.fn(),
},
}));
const mockResolver = vi.fn();
vi.mock('@hookform/resolvers/yup', () => ({
yupResolver: vi.fn(() => mockResolver),
}));
// ── Imports after mocks ────────────────────────────────────────────────────────
import API from '../../../api';
import { yupResolver } from '@hookform/resolvers/yup';
import {
BUILT_IN_COMMANDS,
COMMAND_EXAMPLES,
toCommandSelection,
schema,
addOutputProfile,
updateOutputProfile,
getResolver,
} from '../OutputProfileUtils';
// ──────────────────────────────────────────────────────────────────────────────
describe('OutputProfileUtils', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(yupResolver).mockReturnValue(mockResolver);
});
// ── Constants ──────────────────────────────────────────────────────────────
describe('BUILT_IN_COMMANDS', () => {
it('includes the ffmpeg entry', () => {
expect(BUILT_IN_COMMANDS).toContainEqual({
value: 'ffmpeg',
label: 'FFmpeg',
});
});
it('includes the custom entry', () => {
expect(BUILT_IN_COMMANDS).toContainEqual({
value: '__custom__',
label: 'Custom…',
});
});
});
describe('COMMAND_EXAMPLES', () => {
it('has a non-empty string example for ffmpeg', () => {
expect(typeof COMMAND_EXAMPLES.ffmpeg).toBe('string');
expect(COMMAND_EXAMPLES.ffmpeg.length).toBeGreaterThan(0);
});
});
// ── toCommandSelection ─────────────────────────────────────────────────────
describe('toCommandSelection', () => {
it('returns the command value when it matches a non-custom built-in', () => {
expect(toCommandSelection('ffmpeg')).toBe('ffmpeg');
});
it('returns "__custom__" when command is "__custom__"', () => {
// __custom__ is in BUILT_IN_COMMANDS but excluded by the o.value !== '__custom__' guard
expect(toCommandSelection('__custom__')).toBe('__custom__');
});
it('returns "__custom__" for an unrecognized command string', () => {
expect(toCommandSelection('my-arbitrary-tool')).toBe('__custom__');
});
it('returns "__custom__" for an empty string', () => {
expect(toCommandSelection('')).toBe('__custom__');
});
it('returns "__custom__" for undefined', () => {
expect(toCommandSelection(undefined)).toBe('__custom__');
});
});
// ── schema ─────────────────────────────────────────────────────────────────
describe('schema', () => {
it('validates a fully populated object', async () => {
await expect(
schema.validate({
name: 'HD Profile',
command: 'ffmpeg',
parameters: '-c:v copy',
})
).resolves.toMatchObject({
name: 'HD Profile',
command: 'ffmpeg',
parameters: '-c:v copy',
});
});
it('validates when parameters is omitted (optional)', async () => {
await expect(
schema.validate({ name: 'HD Profile', command: 'ffmpeg' })
).resolves.toMatchObject({ name: 'HD Profile', command: 'ffmpeg' });
});
it('rejects when name is missing', async () => {
await expect(schema.validate({ command: 'ffmpeg' })).rejects.toThrow(
'Name is required'
);
});
it('rejects when name is an empty string', async () => {
await expect(
schema.validate({ name: '', command: 'ffmpeg' })
).rejects.toThrow('Name is required');
});
it('rejects when command is missing', async () => {
await expect(schema.validate({ name: 'HD Profile' })).rejects.toThrow(
'Command is required'
);
});
it('rejects when command is an empty string', async () => {
await expect(
schema.validate({ name: 'HD Profile', command: '' })
).rejects.toThrow('Command is required');
});
});
// ── addOutputProfile ───────────────────────────────────────────────────────
describe('addOutputProfile', () => {
it('calls API.addOutputProfile with the provided values', async () => {
const values = { name: 'New Profile', command: 'ffmpeg', parameters: '' };
vi.mocked(API.addOutputProfile).mockResolvedValue({ id: 1, ...values });
await addOutputProfile(values);
expect(API.addOutputProfile).toHaveBeenCalledWith(values);
expect(API.addOutputProfile).toHaveBeenCalledTimes(1);
});
it('returns the API response', async () => {
const values = { name: 'New Profile', command: 'ffmpeg' };
const response = { id: 42, ...values };
vi.mocked(API.addOutputProfile).mockResolvedValue(response);
const result = await addOutputProfile(values);
expect(result).toEqual(response);
});
it('propagates errors thrown by API.addOutputProfile', async () => {
vi.mocked(API.addOutputProfile).mockRejectedValue(
new Error('Network error')
);
await expect(addOutputProfile({})).rejects.toThrow('Network error');
});
});
// ── updateOutputProfile ────────────────────────────────────────────────────
describe('updateOutputProfile', () => {
it('calls API.updateOutputProfile with the provided values', async () => {
const values = { id: 1, name: 'Updated Profile', command: 'ffmpeg' };
vi.mocked(API.updateOutputProfile).mockResolvedValue(values);
await updateOutputProfile(values);
expect(API.updateOutputProfile).toHaveBeenCalledWith(values);
expect(API.updateOutputProfile).toHaveBeenCalledTimes(1);
});
it('returns the API response', async () => {
const values = { id: 7, name: 'Updated Profile', command: 'ffmpeg' };
vi.mocked(API.updateOutputProfile).mockResolvedValue(values);
const result = await updateOutputProfile(values);
expect(result).toEqual(values);
});
it('propagates errors thrown by API.updateOutputProfile', async () => {
vi.mocked(API.updateOutputProfile).mockRejectedValue(
new Error('Update failed')
);
await expect(updateOutputProfile({})).rejects.toThrow('Update failed');
});
});
// ── getResolver ────────────────────────────────────────────────────────────
describe('getResolver', () => {
it('returns the result of yupResolver called with schema', () => {
const resolver = getResolver();
expect(yupResolver).toHaveBeenCalledWith(schema);
expect(resolver).toBe(mockResolver);
});
});
});

View file

@ -0,0 +1,114 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
// ── Mocks ──────────────────────────────────────────────────────────────────────
vi.mock('../../../api', () => ({
default: {
addServerGroup: vi.fn(),
updateServerGroup: vi.fn(),
},
}));
const mockResolver = vi.fn();
vi.mock('@hookform/resolvers/yup', () => ({
yupResolver: vi.fn(() => mockResolver),
}));
// ── Imports after mocks ────────────────────────────────────────────────────────
import API from '../../../api';
import { yupResolver } from '@hookform/resolvers/yup';
import {
getResolver,
addServerGroup,
updateServerGroup,
} from '../ServerGroupUtils';
// ──────────────────────────────────────────────────────────────────────────────
describe('ServerGroupUtils', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(yupResolver).mockReturnValue(mockResolver);
});
// ── getResolver ────────────────────────────────────────────────────────────
describe('getResolver', () => {
it('calls yupResolver with a schema and returns the result', () => {
const resolver = getResolver();
expect(yupResolver).toHaveBeenCalledTimes(1);
expect(yupResolver).toHaveBeenCalledWith(expect.any(Object));
expect(resolver).toBe(mockResolver);
});
it('returns a new resolver on each call', () => {
const resolverA = getResolver();
const resolverB = getResolver();
expect(yupResolver).toHaveBeenCalledTimes(2);
expect(resolverA).toBe(mockResolver);
expect(resolverB).toBe(mockResolver);
});
});
// ── addServerGroup ─────────────────────────────────────────────────────────
describe('addServerGroup', () => {
it('calls API.addServerGroup with the provided values', async () => {
const values = { name: 'US East' };
vi.mocked(API.addServerGroup).mockResolvedValue({ id: 1, ...values });
await addServerGroup(values);
expect(API.addServerGroup).toHaveBeenCalledWith(values);
expect(API.addServerGroup).toHaveBeenCalledTimes(1);
});
it('returns the API response', async () => {
const values = { name: 'US East' };
const response = { id: 1, ...values };
vi.mocked(API.addServerGroup).mockResolvedValue(response);
const result = await addServerGroup(values);
expect(result).toEqual(response);
});
it('propagates errors thrown by API.addServerGroup', async () => {
vi.mocked(API.addServerGroup).mockRejectedValue(new Error('Network error'));
await expect(addServerGroup({ name: 'Test' })).rejects.toThrow('Network error');
});
});
// ── updateServerGroup ──────────────────────────────────────────────────────
describe('updateServerGroup', () => {
it('calls API.updateServerGroup with the provided values', async () => {
const values = { id: 5, name: 'EU West' };
vi.mocked(API.updateServerGroup).mockResolvedValue(values);
await updateServerGroup(values);
expect(API.updateServerGroup).toHaveBeenCalledWith(values);
expect(API.updateServerGroup).toHaveBeenCalledTimes(1);
});
it('returns the API response', async () => {
const values = { id: 5, name: 'EU West' };
vi.mocked(API.updateServerGroup).mockResolvedValue(values);
const result = await updateServerGroup(values);
expect(result).toEqual(values);
});
it('propagates errors thrown by API.updateServerGroup', async () => {
vi.mocked(API.updateServerGroup).mockRejectedValue(new Error('Update failed'));
await expect(updateServerGroup({ id: 1, name: 'Test' })).rejects.toThrow('Update failed');
});
});
});

View file

@ -11,6 +11,9 @@ vi.mock('../../../api.js', () => ({
reloadPlugins: vi.fn(),
deletePlugin: vi.fn(),
getPluginDetailManifest: vi.fn(),
getPluginRepoSettings: vi.fn(),
updatePluginRepoSettings: vi.fn(),
previewPluginRepo: vi.fn(),
},
}));
@ -344,4 +347,86 @@ describe('PluginsUtils', () => {
expect(API.getPluginDetailManifest).toHaveBeenCalledWith(null, null);
});
});
describe('getPluginRepoSettings', () => {
it('should call API getPluginRepoSettings', () => {
PluginsUtils.getPluginRepoSettings();
expect(API.getPluginRepoSettings).toHaveBeenCalledTimes(1);
});
it('should return API response', () => {
const mockResponse = { repos: [] };
API.getPluginRepoSettings.mockReturnValue(mockResponse);
const result = PluginsUtils.getPluginRepoSettings();
expect(result).toEqual(mockResponse);
});
});
describe('updatePluginRepoSettings', () => {
it('should call API updatePluginRepoSettings with values', () => {
const values = { repos: ['https://example.com/repo.json'] };
PluginsUtils.updatePluginRepoSettings(values);
expect(API.updatePluginRepoSettings).toHaveBeenCalledWith(values);
expect(API.updatePluginRepoSettings).toHaveBeenCalledTimes(1);
});
it('should return API response', () => {
const values = { repos: ['https://example.com/repo.json'] };
const mockResponse = { success: true };
API.updatePluginRepoSettings.mockReturnValue(mockResponse);
const result = PluginsUtils.updatePluginRepoSettings(values);
expect(result).toEqual(mockResponse);
});
});
describe('previewPluginRepo', () => {
it('should call API previewPluginRepo with url and publicKey', () => {
const url = 'https://example.com/repo.json';
const publicKey = 'public-key';
PluginsUtils.previewPluginRepo(url, publicKey);
expect(API.previewPluginRepo).toHaveBeenCalledWith(url, publicKey);
expect(API.previewPluginRepo).toHaveBeenCalledTimes(1);
});
it('should return API response', () => {
const url = 'https://example.com/repo.json';
const publicKey = 'public-key';
const mockResponse = { name: 'Test Repo', plugins: [] };
API.previewPluginRepo.mockReturnValue(mockResponse);
const result = PluginsUtils.previewPluginRepo(url, publicKey);
expect(result).toEqual(mockResponse);
});
it('should handle empty string url and publicKey', () => {
const url = '';
const publicKey = '';
PluginsUtils.previewPluginRepo(url, publicKey);
expect(API.previewPluginRepo).toHaveBeenCalledWith('', '');
});
it('should handle null url and publicKey', () => {
const url = null;
const publicKey = null;
PluginsUtils.previewPluginRepo(url, publicKey);
expect(API.previewPluginRepo).toHaveBeenCalledWith(null, null);
});
});
});