musora-content-services 1.0.98 → 1.0.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/docs/config.js.html +2 -2
- package/docs/index.html +2 -2
- package/docs/module-Config.html +2 -2
- package/docs/module-Railcontent-Services.html +10 -10
- package/docs/module-Sanity-Services.html +458 -29
- package/docs/railcontent.js.html +24 -17
- package/docs/sanity.js.html +102 -9
- package/link_mcs.sh +0 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +45 -3
- package/src/index.d.ts +6 -0
- package/src/index.js +6 -0
- package/src/services/railcontent.js +50 -0
- package/src/services/sanity.js +147 -21
- package/src/services/userContext.js +105 -0
- package/test/localStorageMock.js +21 -0
- package/test/sanityQueryService.test.js +6 -0
- package/test/userContext.test.js +86 -0
package/src/services/sanity.js
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
contentTypeConfig,
|
|
10
10
|
DEFAULT_FIELDS,
|
|
11
11
|
getFieldsForContentType,
|
|
12
|
-
filtersToGroq
|
|
12
|
+
filtersToGroq,
|
|
13
|
+
getUpcomingEventsTypes,
|
|
14
|
+
getNewReleasesTypes,
|
|
13
15
|
} from "../contentTypeConfig";
|
|
14
16
|
import {globalConfig} from "./config";
|
|
15
17
|
|
|
@@ -254,14 +256,8 @@ export async function fetchWorkouts(brand) {
|
|
|
254
256
|
* @returns {Promise<Object|null>} - The fetched new releases data or null if not found.
|
|
255
257
|
*/
|
|
256
258
|
export async function fetchNewReleases(brand, { page = 1, limit = 10, sort="-published_on" } = {}) {
|
|
257
|
-
const newTypes =
|
|
258
|
-
|
|
259
|
-
'pianote': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
|
|
260
|
-
'guitareo': ["student-review", "student-reviews", "question-and-answer", "archives", "recording", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
|
|
261
|
-
'singeo': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"],
|
|
262
|
-
'default': ["student-review", "student-reviews", "question-and-answer", "course", "play-along", "student-focus", "coach-stream", "learning-path-level", "unit", "quick-tips", "live", "question-and-answer", "student-review", "boot-camps", "song", "chords-and-scales", "pack", "podcasts", "workout", "challenge", "challenge-part"]
|
|
263
|
-
};
|
|
264
|
-
const typesString = arrayJoinWithQuotes(newTypes[brand] ?? newTypes['default']);
|
|
259
|
+
const newTypes = getNewReleasesTypes(brand);
|
|
260
|
+
const typesString = arrayJoinWithQuotes(newTypes);
|
|
265
261
|
const start = (page - 1) * limit;
|
|
266
262
|
const end = start + limit;
|
|
267
263
|
const sortOrder = getSortOrder(sort);
|
|
@@ -297,15 +293,8 @@ export async function fetchNewReleases(brand, { page = 1, limit = 10, sort="-pub
|
|
|
297
293
|
* .catch(error => console.error(error));
|
|
298
294
|
*/
|
|
299
295
|
export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {}) {
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
'drumeo': [...baseLiveTypes, "drum-fest-international-2022", "spotlight", "the-history-of-electronic-drums", "backstage-secrets", "quick-tips", "student-collaborations", "live-streams", "podcasts", "solos", "gear-guides", "performances", "in-rhythm", "challenges", "on-the-road", "diy-drum-experiments", "rhythmic-adventures-of-captain-carson", "study-the-greats", "rhythms-from-another-planet", "tama-drums", "paiste-cymbals", "behind-the-scenes", "exploring-beats", "sonor-drums"],
|
|
303
|
-
'pianote': baseLiveTypes,
|
|
304
|
-
'guitareo': [...baseLiveTypes, "archives"],
|
|
305
|
-
'singeo': baseLiveTypes,
|
|
306
|
-
'default': baseLiveTypes
|
|
307
|
-
};
|
|
308
|
-
const typesString = arrayJoinWithQuotes(liveTypes[brand] ?? liveTypes['default']);
|
|
296
|
+
const liveTypes = getUpcomingEventsTypes(brand);
|
|
297
|
+
const typesString = arrayJoinWithQuotes(liveTypes);
|
|
309
298
|
const now = getSanityDate(new Date());
|
|
310
299
|
const start = (page - 1) * limit;
|
|
311
300
|
const end = start + limit;
|
|
@@ -325,6 +314,45 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
|
|
|
325
314
|
return fetchSanity(query, true);
|
|
326
315
|
}
|
|
327
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Fetch scheduled releases for a specific brand.
|
|
319
|
+
*
|
|
320
|
+
* @param {string} brand - The brand for which to fetch scheduled releasess.
|
|
321
|
+
* @returns {Promise<Object|null>} - A promise that resolves to an array of scheduled release objects or null if not found.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* fetchScheduledReleases('drumeo', {
|
|
325
|
+
* page: 2,
|
|
326
|
+
* limit: 20,
|
|
327
|
+
* })
|
|
328
|
+
* .then(content => console.log(content))
|
|
329
|
+
* .catch(error => console.error(error));
|
|
330
|
+
*/
|
|
331
|
+
export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
|
|
332
|
+
const upcomingTypes = getUpcomingEventsTypes(brand);
|
|
333
|
+
const newTypes = getNewReleasesTypes(brand);
|
|
334
|
+
|
|
335
|
+
const scheduledTypes = merge(upcomingTypes, newTypes)
|
|
336
|
+
const typesString = arrayJoinWithQuotes(scheduledTypes);
|
|
337
|
+
const now = getSanityDate(new Date());
|
|
338
|
+
const start = (page - 1) * limit;
|
|
339
|
+
const end = start + limit;
|
|
340
|
+
const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && published_on > '${now}']{
|
|
341
|
+
"id": railcontent_id,
|
|
342
|
+
title,
|
|
343
|
+
"image": thumbnail.asset->url,
|
|
344
|
+
"artist_name": instructor[0]->name,
|
|
345
|
+
"artists": instructor[]->name,
|
|
346
|
+
difficulty,
|
|
347
|
+
difficulty_string,
|
|
348
|
+
length_in_seconds,
|
|
349
|
+
published_on,
|
|
350
|
+
"type": _type,
|
|
351
|
+
web_url_path,
|
|
352
|
+
} | order(published_on asc)[${start}...${end}]`;
|
|
353
|
+
return fetchSanity(query, true);
|
|
354
|
+
}
|
|
355
|
+
|
|
328
356
|
/**
|
|
329
357
|
* Fetch content by a specific Railcontent ID.
|
|
330
358
|
*
|
|
@@ -415,7 +443,7 @@ export async function fetchAll(brand, type, {
|
|
|
415
443
|
const searchFilter = searchTerm
|
|
416
444
|
? groupBy !== "" ?
|
|
417
445
|
`&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
|
|
418
|
-
: `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*")`
|
|
446
|
+
: `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
|
|
419
447
|
: "";
|
|
420
448
|
|
|
421
449
|
// Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
|
|
@@ -499,8 +527,9 @@ export function getSortOrder(sort= '-published_on', groupBy)
|
|
|
499
527
|
case "slug":
|
|
500
528
|
sortOrder = groupBy ? 'name' : "title";
|
|
501
529
|
break;
|
|
530
|
+
case "name":
|
|
502
531
|
case "popularity":
|
|
503
|
-
sortOrder =
|
|
532
|
+
sortOrder = sort;
|
|
504
533
|
break;
|
|
505
534
|
case "published_on":
|
|
506
535
|
default:
|
|
@@ -664,6 +693,7 @@ export async function fetchMethod(brand, slug) {
|
|
|
664
693
|
"type": _type,
|
|
665
694
|
"description": ${descriptionField},
|
|
666
695
|
"url": web_url_path,
|
|
696
|
+
web_url_path,
|
|
667
697
|
xp,
|
|
668
698
|
}
|
|
669
699
|
} | order(published_on asc)`
|
|
@@ -821,7 +851,13 @@ export async function fetchLessonContent(railContentId) {
|
|
|
821
851
|
"coach_profile_image":thumbnail_url.asset->url
|
|
822
852
|
},
|
|
823
853
|
"instructors":instructor[]->name,
|
|
824
|
-
instructor[]
|
|
854
|
+
"instructor": instructor[]->{
|
|
855
|
+
"id":_id,
|
|
856
|
+
name,
|
|
857
|
+
short_bio,
|
|
858
|
+
web_url_path,
|
|
859
|
+
"coach_card_image": coach_card_image.asset->url,
|
|
860
|
+
},
|
|
825
861
|
${assignmentsField}
|
|
826
862
|
video,
|
|
827
863
|
length_in_seconds
|
|
@@ -1052,6 +1088,89 @@ export async function fetchByReference(brand, {
|
|
|
1052
1088
|
return fetchSanity(query, true);
|
|
1053
1089
|
}
|
|
1054
1090
|
|
|
1091
|
+
/**
|
|
1092
|
+
* Fetch the artist's lessons.
|
|
1093
|
+
* @param {string} brand - The brand for which to fetch lessons.
|
|
1094
|
+
* @param {string} name - The name of the artist
|
|
1095
|
+
* @param {string} contentType - The type of the lessons we need to get from the artist. If not defined, groq will get lessons from all content types
|
|
1096
|
+
* @returns {Promise<Object|null>} - The lessons for the artist and some details about the artist (name and thumbnail).
|
|
1097
|
+
*
|
|
1098
|
+
* @example
|
|
1099
|
+
* fetchArtistLessons('10 Years', 'song')
|
|
1100
|
+
* .then(lessons => console.log(lessons))
|
|
1101
|
+
* .catch(error => console.error(error));
|
|
1102
|
+
*/
|
|
1103
|
+
export async function fetchArtistLessons(brand, name, contentType, {
|
|
1104
|
+
sort = '-published_on',
|
|
1105
|
+
searchTerm = '',
|
|
1106
|
+
page = 1,
|
|
1107
|
+
limit = 10,
|
|
1108
|
+
includedFields = [],
|
|
1109
|
+
} = {}) {
|
|
1110
|
+
const fieldsString = DEFAULT_FIELDS.join(',');
|
|
1111
|
+
const start = (page - 1) * limit;
|
|
1112
|
+
const end = start + limit;
|
|
1113
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"`: ''
|
|
1114
|
+
const sortOrder = getSortOrder(sort);
|
|
1115
|
+
const addType = contentType ? `_type == '${contentType}' && `:''
|
|
1116
|
+
const includedFieldsFilter = includedFields.length > 0
|
|
1117
|
+
? filtersToGroq(includedFields)
|
|
1118
|
+
: "";
|
|
1119
|
+
|
|
1120
|
+
const query = `{
|
|
1121
|
+
"entity":
|
|
1122
|
+
*[_type == 'artist' && name == '${name}']
|
|
1123
|
+
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1124
|
+
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1125
|
+
'lessons': *[${addType} brand == '${brand}' && references(^._id) ${searchFilter} ${includedFieldsFilter}]{${fieldsString}}
|
|
1126
|
+
[${start}...${end}]}
|
|
1127
|
+
|order(${sortOrder})
|
|
1128
|
+
}`;
|
|
1129
|
+
return fetchSanity(query, true);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Fetch the genre's lessons.
|
|
1134
|
+
* @param {string} brand - The brand for which to fetch lessons.
|
|
1135
|
+
* @param {string} name - The name of the genre
|
|
1136
|
+
* @param {string} contentType - The type of the lessons we need to get from the genre. If not defined, groq will get lessons from all content types
|
|
1137
|
+
* @returns {Promise<Object|null>} - The lessons for the genre and some details about the genre (name and thumbnail).
|
|
1138
|
+
*
|
|
1139
|
+
* @example
|
|
1140
|
+
* fetchGenreLessons('Blues', 'song')
|
|
1141
|
+
* .then(lessons => console.log(lessons))
|
|
1142
|
+
* .catch(error => console.error(error));
|
|
1143
|
+
*/
|
|
1144
|
+
export async function fetchGenreLessons(brand, name, contentType, {
|
|
1145
|
+
sort = '-published_on',
|
|
1146
|
+
searchTerm = '',
|
|
1147
|
+
page = 1,
|
|
1148
|
+
limit = 10,
|
|
1149
|
+
includedFields = [],
|
|
1150
|
+
} = {}) {
|
|
1151
|
+
const fieldsString = DEFAULT_FIELDS.join(',');
|
|
1152
|
+
const start = (page - 1) * limit;
|
|
1153
|
+
const end = start + limit;
|
|
1154
|
+
const searchFilter = searchTerm ? `&& title match "${searchTerm}*"`: ''
|
|
1155
|
+
const sortOrder = getSortOrder(sort);
|
|
1156
|
+
const addType = contentType ? `_type == '${contentType}' && `:''
|
|
1157
|
+
const includedFieldsFilter = includedFields.length > 0
|
|
1158
|
+
? filtersToGroq(includedFields)
|
|
1159
|
+
: "";
|
|
1160
|
+
|
|
1161
|
+
const query = `{
|
|
1162
|
+
"entity":
|
|
1163
|
+
*[_type == 'genre' && name == '${name}']
|
|
1164
|
+
{'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
|
|
1165
|
+
'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
|
|
1166
|
+
'lessons': *[${addType} brand == '${brand}' && references(^._id) ${searchFilter} ${includedFieldsFilter}]{${fieldsString}}
|
|
1167
|
+
[${start}...${end}]}
|
|
1168
|
+
|order(${sortOrder})
|
|
1169
|
+
}`;
|
|
1170
|
+
return fetchSanity(query, true);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
|
|
1055
1174
|
|
|
1056
1175
|
/**
|
|
1057
1176
|
* Fetch data from the Sanity API based on a provided query.
|
|
@@ -1141,6 +1260,13 @@ function getSanityDate(date) {
|
|
|
1141
1260
|
return date.toISOString();
|
|
1142
1261
|
}
|
|
1143
1262
|
|
|
1263
|
+
const merge = (a, b, predicate = (a, b) => a === b) => {
|
|
1264
|
+
const c = [...a]; // copy to avoid side effects
|
|
1265
|
+
// add all items from B to copy C if they're not already present
|
|
1266
|
+
b.forEach((bItem) => (c.some((cItem) => predicate(bItem, cItem)) ? null : c.push(bItem)))
|
|
1267
|
+
return c;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1144
1270
|
function checkSanityConfig(config) {
|
|
1145
1271
|
if (!config.sanityConfig.token) {
|
|
1146
1272
|
console.warn('fetchSanity: The "token" property is missing in the config object.');
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {fetchUserContext, fetchLikeContent, fetchUnlikeContent} from "./railcontent";
|
|
2
|
+
|
|
3
|
+
const StorageKey = "userContext";
|
|
4
|
+
let userContext = null;
|
|
5
|
+
let cache = null;
|
|
6
|
+
|
|
7
|
+
export function init(localCache) {
|
|
8
|
+
cache = localCache;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function version() {
|
|
12
|
+
ensureLocalContextLoaded();
|
|
13
|
+
return userContext.version;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function getUserContext() {
|
|
17
|
+
ensureLocalContextLoaded();
|
|
18
|
+
if (userContext) {
|
|
19
|
+
verifyContextIsValid();
|
|
20
|
+
}
|
|
21
|
+
if (!userContext) {
|
|
22
|
+
await fetchFromServer();
|
|
23
|
+
}
|
|
24
|
+
return userContext;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function verifyContextIsValid() {
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ensureLocalContextLoaded() {
|
|
32
|
+
if (userContext) return;
|
|
33
|
+
let localData = cache.getItem(StorageKey);
|
|
34
|
+
if (localData) {
|
|
35
|
+
userContext = JSON.parse(localData);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function updateLocalContext(contentId, updateFunction) {
|
|
40
|
+
ensureLocalContextLoaded();
|
|
41
|
+
if (userContext) {
|
|
42
|
+
let contentData = userContext.data[contentId] ?? [];
|
|
43
|
+
updateFunction(contentData);
|
|
44
|
+
userContext.data[contentId] = contentData;
|
|
45
|
+
userContext.version++;
|
|
46
|
+
let data = JSON.stringify(userContext);
|
|
47
|
+
cache.setItem(StorageKey, data);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function fetchFromServer() {
|
|
52
|
+
let data = await fetchUserContext();
|
|
53
|
+
userContext = JSON.parse(data);
|
|
54
|
+
cache.setItem(StorageKey, data);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
function transformData(data, contentId) {
|
|
59
|
+
let transformed = [];
|
|
60
|
+
transformed["contentId"] = contentId;
|
|
61
|
+
transformed["liked"] = (data && data.l) ?? 0;
|
|
62
|
+
return transformed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function fetchContentData(contentId) {
|
|
66
|
+
let userContext = await getUserContext();
|
|
67
|
+
let data = userContext.data[contentId];
|
|
68
|
+
data = transformData(data, contentId);
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function clearCache() {
|
|
73
|
+
userContext = null;
|
|
74
|
+
cache.setItem(StorageKey, null);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function testClearLocal() {
|
|
78
|
+
userContext = null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function likeContent(contentId) {
|
|
82
|
+
updateLocalContext(contentId,
|
|
83
|
+
function (contentData) {
|
|
84
|
+
contentData.l = 1;
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
let result = await fetchLikeContent(contentId);
|
|
89
|
+
if (result.version !== userContext.version) {
|
|
90
|
+
clearCache();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function unlikeContent(contentId) {
|
|
95
|
+
updateLocalContext(contentId,
|
|
96
|
+
function (contentData) {
|
|
97
|
+
contentData.l = 0;
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
let result = await fetchUnlikeContent(contentId);
|
|
102
|
+
if (result.version !== userContext.version) {
|
|
103
|
+
clearCache();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class LocalStorageMock {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.store = {};
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
clear() {
|
|
7
|
+
this.store = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getItem(key) {
|
|
11
|
+
return this.store[key] || null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
setItem(key, value) {
|
|
15
|
+
this.store[key] = String(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
removeItem(key) {
|
|
19
|
+
delete this.store[key];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -32,6 +32,7 @@ const {
|
|
|
32
32
|
fetchPacksAll,
|
|
33
33
|
fetchCoachLessons,
|
|
34
34
|
fetchByReference,
|
|
35
|
+
fetchScheduledReleases
|
|
35
36
|
} = require('../src/services/sanity.js');
|
|
36
37
|
|
|
37
38
|
describe('Sanity Queries', function () {
|
|
@@ -286,4 +287,9 @@ describe('Sanity Queries', function () {
|
|
|
286
287
|
const response = await fetchByReference('drumeo', { includedFields: ['is_featured'] });
|
|
287
288
|
expect(response.entity.length).toBeGreaterThan(0);
|
|
288
289
|
});
|
|
290
|
+
|
|
291
|
+
test('fetchScheduledReleases', async () => {
|
|
292
|
+
const response = await fetchScheduledReleases('drumeo', {});
|
|
293
|
+
expect(response.length).toBeGreaterThan(0);
|
|
294
|
+
});
|
|
289
295
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const railContentModule = require('../src/services/railcontent.js')
|
|
2
|
+
const userContextModule = require('../src/services/userContext.js');
|
|
3
|
+
import {LocalStorageMock} from "./localStorageMock";
|
|
4
|
+
|
|
5
|
+
describe('userContext', function () {
|
|
6
|
+
let mock = null;
|
|
7
|
+
const testVersion = 1;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
userContextModule.init(new LocalStorageMock());
|
|
10
|
+
mock = jest.spyOn(railContentModule, 'fetchUserContext');
|
|
11
|
+
var json = `{"version":${testVersion},"data":{"308516":{"l":1},"308515":{"p":100},"308514":{"p":13},"308518":{"p":100}}}`;
|
|
12
|
+
mock.mockImplementation(() => json);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('contentLiked', async () => {
|
|
16
|
+
let contentData = await userContextModule.fetchContentData(308516);
|
|
17
|
+
expect(contentData.liked).toBe(1);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('contentDoesNotExist', async () => {
|
|
21
|
+
//fetch content that does not exist
|
|
22
|
+
let contentData = await userContextModule.fetchContentData(121111);
|
|
23
|
+
expect(contentData.liked).toBe(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('ensureOnlyOneServerFetchRequest', async () => {
|
|
27
|
+
userContextModule.clearCache();
|
|
28
|
+
await userContextModule.fetchContentData(308516);
|
|
29
|
+
await userContextModule.fetchContentData(308514);
|
|
30
|
+
expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('ensureDataPulledFromLocalCache', async () => {
|
|
34
|
+
userContextModule.clearCache();
|
|
35
|
+
await userContextModule.fetchContentData(308516);
|
|
36
|
+
userContextModule.testClearLocal();
|
|
37
|
+
await userContextModule.fetchContentData(308514);
|
|
38
|
+
expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// test('hashExpiration', async () => {
|
|
42
|
+
// userContextModule.clearCache();
|
|
43
|
+
// await userContextModule.fetchContentData(testHash308516);
|
|
44
|
+
// let newHash = "8g9qg5wn3e5s5oi69q6g22et9w6g34t5";
|
|
45
|
+
// await userContextModule.fetchContentData(newHash, 308516);
|
|
46
|
+
// expect(railContentModule.fetchUserContext).toHaveBeenCalledTimes(2);
|
|
47
|
+
// });
|
|
48
|
+
|
|
49
|
+
test('likeContent', async () => {
|
|
50
|
+
mock = jest.spyOn(railContentModule, 'fetchLikeContent');
|
|
51
|
+
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
52
|
+
mock.mockImplementation(() => json);
|
|
53
|
+
|
|
54
|
+
userContextModule.clearCache();
|
|
55
|
+
await userContextModule.fetchContentData(308515);
|
|
56
|
+
await userContextModule.likeContent(308515);
|
|
57
|
+
let contentData = await userContextModule.fetchContentData(308515);
|
|
58
|
+
expect(contentData.liked).toBe(1);
|
|
59
|
+
|
|
60
|
+
userContextModule.testClearLocal();
|
|
61
|
+
contentData = await userContextModule.fetchContentData(308515);
|
|
62
|
+
expect(contentData.liked).toBe(1);
|
|
63
|
+
|
|
64
|
+
expect(userContextModule.version()).toBe(testVersion + 1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
test('unlikeContent', async () => {
|
|
69
|
+
mock = jest.spyOn(railContentModule, 'fetchUnlikeContent');
|
|
70
|
+
var json = JSON.parse(`{"version":${testVersion + 1}}`);
|
|
71
|
+
mock.mockImplementation(() => json);
|
|
72
|
+
|
|
73
|
+
userContextModule.clearCache();
|
|
74
|
+
await userContextModule.fetchContentData(308516);
|
|
75
|
+
await userContextModule.unlikeContent(308516);
|
|
76
|
+
let contentData = await userContextModule.fetchContentData(308516);
|
|
77
|
+
expect(contentData.liked).toBe(0);
|
|
78
|
+
|
|
79
|
+
userContextModule.testClearLocal();
|
|
80
|
+
contentData = await userContextModule.fetchContentData(308516);
|
|
81
|
+
expect(contentData.liked).toBe(0);
|
|
82
|
+
|
|
83
|
+
expect(userContextModule.version()).toBe(testVersion + 1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
});
|