Compare commits
3 Commits
main
...
frontend-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d391c29e9 | ||
|
|
fbc3817efd | ||
|
|
08189191b5 |
3
frontend/.vscode/settings.json
vendored
3
frontend/.vscode/settings.json
vendored
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
|
"prettier.configPath": "../.prettierrc"
|
||||||
}
|
}
|
||||||
@ -21,6 +21,9 @@
|
|||||||
"@babel/core": "^7.26.9",
|
"@babel/core": "^7.26.9",
|
||||||
"@babel/preset-env": "^7.26.9",
|
"@babel/preset-env": "^7.26.9",
|
||||||
"@babel/preset-react": "^7.26.3",
|
"@babel/preset-react": "^7.26.3",
|
||||||
|
"@testing-library/dom": "^8.20.1",
|
||||||
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
|
"@testing-library/react": "^12.1.5",
|
||||||
"@types/flux": "^3.1.15",
|
"@types/flux": "^3.1.15",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/minimatch": "^5.1.2",
|
"@types/minimatch": "^5.1.2",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -9,88 +9,89 @@ let browserCache;
|
|||||||
const _StoreData = {};
|
const _StoreData = {};
|
||||||
|
|
||||||
class VideoPlayerStore extends EventEmitter {
|
class VideoPlayerStore extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
this.mediacms_config = mediacmsConfig(window.MediaCMS);
|
||||||
|
|
||||||
browserCache = new BrowserCache(this.mediacms_config.site.id, 86400); // Keep cache data "fresh" for one day.
|
browserCache = new BrowserCache(this.mediacms_config.site.id, 86400); // Keep cache data "fresh" for one day.
|
||||||
|
|
||||||
_StoreData.inTheaterMode = browserCache.get('in-theater-mode');
|
_StoreData.inTheaterMode = browserCache.get('in-theater-mode');
|
||||||
_StoreData.inTheaterMode = null !== _StoreData.inTheaterMode ? _StoreData.inTheaterMode : !1;
|
_StoreData.inTheaterMode = null !== _StoreData.inTheaterMode ? _StoreData.inTheaterMode : !1;
|
||||||
|
|
||||||
_StoreData.playerVolume = browserCache.get('player-volume');
|
_StoreData.playerVolume = browserCache.get('player-volume');
|
||||||
_StoreData.playerVolume =
|
_StoreData.playerVolume =
|
||||||
null === _StoreData.playerVolume ? 1 : Math.max(Math.min(Number(_StoreData.playerVolume), 1), 0);
|
null === _StoreData.playerVolume ? 1 : Math.max(Math.min(Number(_StoreData.playerVolume), 1), 0);
|
||||||
|
|
||||||
_StoreData.playerSoundMuted = browserCache.get('player-sound-muted');
|
_StoreData.playerSoundMuted = browserCache.get('player-sound-muted');
|
||||||
_StoreData.playerSoundMuted = null !== _StoreData.playerSoundMuted ? _StoreData.playerSoundMuted : !1;
|
_StoreData.playerSoundMuted = null !== _StoreData.playerSoundMuted ? _StoreData.playerSoundMuted : !1;
|
||||||
|
|
||||||
_StoreData.videoQuality = browserCache.get('video-quality');
|
_StoreData.videoQuality = browserCache.get('video-quality');
|
||||||
_StoreData.videoQuality = null !== _StoreData.videoQuality ? _StoreData.videoQuality : 'Auto';
|
_StoreData.videoQuality = null !== _StoreData.videoQuality ? _StoreData.videoQuality : 'Auto';
|
||||||
|
|
||||||
_StoreData.videoPlaybackSpeed = browserCache.get('video-playback-speed');
|
_StoreData.videoPlaybackSpeed = browserCache.get('video-playback-speed');
|
||||||
_StoreData.videoPlaybackSpeed = null !== _StoreData.videoPlaybackSpeed ? _StoreData.videoPlaybackSpeed : !1;
|
_StoreData.videoPlaybackSpeed = null !== _StoreData.videoPlaybackSpeed ? _StoreData.videoPlaybackSpeed : !1;
|
||||||
}
|
|
||||||
|
|
||||||
get(type) {
|
|
||||||
let r = null;
|
|
||||||
switch (type) {
|
|
||||||
case 'player-volume':
|
|
||||||
r = _StoreData.playerVolume;
|
|
||||||
break;
|
|
||||||
case 'player-sound-muted':
|
|
||||||
r = _StoreData.playerSoundMuted;
|
|
||||||
break;
|
|
||||||
case 'in-theater-mode':
|
|
||||||
r = _StoreData.inTheaterMode;
|
|
||||||
break;
|
|
||||||
case 'video-data':
|
|
||||||
r = _StoreData.videoData;
|
|
||||||
break;
|
|
||||||
case 'video-quality':
|
|
||||||
r = _StoreData.videoQuality;
|
|
||||||
break;
|
|
||||||
case 'video-playback-speed':
|
|
||||||
r = _StoreData.videoPlaybackSpeed;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
actions_handler(action) {
|
get(type) {
|
||||||
switch (action.type) {
|
let r = null;
|
||||||
case 'TOGGLE_VIEWER_MODE':
|
switch (type) {
|
||||||
_StoreData.inTheaterMode = !_StoreData.inTheaterMode;
|
case 'player-volume':
|
||||||
this.emit('changed_viewer_mode');
|
r = _StoreData.playerVolume;
|
||||||
break;
|
break;
|
||||||
case 'SET_VIEWER_MODE':
|
case 'player-sound-muted':
|
||||||
_StoreData.inTheaterMode = action.inTheaterMode;
|
r = _StoreData.playerSoundMuted;
|
||||||
browserCache.set('in-theater-mode', _StoreData.inTheaterMode);
|
break;
|
||||||
this.emit('changed_viewer_mode');
|
case 'in-theater-mode':
|
||||||
break;
|
r = _StoreData.inTheaterMode;
|
||||||
case 'SET_PLAYER_VOLUME':
|
break;
|
||||||
_StoreData.playerVolume = action.playerVolume;
|
case 'video-data':
|
||||||
browserCache.set('player-volume', action.playerVolume);
|
r = _StoreData.videoData;
|
||||||
this.emit('changed_player_volume');
|
break;
|
||||||
break;
|
case 'video-quality':
|
||||||
case 'SET_PLAYER_SOUND_MUTED':
|
r = _StoreData.videoQuality;
|
||||||
_StoreData.playerSoundMuted = action.playerSoundMuted;
|
break;
|
||||||
browserCache.set('player-sound-muted', action.playerSoundMuted);
|
case 'video-playback-speed':
|
||||||
this.emit('changed_player_sound_muted');
|
r = _StoreData.videoPlaybackSpeed;
|
||||||
break;
|
break;
|
||||||
case 'SET_VIDEO_QUALITY':
|
}
|
||||||
_StoreData.videoQuality = action.quality;
|
return r;
|
||||||
browserCache.set('video-quality', action.quality);
|
}
|
||||||
this.emit('changed_video_quality');
|
|
||||||
break;
|
actions_handler(action) {
|
||||||
case 'SET_VIDEO_PLAYBACK_SPEED':
|
switch (action.type) {
|
||||||
_StoreData.videoPlaybackSpeed = action.playbackSpeed;
|
case 'TOGGLE_VIEWER_MODE':
|
||||||
browserCache.set('video-playback-speed', action.playbackSpeed);
|
_StoreData.inTheaterMode = !_StoreData.inTheaterMode;
|
||||||
this.emit('changed_video_playback_speed');
|
browserCache.set('in-theater-mode', _StoreData.inTheaterMode);
|
||||||
break;
|
this.emit('changed_viewer_mode');
|
||||||
|
break;
|
||||||
|
case 'SET_VIEWER_MODE':
|
||||||
|
_StoreData.inTheaterMode = action.inTheaterMode;
|
||||||
|
browserCache.set('in-theater-mode', _StoreData.inTheaterMode);
|
||||||
|
this.emit('changed_viewer_mode');
|
||||||
|
break;
|
||||||
|
case 'SET_PLAYER_VOLUME':
|
||||||
|
_StoreData.playerVolume = action.playerVolume;
|
||||||
|
browserCache.set('player-volume', action.playerVolume);
|
||||||
|
this.emit('changed_player_volume');
|
||||||
|
break;
|
||||||
|
case 'SET_PLAYER_SOUND_MUTED':
|
||||||
|
_StoreData.playerSoundMuted = action.playerSoundMuted;
|
||||||
|
browserCache.set('player-sound-muted', action.playerSoundMuted);
|
||||||
|
this.emit('changed_player_sound_muted');
|
||||||
|
break;
|
||||||
|
case 'SET_VIDEO_QUALITY':
|
||||||
|
_StoreData.videoQuality = action.quality;
|
||||||
|
browserCache.set('video-quality', action.quality);
|
||||||
|
this.emit('changed_video_quality');
|
||||||
|
break;
|
||||||
|
case 'SET_VIDEO_PLAYBACK_SPEED':
|
||||||
|
_StoreData.videoPlaybackSpeed = action.playbackSpeed;
|
||||||
|
browserCache.set('video-playback-speed', action.playbackSpeed);
|
||||||
|
this.emit('changed_video_playback_speed');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default exportStore(new VideoPlayerStore(), 'actions_handler');
|
export default exportStore(new VideoPlayerStore(), 'actions_handler');
|
||||||
|
|||||||
385
frontend/tests/tests-constants.ts
Normal file
385
frontend/tests/tests-constants.ts
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
export const sampleGlobalMediaCMS = {
|
||||||
|
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: '1.0.0',
|
||||||
|
taxonomies: {
|
||||||
|
tags: { enabled: true, title: 'Topic Tags' },
|
||||||
|
categories: { enabled: false, title: 'Kinds' },
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
featured: { enabled: true, title: 'Featured picks' },
|
||||||
|
latest: { enabled: true, title: 'Recent uploads' },
|
||||||
|
members: { enabled: true, title: 'People' },
|
||||||
|
recommended: { enabled: false, title: 'You may like' },
|
||||||
|
},
|
||||||
|
userPages: {
|
||||||
|
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',
|
||||||
|
actions: 'v1/actions',
|
||||||
|
comments: 'v1/comments',
|
||||||
|
},
|
||||||
|
contents: {
|
||||||
|
header: {
|
||||||
|
right: '',
|
||||||
|
onLogoRight: '',
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
messages: { addToLiked: 'Yay', removeFromLiked: 'Oops', addToDisliked: 'nay', removeFromDisliked: 'ok' },
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
belowNavMenu: '__belowNavMenu__',
|
||||||
|
belowThemeSwitcher: '__belowThemeSwitcher__',
|
||||||
|
footer: '__footer__',
|
||||||
|
mainMenuExtraItems: [
|
||||||
|
{ text: '__text_1__', link: '__link_1__', icon: '__icon_1__', className: '__className_1__' },
|
||||||
|
],
|
||||||
|
navMenuItems: [
|
||||||
|
{ text: '__text_2__', link: '__link_2__', icon: '__icon_2__', className: '__className_2__' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
uploader: {
|
||||||
|
belowUploadArea: '__belowUploadArea__',
|
||||||
|
postUploadMessage: '__postUploadMessage__',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
home: {
|
||||||
|
sections: {
|
||||||
|
latest: { title: 'Latest T' },
|
||||||
|
featured: { title: 'Featured T' },
|
||||||
|
recommended: { title: 'Recommended T' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
media: { categoriesWithTitle: true, htmlInDescription: true, hideViews: true, related: { initialSize: 5 } },
|
||||||
|
profile: { htmlInDescription: true, includeHistory: true, includeLikedMedia: true },
|
||||||
|
search: { advancedFilters: 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'],
|
||||||
|
},
|
||||||
|
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 ' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sampleMediaCMSConfig = {
|
||||||
|
api: {
|
||||||
|
archive: {
|
||||||
|
tags: '',
|
||||||
|
categories: '',
|
||||||
|
},
|
||||||
|
featured: '',
|
||||||
|
manage: {
|
||||||
|
media: '',
|
||||||
|
users: '',
|
||||||
|
comments: '',
|
||||||
|
},
|
||||||
|
media: '',
|
||||||
|
playlists: '/v1/playlists',
|
||||||
|
recommended: '',
|
||||||
|
search: {
|
||||||
|
query: '',
|
||||||
|
titles: './search.html?titles=',
|
||||||
|
tag: '',
|
||||||
|
category: '',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
liked: '',
|
||||||
|
history: '',
|
||||||
|
playlists: '/playlists/?author=',
|
||||||
|
},
|
||||||
|
users: '/users',
|
||||||
|
},
|
||||||
|
contents: {
|
||||||
|
header: {
|
||||||
|
right: '',
|
||||||
|
onLogoRight: '',
|
||||||
|
},
|
||||||
|
uploader: {
|
||||||
|
belowUploadArea: '',
|
||||||
|
postUploadMessage: '',
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
belowNavMenu: '__belowNavMenu__',
|
||||||
|
belowThemeSwitcher: '__belowThemeSwitcher__',
|
||||||
|
footer: '__footer__',
|
||||||
|
mainMenuExtra: {
|
||||||
|
items: [{ text: '__text_1__', link: '__link_1__', icon: '__icon_1__', className: '__className_1__' }],
|
||||||
|
},
|
||||||
|
navMenu: {
|
||||||
|
items: [{ text: '__text_2__', link: '__link_2__', icon: '__icon_2__', className: '__className_2__' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabled: {
|
||||||
|
taxonomies: sampleGlobalMediaCMS.site.taxonomies,
|
||||||
|
pages: {
|
||||||
|
featured: { enabled: true, title: 'Featured picks' },
|
||||||
|
latest: { enabled: true, title: 'Recent uploads' },
|
||||||
|
members: { enabled: true, title: 'People' },
|
||||||
|
recommended: { enabled: true, title: 'You may like' },
|
||||||
|
liked: { enabled: true, title: 'Favorites' },
|
||||||
|
history: { enabled: true, title: 'Watched' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
member: {
|
||||||
|
name: null,
|
||||||
|
username: 'john',
|
||||||
|
thumbnail: null,
|
||||||
|
is: {
|
||||||
|
admin: false,
|
||||||
|
anonymous: false,
|
||||||
|
},
|
||||||
|
can: {
|
||||||
|
addComment: false,
|
||||||
|
addMedia: false,
|
||||||
|
canSeeMembersPage: false,
|
||||||
|
changePassword: false,
|
||||||
|
contactUser: false,
|
||||||
|
deleteComment: false,
|
||||||
|
deleteMedia: false,
|
||||||
|
deleteProfile: false,
|
||||||
|
dislikeMedia: false,
|
||||||
|
downloadMedia: false,
|
||||||
|
editMedia: false,
|
||||||
|
editProfile: false,
|
||||||
|
editSubtitle: false,
|
||||||
|
likeMedia: false,
|
||||||
|
login: false,
|
||||||
|
manageComments: false,
|
||||||
|
manageMedia: false,
|
||||||
|
manageUsers: false,
|
||||||
|
mentionComment: false,
|
||||||
|
readComment: true,
|
||||||
|
register: false,
|
||||||
|
reportMedia: false,
|
||||||
|
saveMedia: true,
|
||||||
|
shareMedia: false,
|
||||||
|
usersNeedsToBeApproved: false,
|
||||||
|
},
|
||||||
|
pages: {
|
||||||
|
home: null,
|
||||||
|
about: null,
|
||||||
|
media: null,
|
||||||
|
playlists: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
item: {
|
||||||
|
displayAuthor: false,
|
||||||
|
displayViews: false,
|
||||||
|
displayPublishDate: false,
|
||||||
|
},
|
||||||
|
share: {
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notifications: {
|
||||||
|
messages: {
|
||||||
|
addToLiked: '',
|
||||||
|
removeFromLiked: '',
|
||||||
|
addToDisliked: '',
|
||||||
|
removeFromDisliked: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
pages: {
|
||||||
|
home: {
|
||||||
|
sections: {
|
||||||
|
latest: {
|
||||||
|
title: '',
|
||||||
|
},
|
||||||
|
featured: {
|
||||||
|
title: '',
|
||||||
|
},
|
||||||
|
recommended: {
|
||||||
|
title: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
advancedFilters: false,
|
||||||
|
},
|
||||||
|
media: {
|
||||||
|
categoriesWithTitle: true,
|
||||||
|
htmlInDescription: true,
|
||||||
|
related: { initialSize: 5 },
|
||||||
|
displayViews: true,
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
htmlInDescription: false,
|
||||||
|
includeHistory: false,
|
||||||
|
includeLikedMedia: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
embedded: {
|
||||||
|
video: {
|
||||||
|
dimensions: {
|
||||||
|
width: 0,
|
||||||
|
widthUnit: 'px',
|
||||||
|
height: 0,
|
||||||
|
heightUnit: 'px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
playlists: {
|
||||||
|
mediaTypes: [],
|
||||||
|
},
|
||||||
|
sidebar: {
|
||||||
|
hideHomeLink: false,
|
||||||
|
hideTagsLink: false,
|
||||||
|
hideCategoriesLink: false,
|
||||||
|
},
|
||||||
|
site: {
|
||||||
|
api: '',
|
||||||
|
id: '',
|
||||||
|
title: '',
|
||||||
|
url: '',
|
||||||
|
useRoundedCorners: false,
|
||||||
|
version: '',
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
logo: {
|
||||||
|
lightMode: { img: '/img/light.png', svg: '/img/light.svg' },
|
||||||
|
darkMode: { img: '/img/dark.png', svg: '/img/dark.svg' },
|
||||||
|
},
|
||||||
|
mode: 'dark',
|
||||||
|
switch: {
|
||||||
|
enabled: true,
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
admin: '',
|
||||||
|
archive: {
|
||||||
|
categories: '',
|
||||||
|
tags: '',
|
||||||
|
},
|
||||||
|
changePassword: '',
|
||||||
|
embed: '',
|
||||||
|
error404: '',
|
||||||
|
featured: '',
|
||||||
|
home: '',
|
||||||
|
latest: '',
|
||||||
|
manage: {
|
||||||
|
comments: '',
|
||||||
|
media: '',
|
||||||
|
users: '',
|
||||||
|
},
|
||||||
|
members: '',
|
||||||
|
profile: {
|
||||||
|
about: '',
|
||||||
|
media: '',
|
||||||
|
playlists: '',
|
||||||
|
shared_by_me: '',
|
||||||
|
shared_with_me: '',
|
||||||
|
},
|
||||||
|
recommended: '',
|
||||||
|
register: '',
|
||||||
|
search: {
|
||||||
|
base: '',
|
||||||
|
category: '',
|
||||||
|
query: '',
|
||||||
|
tag: '',
|
||||||
|
},
|
||||||
|
signin: '',
|
||||||
|
signout: '',
|
||||||
|
user: {
|
||||||
|
addMedia: '',
|
||||||
|
editChannel: '',
|
||||||
|
editProfile: '',
|
||||||
|
history: '',
|
||||||
|
liked: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
145
frontend/tests/utils/actions/MediaPageActions.test.ts
Normal file
145
frontend/tests/utils/actions/MediaPageActions.test.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import * as MediaPageActions from '../../../src/static/js/utils/actions/MediaPageActions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by MediaPageActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('MediaPageActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadMediaData', () => {
|
||||||
|
it('Should dispatch LOAD_MEDIA_DATA action', () => {
|
||||||
|
MediaPageActions.loadMediaData();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'LOAD_MEDIA_DATA' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('likeMedia / dislikeMedia', () => {
|
||||||
|
it('Should dispatch LIKE_MEDIA action', () => {
|
||||||
|
MediaPageActions.likeMedia();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'LIKE_MEDIA' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch DISLIKE_MEDIA action', () => {
|
||||||
|
MediaPageActions.dislikeMedia();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'DISLIKE_MEDIA' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reportMedia', () => {
|
||||||
|
it('Should dispatch REPORT_MEDIA with empty string when description is undefined', () => {
|
||||||
|
MediaPageActions.reportMedia();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REPORT_MEDIA', reportDescription: '' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch REPORT_MEDIA with stripped description when provided', () => {
|
||||||
|
MediaPageActions.reportMedia(' some text ');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REPORT_MEDIA', reportDescription: 'sometext' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should remove all whitespace characters including newlines and tabs', () => {
|
||||||
|
MediaPageActions.reportMedia('\n\t spaced\ntext \t');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REPORT_MEDIA', reportDescription: 'spacedtext' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('copyShareLink / copyEmbedMediaCode', () => {
|
||||||
|
it('Should dispatch COPY_SHARE_LINK carrying the provided input element', () => {
|
||||||
|
const inputElem = document.createElement('input');
|
||||||
|
MediaPageActions.copyShareLink(inputElem);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'COPY_SHARE_LINK', inputElement: inputElem });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch COPY_EMBED_MEDIA_CODE carrying the provided textarea element', () => {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
MediaPageActions.copyEmbedMediaCode(textarea);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'COPY_EMBED_MEDIA_CODE', inputElement: textarea });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removeMedia', () => {
|
||||||
|
it('Should dispatch REMOVE_MEDIA action', () => {
|
||||||
|
MediaPageActions.removeMedia();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REMOVE_MEDIA' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('comments', () => {
|
||||||
|
it('Should dispatch SUBMIT_COMMENT with provided text', () => {
|
||||||
|
const commentText = 'Nice one';
|
||||||
|
MediaPageActions.submitComment(commentText);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'SUBMIT_COMMENT', commentText });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch DELETE_COMMENT with provided comment id', () => {
|
||||||
|
const commentId = 'c-123';
|
||||||
|
MediaPageActions.deleteComment(commentId);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'DELETE_COMMENT', commentId });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch DELETE_COMMENT with numeric comment id', () => {
|
||||||
|
const commentId = 42;
|
||||||
|
MediaPageActions.deleteComment(commentId);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'DELETE_COMMENT', commentId });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('playlists', () => {
|
||||||
|
it('Should dispatch CREATE_PLAYLIST with provided data', () => {
|
||||||
|
const payload = { title: 'My list', description: 'Desc' };
|
||||||
|
MediaPageActions.createPlaylist(payload);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'CREATE_PLAYLIST', playlist_data: payload });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch ADD_MEDIA_TO_PLAYLIST with ids', () => {
|
||||||
|
const playlist_id = 'pl-1';
|
||||||
|
const media_id = 'm-1';
|
||||||
|
MediaPageActions.addMediaToPlaylist(playlist_id, media_id);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'ADD_MEDIA_TO_PLAYLIST', playlist_id, media_id });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch REMOVE_MEDIA_FROM_PLAYLIST with ids', () => {
|
||||||
|
const playlist_id = 'pl-1';
|
||||||
|
const media_id = 'm-1';
|
||||||
|
MediaPageActions.removeMediaFromPlaylist(playlist_id, media_id);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REMOVE_MEDIA_FROM_PLAYLIST', playlist_id, media_id });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch APPEND_NEW_PLAYLIST with provided playlist data', () => {
|
||||||
|
const playlist_data = {
|
||||||
|
playlist_id: 'pl-2',
|
||||||
|
add_date: new Date('2020-01-01T00:00:00Z'),
|
||||||
|
description: 'Cool',
|
||||||
|
title: 'T',
|
||||||
|
media_list: ['a', 'b'],
|
||||||
|
};
|
||||||
|
MediaPageActions.addNewPlaylist(playlist_data);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'APPEND_NEW_PLAYLIST', playlist_data });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
55
frontend/tests/utils/actions/PageActions.test.ts
Normal file
55
frontend/tests/utils/actions/PageActions.test.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import * as PageActions from '../../../src/static/js/utils/actions/PageActions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by PageActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('PageActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('initPage', () => {
|
||||||
|
it('Should dispatch INIT_PAGE with provided page string', () => {
|
||||||
|
PageActions.initPage('home');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'INIT_PAGE', page: 'home' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch INIT_PAGE with empty string', () => {
|
||||||
|
PageActions.initPage('');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'INIT_PAGE', page: '' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleMediaAutoPlay', () => {
|
||||||
|
it('Should dispatch TOGGLE_AUTO_PLAY action', () => {
|
||||||
|
PageActions.toggleMediaAutoPlay();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'TOGGLE_AUTO_PLAY' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('addNotification', () => {
|
||||||
|
it('Should dispatch ADD_NOTIFICATION with message and id', () => {
|
||||||
|
const notification = 'Saved!';
|
||||||
|
const notificationId = 'notif-1';
|
||||||
|
PageActions.addNotification(notification, notificationId);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'ADD_NOTIFICATION', notification, notificationId });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch ADD_NOTIFICATION with empty notification message', () => {
|
||||||
|
const notification = '';
|
||||||
|
const notificationId = 'id-empty';
|
||||||
|
PageActions.addNotification(notification, notificationId);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'ADD_NOTIFICATION', notification, notificationId });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
96
frontend/tests/utils/actions/PlaylistPageActions.test.ts
Normal file
96
frontend/tests/utils/actions/PlaylistPageActions.test.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { PlaylistPageActions } from '../../../src/static/js/utils/actions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by PlaylistPageActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('PlaylistPageActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadPlaylistData', () => {
|
||||||
|
it('Should dispatch LOAD_PLAYLIST_DATA action', () => {
|
||||||
|
PlaylistPageActions.loadPlaylistData();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'LOAD_PLAYLIST_DATA' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleSave', () => {
|
||||||
|
it('Should dispatch TOGGLE_SAVE action', () => {
|
||||||
|
PlaylistPageActions.toggleSave();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'TOGGLE_SAVE' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updatePlaylist', () => {
|
||||||
|
it('Should dispatch UPDATE_PLAYLIST with provided title and description', () => {
|
||||||
|
const payload = { title: 'My Playlist', description: 'A description' };
|
||||||
|
PlaylistPageActions.updatePlaylist(payload);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'UPDATE_PLAYLIST', playlist_data: payload });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch UPDATE_PLAYLIST with empty strings for title and description', () => {
|
||||||
|
const payload = { title: '', description: '' };
|
||||||
|
PlaylistPageActions.updatePlaylist(payload);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'UPDATE_PLAYLIST', playlist_data: payload });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removePlaylist', () => {
|
||||||
|
it('Should dispatch REMOVE_PLAYLIST action', () => {
|
||||||
|
PlaylistPageActions.removePlaylist();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REMOVE_PLAYLIST' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('removedMediaFromPlaylist', () => {
|
||||||
|
it('Should dispatch MEDIA_REMOVED_FROM_PLAYLIST with media and playlist ids', () => {
|
||||||
|
PlaylistPageActions.removedMediaFromPlaylist('m1', 'p1');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({
|
||||||
|
type: 'MEDIA_REMOVED_FROM_PLAYLIST',
|
||||||
|
media_id: 'm1',
|
||||||
|
playlist_id: 'p1',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch MEDIA_REMOVED_FROM_PLAYLIST with empty ids as strings', () => {
|
||||||
|
PlaylistPageActions.removedMediaFromPlaylist('', '');
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({
|
||||||
|
type: 'MEDIA_REMOVED_FROM_PLAYLIST',
|
||||||
|
media_id: '',
|
||||||
|
playlist_id: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('reorderedMediaInPlaylist', () => {
|
||||||
|
it('Should dispatch PLAYLIST_MEDIA_REORDERED with provided array', () => {
|
||||||
|
const items = [
|
||||||
|
{ id: '1', url: '/1', thumbnail_url: '/t1' },
|
||||||
|
{ id: '2', url: '/2', thumbnail_url: '/t2' },
|
||||||
|
];
|
||||||
|
PlaylistPageActions.reorderedMediaInPlaylist(items);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'PLAYLIST_MEDIA_REORDERED', playlist_media: items });
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
it('Should dispatch PLAYLIST_MEDIA_REORDERED with empty array for playlist media', () => {
|
||||||
|
const items: any[] = [];
|
||||||
|
PlaylistPageActions.reorderedMediaInPlaylist(items);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'PLAYLIST_MEDIA_REORDERED', playlist_media: items });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
39
frontend/tests/utils/actions/PlaylistViewActions.test.ts
Normal file
39
frontend/tests/utils/actions/PlaylistViewActions.test.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { PlaylistViewActions } from '../../../src/static/js/utils/actions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by PlaylistViewActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('PlaylistViewActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleLoop', () => {
|
||||||
|
it('Should dispatch TOGGLE_LOOP action', () => {
|
||||||
|
PlaylistViewActions.toggleLoop();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'TOGGLE_LOOP' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleShuffle', () => {
|
||||||
|
it('Should dispatch TOGGLE_SHUFFLE action', () => {
|
||||||
|
PlaylistViewActions.toggleShuffle();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'TOGGLE_SHUFFLE' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toggleSave', () => {
|
||||||
|
it('Should dispatch TOGGLE_SAVE action', () => {
|
||||||
|
PlaylistViewActions.toggleSave();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'TOGGLE_SAVE' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
27
frontend/tests/utils/actions/ProfilePageActions.test.ts
Normal file
27
frontend/tests/utils/actions/ProfilePageActions.test.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { ProfilePageActions } from '../../../src/static/js/utils/actions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by ProfilePageActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('ProfilePageActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch LOAD_AUTHOR_DATA ', () => {
|
||||||
|
ProfilePageActions.load_author_data();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'LOAD_AUTHOR_DATA' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should dispatch REMOVE_PROFILE ', () => {
|
||||||
|
ProfilePageActions.remove_profile();
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledWith({ type: 'REMOVE_PROFILE' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
25
frontend/tests/utils/actions/SearchFieldActions.test.ts
Normal file
25
frontend/tests/utils/actions/SearchFieldActions.test.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { SearchFieldActions } from '../../../src/static/js/utils/actions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by SearchFieldActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('SearchFieldActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('requestPredictions', () => {
|
||||||
|
it('Should dispatch REQUEST_PREDICTIONS with provided query strings', () => {
|
||||||
|
SearchFieldActions.requestPredictions('cats');
|
||||||
|
SearchFieldActions.requestPredictions('');
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'REQUEST_PREDICTIONS', query: 'cats' });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'REQUEST_PREDICTIONS', query: '' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
72
frontend/tests/utils/actions/VideoViewerActions.test.ts
Normal file
72
frontend/tests/utils/actions/VideoViewerActions.test.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { VideoViewerActions } from '../../../src/static/js/utils/actions';
|
||||||
|
import dispatcher from '../../../src/static/js/utils/dispatcher';
|
||||||
|
|
||||||
|
// Mock the dispatcher module used by VideoViewerActions
|
||||||
|
jest.mock('../../../src/static/js/utils/dispatcher', () => ({ dispatch: jest.fn() }));
|
||||||
|
|
||||||
|
describe('utils/actions', () => {
|
||||||
|
describe('VideoViewerActions', () => {
|
||||||
|
const dispatch = dispatcher.dispatch;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(dispatcher.dispatch as jest.Mock).mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('set_viewer_mode', () => {
|
||||||
|
it('Should dispatch SET_VIEWER_MODE with "true" and "false" for enabling and disabling theater mode', () => {
|
||||||
|
VideoViewerActions.set_viewer_mode(true);
|
||||||
|
VideoViewerActions.set_viewer_mode(false);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'SET_VIEWER_MODE', inTheaterMode: true });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'SET_VIEWER_MODE', inTheaterMode: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('set_player_volume', () => {
|
||||||
|
it('Should dispatch SET_PLAYER_VOLUME with provided volume numbers', () => {
|
||||||
|
VideoViewerActions.set_player_volume(0);
|
||||||
|
VideoViewerActions.set_player_volume(0.75);
|
||||||
|
VideoViewerActions.set_player_volume(1);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(3);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'SET_PLAYER_VOLUME', playerVolume: 0 });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'SET_PLAYER_VOLUME', playerVolume: 0.75 });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(3, { type: 'SET_PLAYER_VOLUME', playerVolume: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('set_player_sound_muted', () => {
|
||||||
|
it('Should dispatch SET_PLAYER_SOUND_MUTED with "true" and "false"', () => {
|
||||||
|
VideoViewerActions.set_player_sound_muted(true);
|
||||||
|
VideoViewerActions.set_player_sound_muted(false);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'SET_PLAYER_SOUND_MUTED', playerSoundMuted: true });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, {
|
||||||
|
type: 'SET_PLAYER_SOUND_MUTED',
|
||||||
|
playerSoundMuted: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('set_video_quality', () => {
|
||||||
|
it('Should dispatch SET_VIDEO_QUALITY with "auto" and numeric quality', () => {
|
||||||
|
VideoViewerActions.set_video_quality('auto');
|
||||||
|
VideoViewerActions.set_video_quality(720);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'SET_VIDEO_QUALITY', quality: 'auto' });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'SET_VIDEO_QUALITY', quality: 720 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('set_video_playback_speed', () => {
|
||||||
|
it('Should dispatch SET_VIDEO_PLAYBACK_SPEED with different speeds', () => {
|
||||||
|
VideoViewerActions.set_video_playback_speed(1.5);
|
||||||
|
VideoViewerActions.set_video_playback_speed(0.5);
|
||||||
|
VideoViewerActions.set_video_playback_speed(2);
|
||||||
|
expect(dispatch).toHaveBeenCalledTimes(3);
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: 'SET_VIDEO_PLAYBACK_SPEED', playbackSpeed: 1.5 });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: 'SET_VIDEO_PLAYBACK_SPEED', playbackSpeed: 0.5 });
|
||||||
|
expect(dispatch).toHaveBeenNthCalledWith(3, { type: 'SET_VIDEO_PLAYBACK_SPEED', playbackSpeed: 2 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
92
frontend/tests/utils/classes/BrowserCache.test.ts
Normal file
92
frontend/tests/utils/classes/BrowserCache.test.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { BrowserCache } from '../../../src/static/js/utils/classes/BrowserCache';
|
||||||
|
|
||||||
|
// Mocks for helpers used by BrowserCache
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers/', () => ({
|
||||||
|
logErrorAndReturnError: jest.fn((args: any[]) => ({ error: true, args })),
|
||||||
|
logWarningAndReturnError: jest.fn((args: any[]) => ({ warning: true, args })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { logErrorAndReturnError } = jest.requireMock('../../../src/static/js/utils/helpers/');
|
||||||
|
|
||||||
|
describe('utils/classes', () => {
|
||||||
|
describe('BrowserCache', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
localStorage.clear();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Returns error when prefix is missing', () => {
|
||||||
|
const cache = BrowserCache(undefined, 3600);
|
||||||
|
expect(cache).toEqual(expect.objectContaining({ error: true }));
|
||||||
|
expect(logErrorAndReturnError).toHaveBeenCalledWith(['Cache object prefix is required']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Set and get returns stored primitive value before expiration', () => {
|
||||||
|
const cache = BrowserCache('prefix', 3600);
|
||||||
|
|
||||||
|
if (cache instanceof Error) {
|
||||||
|
expect(cache instanceof Error).toBe(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(cache.set('foo', 'bar')).toBe(true);
|
||||||
|
expect(cache.get('foo')).toBe('bar');
|
||||||
|
|
||||||
|
// Ensure value serialized in localStorage with namespaced key
|
||||||
|
const raw = localStorage.getItem('prefix[foo]') as string;
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
expect(parsed.value).toBe('bar');
|
||||||
|
expect(typeof parsed.expire).toBe('number');
|
||||||
|
expect(parsed.expire).toBeGreaterThan(Date.now());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Get returns null when expired', () => {
|
||||||
|
const cache = BrowserCache('prefix', 1);
|
||||||
|
|
||||||
|
if (cache instanceof Error) {
|
||||||
|
expect(cache instanceof Error).toBe(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.set('exp', { a: 1 });
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.advanceTimersByTime(1_000);
|
||||||
|
|
||||||
|
expect(cache.get('exp')).toBeNull();
|
||||||
|
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Clear removes only keys for its prefix', () => {
|
||||||
|
const cacheA = BrowserCache('A', 3600);
|
||||||
|
const cacheB = BrowserCache('B', 3600);
|
||||||
|
|
||||||
|
if (cacheA instanceof Error) {
|
||||||
|
expect(cacheA instanceof Error).toBe(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheB instanceof Error) {
|
||||||
|
expect(cacheB instanceof Error).toBe(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheA.set('x', 1);
|
||||||
|
cacheB.set('x', 2);
|
||||||
|
|
||||||
|
expect(localStorage.getItem('A[x]')).toBeTruthy();
|
||||||
|
expect(localStorage.getItem('B[x]')).toBeTruthy();
|
||||||
|
|
||||||
|
cacheA.clear();
|
||||||
|
|
||||||
|
expect(localStorage.getItem('A[x]')).toBeNull();
|
||||||
|
expect(localStorage.getItem('B[x]')).toBeTruthy();
|
||||||
|
|
||||||
|
cacheB.clear();
|
||||||
|
|
||||||
|
expect(localStorage.getItem('A[x]')).toBeNull();
|
||||||
|
expect(localStorage.getItem('B[x]')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
101
frontend/tests/utils/classes/MediaDurationInfo.test.ts
Normal file
101
frontend/tests/utils/classes/MediaDurationInfo.test.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { MediaDurationInfo } from '../../../src/static/js/utils/classes/MediaDurationInfo';
|
||||||
|
|
||||||
|
describe('utils/classes', () => {
|
||||||
|
describe('MediaDurationInfo', () => {
|
||||||
|
test('Initializes via constructor when seconds is a positive integer (<= 59)', () => {
|
||||||
|
const mdi = new MediaDurationInfo(42);
|
||||||
|
expect(mdi.toString()).toBe('0:42');
|
||||||
|
expect(mdi.ariaLabel()).toBe('42 seconds');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DT0H0M42S');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Formats minutes and zero-pads seconds; no hours prefix under 60 minutes', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
mdi.update(5 * 60 + 7);
|
||||||
|
expect(mdi.toString()).toBe('5:07');
|
||||||
|
expect(mdi.ariaLabel()).toBe('5 minutes, 7 seconds');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DT0H5M7S');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Includes hours when duration >= 1 hour and zero-pads minutes when needed', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
mdi.update(1 * 3600 + 2 * 60 + 3);
|
||||||
|
expect(mdi.toString()).toBe('1:02:03');
|
||||||
|
expect(mdi.ariaLabel()).toBe('1 hours, 2 minutes, 3 seconds');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DT1H2M3S');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Accumulates hours when days are present (e.g., 1 day + 2:03:04 => 26:03:04)', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
const seconds = 1 * 86400 + 2 * 3600 + 3 * 60 + 4; // 1d 2:03:04 => 26:03:04
|
||||||
|
mdi.update(seconds);
|
||||||
|
expect(mdi.toString()).toBe('26:03:04');
|
||||||
|
expect(mdi.ariaLabel()).toBe('26 hours, 3 minutes, 4 seconds');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DT26H3M4S');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Large durations: multiple days correctly mapped into hours', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
const seconds = 3 * 86400 + 10 * 3600 + 15 * 60 + 9; // 3d 10:15:09 => 82:15:09
|
||||||
|
mdi.update(seconds);
|
||||||
|
expect(mdi.toString()).toBe('82:15:09');
|
||||||
|
expect(mdi.ariaLabel()).toBe('82 hours, 15 minutes, 9 seconds');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DT82H15M9S');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Caching: toString and ariaLabel recompute only after update()', () => {
|
||||||
|
const mdi = new MediaDurationInfo(59);
|
||||||
|
const firstToString = mdi.toString();
|
||||||
|
const firstAria = mdi.ariaLabel();
|
||||||
|
expect(firstToString).toBe('0:59');
|
||||||
|
expect(firstAria).toBe('59 seconds');
|
||||||
|
|
||||||
|
// Call again to hit cached path
|
||||||
|
expect(mdi.toString()).toBe(firstToString);
|
||||||
|
expect(mdi.ariaLabel()).toBe(firstAria);
|
||||||
|
|
||||||
|
// Update and ensure cache invalidates
|
||||||
|
mdi.update(60);
|
||||||
|
expect(mdi.toString()).toBe('1:00');
|
||||||
|
expect(mdi.ariaLabel()).toBe('1 minutes');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Ignores invalid (non-positive integer or zero) updates, retaining previous value', () => {
|
||||||
|
const mdi = new MediaDurationInfo(10);
|
||||||
|
expect(mdi.toString()).toBe('0:10');
|
||||||
|
|
||||||
|
mdi.update(1.23);
|
||||||
|
expect(mdi.toString()).toBe('0:10');
|
||||||
|
|
||||||
|
mdi.update(-5);
|
||||||
|
expect(mdi.toString()).toBe('0:10');
|
||||||
|
|
||||||
|
mdi.update('x');
|
||||||
|
expect(mdi.toString()).toBe('0:10');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Boundary conditions around a minute and an hour', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
|
||||||
|
mdi.update(59);
|
||||||
|
expect(mdi.toString()).toBe('0:59');
|
||||||
|
|
||||||
|
mdi.update(60);
|
||||||
|
expect(mdi.toString()).toBe('1:00');
|
||||||
|
|
||||||
|
mdi.update(3599);
|
||||||
|
expect(mdi.toString()).toBe('59:59');
|
||||||
|
|
||||||
|
mdi.update(3600);
|
||||||
|
expect(mdi.toString()).toBe('1:00:00');
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit this behavior
|
||||||
|
test('Constructs without initial seconds', () => {
|
||||||
|
const mdi = new MediaDurationInfo();
|
||||||
|
expect(typeof mdi.toString()).toBe('function');
|
||||||
|
expect(mdi.ariaLabel()).toBe('');
|
||||||
|
expect(mdi.ISO8601()).toBe('P0Y0M0DTundefinedHundefinedMundefinedS');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
102
frontend/tests/utils/classes/UpNextLoaderView.test.ts
Normal file
102
frontend/tests/utils/classes/UpNextLoaderView.test.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { UpNextLoaderView } from '../../../src/static/js/utils/classes/UpNextLoaderView';
|
||||||
|
|
||||||
|
// Minimal helpers mocks used by UpNextLoaderView
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers/', () => ({
|
||||||
|
addClassname: jest.fn((el: any, cn: string) => el && el.classList && el.classList.add(cn)),
|
||||||
|
removeClassname: jest.fn((el: any, cn: string) => el && el.classList && el.classList.remove(cn)),
|
||||||
|
translateString: (s: string) => s,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const { addClassname, removeClassname } = jest.requireMock('../../../src/static/js/utils/helpers/');
|
||||||
|
|
||||||
|
const makeNextItem = () => ({
|
||||||
|
url: '/next-url',
|
||||||
|
title: 'Next title',
|
||||||
|
author_name: 'Jane Doe',
|
||||||
|
thumbnail_url: 'https://example.com/thumb.jpg',
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('utils/classes', () => {
|
||||||
|
describe('UpNextLoaderView', () => {
|
||||||
|
test('html() builds structure with expected classes and content', () => {
|
||||||
|
const v = new UpNextLoaderView(makeNextItem());
|
||||||
|
|
||||||
|
const root = v.html();
|
||||||
|
|
||||||
|
expect(root).toBeInstanceOf(HTMLElement);
|
||||||
|
expect(root.querySelector('.up-next-loader-inner')).not.toBeNull();
|
||||||
|
expect(root.querySelector('.up-next-label')!.textContent).toBe('Up Next');
|
||||||
|
expect(root.querySelector('.next-media-title')!.textContent).toBe('Next title');
|
||||||
|
expect(root.querySelector('.next-media-author')!.textContent).toBe('Jane Doe');
|
||||||
|
|
||||||
|
// poster background
|
||||||
|
const poster = root.querySelector('.next-media-poster') as HTMLElement;
|
||||||
|
expect(poster.style.backgroundImage).toContain('thumb.jpg');
|
||||||
|
|
||||||
|
// go-next link points to next url
|
||||||
|
const link = root.querySelector('.go-next a') as HTMLAnchorElement;
|
||||||
|
expect(link.getAttribute('href')).toBe('/next-url');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setVideoJsPlayerElem marks player with vjs-mediacms-has-up-next-view class', () => {
|
||||||
|
const v = new UpNextLoaderView(makeNextItem());
|
||||||
|
const player = document.createElement('div');
|
||||||
|
|
||||||
|
v.setVideoJsPlayerElem(player);
|
||||||
|
|
||||||
|
expect(addClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-has-up-next-view');
|
||||||
|
expect(v.vjsPlayerElem).toBe(player);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('startTimer shows view, registers scroll, and navigates after 10s', () => {
|
||||||
|
const next = makeNextItem();
|
||||||
|
const v = new UpNextLoaderView(next);
|
||||||
|
const player = document.createElement('div');
|
||||||
|
|
||||||
|
v.setVideoJsPlayerElem(player);
|
||||||
|
v.startTimer();
|
||||||
|
|
||||||
|
expect(removeClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-up-next-hidden');
|
||||||
|
expect(removeClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-canceled-next');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cancelTimer clears timeout, stops scroll, and marks canceled', () => {
|
||||||
|
const v = new UpNextLoaderView(makeNextItem());
|
||||||
|
const player = document.createElement('div');
|
||||||
|
|
||||||
|
v.setVideoJsPlayerElem(player);
|
||||||
|
|
||||||
|
v.startTimer();
|
||||||
|
v.cancelTimer();
|
||||||
|
|
||||||
|
expect(addClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-canceled-next');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Cancel button click hides the view and cancels timer', () => {
|
||||||
|
const v = new UpNextLoaderView(makeNextItem());
|
||||||
|
const player = document.createElement('div');
|
||||||
|
v.setVideoJsPlayerElem(player);
|
||||||
|
|
||||||
|
v.startTimer();
|
||||||
|
const root = v.html();
|
||||||
|
const cancelBtn = root.querySelector('.up-next-cancel button') as HTMLButtonElement;
|
||||||
|
cancelBtn.click();
|
||||||
|
|
||||||
|
expect(addClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-canceled-next');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('showTimerView shows or starts timer based on flag', () => {
|
||||||
|
const v = new UpNextLoaderView(makeNextItem());
|
||||||
|
const player = document.createElement('div');
|
||||||
|
v.setVideoJsPlayerElem(player);
|
||||||
|
|
||||||
|
// beginTimer=false -> just show view
|
||||||
|
v.showTimerView(false);
|
||||||
|
expect(removeClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-up-next-hidden');
|
||||||
|
|
||||||
|
// beginTimer=true -> starts timer
|
||||||
|
v.showTimerView(true);
|
||||||
|
expect(removeClassname).toHaveBeenCalledWith(player, 'vjs-mediacms-canceled-next');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
739
frontend/tests/utils/stores/MediaPageStore.test.ts
Normal file
739
frontend/tests/utils/stores/MediaPageStore.test.ts
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
import { csrfToken, deleteRequest, getRequest, postRequest, putRequest } from '../../../src/static/js/utils/helpers';
|
||||||
|
|
||||||
|
const MEDIA_ID = 'MEDIA_ID';
|
||||||
|
const PLAYLIST_ID = 'PLAYLIST_ID';
|
||||||
|
|
||||||
|
window.history.pushState({}, '', `/?m=${MEDIA_ID}&pl=${PLAYLIST_ID}`);
|
||||||
|
|
||||||
|
import store from '../../../src/static/js/utils/stores/MediaPageStore';
|
||||||
|
|
||||||
|
import { sampleGlobalMediaCMS, sampleMediaCMSConfig } from '../../tests-constants';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/classes/', () => ({
|
||||||
|
BrowserCache: jest.fn().mockImplementation(() => ({
|
||||||
|
get: jest.fn(),
|
||||||
|
set: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
BrowserEvents: jest.fn().mockImplementation(() => ({
|
||||||
|
doc: jest.fn(),
|
||||||
|
win: jest.fn(),
|
||||||
|
})),
|
||||||
|
csrfToken: jest.fn(),
|
||||||
|
deleteRequest: jest.fn(),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
getRequest: jest.fn(),
|
||||||
|
postRequest: jest.fn(),
|
||||||
|
putRequest: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
describe('MediaPageStore', () => {
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onLoadedViewerPlaylistData = jest.fn();
|
||||||
|
const onLoadedPagePlaylistData = jest.fn();
|
||||||
|
const onLoadedViewerPlaylistError = jest.fn();
|
||||||
|
const onLoadedVideoData = jest.fn();
|
||||||
|
const onLoadedImageData = jest.fn();
|
||||||
|
const onLoadedMediaData = jest.fn();
|
||||||
|
const onLoadedMediaError = jest.fn();
|
||||||
|
const onCommentsLoad = jest.fn();
|
||||||
|
const onUsersLoad = jest.fn();
|
||||||
|
const onPlaylistsLoad = jest.fn();
|
||||||
|
const onLikedMediaFailedRequest = jest.fn();
|
||||||
|
const onLikedMedia = jest.fn();
|
||||||
|
const onDislikedMediaFailedRequest = jest.fn();
|
||||||
|
const onDislikedMedia = jest.fn();
|
||||||
|
const onReportedMedia = jest.fn();
|
||||||
|
const onPlaylistCreationCompleted = jest.fn();
|
||||||
|
const onPlaylistCreationFailed = jest.fn();
|
||||||
|
const onMediaPlaylistAdditionCompleted = jest.fn();
|
||||||
|
const onMediaPlaylistAdditionFailed = jest.fn();
|
||||||
|
const onMediaPlaylistRemovalCompleted = jest.fn();
|
||||||
|
const onMediaPlaylistRemovalFailed = jest.fn();
|
||||||
|
const onCopiedMediaLink = jest.fn();
|
||||||
|
const onCopiedEmbedMediaCode = jest.fn();
|
||||||
|
const onMediaDelete = jest.fn();
|
||||||
|
const onMediaDeleteFail = jest.fn();
|
||||||
|
const onCommentDeleteFail = jest.fn();
|
||||||
|
const onCommentDelete = jest.fn();
|
||||||
|
const onCommentSubmitFail = jest.fn();
|
||||||
|
const onCommentSubmit = jest.fn();
|
||||||
|
|
||||||
|
store.on('loaded_viewer_playlist_data', onLoadedViewerPlaylistData);
|
||||||
|
store.on('loaded_page_playlist_data', onLoadedPagePlaylistData);
|
||||||
|
store.on('loaded_viewer_playlist_error', onLoadedViewerPlaylistError);
|
||||||
|
store.on('loaded_video_data', onLoadedVideoData);
|
||||||
|
store.on('loaded_image_data', onLoadedImageData);
|
||||||
|
store.on('loaded_media_data', onLoadedMediaData);
|
||||||
|
store.on('loaded_media_error', onLoadedMediaError);
|
||||||
|
store.on('comments_load', onCommentsLoad);
|
||||||
|
store.on('users_load', onUsersLoad);
|
||||||
|
store.on('playlists_load', onPlaylistsLoad);
|
||||||
|
store.on('liked_media_failed_request', onLikedMediaFailedRequest);
|
||||||
|
store.on('liked_media', onLikedMedia);
|
||||||
|
store.on('disliked_media_failed_request', onDislikedMediaFailedRequest);
|
||||||
|
store.on('disliked_media', onDislikedMedia);
|
||||||
|
store.on('reported_media', onReportedMedia);
|
||||||
|
store.on('playlist_creation_completed', onPlaylistCreationCompleted);
|
||||||
|
store.on('playlist_creation_failed', onPlaylistCreationFailed);
|
||||||
|
store.on('media_playlist_addition_completed', onMediaPlaylistAdditionCompleted);
|
||||||
|
store.on('media_playlist_addition_failed', onMediaPlaylistAdditionFailed);
|
||||||
|
store.on('media_playlist_removal_completed', onMediaPlaylistRemovalCompleted);
|
||||||
|
store.on('media_playlist_removal_failed', onMediaPlaylistRemovalFailed);
|
||||||
|
store.on('copied_media_link', onCopiedMediaLink);
|
||||||
|
store.on('copied_embed_media_code', onCopiedEmbedMediaCode);
|
||||||
|
store.on('media_delete', onMediaDelete);
|
||||||
|
store.on('media_delete_fail', onMediaDeleteFail);
|
||||||
|
store.on('comment_delete_fail', onCommentDeleteFail);
|
||||||
|
store.on('comment_delete', onCommentDelete);
|
||||||
|
store.on('comment_submit_fail', onCommentSubmitFail);
|
||||||
|
store.on('comment_submit', onCommentSubmit);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
(globalThis as any).window.MediaCMS = {
|
||||||
|
// mediaId: MEDIA_ID, // @note: It doesn't belong in 'sampleGlobalMediaCMS, but it could be used
|
||||||
|
features: sampleGlobalMediaCMS.features,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
delete (globalThis as any).window.MediaCMS;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
expect(store.get('users')).toStrictEqual([]);
|
||||||
|
expect(store.get('playlists')).toStrictEqual([]);
|
||||||
|
expect(store.get('media-load-error-type')).toBe(null);
|
||||||
|
expect(store.get('media-load-error-message')).toBe(null);
|
||||||
|
expect(store.get('media-comments')).toStrictEqual([]);
|
||||||
|
expect(store.get('media-data')).toBe(null);
|
||||||
|
expect(store.get('media-id')).toBe(MEDIA_ID);
|
||||||
|
expect(store.get('media-url')).toBe('N/A');
|
||||||
|
expect(store.get('media-edit-subtitle-url')).toBe(null);
|
||||||
|
expect(store.get('media-likes')).toBe('N/A');
|
||||||
|
expect(store.get('media-dislikes')).toBe('N/A');
|
||||||
|
expect(store.get('media-summary')).toBe(null);
|
||||||
|
expect(store.get('media-categories')).toStrictEqual([]);
|
||||||
|
expect(store.get('media-tags')).toStrictEqual([]);
|
||||||
|
expect(store.get('media-type')).toBe(null);
|
||||||
|
expect(store.get('media-original-url')).toBe(null);
|
||||||
|
expect(store.get('media-thumbnail-url')).toBe(null);
|
||||||
|
expect(store.get('user-liked-media')).toBe(false);
|
||||||
|
expect(store.get('user-disliked-media')).toBe(false);
|
||||||
|
expect(store.get('media-author-thumbnail-url')).toBe(null);
|
||||||
|
expect(store.get('playlist-data')).toBe(null);
|
||||||
|
expect(store.get('playlist-id')).toBe(null);
|
||||||
|
expect(store.get('playlist-next-media-url')).toBe(null);
|
||||||
|
expect(store.get('playlist-previous-media-url')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
const MEDIA_DATA = {
|
||||||
|
add_subtitle_url: '/MEDIA_DATA_ADD_SUBTITLE_URL',
|
||||||
|
author_thumbnail: 'MEDIA_DATA_AUTHOR_THUMBNAIL',
|
||||||
|
categories_info: [
|
||||||
|
{ title: 'Art', url: '/search?c=Art' },
|
||||||
|
{ title: 'Documentary', url: '/search?c=Documentary' },
|
||||||
|
],
|
||||||
|
likes: 12,
|
||||||
|
dislikes: 4,
|
||||||
|
media_type: 'video',
|
||||||
|
original_media_url: 'MEDIA_DATA_ORIGINAL_MEDIA_URL',
|
||||||
|
reported_times: 0,
|
||||||
|
summary: 'MEDIA_DATA_SUMMARY',
|
||||||
|
tags_info: [
|
||||||
|
{ title: 'and', url: '/search?t=and' },
|
||||||
|
{ title: 'behavior', url: '/search?t=behavior' },
|
||||||
|
],
|
||||||
|
thumbnail_url: 'MEDIA_DATA_THUMBNAIL_URL',
|
||||||
|
url: '/MEDIA_DATA_URL',
|
||||||
|
};
|
||||||
|
const PLAYLIST_DATA = {
|
||||||
|
playlist_media: [
|
||||||
|
{ friendly_token: `${MEDIA_ID}_2`, url: '/PLAYLIT_MEDIA_URL_2' },
|
||||||
|
{ friendly_token: MEDIA_ID, url: '/PLAYLIT_MEDIA_URL_1' },
|
||||||
|
{ friendly_token: `${MEDIA_ID}_3`, url: '/PLAYLIT_MEDIA_URL_3' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const USER_PLAYLIST_DATA = { playlist_media: [{ url: 'm=PLAYLIST_MEDIA_ID' }] };
|
||||||
|
|
||||||
|
test('Action type: "LOAD_MEDIA_DATA"', () => {
|
||||||
|
const MEDIA_API_URL = `${sampleMediaCMSConfig.api.media}/${MEDIA_ID}`;
|
||||||
|
const MEDIA_COMMENTS_API_URL = `${sampleMediaCMSConfig.api.media}/${MEDIA_ID}/comments`;
|
||||||
|
const PLAYLIST_API_URL = `${sampleMediaCMSConfig.api.playlists}/${PLAYLIST_ID}`;
|
||||||
|
const USERS_API_URL = sampleMediaCMSConfig.api.users;
|
||||||
|
const USER_PLAYLISTS_API_URL = `${sampleMediaCMSConfig.api.user.playlists}${sampleMediaCMSConfig.member.username}`;
|
||||||
|
const USER_PLAYLIST_API_URL = `${sampleMediaCMSConfig.site.url}/${'PLAYLIST_API_URL'.replace(/^\//g, '')}`;
|
||||||
|
|
||||||
|
const MEDIA_COMMENTS_RESULTS = ['COMMENT_ID_1'];
|
||||||
|
const USERS_RESULTS = ['USER_ID_1'];
|
||||||
|
const USER_PLAYLISTS_RESULTS = [
|
||||||
|
{
|
||||||
|
url: `/${PLAYLIST_ID}`,
|
||||||
|
user: sampleMediaCMSConfig.member.username,
|
||||||
|
title: 'PLAYLIST_TITLE',
|
||||||
|
description: 'PLAYLIST_DECRIPTION',
|
||||||
|
add_date: 'PLAYLIST_ADD_DATE',
|
||||||
|
api_url: 'PLAYLIST_API_URL',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
(getRequest as jest.Mock).mockImplementation((url, _cache, successCallback, _failCallback) => {
|
||||||
|
if (url === PLAYLIST_API_URL) {
|
||||||
|
return successCallback({ data: PLAYLIST_DATA });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === USER_PLAYLIST_API_URL) {
|
||||||
|
return successCallback({ data: USER_PLAYLIST_DATA });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === MEDIA_API_URL) {
|
||||||
|
return successCallback({ data: MEDIA_DATA });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === USERS_API_URL) {
|
||||||
|
return successCallback({ data: { count: USERS_RESULTS.length, results: USERS_RESULTS } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === MEDIA_COMMENTS_API_URL) {
|
||||||
|
return successCallback({
|
||||||
|
data: { count: MEDIA_COMMENTS_RESULTS.length, results: MEDIA_COMMENTS_RESULTS },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url === USER_PLAYLISTS_API_URL) {
|
||||||
|
return successCallback({
|
||||||
|
data: { count: USER_PLAYLISTS_RESULTS.length, results: USER_PLAYLISTS_RESULTS },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler({ type: 'LOAD_MEDIA_DATA' });
|
||||||
|
|
||||||
|
expect(getRequest).toHaveBeenCalledTimes(6);
|
||||||
|
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(
|
||||||
|
PLAYLIST_API_URL,
|
||||||
|
false,
|
||||||
|
store.playlistDataResponse,
|
||||||
|
store.playlistDataErrorResponse
|
||||||
|
);
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(
|
||||||
|
MEDIA_API_URL,
|
||||||
|
false,
|
||||||
|
store.dataResponse,
|
||||||
|
store.dataErrorResponse
|
||||||
|
);
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(MEDIA_COMMENTS_API_URL, false, store.commentsResponse);
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(USERS_API_URL, false, store.usersResponse);
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(USER_PLAYLISTS_API_URL, false, store.playlistsResponse);
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(USER_PLAYLIST_API_URL, false, expect.any(Function));
|
||||||
|
|
||||||
|
expect(onLoadedViewerPlaylistData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedPagePlaylistData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedViewerPlaylistError).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onLoadedVideoData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedImageData).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onLoadedMediaData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedMediaError).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCommentsLoad).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onUsersLoad).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onPlaylistsLoad).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLikedMediaFailedRequest).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onLikedMedia).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onDislikedMediaFailedRequest).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onDislikedMedia).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onReportedMedia).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onPlaylistCreationCompleted).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onPlaylistCreationFailed).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaPlaylistAdditionCompleted).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaPlaylistAdditionFailed).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaPlaylistRemovalCompleted).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaPlaylistRemovalFailed).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCopiedMediaLink).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCopiedEmbedMediaCode).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaDelete).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onMediaDeleteFail).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCommentDeleteFail).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCommentDelete).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCommentSubmitFail).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onCommentSubmit).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
expect(store.isVideo()).toBeTruthy();
|
||||||
|
|
||||||
|
expect(store.get('users')).toStrictEqual(USERS_RESULTS);
|
||||||
|
expect(store.get('playlists')).toStrictEqual([
|
||||||
|
{
|
||||||
|
playlist_id: PLAYLIST_ID,
|
||||||
|
title: 'PLAYLIST_TITLE',
|
||||||
|
description: 'PLAYLIST_DECRIPTION',
|
||||||
|
add_date: 'PLAYLIST_ADD_DATE',
|
||||||
|
media_list: ['PLAYLIST_MEDIA_ID'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(store.get('media-load-error-type')).toBe(null);
|
||||||
|
expect(store.get('media-load-error-message')).toBe(null);
|
||||||
|
expect(store.get('media-comments')).toStrictEqual(MEDIA_COMMENTS_RESULTS);
|
||||||
|
expect(store.get('media-data')).toBe(MEDIA_DATA);
|
||||||
|
expect(store.get('media-id')).toBe(MEDIA_ID);
|
||||||
|
expect(store.get('media-url')).toBe(MEDIA_DATA.url);
|
||||||
|
expect(store.get('media-edit-subtitle-url')).toBe(MEDIA_DATA.add_subtitle_url);
|
||||||
|
expect(store.get('media-likes')).toBe(MEDIA_DATA.likes);
|
||||||
|
expect(store.get('media-dislikes')).toBe(MEDIA_DATA.dislikes);
|
||||||
|
expect(store.get('media-summary')).toBe(MEDIA_DATA.summary);
|
||||||
|
expect(store.get('media-categories')).toStrictEqual(MEDIA_DATA.categories_info);
|
||||||
|
expect(store.get('media-tags')).toStrictEqual(MEDIA_DATA.tags_info);
|
||||||
|
expect(store.get('media-type')).toBe(MEDIA_DATA.media_type);
|
||||||
|
expect(store.get('media-original-url')).toBe(MEDIA_DATA.original_media_url);
|
||||||
|
expect(store.get('media-thumbnail-url')).toBe(MEDIA_DATA.thumbnail_url);
|
||||||
|
expect(store.get('user-liked-media')).toBe(false);
|
||||||
|
expect(store.get('user-disliked-media')).toBe(false);
|
||||||
|
expect(store.get('media-author-thumbnail-url')).toBe(`/${MEDIA_DATA.author_thumbnail}`);
|
||||||
|
expect(store.get('playlist-data')).toBe(PLAYLIST_DATA);
|
||||||
|
expect(store.get('playlist-id')).toBe(PLAYLIST_ID);
|
||||||
|
expect(store.get('playlist-next-media-url')).toBe(
|
||||||
|
`${PLAYLIST_DATA.playlist_media[2].url}&pl=${PLAYLIST_ID}`
|
||||||
|
);
|
||||||
|
expect(store.get('playlist-previous-media-url')).toBe(
|
||||||
|
`${PLAYLIST_DATA.playlist_media[0].url}&pl=${PLAYLIST_ID}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "LIKE_MEDIA"', () => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: {} })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'LIKE_MEDIA' });
|
||||||
|
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.media}/${MEDIA_ID}/actions`,
|
||||||
|
{ type: 'like' },
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.likeActionResponse,
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onLikedMedia).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('media-likes')).toBe(MEDIA_DATA.likes + 1);
|
||||||
|
expect(store.get('media-dislikes')).toBe(MEDIA_DATA.dislikes);
|
||||||
|
expect(store.get('user-liked-media')).toBe(true);
|
||||||
|
expect(store.get('user-disliked-media')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "DISLIKE_MEDIA"', () => {
|
||||||
|
handler({ type: 'DISLIKE_MEDIA' });
|
||||||
|
|
||||||
|
expect(postRequest).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onDislikedMedia).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
expect(store.get('media-likes')).toBe(MEDIA_DATA.likes + 1);
|
||||||
|
expect(store.get('media-dislikes')).toBe(MEDIA_DATA.dislikes);
|
||||||
|
expect(store.get('user-liked-media')).toBe(true);
|
||||||
|
expect(store.get('user-disliked-media')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REPORT_MEDIA"', () => {
|
||||||
|
const REPORT_DESCRIPTION = 'REPORT_DESCRIPTION';
|
||||||
|
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: {} })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REPORT_MEDIA', reportDescription: REPORT_DESCRIPTION });
|
||||||
|
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.media}/${MEDIA_ID}/actions`,
|
||||||
|
{ type: 'report', extra_info: REPORT_DESCRIPTION },
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.reportActionResponse,
|
||||||
|
store.reportActionResponse
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onReportedMedia).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "COPY_SHARE_LINK"', () => {
|
||||||
|
document.execCommand = jest.fn(); // @deprecated
|
||||||
|
const inputElement = document.createElement('input');
|
||||||
|
handler({ type: 'COPY_SHARE_LINK', inputElement });
|
||||||
|
expect(onCopiedMediaLink).toHaveBeenCalledTimes(1);
|
||||||
|
expect(document.execCommand).toHaveBeenCalledWith('copy');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "COPY_EMBED_MEDIA_CODE"', () => {
|
||||||
|
document.execCommand = jest.fn(); // @deprecated
|
||||||
|
const inputElement = document.createElement('input');
|
||||||
|
handler({ type: 'COPY_EMBED_MEDIA_CODE', inputElement });
|
||||||
|
expect(onCopiedEmbedMediaCode).toHaveBeenCalledTimes(1);
|
||||||
|
expect(document.execCommand).toHaveBeenCalledWith('copy');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "REMOVE_MEDIA"', () => {
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.media}/${MEDIA_ID}`,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.removeMediaResponse,
|
||||||
|
store.removeMediaFail
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fast-forward time
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _configData, _sync, successCallback, _failCallback) => successCallback({ status: 204 })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_MEDIA' });
|
||||||
|
|
||||||
|
expect(onMediaDelete).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onMediaDelete).toHaveBeenCalledWith(MEDIA_ID);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _configData, _sync, _successCallback, failCallback) => failCallback({})
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_MEDIA' });
|
||||||
|
|
||||||
|
expect(onMediaDeleteFail).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "SUBMIT_COMMENT"', () => {
|
||||||
|
const COMMENT_TEXT = 'COMMENT_TEXT';
|
||||||
|
const COMMENT_UID = 'COMMENT_UID';
|
||||||
|
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.media}/${MEDIA_ID}/comments`,
|
||||||
|
{ text: COMMENT_TEXT },
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.submitCommentResponse,
|
||||||
|
store.submitCommentFail
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fast-forward time
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: { uid: COMMENT_UID }, status: 201 })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'SUBMIT_COMMENT', commentText: COMMENT_TEXT });
|
||||||
|
|
||||||
|
expect(onCommentSubmit).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onCommentSubmit).toHaveBeenCalledWith(COMMENT_UID);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'SUBMIT_COMMENT', commentText: COMMENT_TEXT });
|
||||||
|
|
||||||
|
expect(onCommentSubmitFail).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "DELETE_COMMENT"', () => {
|
||||||
|
const COMMENT_ID = 'COMMENT_ID';
|
||||||
|
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.media}/${MEDIA_ID}/comments/${COMMENT_ID}`,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.removeCommentResponse,
|
||||||
|
store.removeCommentFail
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fast-forward time
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _configData, _sync, successCallback, _failCallback) => successCallback({ status: 204 })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'DELETE_COMMENT', commentId: COMMENT_ID });
|
||||||
|
|
||||||
|
expect(onCommentDelete).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'DELETE_COMMENT', commentId: COMMENT_ID });
|
||||||
|
|
||||||
|
expect(onCommentDeleteFail).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "CREATE_PLAYLIST"', () => {
|
||||||
|
const NEW_PLAYLIST_DATA = {
|
||||||
|
title: 'NEW_PLAYLIST_DATA_TITLE',
|
||||||
|
description: 'NEW_PLAYLIST_DATA_DESCRIPTION',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
sampleMediaCMSConfig.api.playlists,
|
||||||
|
NEW_PLAYLIST_DATA,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
expect.any(Function),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
const NEW_PLAYLIST_RESPONSE_DATA = { uid: 'COMMENT_UID' };
|
||||||
|
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: NEW_PLAYLIST_RESPONSE_DATA, status: 201 })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'CREATE_PLAYLIST', playlist_data: NEW_PLAYLIST_DATA });
|
||||||
|
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
sampleMediaCMSConfig.api.playlists,
|
||||||
|
NEW_PLAYLIST_DATA,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
expect.any(Function),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistCreationCompleted).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onPlaylistCreationCompleted).toHaveBeenCalledWith(NEW_PLAYLIST_RESPONSE_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'CREATE_PLAYLIST', playlist_data: NEW_PLAYLIST_DATA });
|
||||||
|
|
||||||
|
expect(onPlaylistCreationFailed).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "ADD_MEDIA_TO_PLAYLIST"', () => {
|
||||||
|
const NEW_PLAYLIST_MEDIA_ID = 'NEW_PLAYLIST_MEDIA_ID';
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(putRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.playlists}/${PLAYLIST_ID}`,
|
||||||
|
{ type: 'add', media_friendly_token: NEW_PLAYLIST_MEDIA_ID },
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
expect.any(Function),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
// Mock put request
|
||||||
|
(putRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _putData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: {} })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({
|
||||||
|
type: 'ADD_MEDIA_TO_PLAYLIST',
|
||||||
|
playlist_id: PLAYLIST_ID,
|
||||||
|
media_id: NEW_PLAYLIST_MEDIA_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(onMediaPlaylistAdditionCompleted).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock put request
|
||||||
|
(putRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _putData, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({
|
||||||
|
type: 'ADD_MEDIA_TO_PLAYLIST',
|
||||||
|
playlist_id: PLAYLIST_ID,
|
||||||
|
media_id: NEW_PLAYLIST_MEDIA_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(onMediaPlaylistAdditionFailed).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Action type: "REMOVE_MEDIA_FROM_PLAYLIST"', () => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(putRequest).toHaveBeenCalledWith(
|
||||||
|
`${sampleMediaCMSConfig.api.playlists}/${PLAYLIST_ID}`,
|
||||||
|
{ type: 'remove', media_friendly_token: MEDIA_ID },
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
expect.any(Function),
|
||||||
|
expect.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful', () => {
|
||||||
|
// Mock put request
|
||||||
|
(putRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _putData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: {} })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_MEDIA_FROM_PLAYLIST', playlist_id: PLAYLIST_ID, media_id: MEDIA_ID });
|
||||||
|
|
||||||
|
expect(onMediaPlaylistRemovalCompleted).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Failed', () => {
|
||||||
|
// Mock put request
|
||||||
|
(putRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _putData, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_MEDIA_FROM_PLAYLIST', playlist_id: PLAYLIST_ID, media_id: MEDIA_ID });
|
||||||
|
|
||||||
|
expect(onMediaPlaylistRemovalFailed).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "APPEND_NEW_PLAYLIST"', () => {
|
||||||
|
const NEW_USER_PLAYLIST = {
|
||||||
|
add_date: 'PLAYLIST_ADD_DATE_2',
|
||||||
|
description: 'PLAYLIST_DECRIPTION_2',
|
||||||
|
media_list: ['PLAYLIST_MEDIA_ID'],
|
||||||
|
playlist_id: 'PLAYLIST_ID',
|
||||||
|
title: 'PLAYLIST_TITLE_2',
|
||||||
|
};
|
||||||
|
|
||||||
|
handler({ type: 'APPEND_NEW_PLAYLIST', playlist_data: NEW_USER_PLAYLIST });
|
||||||
|
|
||||||
|
expect(onPlaylistsLoad).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('playlists')).toStrictEqual([
|
||||||
|
{
|
||||||
|
add_date: 'PLAYLIST_ADD_DATE',
|
||||||
|
description: 'PLAYLIST_DECRIPTION',
|
||||||
|
media_list: ['PLAYLIST_MEDIA_ID'],
|
||||||
|
playlist_id: PLAYLIST_ID,
|
||||||
|
title: 'PLAYLIST_TITLE',
|
||||||
|
},
|
||||||
|
NEW_USER_PLAYLIST,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
162
frontend/tests/utils/stores/PageStore.test.ts
Normal file
162
frontend/tests/utils/stores/PageStore.test.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import { BrowserCache } from '../../../src/static/js/utils/classes';
|
||||||
|
import store from '../../../src/static/js/utils/stores/PageStore';
|
||||||
|
|
||||||
|
import { sampleMediaCMSConfig } from '../../tests-constants';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/classes/', () => ({
|
||||||
|
BrowserCache: jest.fn().mockImplementation(() => ({
|
||||||
|
get: (key: string) => (key === 'media-auto-play' ? false : undefined),
|
||||||
|
set: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
BrowserEvents: jest.fn().mockImplementation(() => ({
|
||||||
|
doc: jest.fn(),
|
||||||
|
win: jest.fn(),
|
||||||
|
})),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PageStore', () => {
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onInit = jest.fn();
|
||||||
|
const onToggleAutoPlay = jest.fn();
|
||||||
|
const onAddNotification = jest.fn();
|
||||||
|
|
||||||
|
store.on('page_init', onInit);
|
||||||
|
store.on('switched_media_auto_play', onToggleAutoPlay);
|
||||||
|
store.on('added_notification', onAddNotification);
|
||||||
|
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
// BrowserCache mock
|
||||||
|
expect(store.get('browser-cache').get('media-auto-play')).toBe(false);
|
||||||
|
expect(store.get('browser-cache').get('ANY')).toBe(undefined);
|
||||||
|
|
||||||
|
// Autoplay media files
|
||||||
|
expect(store.get('media-auto-play')).toBe(false);
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
expect(store.get('config-contents')).toStrictEqual(sampleMediaCMSConfig.contents);
|
||||||
|
expect(store.get('config-enabled')).toStrictEqual(sampleMediaCMSConfig.enabled);
|
||||||
|
expect(store.get('config-media-item')).toStrictEqual(sampleMediaCMSConfig.media.item);
|
||||||
|
expect(store.get('config-options')).toStrictEqual(sampleMediaCMSConfig.options);
|
||||||
|
expect(store.get('config-site')).toStrictEqual(sampleMediaCMSConfig.site);
|
||||||
|
|
||||||
|
// Playlists API path
|
||||||
|
expect(store.get('api-playlists')).toStrictEqual(sampleMediaCMSConfig.api.playlists);
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
expect(store.get('notifications')).toStrictEqual([]);
|
||||||
|
expect(store.get('notifications-size')).toBe(0);
|
||||||
|
|
||||||
|
expect(store.get('current-page')).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Trigger and validate browser events behavior', () => {
|
||||||
|
const docVisChange = jest.fn();
|
||||||
|
const winScroll = jest.fn();
|
||||||
|
const winResize = jest.fn();
|
||||||
|
|
||||||
|
store.on('document_visibility_change', docVisChange);
|
||||||
|
store.on('window_scroll', winScroll);
|
||||||
|
store.on('window_resize', winResize);
|
||||||
|
|
||||||
|
store.onDocumentVisibilityChange();
|
||||||
|
store.onWindowScroll();
|
||||||
|
store.onWindowResize();
|
||||||
|
|
||||||
|
expect(docVisChange).toHaveBeenCalled();
|
||||||
|
expect(winScroll).toHaveBeenCalled();
|
||||||
|
expect(winResize).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
test('Action type: "INIT_PAGE"', () => {
|
||||||
|
handler({ type: 'INIT_PAGE', page: 'home' });
|
||||||
|
expect(onInit).toHaveBeenCalledTimes(1);
|
||||||
|
expect(store.get('current-page')).toBe('home');
|
||||||
|
|
||||||
|
handler({ type: 'INIT_PAGE', page: 'about' });
|
||||||
|
expect(onInit).toHaveBeenCalledTimes(2);
|
||||||
|
expect(store.get('current-page')).toBe('about');
|
||||||
|
|
||||||
|
handler({ type: 'INIT_PAGE', page: 'profile' });
|
||||||
|
expect(onInit).toHaveBeenCalledTimes(3);
|
||||||
|
expect(store.get('current-page')).toBe('profile');
|
||||||
|
|
||||||
|
expect(onInit).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(onToggleAutoPlay).toHaveBeenCalledTimes(0);
|
||||||
|
expect(onAddNotification).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "TOGGLE_AUTO_PLAY"', () => {
|
||||||
|
const browserCacheInstance = (BrowserCache as jest.Mock).mock.results[0].value;
|
||||||
|
const browserCacheSetSpy = browserCacheInstance.set;
|
||||||
|
|
||||||
|
const initialValue = store.get('media-auto-play');
|
||||||
|
|
||||||
|
handler({ type: 'TOGGLE_AUTO_PLAY' });
|
||||||
|
|
||||||
|
expect(onToggleAutoPlay).toHaveBeenCalledWith();
|
||||||
|
expect(onToggleAutoPlay).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('media-auto-play')).toBe(!initialValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('media-auto-play', !initialValue);
|
||||||
|
|
||||||
|
browserCacheSetSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "ADD_NOTIFICATION"', () => {
|
||||||
|
const notificationMsg1 = 'NOTIFICATION_MSG_1';
|
||||||
|
const notificationMsg2 = 'NOTIFICATION_MSG_2';
|
||||||
|
const invalidNotification = 44;
|
||||||
|
|
||||||
|
// Add notification
|
||||||
|
handler({ type: 'ADD_NOTIFICATION', notification: notificationMsg1 });
|
||||||
|
expect(onAddNotification).toHaveBeenCalledWith();
|
||||||
|
expect(onAddNotification).toHaveBeenCalledTimes(1);
|
||||||
|
expect(store.get('notifications-size')).toBe(1);
|
||||||
|
|
||||||
|
const currentNotifications = store.get('notifications');
|
||||||
|
expect(currentNotifications.length).toBe(1);
|
||||||
|
expect(typeof currentNotifications[0][0]).toBe('string');
|
||||||
|
expect(currentNotifications[0][1]).toBe(notificationMsg1);
|
||||||
|
|
||||||
|
expect(store.get('notifications-size')).toBe(0);
|
||||||
|
expect(store.get('notifications')).toStrictEqual([]);
|
||||||
|
|
||||||
|
// Add another notification
|
||||||
|
handler({ type: 'ADD_NOTIFICATION', notification: notificationMsg2 });
|
||||||
|
|
||||||
|
expect(onAddNotification).toHaveBeenCalledWith();
|
||||||
|
expect(onAddNotification).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
expect(store.get('notifications-size')).toBe(1);
|
||||||
|
expect(store.get('notifications')[0][1]).toBe(notificationMsg2);
|
||||||
|
|
||||||
|
expect(store.get('notifications-size')).toBe(0);
|
||||||
|
expect(store.get('notifications')).toStrictEqual([]);
|
||||||
|
|
||||||
|
// Add invalid notification
|
||||||
|
handler({ type: 'ADD_NOTIFICATION', notification: invalidNotification });
|
||||||
|
expect(onAddNotification).toHaveBeenCalledWith();
|
||||||
|
expect(onAddNotification).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
expect(store.get('notifications-size')).toBe(0);
|
||||||
|
expect(store.get('notifications')).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
390
frontend/tests/utils/stores/PlaylistPageStore.test.ts
Normal file
390
frontend/tests/utils/stores/PlaylistPageStore.test.ts
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
import {
|
||||||
|
publishedOnDate,
|
||||||
|
getRequest,
|
||||||
|
postRequest,
|
||||||
|
deleteRequest,
|
||||||
|
csrfToken,
|
||||||
|
} from '../../../src/static/js/utils/helpers';
|
||||||
|
import store from '../../../src/static/js/utils/stores/PlaylistPageStore';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
publishedOnDate: jest.fn(),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
getRequest: jest.fn(),
|
||||||
|
postRequest: jest.fn(),
|
||||||
|
deleteRequest: jest.fn(),
|
||||||
|
csrfToken: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
(globalThis as any).window.MediaCMS = { playlistId: null };
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
delete (globalThis as any).window.MediaCMS;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PlaylistPageStore', () => {
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onLoadedPlaylistData = jest.fn();
|
||||||
|
const onLoadedPlaylistEerror = jest.fn();
|
||||||
|
const onLoadedMediaError = jest.fn();
|
||||||
|
const onPlaylistUpdateCompleted = jest.fn();
|
||||||
|
const onPlaylistUpdateFailed = jest.fn();
|
||||||
|
const onPlaylistRemovalCompleted = jest.fn();
|
||||||
|
const onPlaylistRemovalFailed = jest.fn();
|
||||||
|
const onSavedUpdated = jest.fn();
|
||||||
|
const onReorderedMediaInPlaylist = jest.fn();
|
||||||
|
const onRemovedMediaFromPlaylist = jest.fn();
|
||||||
|
|
||||||
|
store.on('loaded_playlist_data', onLoadedPlaylistData);
|
||||||
|
store.on('loaded_playlist_error', onLoadedPlaylistEerror);
|
||||||
|
store.on('loaded_media_error', onLoadedMediaError); // @todo: It doesn't get called
|
||||||
|
store.on('playlist_update_completed', onPlaylistUpdateCompleted);
|
||||||
|
store.on('playlist_update_failed', onPlaylistUpdateFailed);
|
||||||
|
store.on('playlist_removal_completed', onPlaylistRemovalCompleted);
|
||||||
|
store.on('playlist_removal_failed', onPlaylistRemovalFailed);
|
||||||
|
store.on('saved-updated', onSavedUpdated);
|
||||||
|
store.on('reordered_media_in_playlist', onReorderedMediaInPlaylist);
|
||||||
|
store.on('removed_media_from_playlist', onRemovedMediaFromPlaylist);
|
||||||
|
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
expect(store.get('INVALID_TYPE')).toBe(null);
|
||||||
|
expect(store.get('playlistId')).toBe(null);
|
||||||
|
expect(store.get('logged-in-user-playlist')).toBe(false);
|
||||||
|
expect(store.get('playlist-media')).toStrictEqual([]);
|
||||||
|
expect(store.get('visibility')).toBe('public');
|
||||||
|
expect(store.get('visibility-icon')).toBe(null);
|
||||||
|
// // expect(store.get('total-items')).toBe(0); // @todo: It throws error
|
||||||
|
expect(store.get('views-count')).toBe('N/A');
|
||||||
|
expect(store.get('title')).toBe(null);
|
||||||
|
expect(store.get('edit-link')).toBe('#');
|
||||||
|
expect(store.get('thumb')).toBe(null);
|
||||||
|
expect(store.get('description')).toBe(null);
|
||||||
|
expect(store.get('author-username')).toBe(null);
|
||||||
|
expect(store.get('author-name')).toBe(null);
|
||||||
|
expect(store.get('author-link')).toBe(null);
|
||||||
|
expect(store.get('author-thumb')).toBe(null);
|
||||||
|
expect(store.get('saved-playlist')).toBe(false);
|
||||||
|
expect(store.get('date-label')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
test('Action type: "LOAD_PLAYLIST_DATA" - failed', () => {
|
||||||
|
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||||
|
const loadDataSpy = jest.spyOn(store, 'loadData');
|
||||||
|
|
||||||
|
handler({ type: 'LOAD_PLAYLIST_DATA' });
|
||||||
|
|
||||||
|
expect(loadDataSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(loadDataSpy).toHaveReturnedWith(false);
|
||||||
|
|
||||||
|
expect(warnSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(warnSpy).toHaveBeenCalledWith('Invalid playlist id:', '');
|
||||||
|
|
||||||
|
expect(store.get('playlistId')).toBe(null);
|
||||||
|
|
||||||
|
loadDataSpy.mockRestore();
|
||||||
|
warnSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "LOAD_PLAYLIST_DATA" - completed successful', () => {
|
||||||
|
const playlistId = 'PLAYLIST_ID_1';
|
||||||
|
window.history.pushState({}, '', `/playlists/${playlistId}`);
|
||||||
|
|
||||||
|
// Mock get request
|
||||||
|
const mockGetRequestResponse = {
|
||||||
|
data: {
|
||||||
|
add_date: Date.now(),
|
||||||
|
description: 'DESCRIPTION',
|
||||||
|
playlist_media: [],
|
||||||
|
title: 'TITLE',
|
||||||
|
user: 'USER',
|
||||||
|
user_thumbnail_url: 'USER_THUMB_URL',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(getRequest as jest.Mock).mockImplementation((_url, _cache, successCallback, _failCallback) =>
|
||||||
|
successCallback(mockGetRequestResponse)
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadDataSpy = jest.spyOn(store, 'loadData');
|
||||||
|
const dataResponseSpy = jest.spyOn(store, 'dataResponse');
|
||||||
|
|
||||||
|
handler({ type: 'LOAD_PLAYLIST_DATA' });
|
||||||
|
|
||||||
|
expect(store.get('playlistId')).toBe(playlistId);
|
||||||
|
expect(store.get('author-name')).toBe(mockGetRequestResponse.data.user);
|
||||||
|
expect(store.get('author-link')).toBe(`/user/${mockGetRequestResponse.data.user}`);
|
||||||
|
expect(store.get('author-thumb')).toBe(`/${mockGetRequestResponse.data.user_thumbnail_url}`);
|
||||||
|
|
||||||
|
expect(store.get('date-label')).toBe('Created on undefined');
|
||||||
|
expect(publishedOnDate).toHaveBeenCalledWith(new Date(mockGetRequestResponse.data.add_date), 3);
|
||||||
|
|
||||||
|
expect(loadDataSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(loadDataSpy).toHaveReturnedWith(undefined);
|
||||||
|
|
||||||
|
expect(dataResponseSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dataResponseSpy).toHaveBeenCalledWith(mockGetRequestResponse);
|
||||||
|
|
||||||
|
// Verify getRequest was called with correct parameters
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
false,
|
||||||
|
store.dataResponse,
|
||||||
|
store.dataErrorResponse
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onLoadedPlaylistData).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedPlaylistData).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
loadDataSpy.mockRestore();
|
||||||
|
dataResponseSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "LOAD_PLAYLIST_DATA" - completed with error', () => {
|
||||||
|
const playlistId = 'PLAYLIST_ID_2';
|
||||||
|
window.history.pushState({}, '', `/playlists/${playlistId}`);
|
||||||
|
|
||||||
|
// Mock get request
|
||||||
|
const mockGetRequestResponse = { type: 'private' };
|
||||||
|
(getRequest as jest.Mock).mockImplementation((_url, _cache, _successCallback, failCallback) =>
|
||||||
|
failCallback(mockGetRequestResponse)
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadDataSpy = jest.spyOn(store, 'loadData');
|
||||||
|
const dataErrorResponseSpy = jest.spyOn(store, 'dataErrorResponse');
|
||||||
|
|
||||||
|
handler({ type: 'LOAD_PLAYLIST_DATA' });
|
||||||
|
|
||||||
|
expect(store.get('playlistId')).toBe(playlistId);
|
||||||
|
|
||||||
|
expect(loadDataSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(loadDataSpy).toHaveReturnedWith(undefined);
|
||||||
|
|
||||||
|
expect(dataErrorResponseSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(dataErrorResponseSpy).toHaveBeenCalledWith(mockGetRequestResponse);
|
||||||
|
|
||||||
|
// Verify getRequest was called with correct parameters
|
||||||
|
expect(getRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
false,
|
||||||
|
store.dataResponse,
|
||||||
|
store.dataErrorResponse
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onLoadedPlaylistEerror).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoadedPlaylistEerror).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
loadDataSpy.mockRestore();
|
||||||
|
dataErrorResponseSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "TOGGLE_SAVE"', () => {
|
||||||
|
const initialValue = store.get('saved-playlist');
|
||||||
|
|
||||||
|
handler({ type: 'TOGGLE_SAVE' });
|
||||||
|
|
||||||
|
expect(onSavedUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onSavedUpdated).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(store.get('saved-playlist')).toBe(!initialValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "UPDATE_PLAYLIST" - failed', () => {
|
||||||
|
// Mock (updated) playlist data
|
||||||
|
const mockPlaylistData = { title: 'PLAYLIST_TITLE', description: 'PLAYLIST_DESCRIPTION' };
|
||||||
|
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialStoreData = {
|
||||||
|
title: store.get('title'),
|
||||||
|
description: store.get('description'),
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(store.get('title')).toBe(initialStoreData.title);
|
||||||
|
expect(store.get('description')).toBe(initialStoreData.description);
|
||||||
|
|
||||||
|
handler({ type: 'UPDATE_PLAYLIST', playlist_data: mockPlaylistData });
|
||||||
|
|
||||||
|
expect(store.get('title')).toBe(initialStoreData.title);
|
||||||
|
expect(store.get('description')).toBe(initialStoreData.description);
|
||||||
|
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
mockPlaylistData,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.onPlaylistUpdateCompleted,
|
||||||
|
store.onPlaylistUpdateFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistUpdateFailed).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "UPDATE_PLAYLIST" - successful', () => {
|
||||||
|
// Mock (updated) playlist data
|
||||||
|
const mockPlaylistData = { title: 'PLAYLIST_TITLE', description: 'PLAYLIST_DESCRIPTION' };
|
||||||
|
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock post request
|
||||||
|
(postRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _postData, _configData, _sync, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: mockPlaylistData })
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialStoreData = {
|
||||||
|
title: store.get('title'),
|
||||||
|
description: store.get('description'),
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(store.get('title')).toBe(initialStoreData.title);
|
||||||
|
expect(store.get('description')).toBe(initialStoreData.description);
|
||||||
|
|
||||||
|
handler({ type: 'UPDATE_PLAYLIST', playlist_data: mockPlaylistData });
|
||||||
|
|
||||||
|
expect(store.get('title')).toBe(mockPlaylistData.title);
|
||||||
|
expect(store.get('description')).toBe(mockPlaylistData.description);
|
||||||
|
|
||||||
|
// Verify postRequest was called with correct parameters
|
||||||
|
expect(postRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
mockPlaylistData,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.onPlaylistUpdateCompleted,
|
||||||
|
store.onPlaylistUpdateFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistUpdateCompleted).toHaveBeenCalledWith(mockPlaylistData);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REMOVE_PLAYLIST" - failed', () => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _config, _sync, _successCallback, failCallback) => failCallback()
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PLAYLIST' });
|
||||||
|
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.onPlaylistRemovalCompleted,
|
||||||
|
store.onPlaylistRemovalFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistRemovalFailed).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REMOVE_PLAYLIST" - completed successful', () => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock delete request
|
||||||
|
const deleteRequestResponse = { status: 204 };
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _config, _sync, successCallback, _failCallback) => successCallback(deleteRequestResponse)
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PLAYLIST' });
|
||||||
|
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.onPlaylistRemovalCompleted,
|
||||||
|
store.onPlaylistRemovalFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistRemovalCompleted).toHaveBeenCalledWith(deleteRequestResponse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REMOVE_PLAYLIST" - completed with invalid status code', () => {
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock delete request
|
||||||
|
const deleteRequestResponse = { status: 403 };
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _config, _sync, successCallback, _failCallback) => successCallback(deleteRequestResponse)
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PLAYLIST' });
|
||||||
|
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
store.playlistAPIUrl,
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.onPlaylistRemovalCompleted,
|
||||||
|
store.onPlaylistRemovalFailed
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onPlaylistRemovalFailed).toHaveBeenCalledWith();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "PLAYLIST_MEDIA_REORDERED"', () => {
|
||||||
|
// Mock playlist media data
|
||||||
|
const mockPlaylistMedia = [
|
||||||
|
{ thumbnail_url: 'THUMB_URL_1', url: '?id=MEDIA_ID_1' },
|
||||||
|
{ thumbnail_url: 'THUMB_URL_2', url: '?id=MEDIA_ID_2' },
|
||||||
|
];
|
||||||
|
|
||||||
|
handler({ type: 'PLAYLIST_MEDIA_REORDERED', playlist_media: mockPlaylistMedia });
|
||||||
|
|
||||||
|
expect(onReorderedMediaInPlaylist).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(store.get('playlist-media')).toStrictEqual(mockPlaylistMedia);
|
||||||
|
expect(store.get('thumb')).toBe(mockPlaylistMedia[0].thumbnail_url);
|
||||||
|
expect(store.get('total-items')).toBe(mockPlaylistMedia.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "MEDIA_REMOVED_FROM_PLAYLIST"', () => {
|
||||||
|
// Mock playlist media data
|
||||||
|
const mockPlaylistMedia = [
|
||||||
|
{ thumbnail_url: 'THUMB_URL_1', url: '?id=MEDIA_ID_1' },
|
||||||
|
{ thumbnail_url: 'THUMB_URL_2', url: '?id=MEDIA_ID_2' },
|
||||||
|
];
|
||||||
|
|
||||||
|
handler({ type: 'PLAYLIST_MEDIA_REORDERED', playlist_media: mockPlaylistMedia });
|
||||||
|
|
||||||
|
handler({ type: 'MEDIA_REMOVED_FROM_PLAYLIST', media_id: 'MEDIA_ID_2' });
|
||||||
|
|
||||||
|
expect(store.get('playlist-media')).toStrictEqual([mockPlaylistMedia[0]]);
|
||||||
|
expect(store.get('thumb')).toBe(mockPlaylistMedia[0].thumbnail_url);
|
||||||
|
expect(store.get('total-items')).toBe(mockPlaylistMedia.length - 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
83
frontend/tests/utils/stores/PlaylistViewStore.test.ts
Normal file
83
frontend/tests/utils/stores/PlaylistViewStore.test.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { BrowserCache } from '../../../src/static/js/utils/classes/';
|
||||||
|
import store from '../../../src/static/js/utils/stores/PlaylistViewStore';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/classes/', () => ({
|
||||||
|
BrowserCache: jest.fn().mockImplementation(() => ({
|
||||||
|
get: jest.fn(),
|
||||||
|
set: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
BrowserEvents: jest.fn().mockImplementation(() => ({
|
||||||
|
doc: jest.fn(),
|
||||||
|
win: jest.fn(),
|
||||||
|
})),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
describe('PlaylistViewStore', () => {
|
||||||
|
const browserCacheInstance = (BrowserCache as jest.Mock).mock.results[0].value;
|
||||||
|
const browserCacheSetSpy = browserCacheInstance.set;
|
||||||
|
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onLoopRepeatUpdated = jest.fn();
|
||||||
|
const onShuffleUpdated = jest.fn();
|
||||||
|
const onSavedUpdated = jest.fn();
|
||||||
|
|
||||||
|
store.on('loop-repeat-updated', onLoopRepeatUpdated);
|
||||||
|
store.on('shuffle-updated', onShuffleUpdated);
|
||||||
|
store.on('saved-updated', onSavedUpdated);
|
||||||
|
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
expect(store.get('INVALID_TYPE')).toBe(null);
|
||||||
|
expect(store.get('logged-in-user-playlist')).toBe(false);
|
||||||
|
expect(store.get('enabled-loop')).toBe(undefined);
|
||||||
|
expect(store.get('enabled-shuffle')).toBe(undefined);
|
||||||
|
expect(store.get('saved-playlist')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
// @todo: Revisit the behavior of this action
|
||||||
|
test('Action type: "TOGGLE_LOOP"', () => {
|
||||||
|
handler({ type: 'TOGGLE_LOOP' });
|
||||||
|
|
||||||
|
expect(onLoopRepeatUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onLoopRepeatUpdated).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(store.get('enabled-loop')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('loopPlaylist[null]', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// @todo: Revisit the behavior of this action
|
||||||
|
test('Action type: "TOGGLE_SHUFFLE"', () => {
|
||||||
|
handler({ type: 'TOGGLE_SHUFFLE' });
|
||||||
|
|
||||||
|
expect(onShuffleUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onShuffleUpdated).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(store.get('enabled-shuffle')).toBe(undefined);
|
||||||
|
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('shufflePlaylist[null]', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "TOGGLE_SAVE"', () => {
|
||||||
|
const initialValue = store.get('saved-playlist');
|
||||||
|
|
||||||
|
handler({ type: 'TOGGLE_SAVE' });
|
||||||
|
|
||||||
|
expect(onSavedUpdated).toHaveBeenCalledTimes(1);
|
||||||
|
expect(onSavedUpdated).toHaveBeenCalledWith();
|
||||||
|
|
||||||
|
expect(store.get('saved-playlist')).toBe(!initialValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
168
frontend/tests/utils/stores/ProfilePageStore.test.ts
Normal file
168
frontend/tests/utils/stores/ProfilePageStore.test.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import { getRequest, deleteRequest, csrfToken } from '../../../src/static/js/utils/helpers';
|
||||||
|
import store from '../../../src/static/js/utils/stores/ProfilePageStore';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => ({
|
||||||
|
...jest.requireActual('../../tests-constants').sampleMediaCMSConfig,
|
||||||
|
api: { ...jest.requireActual('../../tests-constants').sampleMediaCMSConfig.api, users: '' },
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
getRequest: jest.fn(),
|
||||||
|
deleteRequest: jest.fn(),
|
||||||
|
csrfToken: jest.fn(),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
const mockAuthorData = { username: 'testuser', name: 'Test User' };
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
(globalThis as any).window.MediaCMS = { profileId: mockAuthorData.username };
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
delete (globalThis as any).window.MediaCMS;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ProfilePageStore', () => {
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onProfileDelete = jest.fn();
|
||||||
|
const onProfileDeleteFail = jest.fn();
|
||||||
|
const onLoadAuthorData = jest.fn();
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
store.on('profile_delete', onProfileDelete);
|
||||||
|
store.on('profile_delete_fail', onProfileDeleteFail);
|
||||||
|
store.on('load-author-data', onLoadAuthorData);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Reset store state
|
||||||
|
store.authorData = null;
|
||||||
|
store.removingProfile = false;
|
||||||
|
store.authorQuery = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
test('Action type: "REMOVE_PROFILE" - successful deletion', async () => {
|
||||||
|
// Set up author data
|
||||||
|
store.authorData = mockAuthorData;
|
||||||
|
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _config, _sync, successCallback, _failCallback) => successCallback({ status: 204 })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PROFILE' });
|
||||||
|
|
||||||
|
// Verify deleteRequest was called with correct parameters
|
||||||
|
expect(deleteRequest).toHaveBeenCalledWith(
|
||||||
|
'/testuser', // API URL constructed from config + username
|
||||||
|
{ headers: { 'X-CSRFToken': mockCSRFtoken } },
|
||||||
|
false,
|
||||||
|
store.removeProfileResponse,
|
||||||
|
store.removeProfileFail
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify event was emitted
|
||||||
|
expect(onProfileDelete).toHaveBeenCalledWith(mockAuthorData.username);
|
||||||
|
expect(onProfileDelete).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REMOVE_PROFILE" - deletion failure', async () => {
|
||||||
|
// Set up author data
|
||||||
|
store.authorData = mockAuthorData;
|
||||||
|
|
||||||
|
// Mock the CSRF token
|
||||||
|
const mockCSRFtoken = 'test-csrf-token';
|
||||||
|
(csrfToken as jest.Mock).mockReturnValue(mockCSRFtoken);
|
||||||
|
|
||||||
|
// Mock delete request
|
||||||
|
(deleteRequest as jest.Mock).mockImplementation(
|
||||||
|
(_url, _config, _sync, _successCallback, failCallback) => failCallback.call(store)
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PROFILE' });
|
||||||
|
|
||||||
|
// Wait for the setTimeout in removeProfileFail
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
||||||
|
|
||||||
|
// Verify event was emitted
|
||||||
|
expect(onProfileDeleteFail).toHaveBeenCalledWith(mockAuthorData.username);
|
||||||
|
expect(onProfileDeleteFail).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "REMOVE_PROFILE" - prevents duplicate calls while removing', () => {
|
||||||
|
// Set up author data
|
||||||
|
store.authorData = mockAuthorData;
|
||||||
|
|
||||||
|
handler({ type: 'REMOVE_PROFILE' });
|
||||||
|
expect(deleteRequest).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
store.removingProfile = true;
|
||||||
|
handler({ type: 'REMOVE_PROFILE' });
|
||||||
|
expect(deleteRequest).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
store.removingProfile = false;
|
||||||
|
handler({ type: 'REMOVE_PROFILE' });
|
||||||
|
expect(deleteRequest).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "LOAD_AUTHOR_DATA"', async () => {
|
||||||
|
(getRequest as jest.Mock).mockImplementation((_url, _cache, successCallback, _failCallback) =>
|
||||||
|
successCallback({ data: mockAuthorData })
|
||||||
|
);
|
||||||
|
|
||||||
|
handler({ type: 'LOAD_AUTHOR_DATA' });
|
||||||
|
|
||||||
|
// Verify getRequest was called with correct parameters
|
||||||
|
expect(getRequest).toHaveBeenCalledWith('/testuser', false, store.onDataLoad, store.onDataLoadFail);
|
||||||
|
|
||||||
|
// Verify event was emitted
|
||||||
|
expect(onLoadAuthorData).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// Verify author data was processed correctly
|
||||||
|
expect(store.get('author-data')).toStrictEqual(mockAuthorData);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Getter methods', () => {
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
expect(store.get('INVALID_TYPE')).toBe(undefined);
|
||||||
|
expect(store.get('author-data')).toBe(null);
|
||||||
|
expect(store.get('author-query')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get("author-data") returns authorData', () => {
|
||||||
|
store.authorData = mockAuthorData;
|
||||||
|
expect(store.get('author-data')).toBe(mockAuthorData);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get("author-query") - without "aq" parameter in URL', () => {
|
||||||
|
window.history.pushState({}, '', '/path');
|
||||||
|
expect(store.get('author-query')).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get("author-query") - with "aq" parameter in URL', () => {
|
||||||
|
window.history.pushState({}, '', '/path?aq=AUTHOR_QUERY');
|
||||||
|
expect(store.get('author-query')).toBe('AUTHOR_QUERY');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('get("author-query") - empty search string', () => {
|
||||||
|
window.history.pushState({}, '', '/path?aq');
|
||||||
|
expect(store.get('author-query')).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
64
frontend/tests/utils/stores/SearchFieldStore.test.ts
Normal file
64
frontend/tests/utils/stores/SearchFieldStore.test.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
const urlParams = { q: 'search_query', c: 'category_1', t: 'tag_1' };
|
||||||
|
window.history.pushState({}, '', `/?q=${urlParams.q}&c=${urlParams.c}&t=${urlParams.t}`);
|
||||||
|
|
||||||
|
import store from '../../../src/static/js/utils/stores/SearchFieldStore';
|
||||||
|
import { getRequest } from '../../../src/static/js/utils/helpers';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
getRequest: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SearchFieldStore', () => {
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onLoadPredictions = jest.fn();
|
||||||
|
|
||||||
|
store.on('load_predictions', onLoadPredictions);
|
||||||
|
|
||||||
|
test('Validate initial values based on URL params', async () => {
|
||||||
|
expect(store.get('INVALID_TYPE')).toBe(null);
|
||||||
|
expect(store.get('search-query')).toBe(urlParams.q);
|
||||||
|
expect(store.get('search-categories')).toBe(urlParams.c);
|
||||||
|
expect(store.get('search-tags')).toBe(urlParams.t);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "Action type: "TOGGLE_VIEWER_MODE"', async () => {
|
||||||
|
const predictionsQuery_1 = 'predictions_query_1';
|
||||||
|
const predictionsQuery_2 = 'predictions_query_2';
|
||||||
|
|
||||||
|
const response_1 = { data: [{ title: 'Prediction 1' }, { title: 'Prediction 2' }] };
|
||||||
|
const response_2 = { data: [{ title: 'Prediction 3' }, { title: 'Prediction 4' }] };
|
||||||
|
|
||||||
|
(getRequest as jest.Mock)
|
||||||
|
.mockImplementationOnce((_url, _cache, successCallback, _failCallback) => successCallback(response_1))
|
||||||
|
.mockImplementationOnce((_url, _cache, successCallback, _failCallback) => successCallback(response_2));
|
||||||
|
|
||||||
|
handler({ type: 'REQUEST_PREDICTIONS', query: predictionsQuery_1 });
|
||||||
|
handler({ type: 'REQUEST_PREDICTIONS', query: predictionsQuery_2 });
|
||||||
|
|
||||||
|
expect(onLoadPredictions).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
expect(onLoadPredictions).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
predictionsQuery_1,
|
||||||
|
response_1.data.map(({ title }) => title)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(onLoadPredictions).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
predictionsQuery_2,
|
||||||
|
response_2.data.map(({ title }) => title)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
147
frontend/tests/utils/stores/VideoViewerStore.test.ts
Normal file
147
frontend/tests/utils/stores/VideoViewerStore.test.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { BrowserCache } from '../../../src/static/js/utils/classes/';
|
||||||
|
import store from '../../../src/static/js/utils/stores/VideoViewerStore';
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/classes/', () => ({
|
||||||
|
BrowserCache: jest.fn().mockImplementation(() => ({
|
||||||
|
get: (key: string) => {
|
||||||
|
let result: any = undefined;
|
||||||
|
switch (key) {
|
||||||
|
case 'player-volume':
|
||||||
|
result = 0.6;
|
||||||
|
break;
|
||||||
|
case 'player-sound-muted':
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
case 'in-theater-mode':
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
case 'video-quality':
|
||||||
|
result = 720;
|
||||||
|
break;
|
||||||
|
case 'video-playback-speed':
|
||||||
|
result = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
set: jest.fn(),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/settings/config', () => ({
|
||||||
|
config: jest.fn(() => jest.requireActual('../../tests-constants').sampleMediaCMSConfig),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../src/static/js/utils/helpers', () => ({
|
||||||
|
BrowserEvents: jest.fn().mockImplementation(() => ({
|
||||||
|
doc: jest.fn(),
|
||||||
|
win: jest.fn(),
|
||||||
|
})),
|
||||||
|
exportStore: jest.fn((store) => store),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('utils/store', () => {
|
||||||
|
describe('VideoViewerStore', () => {
|
||||||
|
const browserCacheInstance = (BrowserCache as jest.Mock).mock.results[0].value;
|
||||||
|
const browserCacheSetSpy = browserCacheInstance.set;
|
||||||
|
|
||||||
|
const handler = store.actions_handler.bind(store);
|
||||||
|
|
||||||
|
const onChangedViewerMode = jest.fn();
|
||||||
|
const onChangedPlayerVolume = jest.fn();
|
||||||
|
const onChangedPlayerSoundMuted = jest.fn();
|
||||||
|
const onChangedVideoQuality = jest.fn();
|
||||||
|
const onChangedVideoPlaybackSpeed = jest.fn();
|
||||||
|
|
||||||
|
store.on('changed_viewer_mode', onChangedViewerMode);
|
||||||
|
store.on('changed_player_volume', onChangedPlayerVolume);
|
||||||
|
store.on('changed_player_sound_muted', onChangedPlayerSoundMuted);
|
||||||
|
store.on('changed_video_quality', onChangedVideoQuality);
|
||||||
|
store.on('changed_video_playback_speed', onChangedVideoPlaybackSpeed);
|
||||||
|
|
||||||
|
test('Validate initial values', () => {
|
||||||
|
expect(store.get('player-volume')).toBe(0.6);
|
||||||
|
expect(store.get('player-sound-muted')).toBe(false);
|
||||||
|
expect(store.get('in-theater-mode')).toBe(true);
|
||||||
|
expect(store.get('video-data')).toBe(undefined); // @todo: Revisit this behavior
|
||||||
|
expect(store.get('video-quality')).toBe(720);
|
||||||
|
expect(store.get('video-playback-speed')).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Trigger and validate actions behavior', () => {
|
||||||
|
test('Action type: "TOGGLE_VIEWER_MODE"', () => {
|
||||||
|
const initialValue = store.get('in-theater-mode');
|
||||||
|
|
||||||
|
handler({ type: 'TOGGLE_VIEWER_MODE' });
|
||||||
|
|
||||||
|
expect(onChangedViewerMode).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedViewerMode).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('in-theater-mode')).toBe(!initialValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('in-theater-mode', !initialValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "SET_VIEWER_MODE"', () => {
|
||||||
|
const initialValue = store.get('in-theater-mode');
|
||||||
|
const newValue = !initialValue;
|
||||||
|
|
||||||
|
handler({ type: 'SET_VIEWER_MODE', inTheaterMode: newValue });
|
||||||
|
|
||||||
|
expect(onChangedViewerMode).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedViewerMode).toHaveBeenCalledTimes(2); // The first time called by 'TOGGLE_VIEWER_MODE' action.
|
||||||
|
|
||||||
|
expect(store.get('in-theater-mode')).toBe(newValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('in-theater-mode', newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "SET_PLAYER_VOLUME"', () => {
|
||||||
|
const newValue = 0.3;
|
||||||
|
|
||||||
|
handler({ type: 'SET_PLAYER_VOLUME', playerVolume: newValue });
|
||||||
|
|
||||||
|
expect(onChangedPlayerVolume).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedPlayerVolume).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('player-volume')).toBe(newValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('player-volume', newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "SET_PLAYER_SOUND_MUTED"', () => {
|
||||||
|
const initialValue = store.get('player-sound-muted');
|
||||||
|
const newValue = !initialValue;
|
||||||
|
|
||||||
|
handler({ type: 'SET_PLAYER_SOUND_MUTED', playerSoundMuted: newValue });
|
||||||
|
|
||||||
|
expect(onChangedPlayerSoundMuted).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedPlayerSoundMuted).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('player-sound-muted')).toBe(newValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('player-sound-muted', newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "SET_VIDEO_QUALITY"', () => {
|
||||||
|
const newValue = 1080;
|
||||||
|
|
||||||
|
handler({ type: 'SET_VIDEO_QUALITY', quality: newValue });
|
||||||
|
|
||||||
|
expect(onChangedVideoQuality).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedVideoQuality).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('video-quality')).toBe(newValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('video-quality', newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Action type: "SET_VIDEO_PLAYBACK_SPEED"', () => {
|
||||||
|
const newValue = 1.5;
|
||||||
|
|
||||||
|
handler({ type: 'SET_VIDEO_PLAYBACK_SPEED', playbackSpeed: newValue });
|
||||||
|
|
||||||
|
expect(onChangedVideoPlaybackSpeed).toHaveBeenCalledWith();
|
||||||
|
expect(onChangedVideoPlaybackSpeed).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(store.get('video-playback-speed')).toBe(newValue);
|
||||||
|
expect(browserCacheSetSpy).toHaveBeenCalledWith('video-playback-speed', newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -7,6 +7,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.30.tgz"
|
resolved "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.30.tgz"
|
||||||
integrity sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==
|
integrity sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==
|
||||||
|
|
||||||
|
"@adobe/css-tools@^4.0.1":
|
||||||
|
version "4.4.4"
|
||||||
|
resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz"
|
||||||
|
integrity sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==
|
||||||
|
|
||||||
"@asamuzakjp/css-color@^3.2.0":
|
"@asamuzakjp/css-color@^3.2.0":
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz"
|
resolved "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz"
|
||||||
@ -45,7 +50,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz"
|
resolved "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz"
|
||||||
integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==
|
integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1":
|
||||||
version "7.27.1"
|
version "7.27.1"
|
||||||
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz"
|
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz"
|
||||||
integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
|
integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
|
||||||
@ -1002,6 +1007,11 @@
|
|||||||
"@babel/plugin-transform-modules-commonjs" "^7.27.1"
|
"@babel/plugin-transform-modules-commonjs" "^7.27.1"
|
||||||
"@babel/plugin-transform-typescript" "^7.28.5"
|
"@babel/plugin-transform-typescript" "^7.28.5"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.12.5":
|
||||||
|
version "7.28.6"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz"
|
||||||
|
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
|
||||||
|
|
||||||
"@babel/runtime@^7.3.4", "@babel/runtime@7.4.5":
|
"@babel/runtime@^7.3.4", "@babel/runtime@7.4.5":
|
||||||
version "7.4.5"
|
version "7.4.5"
|
||||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz"
|
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz"
|
||||||
@ -1009,6 +1019,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.2"
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.9.2":
|
||||||
|
version "7.28.6"
|
||||||
|
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz"
|
||||||
|
integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==
|
||||||
|
|
||||||
"@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3":
|
"@babel/template@^7.27.1", "@babel/template@^7.27.2", "@babel/template@^7.3.3":
|
||||||
version "7.27.2"
|
version "7.27.2"
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz"
|
resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz"
|
||||||
@ -1807,11 +1822,54 @@
|
|||||||
"@svgr/plugin-svgo" "^5.5.0"
|
"@svgr/plugin-svgo" "^5.5.0"
|
||||||
loader-utils "^2.0.0"
|
loader-utils "^2.0.0"
|
||||||
|
|
||||||
|
"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.20.1":
|
||||||
|
version "8.20.1"
|
||||||
|
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz"
|
||||||
|
integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/code-frame" "^7.10.4"
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
"@types/aria-query" "^5.0.1"
|
||||||
|
aria-query "5.1.3"
|
||||||
|
chalk "^4.1.0"
|
||||||
|
dom-accessibility-api "^0.5.9"
|
||||||
|
lz-string "^1.5.0"
|
||||||
|
pretty-format "^27.0.2"
|
||||||
|
|
||||||
|
"@testing-library/jest-dom@^5.17.0":
|
||||||
|
version "5.17.0"
|
||||||
|
resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz"
|
||||||
|
integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==
|
||||||
|
dependencies:
|
||||||
|
"@adobe/css-tools" "^4.0.1"
|
||||||
|
"@babel/runtime" "^7.9.2"
|
||||||
|
"@types/testing-library__jest-dom" "^5.9.1"
|
||||||
|
aria-query "^5.0.0"
|
||||||
|
chalk "^3.0.0"
|
||||||
|
css.escape "^1.5.1"
|
||||||
|
dom-accessibility-api "^0.5.6"
|
||||||
|
lodash "^4.17.15"
|
||||||
|
redent "^3.0.0"
|
||||||
|
|
||||||
|
"@testing-library/react@^12.1.5":
|
||||||
|
version "12.1.5"
|
||||||
|
resolved "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz"
|
||||||
|
integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
"@testing-library/dom" "^8.0.0"
|
||||||
|
"@types/react-dom" "<18.0.0"
|
||||||
|
|
||||||
"@trysound/sax@0.2.0":
|
"@trysound/sax@0.2.0":
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz"
|
||||||
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
|
||||||
|
|
||||||
|
"@types/aria-query@^5.0.1":
|
||||||
|
version "5.0.4"
|
||||||
|
resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz"
|
||||||
|
integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==
|
||||||
|
|
||||||
"@types/babel__core@^7.1.14", "@types/babel__core@^7.20.5":
|
"@types/babel__core@^7.1.14", "@types/babel__core@^7.20.5":
|
||||||
version "7.20.5"
|
version "7.20.5"
|
||||||
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz"
|
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz"
|
||||||
@ -1977,7 +2035,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/istanbul-lib-report" "*"
|
"@types/istanbul-lib-report" "*"
|
||||||
|
|
||||||
"@types/jest@^29.5.12":
|
"@types/jest@*", "@types/jest@^29.5.12":
|
||||||
version "29.5.14"
|
version "29.5.14"
|
||||||
resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz"
|
resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz"
|
||||||
integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
|
integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==
|
||||||
@ -2046,6 +2104,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz"
|
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz"
|
||||||
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
||||||
|
|
||||||
|
"@types/react-dom@<18.0.0":
|
||||||
|
version "17.0.26"
|
||||||
|
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.26.tgz"
|
||||||
|
integrity sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^19.0.10", "@types/react@^19.2.0":
|
"@types/react@*", "@types/react@^19.0.10", "@types/react@^19.2.0":
|
||||||
version "19.2.7"
|
version "19.2.7"
|
||||||
resolved "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz"
|
resolved "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz"
|
||||||
@ -2104,6 +2167,13 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz"
|
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.12.tgz"
|
||||||
integrity sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==
|
integrity sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==
|
||||||
|
|
||||||
|
"@types/testing-library__jest-dom@^5.9.1":
|
||||||
|
version "5.14.9"
|
||||||
|
resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz"
|
||||||
|
integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==
|
||||||
|
dependencies:
|
||||||
|
"@types/jest" "*"
|
||||||
|
|
||||||
"@types/tough-cookie@*":
|
"@types/tough-cookie@*":
|
||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz"
|
resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz"
|
||||||
@ -2531,6 +2601,13 @@ argparse@^2.0.1:
|
|||||||
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz"
|
||||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||||
|
|
||||||
|
aria-query@^5.0.0, aria-query@5.1.3:
|
||||||
|
version "5.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz"
|
||||||
|
integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
|
||||||
|
dependencies:
|
||||||
|
deep-equal "^2.0.5"
|
||||||
|
|
||||||
arr-diff@^4.0.0:
|
arr-diff@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz"
|
resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz"
|
||||||
@ -2546,7 +2623,7 @@ arr-union@^3.1.0:
|
|||||||
resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz"
|
resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz"
|
||||||
integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==
|
integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==
|
||||||
|
|
||||||
array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2:
|
array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz"
|
||||||
integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==
|
integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==
|
||||||
@ -3273,7 +3350,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-
|
|||||||
es-errors "^1.3.0"
|
es-errors "^1.3.0"
|
||||||
function-bind "^1.1.2"
|
function-bind "^1.1.2"
|
||||||
|
|
||||||
call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.7, call-bind@^1.0.8:
|
call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7, call-bind@^1.0.8:
|
||||||
version "1.0.8"
|
version "1.0.8"
|
||||||
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz"
|
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz"
|
||||||
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
|
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
|
||||||
@ -3972,6 +4049,11 @@ css-what@^6.0.1:
|
|||||||
resolved "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz"
|
resolved "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz"
|
||||||
integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==
|
integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==
|
||||||
|
|
||||||
|
css.escape@^1.5.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz"
|
||||||
|
integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
|
||||||
|
|
||||||
cssesc@^3.0.0:
|
cssesc@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
|
||||||
@ -4191,6 +4273,30 @@ deep-equal@^1.0.1:
|
|||||||
object-keys "^1.1.1"
|
object-keys "^1.1.1"
|
||||||
regexp.prototype.flags "^1.5.1"
|
regexp.prototype.flags "^1.5.1"
|
||||||
|
|
||||||
|
deep-equal@^2.0.5:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz"
|
||||||
|
integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==
|
||||||
|
dependencies:
|
||||||
|
array-buffer-byte-length "^1.0.0"
|
||||||
|
call-bind "^1.0.5"
|
||||||
|
es-get-iterator "^1.1.3"
|
||||||
|
get-intrinsic "^1.2.2"
|
||||||
|
is-arguments "^1.1.1"
|
||||||
|
is-array-buffer "^3.0.2"
|
||||||
|
is-date-object "^1.0.5"
|
||||||
|
is-regex "^1.1.4"
|
||||||
|
is-shared-array-buffer "^1.0.2"
|
||||||
|
isarray "^2.0.5"
|
||||||
|
object-is "^1.1.5"
|
||||||
|
object-keys "^1.1.1"
|
||||||
|
object.assign "^4.1.4"
|
||||||
|
regexp.prototype.flags "^1.5.1"
|
||||||
|
side-channel "^1.0.4"
|
||||||
|
which-boxed-primitive "^1.0.2"
|
||||||
|
which-collection "^1.0.1"
|
||||||
|
which-typed-array "^1.1.13"
|
||||||
|
|
||||||
deepmerge@^4.2.2:
|
deepmerge@^4.2.2:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz"
|
resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz"
|
||||||
@ -4363,6 +4469,11 @@ dns-txt@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
buffer-indexof "^1.0.0"
|
buffer-indexof "^1.0.0"
|
||||||
|
|
||||||
|
dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9:
|
||||||
|
version "0.5.16"
|
||||||
|
resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz"
|
||||||
|
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
|
||||||
|
|
||||||
dom-converter@^0.2.0:
|
dom-converter@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz"
|
||||||
@ -4661,6 +4772,21 @@ es-errors@^1.3.0:
|
|||||||
resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
|
resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
|
||||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||||
|
|
||||||
|
es-get-iterator@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz"
|
||||||
|
integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
get-intrinsic "^1.1.3"
|
||||||
|
has-symbols "^1.0.3"
|
||||||
|
is-arguments "^1.1.1"
|
||||||
|
is-map "^2.0.2"
|
||||||
|
is-set "^2.0.2"
|
||||||
|
is-string "^1.0.7"
|
||||||
|
isarray "^2.0.5"
|
||||||
|
stop-iteration-iterator "^1.0.0"
|
||||||
|
|
||||||
es-module-lexer@^1.2.1:
|
es-module-lexer@^1.2.1:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz"
|
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz"
|
||||||
@ -5226,7 +5352,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
|||||||
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
|
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
|
||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0:
|
get-intrinsic@^1.1.3, get-intrinsic@^1.2.2, get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
|
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
|
||||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||||
@ -5778,6 +5904,11 @@ imurmurhash@^0.1.4:
|
|||||||
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
|
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
|
||||||
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
|
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
|
||||||
|
|
||||||
|
indent-string@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
|
||||||
|
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
|
||||||
|
|
||||||
inflight@^1.0.4:
|
inflight@^1.0.4:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
|
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
|
||||||
@ -5855,7 +5986,7 @@ is-arguments@^1.0.4, is-arguments@^1.1.1:
|
|||||||
call-bound "^1.0.2"
|
call-bound "^1.0.2"
|
||||||
has-tostringtag "^1.0.2"
|
has-tostringtag "^1.0.2"
|
||||||
|
|
||||||
is-array-buffer@^3.0.4, is-array-buffer@^3.0.5:
|
is-array-buffer@^3.0.2, is-array-buffer@^3.0.4, is-array-buffer@^3.0.5:
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz"
|
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz"
|
||||||
integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==
|
integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==
|
||||||
@ -6047,7 +6178,7 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-extglob "^2.1.1"
|
is-extglob "^2.1.1"
|
||||||
|
|
||||||
is-map@^2.0.3:
|
is-map@^2.0.2, is-map@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz"
|
resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz"
|
||||||
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
||||||
@ -6136,12 +6267,12 @@ is-regex@^1.1.4, is-regex@^1.2.1:
|
|||||||
has-tostringtag "^1.0.2"
|
has-tostringtag "^1.0.2"
|
||||||
hasown "^2.0.2"
|
hasown "^2.0.2"
|
||||||
|
|
||||||
is-set@^2.0.3:
|
is-set@^2.0.2, is-set@^2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz"
|
resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz"
|
||||||
integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
|
integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==
|
||||||
|
|
||||||
is-shared-array-buffer@^1.0.4:
|
is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz"
|
resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz"
|
||||||
integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==
|
integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==
|
||||||
@ -6158,7 +6289,7 @@ is-stream@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
|
||||||
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
|
||||||
|
|
||||||
is-string@^1.1.1:
|
is-string@^1.0.7, is-string@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz"
|
||||||
integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==
|
integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==
|
||||||
@ -7077,6 +7208,11 @@ lru-cache@^5.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^3.0.2"
|
yallist "^3.0.2"
|
||||||
|
|
||||||
|
lz-string@^1.5.0:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
||||||
|
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||||
|
|
||||||
magic-string@^0.25.7:
|
magic-string@^0.25.7:
|
||||||
version "0.25.9"
|
version "0.25.9"
|
||||||
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz"
|
resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz"
|
||||||
@ -7302,6 +7438,11 @@ mimic-response@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
|
||||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||||
|
|
||||||
|
min-indent@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz"
|
||||||
|
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
|
||||||
|
|
||||||
mini-css-extract-plugin@^1.6.0:
|
mini-css-extract-plugin@^1.6.0:
|
||||||
version "1.6.2"
|
version "1.6.2"
|
||||||
resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz"
|
resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz"
|
||||||
@ -8400,16 +8541,16 @@ pretty-error@^4.0.0:
|
|||||||
lodash "^4.17.20"
|
lodash "^4.17.20"
|
||||||
renderkid "^3.0.0"
|
renderkid "^3.0.0"
|
||||||
|
|
||||||
pretty-format@^29.0.0:
|
pretty-format@^27.0.2:
|
||||||
version "29.7.0"
|
version "27.5.1"
|
||||||
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz"
|
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz"
|
||||||
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
|
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jest/schemas" "^29.6.3"
|
ansi-regex "^5.0.1"
|
||||||
ansi-styles "^5.0.0"
|
ansi-styles "^5.0.0"
|
||||||
react-is "^18.0.0"
|
react-is "^17.0.1"
|
||||||
|
|
||||||
pretty-format@^29.7.0:
|
pretty-format@^29.0.0, pretty-format@^29.7.0:
|
||||||
version "29.7.0"
|
version "29.7.0"
|
||||||
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz"
|
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz"
|
||||||
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
|
integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
|
||||||
@ -8596,7 +8737,7 @@ raw-body@2.5.2:
|
|||||||
iconv-lite "0.4.24"
|
iconv-lite "0.4.24"
|
||||||
unpipe "1.0.0"
|
unpipe "1.0.0"
|
||||||
|
|
||||||
react-dom@^17.0.2, react-dom@>=16.8.0, react-dom@>=16.8.3:
|
react-dom@^17.0.2, react-dom@<18.0.0, react-dom@>=16.8.0, react-dom@>=16.8.3:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
|
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
|
||||||
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
|
||||||
@ -8610,6 +8751,11 @@ react-is@^16.13.1:
|
|||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
|
react-is@^17.0.1:
|
||||||
|
version "17.0.2"
|
||||||
|
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
|
||||||
|
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||||
|
|
||||||
react-is@^18.0.0, react-is@^18.3.1:
|
react-is@^18.0.0, react-is@^18.3.1:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz"
|
||||||
@ -8625,7 +8771,7 @@ react-mentions@^4.3.1:
|
|||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
substyle "^9.1.0"
|
substyle "^9.1.0"
|
||||||
|
|
||||||
"react@^15.0.2 || ^16.0.0 || ^17.0.0", react@^17.0.2, react@>=16.8.0, react@>=16.8.3, react@17.0.2:
|
"react@^15.0.2 || ^16.0.0 || ^17.0.0", react@^17.0.2, react@<18.0.0, react@>=16.8.0, react@>=16.8.3, react@17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
||||||
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||||
@ -8702,6 +8848,14 @@ readdirp@^4.0.1:
|
|||||||
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz"
|
resolved "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz"
|
||||||
integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
|
integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
|
||||||
|
|
||||||
|
redent@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz"
|
||||||
|
integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==
|
||||||
|
dependencies:
|
||||||
|
indent-string "^4.0.0"
|
||||||
|
strip-indent "^3.0.0"
|
||||||
|
|
||||||
reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
|
reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9:
|
||||||
version "1.0.10"
|
version "1.0.10"
|
||||||
resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz"
|
resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz"
|
||||||
@ -9360,7 +9514,7 @@ side-channel-weakmap@^1.0.2:
|
|||||||
object-inspect "^1.13.3"
|
object-inspect "^1.13.3"
|
||||||
side-channel-map "^1.0.1"
|
side-channel-map "^1.0.1"
|
||||||
|
|
||||||
side-channel@^1.0.6, side-channel@^1.1.0:
|
side-channel@^1.0.4, side-channel@^1.0.6, side-channel@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz"
|
||||||
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
|
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
|
||||||
@ -9645,7 +9799,7 @@ statuses@2.0.1:
|
|||||||
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz"
|
||||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||||
|
|
||||||
stop-iteration-iterator@^1.1.0:
|
stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz"
|
||||||
integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==
|
integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==
|
||||||
@ -9805,6 +9959,13 @@ strip-final-newline@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
|
||||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||||
|
|
||||||
|
strip-indent@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz"
|
||||||
|
integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==
|
||||||
|
dependencies:
|
||||||
|
min-indent "^1.0.0"
|
||||||
|
|
||||||
strip-json-comments@^3.1.1:
|
strip-json-comments@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||||
@ -10670,15 +10831,7 @@ whatwg-url@^14.0.0, whatwg-url@^14.1.1:
|
|||||||
tr46 "^5.1.0"
|
tr46 "^5.1.0"
|
||||||
webidl-conversions "^7.0.0"
|
webidl-conversions "^7.0.0"
|
||||||
|
|
||||||
whatwg-url@^15.0.0:
|
whatwg-url@^15.0.0, whatwg-url@^15.1.0:
|
||||||
version "15.1.0"
|
|
||||||
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz"
|
|
||||||
integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==
|
|
||||||
dependencies:
|
|
||||||
tr46 "^6.0.0"
|
|
||||||
webidl-conversions "^8.0.0"
|
|
||||||
|
|
||||||
whatwg-url@^15.1.0:
|
|
||||||
version "15.1.0"
|
version "15.1.0"
|
||||||
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz"
|
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz"
|
||||||
integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==
|
integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==
|
||||||
@ -10694,7 +10847,7 @@ whatwg-url@^5.0.0:
|
|||||||
tr46 "~0.0.3"
|
tr46 "~0.0.3"
|
||||||
webidl-conversions "^3.0.0"
|
webidl-conversions "^3.0.0"
|
||||||
|
|
||||||
which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1:
|
which-boxed-primitive@^1.0.2, which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz"
|
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz"
|
||||||
integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==
|
integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==
|
||||||
@ -10724,7 +10877,7 @@ which-builtin-type@^1.2.1:
|
|||||||
which-collection "^1.0.2"
|
which-collection "^1.0.2"
|
||||||
which-typed-array "^1.1.16"
|
which-typed-array "^1.1.16"
|
||||||
|
|
||||||
which-collection@^1.0.2:
|
which-collection@^1.0.1, which-collection@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz"
|
||||||
integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
|
integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==
|
||||||
@ -10739,7 +10892,7 @@ which-module@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz"
|
||||||
integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
|
integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
|
||||||
|
|
||||||
which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2:
|
which-typed-array@^1.1.13, which-typed-array@^1.1.16, which-typed-array@^1.1.19, which-typed-array@^1.1.2:
|
||||||
version "1.1.19"
|
version "1.1.19"
|
||||||
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz"
|
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz"
|
||||||
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
|
integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==
|
||||||
@ -10829,12 +10982,7 @@ ws@^7.3.1:
|
|||||||
resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz"
|
resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz"
|
||||||
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
|
integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==
|
||||||
|
|
||||||
ws@^8.18.0:
|
ws@^8.18.0, ws@^8.18.3:
|
||||||
version "8.18.3"
|
|
||||||
resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz"
|
|
||||||
integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==
|
|
||||||
|
|
||||||
ws@^8.18.3:
|
|
||||||
version "8.18.3"
|
version "8.18.3"
|
||||||
resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz"
|
resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz"
|
||||||
integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==
|
integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user