- Sidebar: useSidebarNavigation hook, ARIA, token-based layout - Layout: lg:ml-main-expanded/collapsed (replace arbitrary ml-64) - TrackCardSkeleton + PlaylistCardSkeleton: KŌDŌ tokens, min-heights for CLS - ContentFadeIn: 200ms fade-in with --ease-out - TrackGrid, PlaylistList, LibraryPage: integrate skeletons + fade-in - Player: player-bar subcomponents, useAudioAnalyser - Tests: TrackGrid wrapper (QueryClient, ToastProvider) Co-authored-by: Cursor <cursoragent@cursor.com>
328 lines
8.6 KiB
TypeScript
328 lines
8.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { AxiosError } from 'axios';
|
|
import { getSettings, getDefaultSettings, updateSettings } from './settingsService';
|
|
import { apiClient } from '@/services/api/client';
|
|
import { UserSettings, UpdateSettingsRequest } from '../types/settings';
|
|
|
|
// Mock apiClient
|
|
vi.mock('@/services/api/client', () => ({
|
|
apiClient: {
|
|
get: vi.fn(),
|
|
put: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
describe('settingsService', () => {
|
|
const mockSettings: UserSettings = {
|
|
notifications: {
|
|
email_notifications: true,
|
|
push_notifications: true,
|
|
browser_notifications: true,
|
|
email_on_follow: true,
|
|
email_on_like: true,
|
|
email_on_comment: true,
|
|
email_on_message: true,
|
|
email_on_mention: true,
|
|
email_marketing: false,
|
|
},
|
|
privacy: {
|
|
allow_search_indexing: true,
|
|
show_activity: true,
|
|
},
|
|
content: {
|
|
explicit_content: false,
|
|
autoplay: true,
|
|
},
|
|
preferences: {
|
|
language: 'en',
|
|
timezone: 'UTC',
|
|
theme: 'auto',
|
|
},
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('getSettings', () => {
|
|
it('should return settings successfully', async () => {
|
|
vi.mocked(apiClient.get).mockResolvedValue({
|
|
data: mockSettings,
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config: {} as any,
|
|
});
|
|
|
|
const result = await getSettings(1);
|
|
|
|
expect(result).toEqual(mockSettings);
|
|
expect(apiClient.get).toHaveBeenCalledWith('/users/1/settings');
|
|
});
|
|
|
|
it('should throw error on 401 Unauthorized', async () => {
|
|
const error = new AxiosError('Unauthorized');
|
|
error.response = {
|
|
status: 401,
|
|
data: {},
|
|
statusText: 'Unauthorized',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.get).mockRejectedValue(error);
|
|
|
|
await expect(getSettings(1)).rejects.toThrow(
|
|
'Unauthorized: Please log in to access settings',
|
|
);
|
|
});
|
|
|
|
it('should throw error on 403 Forbidden', async () => {
|
|
const error = new AxiosError('Forbidden');
|
|
error.response = {
|
|
status: 403,
|
|
data: {},
|
|
statusText: 'Forbidden',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.get).mockRejectedValue(error);
|
|
|
|
await expect(getSettings(1)).rejects.toThrow(
|
|
'Forbidden: You cannot access these settings',
|
|
);
|
|
});
|
|
|
|
it('should return default settings on 404 Not Found', async () => {
|
|
const error = new AxiosError('Not Found');
|
|
error.response = {
|
|
status: 404,
|
|
data: {},
|
|
statusText: 'Not Found',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.get).mockRejectedValue(error);
|
|
|
|
const result = await getSettings('1');
|
|
const defaults = getDefaultSettings();
|
|
expect(result.preferences.language).toBe(defaults.preferences.language);
|
|
expect(result.preferences.theme).toBe(defaults.preferences.theme);
|
|
expect(result.notifications).toEqual(defaults.notifications);
|
|
expect(result.privacy).toEqual(defaults.privacy);
|
|
expect(result.content).toEqual(defaults.content);
|
|
});
|
|
|
|
it('should throw error with custom message from response', async () => {
|
|
const error = new AxiosError('Error');
|
|
error.response = {
|
|
status: 500,
|
|
data: { error: 'Internal server error' },
|
|
statusText: 'Internal Server Error',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.get).mockRejectedValue(error);
|
|
|
|
await expect(getSettings(1)).rejects.toThrow('Internal server error');
|
|
});
|
|
|
|
it('should throw generic error on unknown error', async () => {
|
|
const error = new Error('Network error');
|
|
vi.mocked(apiClient.get).mockRejectedValue(error);
|
|
|
|
await expect(getSettings(1)).rejects.toThrow(error);
|
|
});
|
|
});
|
|
|
|
describe('updateSettings', () => {
|
|
const updateRequest: UpdateSettingsRequest = {
|
|
preferences: {
|
|
language: 'fr',
|
|
timezone: 'Europe/Paris',
|
|
theme: 'dark',
|
|
},
|
|
};
|
|
|
|
it('should update settings successfully', async () => {
|
|
vi.mocked(apiClient.put).mockResolvedValue({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config: {} as any,
|
|
});
|
|
|
|
await updateSettings(1, updateRequest);
|
|
|
|
expect(apiClient.put).toHaveBeenCalledWith(
|
|
'/users/settings',
|
|
updateRequest,
|
|
);
|
|
});
|
|
|
|
it('should throw error on 400 Bad Request', async () => {
|
|
const error = new AxiosError('Bad Request');
|
|
error.response = {
|
|
status: 400,
|
|
data: { error: 'Invalid settings data' },
|
|
statusText: 'Bad Request',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(
|
|
'Invalid settings data',
|
|
);
|
|
});
|
|
|
|
it('should throw error on 401 Unauthorized', async () => {
|
|
const error = new AxiosError('Unauthorized');
|
|
error.response = {
|
|
status: 401,
|
|
data: {},
|
|
statusText: 'Unauthorized',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(
|
|
'Unauthorized: Please log in to update settings',
|
|
);
|
|
});
|
|
|
|
it('should throw error on 403 Forbidden', async () => {
|
|
const error = new AxiosError('Forbidden');
|
|
error.response = {
|
|
status: 403,
|
|
data: {},
|
|
statusText: 'Forbidden',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(
|
|
'Forbidden: You cannot update these settings',
|
|
);
|
|
});
|
|
|
|
it('should throw error on 404 Not Found', async () => {
|
|
const error = new AxiosError('Not Found');
|
|
error.response = {
|
|
status: 404,
|
|
data: {},
|
|
statusText: 'Not Found',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(
|
|
'Settings not found',
|
|
);
|
|
});
|
|
|
|
it('should throw error with custom message from response', async () => {
|
|
const error = new AxiosError('Error');
|
|
error.response = {
|
|
status: 500,
|
|
data: { error: 'Internal server error' },
|
|
statusText: 'Internal Server Error',
|
|
headers: {},
|
|
config: {} as any,
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(
|
|
'Internal server error',
|
|
);
|
|
});
|
|
|
|
it('should throw generic error on unknown error', async () => {
|
|
const error = new Error('Network error');
|
|
vi.mocked(apiClient.put).mockRejectedValue(error);
|
|
|
|
await expect(updateSettings(1, updateRequest)).rejects.toThrow(error);
|
|
});
|
|
|
|
it('should update only notifications', async () => {
|
|
const notificationsOnly: UpdateSettingsRequest = {
|
|
notifications: {
|
|
email_notifications: false,
|
|
},
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockResolvedValue({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config: {} as any,
|
|
});
|
|
|
|
await updateSettings(1, notificationsOnly);
|
|
|
|
expect(apiClient.put).toHaveBeenCalledWith(
|
|
'/users/settings',
|
|
notificationsOnly,
|
|
);
|
|
});
|
|
|
|
it('should update only privacy', async () => {
|
|
const privacyOnly: UpdateSettingsRequest = {
|
|
privacy: {
|
|
allow_search_indexing: false,
|
|
},
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockResolvedValue({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config: {} as any,
|
|
});
|
|
|
|
await updateSettings(1, privacyOnly);
|
|
|
|
expect(apiClient.put).toHaveBeenCalledWith(
|
|
'/users/settings',
|
|
privacyOnly,
|
|
);
|
|
});
|
|
|
|
it('should update only content', async () => {
|
|
const contentOnly: UpdateSettingsRequest = {
|
|
content: {
|
|
explicit_content: true,
|
|
},
|
|
};
|
|
|
|
vi.mocked(apiClient.put).mockResolvedValue({
|
|
data: {},
|
|
status: 200,
|
|
statusText: 'OK',
|
|
headers: {},
|
|
config: {} as any,
|
|
});
|
|
|
|
await updateSettings(1, contentOnly);
|
|
|
|
expect(apiClient.put).toHaveBeenCalledWith(
|
|
'/users/settings',
|
|
contentOnly,
|
|
);
|
|
});
|
|
});
|
|
});
|