feat: frontend unit tests

This commit is contained in:
Yiannis Stergiou
2026-01-07 19:47:54 +02:00
committed by GitHub
parent ed5cfa1a84
commit 1c15880ae3
74 changed files with 6000 additions and 877 deletions

View File

@@ -0,0 +1,79 @@
import { init, endpoints } from '../../../src/static/js/utils/settings/api';
const apiConfig = (url: any, ep: any) => {
init(url, ep);
return endpoints();
};
describe('utils/settings', () => {
describe('api', () => {
const sampleGlobal = {
site: { api: 'https://example.com/api/v1///' },
// The endpoints below intentionally contain leading slashes to ensure they are stripped
api: {
media: '/media/',
members: '/users//',
playlists: '/playlists',
liked: '/user/liked',
history: '/user/history',
tags: '/tags',
categories: '/categories',
manage_media: '/manage/media',
manage_users: '/manage/users',
manage_comments: '/manage/comments',
search: '/search',
},
} as const;
test('Trims trailing slashes on base and ensures single slash joins', () => {
const cfg = apiConfig(sampleGlobal.site.api, sampleGlobal.api);
// @todo: Check again the cases of trailing slashes
expect(cfg.media).toBe('https://example.com/api/v1/media/');
expect(cfg.users).toBe('https://example.com/api/v1/users//');
});
test('Adds featured/recommended query to media variants', () => {
const cfg = apiConfig(sampleGlobal.site.api, sampleGlobal.api);
expect(cfg.featured).toBe('https://example.com/api/v1/media/?show=featured');
expect(cfg.recommended).toBe('https://example.com/api/v1/media/?show=recommended');
});
test('Builds nested user, archive, manage maps', () => {
const cfg = apiConfig(sampleGlobal.site.api, sampleGlobal.api);
expect(cfg.user.liked).toBe('https://example.com/api/v1/user/liked');
expect(cfg.user.history).toBe('https://example.com/api/v1/user/history');
expect(cfg.user.playlists).toBe('https://example.com/api/v1/playlists?author=');
expect(cfg.archive.tags).toBe('https://example.com/api/v1/tags');
expect(cfg.archive.categories).toBe('https://example.com/api/v1/categories');
expect(cfg.manage.media).toBe('https://example.com/api/v1/manage/media');
expect(cfg.manage.users).toBe('https://example.com/api/v1/manage/users');
expect(cfg.manage.comments).toBe('https://example.com/api/v1/manage/comments');
});
test('Builds search endpoints with expected query fragments', () => {
const cfg = apiConfig(sampleGlobal.site.api, sampleGlobal.api);
expect(cfg.search.query).toBe('https://example.com/api/v1/search?q=');
expect(cfg.search.titles).toBe('https://example.com/api/v1/search?show=titles&q=');
expect(cfg.search.tag).toBe('https://example.com/api/v1/search?t=');
expect(cfg.search.category).toBe('https://example.com/api/v1/search?c=');
});
test('Handles base url with path and endpoint with existing query', () => {
const cfg = apiConfig('https://example.com/base/', {
media: 'items?x=1',
playlists: '/pls/',
liked: 'me/liked',
categories: '/c',
search: '/s',
});
expect(cfg.media).toBe('https://example.com/base/items?x=1');
expect(cfg.playlists).toBe('https://example.com/base/pls/');
expect(cfg.user.liked).toBe('https://example.com/base/me/liked');
expect(cfg.archive.categories).toBe('https://example.com/base/c');
expect(cfg.search.query).toBe('https://example.com/base/s?q=');
});
});
});

View File

