musora-content-services 2.1.1 → 2.2.1

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/docs/Content-Organization.html +2 -2
  3. package/docs/Gamification.html +245 -0
  4. package/docs/api_types.js.html +97 -0
  5. package/docs/config.js.html +2 -2
  6. package/docs/content-org_playlists-types.js.html +3 -5
  7. package/docs/content-org_playlists.js.html +8 -9
  8. package/docs/content.js.html +2 -2
  9. package/docs/gamification_awards.js.html +664 -0
  10. package/docs/gamification_gamification.js.html +76 -0
  11. package/docs/gamification_types.js.html +98 -0
  12. package/docs/global.html +1441 -153
  13. package/docs/index.html +2 -2
  14. package/docs/module-Awards.html +354 -0
  15. package/docs/module-Config.html +2 -2
  16. package/docs/module-Content-Services-V2.html +2 -2
  17. package/docs/module-Playlists.html +6 -6
  18. package/docs/module-Railcontent-Services.html +33 -194
  19. package/docs/module-Sanity-Services.html +885 -90
  20. package/docs/module-Session-Management.html +2 -2
  21. package/docs/module-User-Permissions.html +2 -2
  22. package/docs/railcontent.js.html +13 -25
  23. package/docs/sanity.js.html +76 -5
  24. package/docs/user_permissions.js.html +3 -3
  25. package/docs/user_sessions.js.html +4 -4
  26. package/docs/user_types.js.html +2 -2
  27. package/jsdoc.json +4 -2
  28. package/package.json +1 -1
  29. package/src/contentTypeConfig.js +1 -0
  30. package/src/index.d.ts +40 -3
  31. package/src/index.js +40 -2
  32. package/src/services/api/types.js +25 -0
  33. package/src/services/dataContext.js +15 -2
  34. package/src/services/gamification/awards.js +592 -0
  35. package/src/services/gamification/gamification.js +4 -0
  36. package/src/services/gamification/types.js +26 -0
  37. package/src/services/railcontent.js +60 -0
  38. package/src/services/sanity.js +22 -21
  39. package/src/services/userActivity.js +377 -23
  40. package/test/contentLikes.test.js +2 -0
  41. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +35 -0
  42. package/test/userActivity.test.js +118 -0
