mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-03-22 20:43:10 -04:00
refactor(frontend): replace legacy settings init/settings pattern with typed config functions
This commit is contained in:
@@ -1,46 +1,40 @@
|
||||
import { init, endpoints } from '../../../src/static/js/utils/settings/api';
|
||||
import { apiConfig } from '../../../src/static/js/utils/settings/api';
|
||||
|
||||
const apiConfig = (url: any, ep: any) => {
|
||||
init(url, ep);
|
||||
return endpoints();
|
||||
};
|
||||
const sampleGlobal = {
|
||||
site: { api: 'https://example.com/api///' },
|
||||
// endpoints below intentionally contain leading slashes to ensure they are stripped
|
||||
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',
|
||||
},
|
||||
} as const;
|
||||
|
||||
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
|
||||
test('trims trailing slashes on base and ensures single slash joins', () => {
|
||||
const cfg = apiConfig(sampleGlobal.site.api as any, sampleGlobal.api as any);
|
||||
expect(cfg.media).toBe('https://example.com/api/v1/media/');
|
||||
expect(cfg.users).toBe('https://example.com/api/v1/users//');
|
||||
// base should not end with a slash and endpoint leading slash stripped
|
||||
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);
|
||||
test('adds featured/recommended query to media variants', () => {
|
||||
const cfg = apiConfig(sampleGlobal.site.api as any, sampleGlobal.api as any);
|
||||
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);
|
||||
|
||||
test('builds nested user, archive, manage maps', () => {
|
||||
const cfg = apiConfig(sampleGlobal.site.api as any, sampleGlobal.api as any);
|
||||
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=');
|
||||
@@ -53,22 +47,30 @@ describe('utils/settings', () => {
|
||||
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);
|
||||
test('builds search endpoints with expected query fragments', () => {
|
||||
const cfg = apiConfig(sampleGlobal.site.api as any, sampleGlobal.api as any);
|
||||
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/', {
|
||||
test('handles base url with path and endpoint with existing query', () => {
|
||||
const base = 'https://example.com/base/';
|
||||
const endpoints = {
|
||||
media: 'items?x=1',
|
||||
playlists: '/pls/',
|
||||
members: 'users',
|
||||
liked: 'me/liked',
|
||||
history: 'me/history',
|
||||
tags: 't',
|
||||
categories: '/c',
|
||||
manage_media: 'm/media',
|
||||
manage_users: 'm/users',
|
||||
manage_comments: 'm/comments',
|
||||
search: '/s',
|
||||
});
|
||||
} as any;
|
||||
const cfg = apiConfig(base as any, endpoints);
|
||||
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');
|
||||
|
||||
@@ -1,140 +1,144 @@
|
||||
import { config } from '../../../src/static/js/utils/settings/config';
|
||||
|
||||
// The aggregator pulls all sub-configs together. We validate composition and memoization.
|
||||
// Behaviors to test:
|
||||
// 1) Builds enabled pages by merging site.pages and site.userPages and passes to optionsPages
|
||||
// 2) Produces api endpoints derived from site.api + api endpoints
|
||||
// 3) Honors user/feature flags within member capabilities and url manage links visibility
|
||||
// 4) Applies theme and logo mapping and site defaults when missing
|
||||
// 5) Memoizes result and returns same object instance on subsequent calls
|
||||
|
||||
// @todo: Replace 'baseGlobal' with 'sampleGlobalMediaCMS' and add missing properties value checks.
|
||||
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;
|
||||
|
||||
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);
|
||||
const cfg = config(baseGlobal as any);
|
||||
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' });
|
||||
@@ -144,14 +148,14 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
test('produces api endpoints based on site.api and api endpoints', () => {
|
||||
const cfg = config(baseGlobal);
|
||||
const cfg = config(baseGlobal as any);
|
||||
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);
|
||||
const cfg = config(baseGlobal as any);
|
||||
expect(cfg.member.is).toStrictEqual({ admin: true, anonymous: false });
|
||||
expect(cfg.member.can).toMatchObject({
|
||||
manageMedia: true,
|
||||
@@ -166,7 +170,7 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
test('theme and site defaults propagate correctly', () => {
|
||||
const cfg = config(baseGlobal);
|
||||
const cfg = config(baseGlobal as any);
|
||||
expect(cfg.theme.mode).toBe('dark');
|
||||
expect(cfg.theme.switch.position).toBe('sidebar');
|
||||
expect(cfg.theme.logo.darkMode.img).toBe('/img/dark.png');
|
||||
@@ -175,13 +179,13 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
test('memoizes and returns the same object instance on repeated calls', () => {
|
||||
const first = config(baseGlobal);
|
||||
const second = config(baseGlobal);
|
||||
const first = config(baseGlobal as any);
|
||||
const second = config(baseGlobal as any);
|
||||
expect(second).toBe(first);
|
||||
});
|
||||
|
||||
test('url profile paths use site.url when not in dev env', () => {
|
||||
const cfg = config(baseGlobal);
|
||||
const cfg = config(baseGlobal as any);
|
||||
expect(cfg.url.profile.media).toBe('https://example.com/user/john');
|
||||
expect(cfg.url.embed).toBe('https://example.com/embed?m=');
|
||||
});
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/contents';
|
||||
|
||||
const contentsConfig = (obj: any) => {
|
||||
init(obj);
|
||||
return settings();
|
||||
};
|
||||
import { contentsConfig } from '../../../src/static/js/utils/settings/contents';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('contents', () => {
|
||||
@@ -53,8 +48,7 @@ describe('utils/settings', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @todo: Revisit this behavior
|
||||
test('Sidebar menu items require text, link, icon and NOT get trimmed', () => {
|
||||
test('Sidebar menu items require text, link, icon and get trimmed', () => {
|
||||
const cfg = contentsConfig({
|
||||
sidebar: {
|
||||
mainMenuExtraItems: [
|
||||
@@ -62,22 +56,63 @@ describe('utils/settings', () => {
|
||||
{ text: 'no-link', icon: 'i' },
|
||||
{ link: '/missing-text', icon: 'i' },
|
||||
{ text: 'no-icon', link: '/x' },
|
||||
null as any,
|
||||
undefined,
|
||||
],
|
||||
navMenuItems: [
|
||||
{ text: ' B ', link: ' /b ', icon: ' i-b ' },
|
||||
{ text: ' ', link: '/bad', icon: 'i' },
|
||||
null as any,
|
||||
undefined,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(cfg.sidebar.mainMenuExtra.items).toStrictEqual([
|
||||
{ text: ' A ', link: ' /a ', icon: ' i-a ', className: ' cls ' },
|
||||
]);
|
||||
expect(cfg.sidebar.mainMenuExtra.items).toEqual([{ 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 },
|
||||
]);
|
||||
expect(cfg.sidebar.navMenu.items).toEqual([{ text: 'B', link: '/b', icon: 'i-b', className: '' }]);
|
||||
});
|
||||
|
||||
test('sidebar strings are trimmed or default to empty', () => {
|
||||
const cfg = contentsConfig({
|
||||
sidebar: {
|
||||
belowNavMenu: ' X ',
|
||||
belowThemeSwitcher: ' Y ',
|
||||
footer: ' Z ',
|
||||
},
|
||||
} as any);
|
||||
|
||||
expect(cfg.sidebar.belowNavMenu).toBe('X');
|
||||
expect(cfg.sidebar.belowThemeSwitcher).toBe('Y');
|
||||
expect(cfg.sidebar.footer).toBe('Z');
|
||||
|
||||
const cfg2 = contentsConfig({ sidebar: {} } as any);
|
||||
expect(cfg2.sidebar.belowNavMenu).toBe('');
|
||||
expect(cfg2.sidebar.belowThemeSwitcher).toBe('');
|
||||
expect(cfg2.sidebar.footer).toBe('');
|
||||
});
|
||||
|
||||
test('uploader strings are trimmed or default to empty', () => {
|
||||
const cfg = contentsConfig({
|
||||
uploader: { belowUploadArea: ' U1 ', postUploadMessage: ' U2 ' },
|
||||
} as any);
|
||||
|
||||
expect(cfg.uploader.belowUploadArea).toBe('U1');
|
||||
expect(cfg.uploader.postUploadMessage).toBe('U2');
|
||||
|
||||
const cfg2 = contentsConfig({ uploader: {} } as any);
|
||||
expect(cfg2.uploader.belowUploadArea).toBe('');
|
||||
expect(cfg2.uploader.postUploadMessage).toBe('');
|
||||
});
|
||||
|
||||
test('handles completely missing settings by returning defaults', () => {
|
||||
const cfg = contentsConfig(undefined as any);
|
||||
expect(cfg.header.right).toBe('');
|
||||
expect(cfg.header.onLogoRight).toBe('');
|
||||
expect(cfg.sidebar.mainMenuExtra.items).toEqual([]);
|
||||
expect(cfg.sidebar.navMenu.items).toEqual([]);
|
||||
expect(cfg.sidebar.footer).toBe('');
|
||||
expect(cfg.uploader.postUploadMessage).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/media';
|
||||
|
||||
const mediaConfig = (item?: any, shareOptions?: any) => {
|
||||
init(item, shareOptions);
|
||||
return settings();
|
||||
};
|
||||
import { mediaConfig } from '../../../src/static/js/utils/settings/media';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('media', () => {
|
||||
@@ -27,14 +22,17 @@ describe('utils/settings', () => {
|
||||
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']);
|
||||
const cfg = mediaConfig(undefined, [' embed ', 'email', ' email '] as unknown as Array<
|
||||
'embed' | 'email' | undefined
|
||||
>);
|
||||
expect(cfg.share.options).toEqual(['embed', 'email', 'email']); // @todo: Revisit this.
|
||||
});
|
||||
|
||||
test('Ignores falsy and invalid share options', () => {
|
||||
const cfg = mediaConfig(undefined, [undefined, '', ' ', 'invalid', 'share', 'EMBED']);
|
||||
const cfg = mediaConfig(undefined, [undefined, '', ' ', 'invalid', 'share', 'EMBED'] as unknown as Array<
|
||||
'embed' | 'email' | undefined
|
||||
>);
|
||||
expect(cfg.share.options).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/member';
|
||||
|
||||
const memberConfig = (user?: any, features?: any) => {
|
||||
init(user, features);
|
||||
return settings();
|
||||
};
|
||||
import { memberConfig } from '../../../src/static/js/utils/settings/member';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('member', () => {
|
||||
@@ -56,8 +51,8 @@ describe('utils/settings', () => {
|
||||
can: {
|
||||
changePassword: true,
|
||||
deleteProfile: true,
|
||||
addComment: true,
|
||||
mentionComment: true,
|
||||
addComment: false,
|
||||
mentionComment: false,
|
||||
deleteComment: true,
|
||||
editMedia: true,
|
||||
deleteMedia: true,
|
||||
@@ -90,8 +85,8 @@ describe('utils/settings', () => {
|
||||
changePassword: true,
|
||||
deleteProfile: true,
|
||||
readComment: true,
|
||||
addComment: true,
|
||||
mentionComment: true,
|
||||
addComment: false,
|
||||
mentionComment: false,
|
||||
deleteComment: true,
|
||||
editMedia: true,
|
||||
deleteMedia: true,
|
||||
@@ -127,6 +122,12 @@ describe('utils/settings', () => {
|
||||
expect(cfg2.can.mentionComment).toBe(true);
|
||||
});
|
||||
|
||||
test('Preserves comment capabilities from user.can when media.actions is missing', () => {
|
||||
const cfg = memberConfig({ is: { anonymous: false }, can: { addComment: true, mentionComment: true } });
|
||||
expect(cfg.can.addComment).toBe(true);
|
||||
expect(cfg.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);
|
||||
@@ -149,6 +150,16 @@ describe('utils/settings', () => {
|
||||
expect(cfg1.can.saveMedia).toBe(true);
|
||||
});
|
||||
|
||||
test('Applies legacy defaults when media.actions exists but fields are missing', () => {
|
||||
const cfg = memberConfig(undefined, { media: { actions: {} } });
|
||||
expect(cfg.can.likeMedia).toBe(true);
|
||||
expect(cfg.can.dislikeMedia).toBe(true);
|
||||
expect(cfg.can.reportMedia).toBe(true);
|
||||
expect(cfg.can.downloadMedia).toBe(false);
|
||||
expect(cfg.can.saveMedia).toBe(false);
|
||||
expect(cfg.can.shareMedia).toBe(false);
|
||||
});
|
||||
|
||||
test('User flags canSeeMembersPage/usersNeedsToBeApproved/readComment default handling', () => {
|
||||
const cfg1 = memberConfig({
|
||||
is: { anonymous: false },
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/notifications';
|
||||
|
||||
const notificationsConfig = (sett?: any) => {
|
||||
init(sett);
|
||||
return settings();
|
||||
};
|
||||
import { notificationsConfig } from '../../../src/static/js/utils/settings/notifications';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('notifications', () => {
|
||||
@@ -19,8 +14,7 @@ describe('utils/settings', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @todo: Revisit this behavior
|
||||
test('Keep incoming message values without processing', () => {
|
||||
test('Trims incoming message values and applies only when non-empty', () => {
|
||||
const cfg = notificationsConfig({
|
||||
messages: {
|
||||
addToLiked: ' Yay ',
|
||||
@@ -29,18 +23,19 @@ describe('utils/settings', () => {
|
||||
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');
|
||||
expect(cfg.messages.addToLiked).toBe('Yay');
|
||||
// empty after trim -> keep default
|
||||
expect(cfg.messages.removeFromLiked).toBe('Removed from liked media');
|
||||
expect(cfg.messages.addToDisliked).toBe('Nope');
|
||||
expect(cfg.messages.removeFromDisliked).toBe('OK');
|
||||
});
|
||||
|
||||
test('Ignores undefined, keeping defaults', () => {
|
||||
test('Ignores undefined or empty-string overrides, keeping defaults', () => {
|
||||
const cfg = notificationsConfig({
|
||||
messages: {
|
||||
addToLiked: undefined,
|
||||
removeFromLiked: undefined,
|
||||
addToDisliked: undefined,
|
||||
removeFromLiked: '',
|
||||
addToDisliked: ' ',
|
||||
removeFromDisliked: undefined,
|
||||
},
|
||||
});
|
||||
@@ -59,9 +54,18 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
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();
|
||||
const cfg = notificationsConfig({
|
||||
messages: {
|
||||
addToLiked: 'A',
|
||||
// Inject an unknown key; current implementation passes unknown keys through
|
||||
...{ notARealKey: 'x' },
|
||||
},
|
||||
});
|
||||
|
||||
expect(cfg.messages.addToLiked).toBe('A');
|
||||
// extraneous key currently copied over
|
||||
expect((cfg.messages as any).notARealKey).toBe('x');
|
||||
// sanity check known defaults remain for untouched keys
|
||||
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');
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/optionsEmbedded';
|
||||
|
||||
const optionsEmbeddedConfig = (embeddedVideo?: any) => {
|
||||
init(embeddedVideo);
|
||||
return settings();
|
||||
};
|
||||
import { optionsEmbeddedConfig } from '../../../src/static/js/utils/settings/optionsEmbedded';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('optionsEmbedded', () => {
|
||||
@@ -22,32 +17,30 @@ describe('utils/settings', () => {
|
||||
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 } });
|
||||
const cfg1 = optionsEmbeddedConfig({ initialDimensions: { width: NaN, height: NaN } } as any);
|
||||
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',
|
||||
});
|
||||
const cfg2 = optionsEmbeddedConfig({ initialDimensions: { width: '640', height: '360' } } as any);
|
||||
expect(cfg2.video.dimensions).toStrictEqual({ width: 560, widthUnit: 'px', height: 315, 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' },
|
||||
});
|
||||
initialDimensions: {
|
||||
width: 800,
|
||||
height: 450,
|
||||
widthUnit: 'percent',
|
||||
heightUnit: 'percent',
|
||||
},
|
||||
} as any);
|
||||
// units should remain default 'px'
|
||||
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' },
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
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();
|
||||
};
|
||||
import { optionsPagesConfig } from '../../../src/static/js/utils/settings/optionsPages';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('optionsPages', () => {
|
||||
@@ -12,7 +7,8 @@ describe('utils/settings', () => {
|
||||
latest: { title: 'Recent' },
|
||||
featured: { title: 'Spotlight' },
|
||||
recommended: { title: 'You may like' },
|
||||
});
|
||||
} as any);
|
||||
|
||||
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');
|
||||
@@ -26,86 +22,97 @@ describe('utils/settings', () => {
|
||||
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('Falls back to VALID_PAGES titles when custom home section titles are whitespace-only', () => {
|
||||
const cfg = optionsPagesConfig(
|
||||
{
|
||||
sections: {
|
||||
latest: { title: ' ' },
|
||||
featured: { title: '\n\t' },
|
||||
recommended: { title: ' ' },
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
latest: { title: 'Recent' },
|
||||
featured: { title: 'Spotlight' },
|
||||
recommended: { title: 'You may like' },
|
||||
} as any
|
||||
);
|
||||
|
||||
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('Sets search.advancedFilters true only when explicitly true', () => {
|
||||
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
|
||||
const def = optionsPagesConfig();
|
||||
expect(def.search.advancedFilters).toBe(false);
|
||||
|
||||
const falsy = optionsPagesConfig(undefined, { advancedFilters: false }, undefined, undefined, {});
|
||||
const falsy = optionsPagesConfig(undefined, { advancedFilters: false } as any);
|
||||
expect(falsy.search.advancedFilters).toBe(false);
|
||||
|
||||
const truthy = optionsPagesConfig(undefined, { advancedFilters: true }, undefined, undefined, {});
|
||||
const truthy = optionsPagesConfig(undefined, { advancedFilters: true } as any);
|
||||
expect(truthy.search.advancedFilters).toBe(true);
|
||||
});
|
||||
|
||||
test('Configures media options with correct defaults and overrides', () => {
|
||||
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
|
||||
|
||||
const def = optionsPagesConfig();
|
||||
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,
|
||||
{}
|
||||
);
|
||||
const override = optionsPagesConfig(undefined, undefined, {
|
||||
categoriesWithTitle: true,
|
||||
htmlInDescription: true,
|
||||
hideViews: true,
|
||||
related: { initialSize: 25 },
|
||||
});
|
||||
|
||||
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.
|
||||
expect(override.media.related.initialSize).toBe(25);
|
||||
});
|
||||
|
||||
test('Ignores NaN and non-numeric media.related.initialSize and keeps default 10', () => {
|
||||
const cfg1 = optionsPagesConfig(undefined, undefined, { related: { initialSize: NaN } }, undefined, {});
|
||||
const cfg1 = optionsPagesConfig(undefined, undefined, { related: { initialSize: NaN } } as any);
|
||||
expect(cfg1.media.related.initialSize).toBe(10);
|
||||
|
||||
const cfg2 = optionsPagesConfig(undefined, undefined, { related: { initialSize: '12' } }, undefined, {});
|
||||
const cfg2 = optionsPagesConfig(undefined, undefined, { related: { initialSize: '12' as any } } as any);
|
||||
expect(cfg2.media.related.initialSize).toBe(10);
|
||||
});
|
||||
|
||||
test('Profile settings true only when explicitly true', () => {
|
||||
const def = optionsPagesConfig(undefined, undefined, undefined, undefined, {});
|
||||
const def = optionsPagesConfig();
|
||||
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,
|
||||
},
|
||||
{}
|
||||
);
|
||||
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 validPages: any = { latest: { title: 'L' }, featured: { title: 'F' }, recommended: { title: 'R' } };
|
||||
|
||||
const homeCopy = JSON.parse(JSON.stringify(home));
|
||||
const searchCopy = JSON.parse(JSON.stringify(search));
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/pages';
|
||||
|
||||
const pagesConfig = (sett?: any) => {
|
||||
init(sett);
|
||||
return settings();
|
||||
};
|
||||
import { pagesConfig } from '../../../src/static/js/utils/settings/pages';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('pages', () => {
|
||||
@@ -25,9 +20,10 @@ describe('utils/settings', () => {
|
||||
featured: { enabled: true },
|
||||
recommended: { enabled: false },
|
||||
members: { enabled: undefined },
|
||||
liked: { enabled: null },
|
||||
history: { enabled: 0 },
|
||||
liked: { enabled: null as any },
|
||||
history: { enabled: 0 as any },
|
||||
});
|
||||
|
||||
expect(cfg.latest.enabled).toBe(true);
|
||||
expect(cfg.featured.enabled).toBe(true);
|
||||
expect(cfg.recommended.enabled).toBe(false);
|
||||
@@ -42,19 +38,28 @@ describe('utils/settings', () => {
|
||||
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 } });
|
||||
const cfg = pagesConfig({
|
||||
// @ts-ignore
|
||||
unknownKey: { enabled: true, title: 'X' },
|
||||
latest: { enabled: true },
|
||||
});
|
||||
|
||||
expect(cfg.latest.enabled).toBe(true);
|
||||
expect(cfg.unknownKey).toBeUndefined();
|
||||
expect((cfg as any).unknownKey).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Does not mutate the input settings object', () => {
|
||||
const input = { latest: { enabled: false, title: ' A ' }, featured: { enabled: true, title: ' B ' } };
|
||||
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);
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/playlists';
|
||||
|
||||
const playlistsConfig = (plists?: any) => {
|
||||
init(plists);
|
||||
return settings();
|
||||
};
|
||||
import { playlistsConfig } from '../../../src/static/js/utils/settings/playlists';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('playlists', () => {
|
||||
@@ -18,32 +13,33 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
test('Includes only valid media types when both valid and invalid are provided', () => {
|
||||
const cfg = playlistsConfig({ mediaTypes: ['audio', 'invalid', 'video', 'something'] });
|
||||
const cfg = playlistsConfig({ mediaTypes: ['audio', 'invalid', 'video', 'something'] as any });
|
||||
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']);
|
||||
expect(playlistsConfig({} as any).mediaTypes).toEqual(['audio', 'video']);
|
||||
expect(playlistsConfig({ mediaTypes: undefined } as any).mediaTypes).toEqual(['audio', 'video']);
|
||||
expect(playlistsConfig({ mediaTypes: null as any }).mediaTypes).toEqual(['audio', 'video']);
|
||||
expect(playlistsConfig({ mediaTypes: 'audio' as any }).mediaTypes).toEqual(['audio', 'video']);
|
||||
expect(playlistsConfig({ mediaTypes: 123 as any }).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'] });
|
||||
const cfg = playlistsConfig({ mediaTypes: ['video', 'audio', 'video', 'audio', 'invalid'] as any });
|
||||
// Implementation preserves order and includes duplicates; however, it later enforces default if empty only.
|
||||
// Since duplicates are allowed by implementation, expect duplicates to be preserved.
|
||||
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'] });
|
||||
const cfg = playlistsConfig({ mediaTypes: ['Audio', 'Video'] as any });
|
||||
// None match exactly, so default should apply.
|
||||
expect(cfg.mediaTypes).toEqual(['audio', 'video']);
|
||||
});
|
||||
|
||||
test('does not mutate the input object', () => {
|
||||
const input = { mediaTypes: ['audio', 'video', 'invalid'] };
|
||||
const input: any = { mediaTypes: ['audio', 'video', 'invalid'] };
|
||||
const copy = JSON.parse(JSON.stringify(input));
|
||||
playlistsConfig(input);
|
||||
expect(input).toEqual(copy);
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/sidebar';
|
||||
|
||||
const sidebarConfig = (sett?: any) => {
|
||||
init(sett);
|
||||
return settings();
|
||||
};
|
||||
import { sidebarConfig } from '../../../src/static/js/utils/settings/sidebar';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('sidebar', () => {
|
||||
@@ -32,14 +27,14 @@ describe('utils/settings', () => {
|
||||
// undefined
|
||||
expect(sidebarConfig({}).hideHomeLink).toBe(false);
|
||||
// null
|
||||
expect(sidebarConfig({ hideTagsLink: null }).hideTagsLink).toBe(false);
|
||||
expect(sidebarConfig({ hideTagsLink: null as any }).hideTagsLink).toBe(false);
|
||||
// other types
|
||||
expect(sidebarConfig({ hideCategoriesLink: 'yes' }).hideCategoriesLink).toBe(false);
|
||||
expect(sidebarConfig({ hideCategoriesLink: 1 }).hideCategoriesLink).toBe(false);
|
||||
expect(sidebarConfig({ hideCategoriesLink: 'yes' as any }).hideCategoriesLink).toBe(false);
|
||||
expect(sidebarConfig({ hideCategoriesLink: 1 as any }).hideCategoriesLink).toBe(false);
|
||||
});
|
||||
|
||||
test('Is resilient to partial inputs and ignores extra properties', () => {
|
||||
const cfg = sidebarConfig({ hideTagsLink: true, extra: 'prop' });
|
||||
const cfg = sidebarConfig({ hideTagsLink: true, extra: 'prop' } as any);
|
||||
expect(cfg).toStrictEqual({ hideHomeLink: false, hideTagsLink: true, hideCategoriesLink: false });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/site';
|
||||
|
||||
const siteConfig = (sett?: any) => {
|
||||
init(sett);
|
||||
return settings();
|
||||
};
|
||||
import { siteConfig } from '../../../src/static/js/utils/settings/site';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('site', () => {
|
||||
@@ -27,6 +22,7 @@ describe('utils/settings', () => {
|
||||
title: ' Media CMS ',
|
||||
version: ' 2.3.4 ',
|
||||
});
|
||||
|
||||
expect(cfg).toStrictEqual({
|
||||
id: 'my-site',
|
||||
url: 'https://example.com/',
|
||||
@@ -42,22 +38,24 @@ describe('utils/settings', () => {
|
||||
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);
|
||||
expect(siteConfig({ useRoundedCorners: 'no' as any }).useRoundedCorners).toBe(true);
|
||||
expect(siteConfig({ useRoundedCorners: 0 as any }).useRoundedCorners).toBe(true);
|
||||
expect(siteConfig({ useRoundedCorners: null as any }).useRoundedCorners).toBe(true);
|
||||
});
|
||||
|
||||
test('Is resilient to partial inputs and ignores extra properties', () => {
|
||||
const cfg = siteConfig({ id: ' x ', extra: 'y' });
|
||||
const cfg = siteConfig({ id: ' x ', extra: 'y' } as any);
|
||||
expect(cfg).toMatchObject({ id: 'x' });
|
||||
expect(Object.keys(cfg).sort()).toEqual(['api', 'id', 'title', 'url', 'useRoundedCorners', 'version']);
|
||||
expect(Object.keys(cfg).sort()).toStrictEqual(
|
||||
['api', 'id', 'title', 'url', 'useRoundedCorners', 'version'].sort()
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(input).toStrictEqual(copy);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/taxonomies';
|
||||
import { taxonomiesConfig } 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' },
|
||||
describe('utils/settings', () => {
|
||||
describe('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 enable a taxonomy when enabled is true', () => {
|
||||
const res = taxonomiesConfig({ tags: { enabled: true } });
|
||||
expect(res.tags).toStrictEqual({ enabled: true, title: 'Tags' });
|
||||
});
|
||||
});
|
||||
|
||||
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' });
|
||||
test('Should keep taxonomy disabled when enabled is explicitly false', () => {
|
||||
const res = taxonomiesConfig({ categories: { enabled: false } });
|
||||
expect(res.categories).toStrictEqual({ enabled: false, 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' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import { init, settings } from '../../../src/static/js/utils/settings/theme';
|
||||
|
||||
const themeConfig = (theme?: any, logo?: any) => {
|
||||
init(theme, logo);
|
||||
return settings();
|
||||
};
|
||||
import { themeConfig } from '../../../src/static/js/utils/settings/theme';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('theme', () => {
|
||||
@@ -18,10 +13,10 @@ describe('utils/settings', () => {
|
||||
|
||||
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: ' dark ' } as any).mode).toBe('dark');
|
||||
expect(themeConfig({ mode: 'Dark' } as any).mode).toBe('light');
|
||||
expect(themeConfig({ mode: 'light' }).mode).toBe('light');
|
||||
expect(themeConfig({ mode: ' ' }).mode).toBe('light');
|
||||
expect(themeConfig({ mode: ' ' } as any).mode).toBe('light');
|
||||
});
|
||||
|
||||
test('Switch config: enabled only toggles off when explicitly false; position set to sidebar only when exactly sidebar after trim', () => {
|
||||
@@ -30,9 +25,9 @@ describe('utils/settings', () => {
|
||||
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: ' sidebar ' } } as any).switch.position).toBe('sidebar');
|
||||
expect(themeConfig({ switch: { position: 'header' } }).switch.position).toBe('header');
|
||||
expect(themeConfig({ switch: { position: 'foot' } }).switch.position).toBe('header');
|
||||
expect(themeConfig({ switch: { position: 'foot' } } as any).switch.position).toBe('header');
|
||||
});
|
||||
|
||||
test('Trims and maps logo URLs for both light and dark modes; ignores missing fields', () => {
|
||||
@@ -63,7 +58,7 @@ describe('utils/settings', () => {
|
||||
});
|
||||
|
||||
test('Does not mutate input objects', () => {
|
||||
const themeIn = { mode: ' dark ', switch: { enabled: false, position: ' sidebar ' } };
|
||||
const themeIn: any = { 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));
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { init, pages } from '../../../src/static/js/utils/settings/url';
|
||||
|
||||
const urlConfig = (pages_url?: any) => {
|
||||
init(pages_url);
|
||||
return pages();
|
||||
};
|
||||
import { urlConfig } from '../../../src/static/js/utils/settings/url';
|
||||
|
||||
describe('utils/settings', () => {
|
||||
describe('url', () => {
|
||||
const baseGlobal = {
|
||||
const base = {
|
||||
profileId: 'john',
|
||||
site: {
|
||||
url: 'https://example.com/',
|
||||
@@ -43,57 +38,81 @@ describe('utils/settings', () => {
|
||||
},
|
||||
} 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('non-admin authenticated user: admin hidden, signout/changePassword visible, manage visible', () => {
|
||||
const cfg = urlConfig(base as any);
|
||||
expect(cfg.admin).toBe('');
|
||||
expect(cfg.signout).toBe('/signout');
|
||||
expect(cfg.changePassword).toBe('/password');
|
||||
expect(cfg.manage.media).toBe('/manage/media');
|
||||
expect(cfg.manage.users).toBe('/manage/users');
|
||||
expect(cfg.manage.comments).toBe('/manage/comments');
|
||||
});
|
||||
|
||||
test('Admin user', () => {
|
||||
test('anonymous user: admin, signout, changePassword, manage all hidden', () => {
|
||||
const cfg = urlConfig({
|
||||
...baseGlobal,
|
||||
user: { ...baseGlobal.user, is: { anonymous: false, admin: true } },
|
||||
});
|
||||
expect(cfg.user.is).toStrictEqual({ anonymous: false, admin: true });
|
||||
...base,
|
||||
user: { ...base.user, is: { anonymous: true, admin: false } },
|
||||
} as any);
|
||||
expect(cfg.admin).toBe('');
|
||||
expect(cfg.signout).toBe('');
|
||||
expect(cfg.changePassword).toBe('');
|
||||
expect(cfg.manage.media).toBe('');
|
||||
expect(cfg.manage.users).toBe('');
|
||||
expect(cfg.manage.comments).toBe('');
|
||||
});
|
||||
|
||||
test('Anonymous user', () => {
|
||||
test('admin user: admin visible', () => {
|
||||
const cfg = urlConfig({
|
||||
...baseGlobal,
|
||||
user: { ...baseGlobal.user, is: { anonymous: true, admin: true } },
|
||||
...base,
|
||||
user: { ...base.user, is: { anonymous: false, admin: true } },
|
||||
} as any);
|
||||
expect(cfg.admin).toBe('/admin');
|
||||
});
|
||||
|
||||
test('embed URL strips trailing slashes from site.url', () => {
|
||||
const cfg1 = urlConfig(base as any);
|
||||
expect(cfg1.embed).toBe('https://example.com/embed?m=');
|
||||
|
||||
const cfg2 = urlConfig({ ...base, site: { ...base.site, url: 'https://example.com////' } } as any);
|
||||
expect(cfg2.embed).toBe('https://example.com/embed?m=');
|
||||
});
|
||||
|
||||
test('search URLs are composed correctly', () => {
|
||||
const cfg = urlConfig(base as any);
|
||||
expect(cfg.search.base).toBe('/search');
|
||||
expect(cfg.search.query).toBe('/search?q=');
|
||||
expect(cfg.search.tag).toBe('/search?t=');
|
||||
expect(cfg.search.category).toBe('/search?c=');
|
||||
});
|
||||
|
||||
test('profile URLs: devEnv=false use site.url + profileId', () => {
|
||||
const cfg = urlConfig(base as any);
|
||||
expect(cfg.profile.media).toBe('https://example.com/user/john');
|
||||
expect(cfg.profile.about).toBe('https://example.com/user/john/about');
|
||||
expect(cfg.profile.playlists).toBe('https://example.com/user/john/playlists');
|
||||
expect(cfg.profile.shared_by_me).toBe('https://example.com/user/john/shared_by_me');
|
||||
expect(cfg.profile.shared_with_me).toBe('https://example.com/user/john/shared_with_me');
|
||||
});
|
||||
|
||||
test('profile URLs: devEnv=true use user.pages and append shared paths', () => {
|
||||
const cfg = urlConfig({ ...base, site: { ...base.site, devEnv: true } } as any);
|
||||
expect(cfg.profile.media).toBe('/u/john');
|
||||
expect(cfg.profile.about).toBe('/u/john/about');
|
||||
expect(cfg.profile.playlists).toBe('/u/john/playlists');
|
||||
expect(cfg.profile.shared_by_me).toBe('/u/john/shared_by_me');
|
||||
expect(cfg.profile.shared_with_me).toBe('/u/john/shared_with_me');
|
||||
});
|
||||
|
||||
test('passes through archive and user URLs', () => {
|
||||
const cfg = urlConfig(base as any);
|
||||
expect(cfg.user).toStrictEqual({
|
||||
liked: '/liked',
|
||||
history: '/history',
|
||||
addMedia: '/add',
|
||||
editChannel: '/edit/channel',
|
||||
editProfile: '/edit/profile',
|
||||
});
|
||||
expect(cfg.user.is).toStrictEqual({ anonymous: true, admin: true });
|
||||
expect(cfg.archive).toStrictEqual({ tags: '/tags', categories: '/categories' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user