@@ -0,0 +1,189 @@
import { config } from '../../../src/static/js/utils/settings/config';
describe('utils/settings', () => {
describe('config', () => {
const baseGlobal = {
profileId: 'john',
site: {
id: 'my-site',
url: 'https://example.com/',
api: 'https://example.com/api/',
title: 'Example',
theme: { mode: 'dark', switch: { enabled: true, position: 'sidebar' } },
logo: {
lightMode: { img: '/img/light.png', svg: '/img/light.svg' },
darkMode: { img: '/img/dark.png', svg: '/img/dark.svg' },
},
devEnv: false,
useRoundedCorners: true,
version: '2.0.0',
taxonomies: {
tags: { enabled: true, title: 'Topic Tags' },
categories: { enabled: false, title: 'Kinds' },
},
pages: {
latest: { enabled: true, title: 'Recent uploads' },
featured: { enabled: true, title: 'Featured picks' },
recommended: { enabled: false, title: 'You may like' },
},
userPages: {
members: { enabled: true, title: 'People' },
liked: { enabled: true, title: 'Favorites' },
history: { enabled: true, title: 'Watched' },
},
},
url: {
home: '/',
admin: '/admin',
error404: '/404',
latestMedia: '/latest',
featuredMedia: '/featured',
recommendedMedia: '/recommended',
signin: '/signin',
signout: '/signout',
register: '/register',
changePassword: '/password',
members: '/members',
search: '/search',
likedMedia: '/liked',
history: '/history',
addMedia: '/add',
editChannel: '/edit/channel',
editProfile: '/edit/profile',
tags: '/tags',
categories: '/categories',
manageMedia: '/manage/media',
manageUsers: '/manage/users',
manageComments: '/manage/comments',
},
api: {
media: 'v1/media/',
playlists: 'v1/playlists',
members: 'v1/users',
liked: 'v1/user/liked',
history: 'v1/user/history',
tags: 'v1/tags',
categories: 'v1/categories',
manage_media: 'v1/manage/media',
manage_users: 'v1/manage/users',
manage_comments: 'v1/manage/comments',
search: 'v1/search',
},
contents: {
notifications: {
messages: {
addToLiked: 'Yay',
removeFromLiked: 'Oops',
addToDisliked: 'nay',
removeFromDisliked: 'ok',
},
},
},
pages: {
home: { sections: { latest: { title: 'Latest T' } } },
search: { advancedFilters: true },
media: { categoriesWithTitle: true, hideViews: true, related: { initialSize: 5 } },
profile: { htmlInDescription: true, includeHistory: true, includeLikedMedia: true },
},
features: {
mediaItem: { hideAuthor: true, hideViews: false, hideDate: true },
media: {
actions: {
like: true,
dislike: true,
report: true,
comment: true,
comment_mention: true,
download: true,
save: true,
share: true,
},
shareOptions: ['embed', 'email', 'invalid'],
},
playlists: { mediaTypes: ['audio'] },
sideBar: { hideHomeLink: false, hideTagsLink: true, hideCategoriesLink: false },
embeddedVideo: { initialDimensions: { width: 640, height: 360 } },
headerBar: { hideLogin: false, hideRegister: true },
},
user: {
is: { anonymous: false, admin: true },
name: ' John ',
username: ' john ',
thumbnail: ' /img/j.png ',
can: {
changePassword: true,
deleteProfile: true,
addComment: true,
mentionComment: true,
deleteComment: true,
editMedia: true,
deleteMedia: true,
editSubtitle: true,
manageMedia: true,
manageUsers: true,
manageComments: true,
contactUser: true,
canSeeMembersPage: true,
usersNeedsToBeApproved: false,
addMedia: true,
editProfile: true,
readComment: true,
},
pages: { about: '/u/john/about ', media: '/u/john ', playlists: '/u/john/playlists ' },
},
} as const;
test('merges enabled pages and passes titles into options.pages.home sections', () => {
const cfg = config(baseGlobal);
expect(cfg.enabled.pages.latest).toStrictEqual({ enabled: true, title: 'Recent uploads' });
expect(cfg.enabled.pages.featured).toStrictEqual({ enabled: true, title: 'Featured picks' });
expect(cfg.enabled.pages.recommended).toStrictEqual({ enabled: false, title: 'You may like' });
expect(cfg.enabled.pages.members).toStrictEqual({ enabled: true, title: 'People' });
expect(cfg.options.pages.home.sections.latest.title).toBe('Latest T');
expect(cfg.options.pages.home.sections.featured.title).toBe('Featured picks');
});
test('produces api endpoints based on site.api and api endpoints', () => {
const cfg = config(baseGlobal);
expect(cfg.api.media).toBe('https://example.com/api/v1/media/');
expect(cfg.api.user.liked).toBe('https://example.com/api/v1/user/liked');
expect(cfg.api.search.query).toBe('https://example.com/api/v1/search?q=');
});
test('member and url manage links reflect user and feature flags', () => {
const cfg = config(baseGlobal);
expect(cfg.member.is).toStrictEqual({ admin: true, anonymous: false });
expect(cfg.member.can).toMatchObject({
manageMedia: true,
manageUsers: true,
manageComments: true,
likeMedia: true,
});
expect(cfg.url.manage.media).toBe('/manage/media');
expect(cfg.url.signout).toBe('/signout');
// admin visible
expect(cfg.url.admin).toBe('/admin');
});
test('theme and site defaults propagate correctly', () => {
const cfg = config(baseGlobal);
expect(cfg.theme.mode).toBe('dark');
expect(cfg.theme.switch.position).toBe('sidebar');
expect(cfg.theme.logo.darkMode.img).toBe('/img/dark.png');
expect(cfg.site.id).toBe('my-site');
expect(cfg.site.version).toBe('2.0.0');
});
test('memoizes and returns the same object instance on repeated calls', () => {
const first = config(baseGlobal);
const second = config(baseGlobal);
expect(second).toBe(first);
});
test('url profile paths use site.url when not in dev env', () => {
const cfg = config(baseGlobal);
expect(cfg.url.profile.media).toBe('https://example.com/user/john');
expect(cfg.url.embed).toBe('https://example.com/embed?m=');
});
});
});

View File

@@ -0,0 +1,83 @@
import { init, settings } from '../../../src/static/js/utils/settings/contents';
const contentsConfig = (obj: any) => {
init(obj);
return settings();
};
describe('utils/settings', () => {
describe('contents', () => {
test('Strings are trimmed and default to empty', () => {
const cfg1 = contentsConfig({
header: { right: ' R ', onLogoRight: ' OLR ' },
sidebar: { belowNavMenu: ' X ', belowThemeSwitcher: ' Y ', footer: ' Z ' },
uploader: { belowUploadArea: ' U1 ', postUploadMessage: ' U2 ' },
});
const cfg2 = contentsConfig({});
const cfg3 = contentsConfig({ header: {}, sidebar: {}, uploader: {} });
expect(cfg1).toStrictEqual({
header: { right: 'R', onLogoRight: 'OLR' },
sidebar: {
navMenu: { items: [] },
mainMenuExtra: { items: [] },
belowNavMenu: 'X',
belowThemeSwitcher: 'Y',
footer: 'Z',
},
uploader: { belowUploadArea: 'U1', postUploadMessage: 'U2' },
});
expect(cfg2).toStrictEqual({
header: { right: '', onLogoRight: '' },
sidebar: {
navMenu: { items: [] },
mainMenuExtra: { items: [] },
belowNavMenu: '',
belowThemeSwitcher: '',
footer: '',
},
uploader: { belowUploadArea: '', postUploadMessage: '' },
});
expect(cfg3).toStrictEqual({
header: { right: '', onLogoRight: '' },
sidebar: {
navMenu: { items: [] },
mainMenuExtra: { items: [] },
belowNavMenu: '',
belowThemeSwitcher: '',
footer: '',
},
uploader: { belowUploadArea: '', postUploadMessage: '' },
});
});
// @todo: Revisit this behavior
test('Sidebar menu items require text, link, icon and NOT get trimmed', () => {
const cfg = contentsConfig({
sidebar: {
mainMenuExtraItems: [
{ text: ' A ', link: ' /a ', icon: ' i-a ', className: ' cls ' },
{ text: 'no-link', icon: 'i' },
{ link: '/missing-text', icon: 'i' },
{ text: 'no-icon', link: '/x' },
],
navMenuItems: [
{ text: ' B ', link: ' /b ', icon: ' i-b ' },
{ text: ' ', link: '/bad', icon: 'i' },
],
},
});
expect(cfg.sidebar.mainMenuExtra.items).toStrictEqual([
{ text: ' A ', link: ' /a ', icon: ' i-a ', className: ' cls ' },
]);
expect(cfg.sidebar.navMenu.items).toStrictEqual([
{ text: ' B ', link: ' /b ', icon: ' i-b ', className: undefined },
{ text: ' ', link: '/bad', icon: 'i', className: undefined },
]);
});
});
});