@@ -0,0 +1,35 @@
1
+ [
2
+ {
3
+ "permission_id": [
4
+ 78,
5
+ 89,
6
+ 91,
7
+ 92,
8
+ 88,
9
+ 90
10
+ ],
11
+ "thumbnail": "https://cdn.sanity.io/images/4032r8py/staging/5f15b20b428c06263fd39599fc310ab00eb05fee-1920x1080.jpg",
12
+ "difficulty_string": "Beginner",
13
+ "url": "/drumeo/quick-tips/how-to-play-drums/415183",
14
+ "lesson_count": null,
15
+ "id": 415183,
16
+ "image": "https://cdn.sanity.io/images/4032r8py/staging/5f15b20b428c06263fd39599fc310ab00eb05fee-1920x1080.jpg",
17
+ "web_url_path": "/drumeo/quick-tips/how-to-play-drums/415183",
18
+ "type": "quick-tips",
19
+ "brand": "drumeo",
20
+ "genre": null,
21
+ "status": "published",
22
+ "xp": 100,
23
+ "railcontent_id": 415183,
24
+ "artist": null,
25
+ "progress_percent": null,
26
+ "child_count": null,
27
+ "sanity_id": "quick-tips_415183",
28
+ "artist_name": "Brandon Toews",
29
+ "title": "How To Play Drums",
30
+ "difficulty": 3,
31
+ "published_on": "2024-12-13T12:00:00.000000Z",
32
+ "length_in_seconds": 576,
33
+ "slug": "how-to-play-drums"
34
+ }
35
+ ]
@@ -0,0 +1,118 @@
1
+ import { initializeTestService } from './initializeTests.js'
2
+ import {getUserMonthlyStats, getUserWeeklyStats, userActivityContext, recordUserPractice, getUserPractices} from '../src/services/userActivity.js'
3
+ import { logUserPractice } from '../src/services/railcontent.js'
4
+ import {fetchByRailContentIds} from "../src";
5
+ import mockData_fetchByRailContentIds_one_content from './mockData/mockData_fetchByRailContentIds_one_content.json';
6
+
7
+ global.fetch = jest.fn()
8
+ let mock = null
9
+ const testVersion = 1
10
+ const DEBUG = true
11
+
12
+ jest.mock('../src/services/railcontent', () => ({
13
+ ...jest.requireActual('../src/services/railcontent'),
14
+ logUserPractice: jest.fn(() => Promise.resolve()),
15
+ fetchUserPermissionsData: jest.fn(() => ({ permissions: [78, 91, 92], isAdmin: false }))
16
+ }))
17
+
18
+ jest.mock('../src/services/sanity', () => ({
19
+ ...jest.requireActual('../src/services/sanity'),
20
+ fetchByRailContentIds: jest.fn(() => Promise.resolve(mockData_fetchByRailContentIds_one_content)),
21
+ }))
22
+ describe('User Activity API Tests', function () {
23
+ beforeEach(() => {
24
+ initializeTestService()
25
+ mock = jest.spyOn(userActivityContext, 'fetchData')
26
+ var json = JSON.parse(
27
+ `{
28
+ "version": ${testVersion},
29
+ "config": { "key": 1, "enabled": 1, "checkInterval": 1, "refreshInterval": 2 },
30
+ "data": {
31
+ "practices": {
32
+ "2025-02-10": [{ "duration_seconds": 190 }],
33
+ "2025-02-11": [{ "duration_seconds": 340 }],
34
+ "2025-02-19": [{ "duration_seconds": 340 }],
35
+ "2025-03-01": [{ "duration_seconds": 360 }],
36
+ "2025-03-03": [{ "duration_seconds": 360 }],
37
+ "2025-03-05": [{ "duration_seconds": 100 }],
38
+ "2025-03-11": [{ "duration_seconds": 190 }],
39
+ "2025-03-14": [{ "duration_seconds": 456 }],
40
+ "2025-03-15": [{ "duration_seconds": 124 }],
41
+ "2025-03-16": [{ "duration_seconds": 452 }, { "duration_seconds": 456 }],
42
+ "2025-03-17": [{ "duration_seconds": 122 }]
43
+ }
44
+ }
45
+ }`
46
+ )
47
+ mock.mockImplementation(() => json)
48
+ userActivityContext.ensureLocalContextLoaded()
49
+ })
50
+
51
+ test('fetches user practices successfully', async () => {
52
+ userActivityContext.clearCache()
53
+ const practices = await getUserMonthlyStats()
54
+ consoleLog(practices)
55
+ // Assert that dailyActiveStats contains correct data
56
+ const dailyStats = practices.dailyActiveStats
57
+ const currentDate = new Date()
58
+ const currentDateString = currentDate.toISOString().split('T')[0]
59
+ expect(dailyStats).toHaveLength(42)
60
+
61
+ // Verify current day's stats (e.g., March 17, 2025)
62
+ const current = dailyStats.find(stat => stat.label === currentDateString)
63
+ expect(current).toBeTruthy()
64
+ expect(current.isActive).toBe(true)
65
+ expect(current.type).toBe('active')
66
+ expect(current.inStreak).toBe(false)
67
+
68
+ // Ensure that mock was called as expected
69
+ expect(mock).toHaveBeenCalledTimes(1)
70
+ })
71
+
72
+ test('fetches user practices from past', async () => {
73
+ userActivityContext.clearCache()
74
+ const practices = await getUserMonthlyStats( 2025, 1)
75
+ consoleLog(practices)
76
+
77
+ // Assert that dailyActiveStats contains correct data
78
+ const dailyStats = practices.dailyActiveStats
79
+ const feb10 = dailyStats.find(stat => stat.label === '2025-02-10')
80
+ expect(feb10.inStreak).toBe(true)
81
+ expect(feb10.type).toBe('tracked')
82
+ expect(feb10.isActive).toBe(false)
83
+ })
84
+
85
+ test('fetches user practices for current week', async () => {
86
+ userActivityContext.clearCache()
87
+ const practices = await getUserWeeklyStats( )
88
+ consoleLog(practices)
89
+
90
+ const dailyStats = practices.dailyActiveStats
91
+ const monday = dailyStats.find(stat => stat.label === 'M')
92
+ expect(monday).toBeDefined
93
+ const tuesday = dailyStats.find(stat => stat.label === 'T')
94
+ expect(tuesday).toBeDefined
95
+ })
96
+
97
+ test('should add a new practice entry and call logUserPractice', async () => {
98
+ userActivityContext.clearCache()
99
+ const mockPractice = {
100
+ duration_seconds: 300,
101
+ content_id: 415183
102
+ }
103
+
104
+ jest.spyOn(userActivityContext, 'update').mockImplementation(async (callback) => {
105
+ await callback(userActivityContext)
106
+ })
107
+
108
+ await recordUserPractice(mockPractice)
109
+
110
+ expect(userActivityContext.update).toHaveBeenCalledTimes(1)
111
+ })
112
+
113
+ function consoleLog(message, object=null, debug=false) {
114
+ if (debug || DEBUG) {
115
+ console.log(message, object);
116
+ }
117
+ }
118
+ })