View File

@@ -0,0 +1,41 @@
import { init, settings } from '../../../src/static/js/utils/settings/media';
const mediaConfig = (item?: any, shareOptions?: any) => {
init(item, shareOptions);
return settings();
};
describe('utils/settings', () => {
describe('media', () => {
test('Defaults display flags to true when not hidden', () => {
const cfg = mediaConfig();
expect(cfg.item.displayAuthor).toBe(true);
expect(cfg.item.displayViews).toBe(true);
expect(cfg.item.displayPublishDate).toBe(true);
expect(cfg.share.options).toEqual([]);
});
test('Respects hide flags for author, views and date', () => {
const cfg = mediaConfig({ hideAuthor: true, hideViews: true, hideDate: true });
expect(cfg.item.displayAuthor).toBe(false);
expect(cfg.item.displayViews).toBe(false);
expect(cfg.item.displayPublishDate).toBe(false);
});
test('Returns empty share options when not provided', () => {
const cfg = mediaConfig({ hideAuthor: false }, undefined);
expect(cfg.share.options).toEqual([]);
});
// @todo: Revisit this behavior
test('Filters share options to valid ones and trims whitespace', () => {
const cfg = mediaConfig(undefined, [' embed ', 'email', ' email ']);
expect(cfg.share.options).toEqual(['email']);
});
test('Ignores falsy and invalid share options', () => {
const cfg = mediaConfig(undefined, [undefined, '', ' ', 'invalid', 'share', 'EMBED']);
expect(cfg.share.options).toEqual([]);
});
});
});

View File

@@ -0,0 +1,162 @@
import { init, settings } from '../../../src/static/js/utils/settings/member';
const memberConfig = (user?: any, features?: any) => {
init(user, features);
return settings();
};
describe('utils/settings', () => {
describe('member', () => {
// @todo: Revisit this behavior
test('Returns anonymous defaults when user not provided', () => {
const cfg = memberConfig();
expect(cfg).toStrictEqual({
name: null,
username: null,
thumbnail: null,
is: { admin: false, anonymous: true },
can: {
login: true,
register: true,
addMedia: false,
editProfile: false,
canSeeMembersPage: true,
usersNeedsToBeApproved: true,
changePassword: true,
deleteProfile: false,
readComment: true,
addComment: false,
mentionComment: false,
deleteComment: false,
editMedia: false,
deleteMedia: false,
editSubtitle: false,
manageMedia: false,
manageUsers: false,
manageComments: false,
reportMedia: false,
downloadMedia: false,
saveMedia: false,
likeMedia: true,
dislikeMedia: true,
shareMedia: true,
contactUser: false,
},
pages: { home: null, about: null, media: null, playlists: null },
});
});
test('Trims user strings and applies user capability booleans when authenticated', () => {
const cfg = memberConfig({
is: { anonymous: false, admin: true },
name: ' John Doe ',
username: ' johnd ',
thumbnail: ' /img/j.png ',
can: {
changePassword: true,
deleteProfile: true,
addComment: true,
mentionComment: true,
deleteComment: true,
editMedia: true,
deleteMedia: true,
editSubtitle: true,
manageMedia: true,
manageUsers: true,
manageComments: true,
contactUser: true,
addMedia: true,
editProfile: true,
readComment: true,
canSeeMembersPage: true,
usersNeedsToBeApproved: false,
},
pages: { about: ' /u/john/about ', media: ' /u/john ', playlists: ' /u/john/playlists ' },
});
expect(cfg).toStrictEqual({
name: 'John Doe',
username: 'johnd',
thumbnail: '/img/j.png',
is: { admin: true, anonymous: false },
can: {
login: true,
register: true,
addMedia: true,
editProfile: true,
canSeeMembersPage: true,
usersNeedsToBeApproved: false,
changePassword: true,
deleteProfile: true,
readComment: true,
addComment: true,
mentionComment: true,
deleteComment: true,
editMedia: true,
deleteMedia: true,
editSubtitle: true,
manageMedia: true,
manageUsers: true,
manageComments: true,
reportMedia: false,
downloadMedia: false,
saveMedia: false,
likeMedia: true,
dislikeMedia: true,
shareMedia: true,
contactUser: true,
},
pages: { home: null, about: '/u/john/about', media: '/u/john', playlists: '/u/john/playlists' },
});
});
test('Comment capabilities require both user.can and features.media.actions', () => {
const cfg1 = memberConfig(
{ is: { anonymous: false }, can: { addComment: true, mentionComment: true } },
{ media: { actions: { comment: false, comment_mention: true } } }
);
expect(cfg1.can.addComment).toBe(false);
expect(cfg1.can.mentionComment).toBe(true);
const cfg2 = memberConfig(
{ is: { anonymous: false }, can: { addComment: true, mentionComment: true } },
{ media: { actions: { comment: true, comment_mention: true } } }
);
expect(cfg2.can.addComment).toBe(true);
expect(cfg2.can.mentionComment).toBe(true);
});
test('Header login/register reflect headerBar feature flags', () => {
expect(memberConfig(undefined, { headerBar: { hideLogin: true } }).can.login).toBe(false);
expect(memberConfig(undefined, { headerBar: { hideRegister: true } }).can.register).toBe(false);
expect(memberConfig(undefined, { headerBar: { hideLogin: false, hideRegister: false } }).can).toMatchObject(
{ login: true, register: true }
);
});
test('Media actions flags set like/dislike/share/report/download/save with correct defaults', () => {
const cfg1 = memberConfig(undefined, {
media: {
actions: { like: false, dislike: false, share: false, report: true, download: true, save: true },
},
});
expect(cfg1.can.likeMedia).toBe(false);
expect(cfg1.can.dislikeMedia).toBe(false);
expect(cfg1.can.shareMedia).toBe(false);
expect(cfg1.can.reportMedia).toBe(true);
expect(cfg1.can.downloadMedia).toBe(true);
expect(cfg1.can.saveMedia).toBe(true);
});
test('User flags canSeeMembersPage/usersNeedsToBeApproved/readComment default handling', () => {
const cfg1 = memberConfig({
is: { anonymous: false },
can: { canSeeMembersPage: false, usersNeedsToBeApproved: false, readComment: false },
});
expect(cfg1.can.canSeeMembersPage).toBe(false);
expect(cfg1.can.usersNeedsToBeApproved).toBe(false);
expect(cfg1.can.readComment).toBe(false);
});
});
});

View File

@@ -0,0 +1,70 @@
import { init, settings } from '../../../src/static/js/utils/settings/notifications';
const notificationsConfig = (sett?: any) => {
init(sett);
return settings();
};
describe('utils/settings', () => {
describe('notifications', () => {
test('Returns defaults when no settings provided', () => {
const cfg = notificationsConfig();
expect(cfg).toStrictEqual({
messages: {
addToLiked: 'Added to liked media',
removeFromLiked: 'Removed from liked media',
addToDisliked: 'Added to disliked media',
removeFromDisliked: 'Removed from disliked media',
},
});
});
// @todo: Revisit this behavior
test('Keep incoming message values without processing', () => {
const cfg = notificationsConfig({
messages: {
addToLiked: ' Yay ',
removeFromLiked: ' ',
addToDisliked: '\nNope',
removeFromDisliked: '\t OK\t',
},
});
expect(cfg.messages.addToLiked).toBe(' Yay ');
expect(cfg.messages.removeFromLiked).toBe(' ');
expect(cfg.messages.addToDisliked).toBe('\nNope');
expect(cfg.messages.removeFromDisliked).toBe('\t OK\t');
});
test('Ignores undefined, keeping defaults', () => {
const cfg = notificationsConfig({
messages: {
addToLiked: undefined,
removeFromLiked: undefined,
addToDisliked: undefined,
removeFromDisliked: undefined,
},
});
expect(cfg.messages.addToLiked).toBe('Added to liked media');
expect(cfg.messages.removeFromLiked).toBe('Removed from liked media');
expect(cfg.messages.addToDisliked).toBe('Added to disliked media');
expect(cfg.messages.removeFromDisliked).toBe('Removed from disliked media');
});
test('Allows partial overrides without affecting other keys', () => {
const cfg = notificationsConfig({ messages: { addToLiked: 'Nice!' } });
expect(cfg.messages.addToLiked).toBe('Nice!');
expect(cfg.messages.removeFromLiked).toBe('Removed from liked media');
expect(cfg.messages.addToDisliked).toBe('Added to disliked media');
expect(cfg.messages.removeFromDisliked).toBe('Removed from disliked media');
});
test('Handles extraneous keys by passing them through while keeping known defaults intact', () => {
const cfg = notificationsConfig({ messages: { addToLiked: 'A', notARealKey: 'x' } });
expect(cfg.messages.notARealKey).toBeUndefined();
expect(cfg.messages.addToLiked).toBe('A');
expect(cfg.messages.removeFromLiked).toBe('Removed from liked media');
expect(cfg.messages.addToDisliked).toBe('Added to disliked media');
expect(cfg.messages.removeFromDisliked).toBe('Removed from disliked media');
});
});
});

View File

@@ -0,0 +1,60 @@
import { init, settings } from '../../../src/static/js/utils/settings/optionsEmbedded';
const optionsEmbeddedConfig = (embeddedVideo?: any) => {
init(embeddedVideo);
return settings();
};
describe('utils/settings', () => {
describe('optionsEmbedded', () => {
test('Returns default dimensions when settings is undefined', () => {
const cfg = optionsEmbeddedConfig(undefined);
expect(cfg.video.dimensions).toStrictEqual({ width: 560, widthUnit: 'px', height: 315, heightUnit: 'px' });
});
test('Returns default dimensions when settings.initialDimensions is undefined', () => {
const cfg = optionsEmbeddedConfig({});
expect(cfg.video.dimensions).toStrictEqual({ width: 560, widthUnit: 'px', height: 315, heightUnit: 'px' });
});
test('Applies valid numeric width and height from initialDimensions', () => {
const cfg = optionsEmbeddedConfig({ initialDimensions: { width: 640, height: 360 } });
expect(cfg.video.dimensions).toStrictEqual({ width: 640, widthUnit: 'px', height: 360, heightUnit: 'px' });
});
// @todo: Revisit this behavior
test('Ignores NaN and non-numeric width/height and keeps defaults', () => {
const cfg1 = optionsEmbeddedConfig({ initialDimensions: { width: NaN, height: NaN } });
expect(cfg1.video.dimensions).toStrictEqual({ width: 560, widthUnit: 'px', height: 315, heightUnit: 'px' });
const cfg2 = optionsEmbeddedConfig({ initialDimensions: { width: '640', height: '360' } });
expect(cfg2.video.dimensions).toStrictEqual({
width: '640',
widthUnit: 'px',
height: '360',
heightUnit: 'px',
});
});
// @todo: Revisit this behavior
test('Ignores provided widthUnit/heightUnit as they are not used', () => {
const cfg = optionsEmbeddedConfig({
initialDimensions: { width: 800, height: 450, widthUnit: 'percent', heightUnit: 'percent' },
});
expect(cfg.video.dimensions.width).toBe(800);
expect(cfg.video.dimensions.height).toBe(450);
expect(cfg.video.dimensions.widthUnit).toBe('px');
expect(cfg.video.dimensions.heightUnit).toBe('px');
});
// @todo: Revisit this behavior
test('Does not mutate the provided settings object', () => {
const input = {
initialDimensions: { width: 700, height: 400, widthUnit: 'percent', heightUnit: 'percent' },
};
const copy = JSON.parse(JSON.stringify(input));
optionsEmbeddedConfig(input);
expect(input).toStrictEqual(copy);
});
});
});

View File

@@ -0,0 +1,125 @@
import { init, settings } from '../../../src/static/js/utils/settings/optionsPages';
const optionsPagesConfig = (home?: any, search?: any, media?: any, profile?: any, VALID_PAGES?: any) => {
init(home, search, media, profile, VALID_PAGES);
return settings();
};
describe('utils/settings', () => {
describe('optionsPages', () => {
test('Uses VALID_PAGES titles as defaults for home sections when provided', () => {
const cfg = optionsPagesConfig(undefined, undefined, undefined, undefined, {
latest: { title: 'Recent' },
featured: { title: 'Spotlight' },
recommended: { title: 'You may like' },
});
expect(cfg.home.sections.latest.title).toBe('Recent');
expect(cfg.home.sections.featured.title).toBe('Spotlight');
expect(cfg.home.sections.recommended.title).toBe('You may like');
});
test('Trims custom home section titles from input', () => {
const cfg = optionsPagesConfig({
sections: {
latest: { title: ' LATEST ' },
featured: { title: '\nFeatured ' },
recommended: { title: ' Recommended' },
},
});
expect(cfg.home.sections.latest.title).toBe('LATEST');
expect(cfg.home.sections.featured.title).toBe('Featured');
expect(cfg.home.sections.recommended.title).toBe('Recommended');
});
test('Sets search.advancedFilters true only when explicitly true', () => {
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
expect(def.search.advancedFilters).toBe(false);
const falsy = optionsPagesConfig(undefined, { advancedFilters: false }, undefined, undefined, {});
expect(falsy.search.advancedFilters).toBe(false);
const truthy = optionsPagesConfig(undefined, { advancedFilters: true }, undefined, undefined, {});
expect(truthy.search.advancedFilters).toBe(true);
});
test('Configures media options with correct defaults and overrides', () => {
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
expect(def.media.categoriesWithTitle).toBe(false);
expect(def.media.htmlInDescription).toBe(false);
expect(def.media.displayViews).toBe(true);
expect(def.media.related.initialSize).toBe(10);
const override = optionsPagesConfig(
undefined,
undefined,
{
categoriesWithTitle: true,
htmlInDescription: true,
hideViews: true,
related: { initialSize: 25 },
},
undefined,
{}
);
expect(override.media.categoriesWithTitle).toBe(true);
expect(override.media.htmlInDescription).toBe(true);
expect(override.media.displayViews).toBe(false);
expect(override.media.related.initialSize).toBe(10); // @todo: Fix this! It should return 25.
});
test('Ignores NaN and non-numeric media.related.initialSize and keeps default 10', () => {
const cfg1 = optionsPagesConfig(undefined, undefined, { related: { initialSize: NaN } }, undefined, {});
expect(cfg1.media.related.initialSize).toBe(10);
const cfg2 = optionsPagesConfig(undefined, undefined, { related: { initialSize: '12' } }, undefined, {});
expect(cfg2.media.related.initialSize).toBe(10);
});
test('Profile settings true only when explicitly true', () => {
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
expect(def.profile.htmlInDescription).toBe(false);
expect(def.profile.includeHistory).toBe(false);
expect(def.profile.includeLikedMedia).toBe(false);
const truthy = optionsPagesConfig(
undefined,
undefined,
undefined,
{
htmlInDescription: true,
includeHistory: true,
includeLikedMedia: true,
},
{}
);
expect(truthy.profile.htmlInDescription).toBe(true);
expect(truthy.profile.includeHistory).toBe(true);
expect(truthy.profile.includeLikedMedia).toBe(true);
});
// @todo: Revisit this behavior
test('Does not mutate provided input objects', () => {
const home = { sections: { latest: { title: ' A ' } } };
const search = { advancedFilters: true };
const media = { hideViews: true, related: { initialSize: 5 } };
const profile = { includeHistory: true };
const validPages = { latest: { title: 'L' }, featured: { title: 'F' }, recommended: { title: 'R' } };
const homeCopy = JSON.parse(JSON.stringify(home));
const searchCopy = JSON.parse(JSON.stringify(search));
const mediaCopy = JSON.parse(JSON.stringify(media));
const profileCopy = JSON.parse(JSON.stringify(profile));
const validPagesCopy = JSON.parse(JSON.stringify(validPages));
optionsPagesConfig(home, search, media, profile, validPages);
expect(home).toStrictEqual(homeCopy);
expect(search).toStrictEqual(searchCopy);
expect(media).toStrictEqual(mediaCopy);
expect(profile).toStrictEqual(profileCopy);
expect(validPages).toStrictEqual(validPagesCopy);
});
});
});

View File

@@ -0,0 +1,63 @@
import { init, settings } from '../../../src/static/js/utils/settings/pages';
const pagesConfig = (sett?: any) => {
init(sett);
return settings();
};
describe('utils/settings', () => {
describe('pages', () => {
test('Defaults: all known pages disabled with default titles', () => {
const cfg = pagesConfig();
expect(cfg).toStrictEqual({
latest: { enabled: false, title: 'Recent uploads' },
featured: { enabled: false, title: 'Featured' },
recommended: { enabled: false, title: 'Recommended' },
members: { enabled: false, title: 'Members' },
liked: { enabled: false, title: 'Liked media' },
history: { enabled: false, title: 'History' },
});
});
test('Enables each page unless explicitly disabled', () => {
const cfg = pagesConfig({
latest: {},
featured: { enabled: true },
recommended: { enabled: false },
members: { enabled: undefined },
liked: { enabled: null },
history: { enabled: 0 },
});
expect(cfg.latest.enabled).toBe(true);
expect(cfg.featured.enabled).toBe(true);
expect(cfg.recommended.enabled).toBe(false);
expect(cfg.members.enabled).toBe(true);
expect(cfg.liked.enabled).toBe(true);
expect(cfg.history.enabled).toBe(true);
});
test('Trims provided titles and preserves defaults when title is undefined', () => {
const cfg = pagesConfig({
latest: { title: ' Latest ' },
featured: { title: '\nFeatured' },
recommended: {},
});
expect(cfg.latest.title).toBe('Latest');
expect(cfg.featured.title).toBe('Featured');
expect(cfg.recommended.title).toBe('Recommended');
});
test('Ignores unknown keys in settings', () => {
const cfg = pagesConfig({ unknownKey: { enabled: true, title: 'X' }, latest: { enabled: true } });
expect(cfg.latest.enabled).toBe(true);
expect(cfg.unknownKey).toBeUndefined();
});
test('Does not mutate the input settings object', () => {
const input = { latest: { enabled: false, title: ' A ' }, featured: { enabled: true, title: ' B ' } };
const snapshot = JSON.parse(JSON.stringify(input));
pagesConfig(input);
expect(input).toStrictEqual(snapshot);
});
});
});

View File

@@ -0,0 +1,52 @@
import { init, settings } from '../../../src/static/js/utils/settings/playlists';
const playlistsConfig = (plists?: any) => {
init(plists);
return settings();
};
describe('utils/settings', () => {
describe('playlists', () => {
test('Defaults to both audio and video when no settings provided', () => {
const cfg = playlistsConfig();
expect(cfg.mediaTypes).toEqual(['audio', 'video']);
});
test('Returns default when provided mediaTypes array is empty', () => {
const cfg = playlistsConfig({ mediaTypes: [] });
expect(cfg.mediaTypes).toEqual(['audio', 'video']);
});
test('Includes only valid media types when both valid and invalid are provided', () => {
const cfg = playlistsConfig({ mediaTypes: ['audio', 'invalid', 'video', 'something'] });
expect(cfg.mediaTypes).toEqual(['audio', 'video']);
});
test('Returns default when provided mediaTypes is non-array or undefined/null', () => {
expect(playlistsConfig({}).mediaTypes).toEqual(['audio', 'video']);
expect(playlistsConfig({ mediaTypes: undefined }).mediaTypes).toEqual(['audio', 'video']);
// expect(playlistsConfig({ mediaTypes: null }).mediaTypes).toEqual(['audio', 'video']); // @todo: Revisit this behavior
expect(playlistsConfig({ mediaTypes: 'audio' }).mediaTypes).toEqual(['audio', 'video']);
expect(playlistsConfig({ mediaTypes: 123 }).mediaTypes).toEqual(['audio', 'video']);
});
// @todo: Revisit this behavior
test('Handles duplicates and preserves order among valid items', () => {
const cfg = playlistsConfig({ mediaTypes: ['video', 'audio', 'video', 'audio', 'invalid'] });
expect(cfg.mediaTypes).toEqual(['video', 'audio', 'video', 'audio']);
});
// @todo: Revisit this behavior
test('Rejects non-exact case values (e.g., \"Audio\")', () => {
const cfg = playlistsConfig({ mediaTypes: ['Audio', 'Video'] });
expect(cfg.mediaTypes).toEqual(['audio', 'video']);
});
test('does not mutate the input object', () => {
const input = { mediaTypes: ['audio', 'video', 'invalid'] };
const copy = JSON.parse(JSON.stringify(input));
playlistsConfig(input);
expect(input).toEqual(copy);
});
});
});

View File

@@ -0,0 +1,53 @@
import { init, settings } from '../../../src/static/js/utils/settings/sidebar';
const sidebarConfig = (sett?: any) => {
init(sett);
return settings();
};
describe('utils/settings', () => {
describe('sidebar', () => {
test('Defaults to all links visible when no settings provided', () => {
const cfg = sidebarConfig();
expect(cfg).toStrictEqual({ hideHomeLink: false, hideTagsLink: false, hideCategoriesLink: false });
});
test('Hides only those explicitly set to true', () => {
const cfg1 = sidebarConfig({ hideHomeLink: true });
expect(cfg1).toStrictEqual({ hideHomeLink: true, hideTagsLink: false, hideCategoriesLink: false });
const cfg2 = sidebarConfig({ hideTagsLink: true });
expect(cfg2).toStrictEqual({ hideHomeLink: false, hideTagsLink: true, hideCategoriesLink: false });
const cfg3 = sidebarConfig({ hideCategoriesLink: true });
expect(cfg3).toStrictEqual({ hideHomeLink: false, hideTagsLink: false, hideCategoriesLink: true });
const cfgAll = sidebarConfig({ hideHomeLink: true, hideTagsLink: true, hideCategoriesLink: true });
expect(cfgAll).toStrictEqual({ hideHomeLink: true, hideTagsLink: true, hideCategoriesLink: true });
});
test('Treats non-true values as false', () => {
// false
expect(sidebarConfig({ hideHomeLink: false }).hideHomeLink).toBe(false);
// undefined
expect(sidebarConfig({}).hideHomeLink).toBe(false);
// null
expect(sidebarConfig({ hideTagsLink: null }).hideTagsLink).toBe(false);
// other types
expect(sidebarConfig({ hideCategoriesLink: 'yes' }).hideCategoriesLink).toBe(false);
expect(sidebarConfig({ hideCategoriesLink: 1 }).hideCategoriesLink).toBe(false);
});
test('Is resilient to partial inputs and ignores extra properties', () => {
const cfg = sidebarConfig({ hideTagsLink: true, extra: 'prop' });
expect(cfg).toStrictEqual({ hideHomeLink: false, hideTagsLink: true, hideCategoriesLink: false });
});
test('Does not mutate input object', () => {
const input: any = { hideHomeLink: true };
const copy = JSON.parse(JSON.stringify(input));
sidebarConfig(input);
expect(input).toStrictEqual(copy);
});
});
});

View File

@@ -0,0 +1,63 @@
import { init, settings } from '../../../src/static/js/utils/settings/site';
const siteConfig = (sett?: any) => {
init(sett);
return settings();
};
describe('utils/settings', () => {
describe('site', () => {
test('Applies defaults when no settings provided', () => {
const cfg = siteConfig();
expect(cfg).toStrictEqual({
id: 'media-cms',
url: '',
api: '',
title: '',
useRoundedCorners: true,
version: '1.0.0',
});
});
test('Trims string fields (id, url, api, title, version)', () => {
const cfg = siteConfig({
id: ' my-site ',
url: ' https://example.com/ ',
api: ' https://example.com/api/ ',
title: ' Media CMS ',
version: ' 2.3.4 ',
});
expect(cfg).toStrictEqual({
id: 'my-site',
url: 'https://example.com/',
api: 'https://example.com/api/',
title: 'Media CMS',
useRoundedCorners: true,
version: '2.3.4',
});
});
test('Handles useRoundedCorners: defaults to true unless explicitly false', () => {
expect(siteConfig({}).useRoundedCorners).toBe(true);
expect(siteConfig({ useRoundedCorners: true }).useRoundedCorners).toBe(true);
expect(siteConfig({ useRoundedCorners: false }).useRoundedCorners).toBe(false);
// non-boolean should still evaluate to default true because only === false toggles it off
expect(siteConfig({ useRoundedCorners: 'no' }).useRoundedCorners).toBe(true);
expect(siteConfig({ useRoundedCorners: 0 }).useRoundedCorners).toBe(true);
expect(siteConfig({ useRoundedCorners: null }).useRoundedCorners).toBe(true);
});
test('Is resilient to partial inputs and ignores extra properties', () => {
const cfg = siteConfig({ id: ' x ', extra: 'y' });
expect(cfg).toMatchObject({ id: 'x' });
expect(Object.keys(cfg).sort()).toEqual(['api', 'id', 'title', 'url', 'useRoundedCorners', 'version']);
});
test('Does not mutate input object', () => {
const input = { id: ' my-id ', useRoundedCorners: false };
const copy = JSON.parse(JSON.stringify(input));
siteConfig(input);
expect(input).toEqual(copy);
});
});
});

View File

@@ -0,0 +1,53 @@
import { init, settings } from '../../../src/static/js/utils/settings/taxonomies';
const taxonomiesConfig = (sett?: any) => {
init(sett);
return settings();
};
describe('utils-settings/taxonomies', () => {
test('Should return defaults when settings is undefined', () => {
const res = taxonomiesConfig();
expect(res).toStrictEqual({
tags: { enabled: false, title: 'Tags' },
categories: { enabled: false, title: 'Categories' },
});
});
test('Should enable a taxonomy when enabled is true', () => {
const res = taxonomiesConfig({ tags: { enabled: true } });
expect(res.tags).toStrictEqual({ enabled: true, title: 'Tags' });
});
test('Should keep taxonomy disabled when enabled is true', () => {
const res = taxonomiesConfig({ categories: { enabled: true } });
expect(res.categories).toStrictEqual({ enabled: true, title: 'Categories' });
});
test('Should default to enabled=true when enabled is omitted but key exists', () => {
const res = taxonomiesConfig({ tags: {} });
expect(res.tags).toStrictEqual({ enabled: true, title: 'Tags' });
});
test('Should trim title when provided', () => {
const res = taxonomiesConfig({ tags: { title: ' My Tags ' } });
expect(res.tags).toStrictEqual({ enabled: true, title: 'My Tags' });
});
test('Should ignore unknown taxonomy keys', () => {
const input = {
unknownKey: { enabled: true, title: 'X' },
tags: { enabled: true, title: 'Tagz' },
};
const res = taxonomiesConfig(input);
expect(res).toStrictEqual({
tags: { enabled: true, title: 'Tagz' },
categories: { enabled: false, title: 'Categories' },
});
});
test('Should not change title when title is undefined', () => {
const res = taxonomiesConfig({ categories: { enabled: true, title: undefined } });
expect(res.categories).toStrictEqual({ enabled: true, title: 'Categories' });
});
});

View File

@@ -0,0 +1,77 @@
import { init, settings } from '../../../src/static/js/utils/settings/theme';
const themeConfig = (theme?: any, logo?: any) => {
init(theme, logo);
return settings();
};
describe('utils/settings', () => {
describe('theme', () => {
test('Applies defaults when no inputs provided', () => {
const cfg = themeConfig();
expect(cfg).toStrictEqual({
mode: 'light',
switch: { enabled: true, position: 'header' },
logo: { lightMode: { img: '', svg: '' }, darkMode: { img: '', svg: '' } },
});
});
test("Sets dark mode only when theme.mode is exactly 'dark' after trim", () => {
expect(themeConfig({ mode: 'dark' }).mode).toBe('dark');
expect(themeConfig({ mode: ' dark ' }).mode).toBe('dark');
expect(themeConfig({ mode: 'Dark' }).mode).toBe('light');
expect(themeConfig({ mode: 'light' }).mode).toBe('light');
expect(themeConfig({ mode: ' ' }).mode).toBe('light');
});
test('Switch config: enabled only toggles off when explicitly false; position set to sidebar only when exactly sidebar after trim', () => {
expect(themeConfig({ switch: { enabled: false } }).switch.enabled).toBe(false);
expect(themeConfig({ switch: { enabled: true } }).switch.enabled).toBe(true);
expect(themeConfig({ switch: { enabled: undefined } }).switch.enabled).toBe(true);
expect(themeConfig({ switch: { position: 'sidebar' } }).switch.position).toBe('sidebar');
expect(themeConfig({ switch: { position: ' sidebar ' } }).switch.position).toBe('header'); // @todo: Fix this. It should be 'sidebar'
expect(themeConfig({ switch: { position: 'header' } }).switch.position).toBe('header');
expect(themeConfig({ switch: { position: 'foot' } }).switch.position).toBe('header');
});
test('Trims and maps logo URLs for both light and dark modes; ignores missing fields', () => {
const cfg = themeConfig(undefined, {
lightMode: { img: ' /img/light.png ', svg: ' /img/light.svg ' },
darkMode: { img: ' /img/dark.png ', svg: ' /img/dark.svg ' },
});
expect(cfg).toStrictEqual({
mode: 'light',
switch: { enabled: true, position: 'header' },
logo: {
lightMode: { img: '/img/light.png', svg: '/img/light.svg' },
darkMode: { img: '/img/dark.png', svg: '/img/dark.svg' },
},
});
const partial = themeConfig(undefined, { lightMode: { img: ' /only-light.png ' } });
expect(partial).toStrictEqual({
mode: 'light',
switch: { enabled: true, position: 'header' },
logo: {
lightMode: { img: '/only-light.png', svg: '' },
darkMode: { img: '', svg: '' },
},
});
});
test('Does not mutate input objects', () => {
const themeIn = { mode: ' dark ', switch: { enabled: false, position: ' sidebar ' } };
const logoIn = { lightMode: { img: ' x ', svg: ' y ' }, darkMode: { img: ' z ', svg: ' w ' } };
const themeCopy = JSON.parse(JSON.stringify(themeIn));
const logoCopy = JSON.parse(JSON.stringify(logoIn));
themeConfig(themeIn, logoIn);
expect(themeIn).toStrictEqual(themeCopy);
expect(logoIn).toStrictEqual(logoCopy);
});
});
});

View File

@@ -0,0 +1,99 @@
import { init, pages } from '../../../src/static/js/utils/settings/url';
const urlConfig = (pages_url?: any) => {
init(pages_url);
return pages();
};
describe('utils/settings', () => {
describe('url', () => {
const baseGlobal = {
profileId: 'john',
site: {
url: 'https://example.com/',
devEnv: false,
},
url: {
home: '/',
admin: '/admin',
error404: '/404',
latestMedia: '/latest',
featuredMedia: '/featured',
recommendedMedia: '/recommended',
signin: '/signin',
signout: '/signout',
register: '/register',
changePassword: '/password',
members: '/members',
search: '/search',
likedMedia: '/liked',
history: '/history',
addMedia: '/add',
editChannel: '/edit/channel',
editProfile: '/edit/profile',
tags: '/tags',
categories: '/categories',
manageMedia: '/manage/media',
manageUsers: '/manage/users',
manageComments: '/manage/comments',
},
user: {
is: { anonymous: false, admin: false },
pages: { media: '/u/john', about: '/u/john/about', playlists: '/u/john/playlists' },
},
} as const;
test('Authenticated non-admin user', () => {
const cfg = urlConfig(baseGlobal);
expect(cfg).toStrictEqual({
profileId: 'john',
site: { url: 'https://example.com/', devEnv: false },
url: {
home: '/',
admin: '/admin',
error404: '/404',
latestMedia: '/latest',
featuredMedia: '/featured',
recommendedMedia: '/recommended',
signin: '/signin',
signout: '/signout',
register: '/register',
changePassword: '/password',
members: '/members',
search: '/search',
likedMedia: '/liked',
history: '/history',
addMedia: '/add',
editChannel: '/edit/channel',
editProfile: '/edit/profile',
tags: '/tags',
categories: '/categories',
manageMedia: '/manage/media',
manageUsers: '/manage/users',
manageComments: '/manage/comments',
},
user: {
is: { anonymous: false, admin: false },
pages: { media: '/u/john', about: '/u/john/about', playlists: '/u/john/playlists' },
},
});
});
test('Admin user', () => {
const cfg = urlConfig({
...baseGlobal,
user: { ...baseGlobal.user, is: { anonymous: false, admin: true } },
});
expect(cfg.user.is).toStrictEqual({ anonymous: false, admin: true });
});
test('Anonymous user', () => {
const cfg = urlConfig({
...baseGlobal,
user: { ...baseGlobal.user, is: { anonymous: true, admin: true } },
});
expect(cfg.user.is).toStrictEqual({ anonymous: true, admin: true });
});
});
});