musora-content-services 2.121.2 โ†’ 2.122.0

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 (233) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/.coderabbit.yaml +0 -0
  3. package/.editorconfig +0 -0
  4. package/.github/pull_request_template.md +0 -0
  5. package/.github/workflows/conventional-commits.yaml +0 -0
  6. package/.github/workflows/docs.js.yml +0 -0
  7. package/.github/workflows/node.js.yml +0 -0
  8. package/.prettierignore +0 -0
  9. package/.prettierrc +0 -0
  10. package/.yarnrc.yml +1 -0
  11. package/CHANGELOG.md +19 -0
  12. package/CLAUDE.md +0 -0
  13. package/README.md +0 -0
  14. package/babel.config.cjs +0 -0
  15. package/check_content.js +30 -0
  16. package/check_content.mjs +32 -0
  17. package/jest.config.js +0 -0
  18. package/jsdoc.json +0 -0
  19. package/package.json +1 -1
  20. package/src/constants/award-assets.js +0 -0
  21. package/src/constants/membership-permissions.ts +0 -0
  22. package/src/contentMetaData.js +36 -0
  23. package/src/contentTypeConfig.js +32 -5
  24. package/src/filterBuilder.js +0 -0
  25. package/src/index.d.ts +0 -0
  26. package/src/index.js +0 -0
  27. package/src/infrastructure/http/HttpClient.ts +0 -0
  28. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  29. package/src/infrastructure/http/index.ts +0 -0
  30. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  31. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  32. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  33. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  34. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  35. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  36. package/src/lib/ads/monoid.ts +0 -0
  37. package/src/lib/ads/semigroup.ts +0 -0
  38. package/src/lib/brands.ts +0 -0
  39. package/src/lib/lastUpdated.js +0 -0
  40. package/src/lib/sanity/filter.ts +0 -0
  41. package/src/lib/sanity/query.ts +0 -0
  42. package/src/services/api/types.js +0 -0
  43. package/src/services/api/types.ts +0 -0
  44. package/src/services/awards/award-callbacks.js +0 -0
  45. package/src/services/awards/award-query.js +0 -0
  46. package/src/services/awards/internal/.indexignore +0 -0
  47. package/src/services/awards/internal/award-definitions.js +0 -0
  48. package/src/services/awards/internal/award-events.js +0 -0
  49. package/src/services/awards/internal/award-manager.js +0 -0
  50. package/src/services/awards/internal/certificate-builder.js +0 -0
  51. package/src/services/awards/internal/completion-data-generator.js +0 -0
  52. package/src/services/awards/internal/content-progress-observer.js +0 -0
  53. package/src/services/awards/internal/image-utils.js +0 -0
  54. package/src/services/awards/internal/message-generator.js +0 -0
  55. package/src/services/awards/internal/types.js +0 -0
  56. package/src/services/awards/types.d.ts +0 -0
  57. package/src/services/awards/types.js +0 -0
  58. package/src/services/config.js +0 -0
  59. package/src/services/content/artist.ts +0 -0
  60. package/src/services/content/content.ts +0 -0
  61. package/src/services/content/genre.ts +0 -0
  62. package/src/services/content/instructor.ts +0 -0
  63. package/src/services/content-org/content-org.js +0 -0
  64. package/src/services/content-org/guided-courses.ts +0 -0
  65. package/src/services/content-org/learning-paths.ts +0 -0
  66. package/src/services/content-org/playlists-types.js +0 -0
  67. package/src/services/content-org/playlists.js +0 -0
  68. package/src/services/content.js +0 -0
  69. package/src/services/contentAggregator.js +0 -0
  70. package/src/services/contentLikes.js +0 -0
  71. package/src/services/contentProgress.js +0 -0
  72. package/src/services/dataContext.js +0 -0
  73. package/src/services/dateUtils.js +0 -0
  74. package/src/services/eventsAPI.js +0 -0
  75. package/src/services/forums/categories.ts +0 -0
  76. package/src/services/forums/forums.ts +0 -0
  77. package/src/services/forums/posts.ts +0 -0
  78. package/src/services/forums/threads.ts +0 -0
  79. package/src/services/forums/types.ts +0 -0
  80. package/src/services/gamification/awards.ts +0 -0
  81. package/src/services/gamification/gamification.js +0 -0
  82. package/src/services/imageSRCBuilder.js +0 -0
  83. package/src/services/imageSRCVerify.js +0 -0
  84. package/src/services/liveTesting.ts +0 -0
  85. package/src/services/permissions/PermissionsAdapter.ts +0 -0
  86. package/src/services/permissions/PermissionsAdapterFactory.ts +0 -0
  87. package/src/services/permissions/PermissionsV1Adapter.ts +0 -0
  88. package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
  89. package/src/services/permissions/README.md +0 -0
  90. package/src/services/permissions/index.ts +0 -0
  91. package/src/services/progress-events.js +0 -0
  92. package/src/services/progress-row/base.js +0 -0
  93. package/src/services/progress-row/rows/.indexignore +0 -0
  94. package/src/services/progress-row/rows/content-card.js +0 -0
  95. package/src/services/progress-row/rows/method-card.js +0 -0
  96. package/src/services/progress-row/rows/playlist-card.js +0 -0
  97. package/src/services/railcontent.js +0 -0
  98. package/src/services/recommendations.js +0 -0
  99. package/src/services/reporting/README.md +0 -0
  100. package/src/services/reporting/reporting.ts +0 -0
  101. package/src/services/reporting/types.ts +0 -0
  102. package/src/services/sanity.js +25 -52
  103. package/src/services/sentry/.indexignore +0 -0
  104. package/src/services/sentry/index.ts +0 -0
  105. package/src/services/sync/.indexignore +0 -0
  106. package/src/services/sync/adapters/factory.ts +0 -0
  107. package/src/services/sync/adapters/lokijs.ts +0 -0
  108. package/src/services/sync/adapters/sqlite.ts +0 -0
  109. package/src/services/sync/context/index.ts +0 -0
  110. package/src/services/sync/context/providers/base.ts +0 -0
  111. package/src/services/sync/context/providers/connectivity.ts +0 -0
  112. package/src/services/sync/context/providers/durability.ts +0 -0
  113. package/src/services/sync/context/providers/index.ts +0 -0
  114. package/src/services/sync/context/providers/session.ts +0 -0
  115. package/src/services/sync/context/providers/tabs.ts +0 -0
  116. package/src/services/sync/context/providers/visibility.ts +0 -0
  117. package/src/services/sync/database/factory.ts +0 -0
  118. package/src/services/sync/effects/index.ts +0 -0
  119. package/src/services/sync/effects/logout-warning.ts +0 -0
  120. package/src/services/sync/errors/boundary.ts +0 -0
  121. package/src/services/sync/errors/index.ts +0 -0
  122. package/src/services/sync/errors/validators.ts +0 -0
  123. package/src/services/sync/fetch.ts +0 -0
  124. package/src/services/sync/index.ts +0 -0
  125. package/src/services/sync/manager.ts +0 -0
  126. package/src/services/sync/models/Base.ts +0 -0
  127. package/src/services/sync/models/ContentLike.ts +0 -0
  128. package/src/services/sync/models/ContentProgress.ts +0 -0
  129. package/src/services/sync/models/Practice.ts +0 -0
  130. package/src/services/sync/models/PracticeDayNote.ts +0 -0
  131. package/src/services/sync/models/UserAwardProgress.ts +0 -0
  132. package/src/services/sync/models/index.ts +0 -0
  133. package/src/services/sync/repositories/base.ts +0 -0
  134. package/src/services/sync/repositories/content-likes.ts +0 -0
  135. package/src/services/sync/repositories/content-progress.ts +0 -0
  136. package/src/services/sync/repositories/index.ts +0 -0
  137. package/src/services/sync/repositories/practice-day-notes.ts +0 -0
  138. package/src/services/sync/repositories/practices.ts +0 -0
  139. package/src/services/sync/repositories/user-award-progress.ts +0 -0
  140. package/src/services/sync/repository-proxy.ts +0 -0
  141. package/src/services/sync/resolver.ts +0 -0
  142. package/src/services/sync/retry.ts +0 -0
  143. package/src/services/sync/run-scope.ts +0 -0
  144. package/src/services/sync/schema/index.ts +0 -0
  145. package/src/services/sync/serializers/index.ts +0 -0
  146. package/src/services/sync/serializers/model.ts +0 -0
  147. package/src/services/sync/serializers/raw.ts +0 -0
  148. package/src/services/sync/store/index.ts +0 -0
  149. package/src/services/sync/store/push-coalescer.ts +0 -0
  150. package/src/services/sync/store-configs.ts +0 -0
  151. package/src/services/sync/strategies/base.ts +0 -0
  152. package/src/services/sync/strategies/index.ts +0 -0
  153. package/src/services/sync/strategies/initial.ts +0 -0
  154. package/src/services/sync/strategies/polling.ts +0 -0
  155. package/src/services/sync/telemetry/flood-prevention.ts +0 -0
  156. package/src/services/sync/telemetry/index.ts +0 -0
  157. package/src/services/sync/telemetry/sampling.ts +0 -0
  158. package/src/services/sync/utils/event-emitter.ts +0 -0
  159. package/src/services/sync/utils/index.ts +0 -0
  160. package/src/services/sync/utils/throttle.ts +0 -0
  161. package/src/services/sync/utils/timers.ts +0 -0
  162. package/src/services/types.js +0 -0
  163. package/src/services/urlBuilder.ts +0 -0
  164. package/src/services/user/account.ts +5 -1
  165. package/src/services/user/chat.js +0 -0
  166. package/src/services/user/interests.js +0 -0
  167. package/src/services/user/management.js +0 -0
  168. package/src/services/user/memberships.ts +0 -0
  169. package/src/services/user/notifications.js +0 -0
  170. package/src/services/user/onboarding.ts +0 -0
  171. package/src/services/user/payments.ts +0 -0
  172. package/src/services/user/permissions.js +0 -0
  173. package/src/services/user/profile.js +0 -0
  174. package/src/services/user/sessions.js +0 -0
  175. package/src/services/user/types.d.ts +0 -0
  176. package/src/services/user/types.js +0 -0
  177. package/src/services/user/user-management-system.js +0 -0
  178. package/src/services/userActivity.js +0 -0
  179. package/test/HttpClient.test.js +0 -0
  180. package/test/awards/award-alacarte-observer.test.js +0 -0
  181. package/test/awards/award-auto-refresh.test.js +0 -0
  182. package/test/awards/award-calculations.test.js +0 -0
  183. package/test/awards/award-certificate-display.test.js +0 -0
  184. package/test/awards/award-collection-edge-cases.test.js +0 -0
  185. package/test/awards/award-collection-filtering.test.js +0 -0
  186. package/test/awards/award-completion-flow.test.js +0 -0
  187. package/test/awards/award-exclusion-handling.test.js +0 -0
  188. package/test/awards/award-multi-lesson.test.js +0 -0
  189. package/test/awards/award-observer-integration.test.js +0 -0
  190. package/test/awards/award-query-messages.test.js +0 -0
  191. package/test/awards/award-user-collection.test.js +0 -0
  192. package/test/awards/duplicate-prevention.test.js +0 -0
  193. package/test/awards/helpers/completion-mock.js +0 -0
  194. package/test/awards/helpers/index.js +0 -0
  195. package/test/awards/helpers/mock-setup.js +0 -0
  196. package/test/awards/helpers/progress-emitter.js +0 -0
  197. package/test/awards/message-generator.test.js +0 -0
  198. package/test/content.test.js +0 -0
  199. package/test/contentLikes.test.js +0 -0
  200. package/test/contentProgress.test.js +0 -0
  201. package/test/dataContext.test.js +0 -0
  202. package/test/forum.test.js +0 -0
  203. package/test/imageSRCBuilder.test.js +0 -0
  204. package/test/imageSRCVerify.test.js +0 -0
  205. package/test/initializeTests.js +0 -0
  206. package/test/learningPaths.test.js +0 -0
  207. package/test/lib/__snapshots__/filter.test.ts.snap +0 -0
  208. package/test/lib/filter.test.ts +0 -0
  209. package/test/lib/lastUpdated.test.js +0 -0
  210. package/test/lib/query.test.ts +0 -0
  211. package/test/live/contentProgressLive.test.js +0 -0
  212. package/test/live/railcontentLive.test.js +0 -0
  213. package/test/localStorageMock.js +0 -0
  214. package/test/log.js +0 -0
  215. package/test/logout.test.js +199 -0
  216. package/test/mockData/award-definitions.js +0 -0
  217. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  218. package/test/mockData/mockData_progress_content.json +0 -0
  219. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  220. package/test/mockData/mockData_user_practices.json +0 -0
  221. package/test/notifications.test.js +0 -0
  222. package/test/progressRows.test.js +0 -0
  223. package/test/reporting.test.js +132 -0
  224. package/test/sanityQueryService.test.js +0 -0
  225. package/test/streakMessage.test.js +0 -0
  226. package/test/sync/adapter.ts +0 -0
  227. package/test/sync/initialize-sync-manager.js +0 -0
  228. package/test/sync/models/award-database-integration.test.js +0 -0
  229. package/test/user/permissions.test.js +0 -0
  230. package/test/userActivity.test.js +0 -0
  231. package/test_owned_navigate.js +74 -0
  232. package/tools/generate-index.cjs +0 -0
  233. package/tsconfig.json +17 -0
@@ -0,0 +1,199 @@
1
+ import { initializeService, globalConfig } from '../src/services/config.js'
2
+ import { LocalStorageMock } from './localStorageMock'
3
+ import { logout } from '../src/services/user/sessions.js'
4
+ import { clearAllCachedData } from '../src/services/dataContext.js'
5
+ import { USER_PIN_PROGRESS_KEY } from '../src/services/progress-row/base.js'
6
+
7
+ describe('logout cache clearing', function () {
8
+ let mockLocalStorage
9
+
10
+ beforeEach(() => {
11
+ // Create fresh localStorage for each test
12
+ mockLocalStorage = new LocalStorageMock()
13
+
14
+ // Simple initialization without SyncManager
15
+ const config = {
16
+ sanityConfig: {
17
+ token: 'test-token',
18
+ projectId: 'test-project',
19
+ dataset: 'test',
20
+ useCachedAPI: true,
21
+ version: '2021-06-07',
22
+ debug: false,
23
+ useDummyRailContentMethods: true,
24
+ },
25
+ railcontentConfig: {
26
+ baseUrl: 'https://test.musora.com',
27
+ token: 'test-token',
28
+ userId: 123,
29
+ authToken: 'test-token',
30
+ },
31
+ sessionConfig: { token: 'test-token', userId: 123, authToken: 'test-token' },
32
+ baseUrl: 'https://test.musora.com',
33
+ localStorage: mockLocalStorage,
34
+ isMA: false,
35
+ }
36
+ initializeService(config)
37
+ })
38
+
39
+ afterEach(() => {
40
+ jest.restoreAllMocks()
41
+ })
42
+
43
+ test('clearAllCachedData does NOT remove user_pin_progress_row keys', async () => {
44
+ const localStorage = globalConfig.localStorage
45
+ const userId = 123
46
+ const userPinKey = `user_pin_progress_row_${userId}`
47
+
48
+ // Add pinned progress data for user 123
49
+ localStorage.setItem(userPinKey, JSON.stringify({
50
+ drumeo: { id: 123, progressType: 'content', pinnedAt: '2025-01-01' },
51
+ pianote: { id: 456, progressType: 'playlist', pinnedAt: '2025-01-02' }
52
+ }))
53
+
54
+ // Add pinned progress data for another user
55
+ localStorage.setItem('user_pin_progress_row_456', JSON.stringify({
56
+ drumeo: { id: 789, progressType: 'content' }
57
+ }))
58
+
59
+ // Verify data exists
60
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
61
+ expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
62
+
63
+ // Clear cached data
64
+ await clearAllCachedData()
65
+
66
+ // Verify user pin progress rows are NOT removed
67
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
68
+ expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
69
+ })
70
+
71
+ test('clearAllCachedData removes dataContext keys', async () => {
72
+ const localStorage = globalConfig.localStorage
73
+
74
+ // Add some dataContext keys
75
+ localStorage.setItem('dataContext_test1', JSON.stringify({ data: 'test1' }))
76
+ localStorage.setItem('dataContext_test2', JSON.stringify({ data: 'test2' }))
77
+ localStorage.setItem('other_key', 'should_remain')
78
+
79
+ // Verify data exists
80
+ expect(localStorage.getItem('dataContext_test1')).not.toBeNull()
81
+ expect(localStorage.getItem('dataContext_test2')).not.toBeNull()
82
+ expect(localStorage.getItem('other_key')).not.toBeNull()
83
+
84
+ // Clear all cached data
85
+ await clearAllCachedData()
86
+
87
+ // Verify dataContext keys were removed but other key remains
88
+ expect(localStorage.getItem('dataContext_test1')).toBeNull()
89
+ expect(localStorage.getItem('dataContext_test2')).toBeNull()
90
+ expect(localStorage.getItem('other_key')).toBe('should_remain')
91
+ })
92
+
93
+ test('clearAllCachedData removes ONLY dataContext keys', async () => {
94
+ const localStorage = globalConfig.localStorage
95
+ const userId = 123
96
+ const userPinKey = `user_pin_progress_row_${userId}`
97
+
98
+ // Add various cached data
99
+ localStorage.setItem(userPinKey, JSON.stringify({ drumeo: { id: 123 } }))
100
+ localStorage.setItem('dataContext_progress', JSON.stringify({ data: 'progress' }))
101
+ localStorage.setItem('dataContext_likes', JSON.stringify({ data: 'likes' }))
102
+ localStorage.setItem('unrelated_key', 'preserved')
103
+ localStorage.setItem('user_pin_progress_row_999', JSON.stringify({ drumeo: { id: 999 } }))
104
+
105
+ // Verify all data exists
106
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
107
+ expect(localStorage.getItem('dataContext_progress')).not.toBeNull()
108
+ expect(localStorage.getItem('dataContext_likes')).not.toBeNull()
109
+ expect(localStorage.getItem('unrelated_key')).not.toBeNull()
110
+
111
+ // Clear cached data
112
+ await clearAllCachedData()
113
+
114
+ // Verify only dataContext keys were cleared
115
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
116
+ expect(localStorage.getItem('dataContext_progress')).toBeNull()
117
+ expect(localStorage.getItem('dataContext_likes')).toBeNull()
118
+ expect(localStorage.getItem('unrelated_key')).toBe('preserved')
119
+ // All user pinned data should remain
120
+ expect(localStorage.getItem('user_pin_progress_row_999')).not.toBeNull()
121
+ })
122
+
123
+ test('logout calls clearAllCachedData', async () => {
124
+ // Mock fetch to prevent actual API call
125
+ global.fetch = jest.fn().mockResolvedValue({
126
+ ok: true,
127
+ json: async () => ({ success: true })
128
+ })
129
+
130
+ const localStorage = globalConfig.localStorage
131
+ const userId = 123
132
+ const userPinKey = `user_pin_progress_row_${userId}`
133
+
134
+ // Add cached data
135
+ localStorage.setItem(userPinKey, JSON.stringify({ drumeo: { id: 123 } }))
136
+ localStorage.setItem('dataContext_test', JSON.stringify({ data: 'test' }))
137
+ localStorage.setItem('user_pin_progress_row_456', JSON.stringify({ drumeo: { id: 456 } }))
138
+
139
+ // Verify data exists before logout
140
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
141
+ expect(localStorage.getItem('dataContext_test')).not.toBeNull()
142
+
143
+ // Call logout
144
+ await logout()
145
+
146
+ // Verify only dataContext was cleared, pinned progress remains
147
+ expect(localStorage.getItem(userPinKey)).not.toBeNull()
148
+ expect(localStorage.getItem('dataContext_test')).toBeNull()
149
+ expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
150
+
151
+ // Verify fetch was called (logout API endpoint)
152
+ expect(global.fetch).toHaveBeenCalledWith(
153
+ expect.stringContaining('/api/user-management-system/v1/sessions'),
154
+ expect.objectContaining({
155
+ method: 'DELETE'
156
+ })
157
+ )
158
+ })
159
+
160
+ test('logout prevents cross-account data leakage', async () => {
161
+ // Mock fetch to prevent actual API call
162
+ global.fetch = jest.fn().mockResolvedValue({
163
+ ok: true,
164
+ json: async () => ({ success: true })
165
+ })
166
+
167
+ const localStorage = globalConfig.localStorage
168
+ const user1Id = 123
169
+ const user2Id = 456
170
+ const user1PinKey = `user_pin_progress_row_${user1Id}`
171
+ const user2PinKey = `user_pin_progress_row_${user2Id}`
172
+
173
+ // Simulate user 1 (id: 123) with pinned content and dataContext
174
+ localStorage.setItem(user1PinKey, JSON.stringify({
175
+ drumeo: { id: 12345, progressType: 'content', pinnedAt: '2025-01-14' }
176
+ }))
177
+ localStorage.setItem('dataContext_progress', JSON.stringify({ userId: 1, data: 'user1' }))
178
+
179
+ // Simulate user 2 (id: 456) already has pinned content
180
+ localStorage.setItem(user2PinKey, JSON.stringify({
181
+ drumeo: { id: 67890, progressType: 'content', pinnedAt: '2025-01-13' }
182
+ }))
183
+
184
+ // User 1 logs out
185
+ await logout()
186
+
187
+ // Verify dataContext is cleared (prevents cross-account leakage)
188
+ expect(localStorage.getItem('dataContext_progress')).toBeNull()
189
+
190
+ // Verify user 1's pinned progress remains (user-specific key)
191
+ expect(localStorage.getItem(user1PinKey)).not.toBeNull()
192
+
193
+ // Verify user 2's pinned content is NOT affected by user 1's logout
194
+ const user2PinnedData = localStorage.getItem(user2PinKey)
195
+ expect(user2PinnedData).not.toBeNull()
196
+ const user2Data = JSON.parse(user2PinnedData)
197
+ expect(user2Data.drumeo.id).toBe(67890)
198
+ })
199
+ })
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Tests for Reporting Service
3
+ *
4
+ * Note: These tests use mocked HTTP calls. For integration tests,
5
+ * ensure the backend API is running at the configured baseUrl.
6
+ */
7
+
8
+ import { initializeTestService } from './initializeTests.js'
9
+ import {
10
+ reportContent,
11
+ reportForumPost,
12
+ submitReport
13
+ } from '../src/services/reporting/reporting.ts'
14
+
15
+ // Mock HttpClient to avoid actual API calls in tests
16
+ jest.mock('../src/infrastructure/http/HttpClient', () => {
17
+ return {
18
+ HttpClient: jest.fn().mockImplementation(() => ({
19
+ post: jest.fn().mockResolvedValue({
20
+ report_id: 12345,
21
+ message: 'Report submitted successfully',
22
+ is_duplicate: false
23
+ })
24
+ }))
25
+ }
26
+ })
27
+
28
+ describe('Reporting Service', () => {
29
+ beforeEach(() => {
30
+ initializeTestService()
31
+ })
32
+
33
+ describe('reportContent', () => {
34
+ test('should submit a content report with video_not_working category', async () => {
35
+ const result = await reportContent({
36
+ contentId: 123,
37
+ category: 'video_not_working',
38
+ description: 'Video freezes at 2:30'
39
+ })
40
+
41
+ expect(result).toBeDefined()
42
+ expect(result.report_id).toBe(12345)
43
+ expect(result.message).toBe('Report submitted successfully')
44
+ expect(result.is_duplicate).toBe(false)
45
+ })
46
+
47
+ test('should submit a content report without description', async () => {
48
+ const result = await reportContent({
49
+ contentId: 456,
50
+ category: 'incorrect_content'
51
+ })
52
+
53
+ expect(result).toBeDefined()
54
+ expect(result.report_id).toBeDefined()
55
+ })
56
+
57
+ test('should submit a content report with other category and description', async () => {
58
+ const result = await reportContent({
59
+ contentId: 789,
60
+ category: 'other',
61
+ description: 'Audio quality is very poor'
62
+ })
63
+
64
+ expect(result).toBeDefined()
65
+ expect(result.report_id).toBeDefined()
66
+ expect(result.message).toBeDefined()
67
+ })
68
+ })
69
+
70
+ describe('reportForumPost', () => {
71
+ test('should submit a forum post report with required brand', async () => {
72
+ const result = await reportForumPost({
73
+ postId: 555,
74
+ brand: 'drumeo',
75
+ category: 'spam'
76
+ })
77
+
78
+ expect(result).toBeDefined()
79
+ expect(result.report_id).toBeDefined()
80
+ })
81
+
82
+ test('should submit a forum post report with description', async () => {
83
+ const result = await reportForumPost({
84
+ postId: 666,
85
+ brand: 'pianote',
86
+ category: 'harassment',
87
+ description: 'User is harassing other members'
88
+ })
89
+
90
+ expect(result).toBeDefined()
91
+ expect(result.report_id).toBeDefined()
92
+ expect(result.message).toBeDefined()
93
+ })
94
+ })
95
+
96
+ describe('submitReport', () => {
97
+ test('should submit a generic report for content', async () => {
98
+ const result = await submitReport({
99
+ reportableType: 'content',
100
+ reportableId: 999,
101
+ category: 'technical_issue',
102
+ description: 'Page not loading'
103
+ })
104
+
105
+ expect(result).toBeDefined()
106
+ expect(result.report_id).toBeDefined()
107
+ })
108
+
109
+ test('should submit a generic report for playlist', async () => {
110
+ const result = await submitReport({
111
+ reportableType: 'playlist',
112
+ reportableId: 777,
113
+ category: 'incorrect_content'
114
+ })
115
+
116
+ expect(result).toBeDefined()
117
+ expect(result.report_id).toBeDefined()
118
+ })
119
+
120
+ test('should submit a generic report for comment', async () => {
121
+ const result = await submitReport({
122
+ reportableType: 'comment',
123
+ reportableId: 888,
124
+ category: 'inappropriate',
125
+ description: 'Comment contains offensive language'
126
+ })
127
+
128
+ expect(result).toBeDefined()
129
+ expect(result.report_id).toBeDefined()
130
+ })
131
+ })
132
+ })
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,74 @@
1
+ import { initializeService } from './src/services/config.js'
2
+ import { getOwnedContent } from './src/services/content.js'
3
+ import dotenv from 'dotenv'
4
+
5
+ dotenv.config()
6
+
7
+ // Initialize service with credentials from .env
8
+ initializeService({
9
+ sanityConfig: {
10
+ token: process.env.SANITY_API_TOKEN,
11
+ projectId: process.env.SANITY_PROJECT_ID,
12
+ dataset: process.env.SANITY_DATASET,
13
+ version: '2023-01-01',
14
+ },
15
+ railcontentConfig: {
16
+ email: process.env.RAILCONTENT_EMAIL,
17
+ password: process.env.RAILCONTENT_PASSWORD,
18
+ userId: parseInt(process.env.RAILCONTENT_USER_ID),
19
+ baseUrl: process.env.RAILCONTENT_BASE_URL,
20
+ },
21
+ localStorage: null,
22
+ isMA: false,
23
+ })
24
+
25
+ async function testOwnedContentNavigateTo() {
26
+ console.log('\n๐Ÿงช Testing owned content with navigateTo...\n')
27
+
28
+ try {
29
+ // Test for drumeo lessons
30
+ const result = await getOwnedContent('drumeo', {
31
+ type: ['course', 'guided-course', 'pack'],
32
+ limit: 5,
33
+ })
34
+
35
+ console.log('๐Ÿ“Š Results:', {
36
+ total: result.total,
37
+ count: result.entity?.length || 0,
38
+ })
39
+
40
+ if (result.entity && result.entity.length > 0) {
41
+ result.entity.forEach((item, index) => {
42
+ console.log(`\n๐Ÿ“ฆ Item ${index + 1}:`)
43
+ console.log(` - ID: ${item.id}`)
44
+ console.log(` - Title: ${item.title}`)
45
+ console.log(` - Type: ${item.type}`)
46
+ console.log(` - Has children: ${item.children ? 'โœ… YES' : 'โŒ NO'}`)
47
+ console.log(` - Children count: ${item.children?.length || 0}`)
48
+ console.log(` - Has navigateTo: ${item.navigateTo ? 'โœ… YES' : 'โŒ NO'}`)
49
+
50
+ if (item.navigateTo) {
51
+ console.log(` - NavigateTo ID: ${item.navigateTo.id}`)
52
+ console.log(` - NavigateTo Type: ${item.navigateTo.type}`)
53
+ console.log(` - NavigateTo Title: ${item.children?.find(c => c.id === item.navigateTo.id)?.title || 'N/A'}`)
54
+ }
55
+
56
+ if (item.progressPercentage !== undefined) {
57
+ console.log(` - Progress: ${item.progressPercentage}%`)
58
+ }
59
+ if (item.progressStatus) {
60
+ console.log(` - Status: ${item.progressStatus}`)
61
+ }
62
+ })
63
+ } else {
64
+ console.log('\nโš ๏ธ No owned content found for this user')
65
+ }
66
+
67
+ console.log('\nโœ… Test completed successfully!')
68
+ } catch (error) {
69
+ console.error('\nโŒ Test failed:', error.message)
70
+ console.error(error.stack)
71
+ }
72
+ }
73
+
74
+ testOwnedContentNavigateTo()
File without changes
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "skipLibCheck": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "strict": false,
10
+ "noImplicitAny": false,
11
+ "resolveJsonModule": true,
12
+ "allowJs": true,
13
+ "checkJs": false
14
+ },
15
+ "include": ["src/**/*", "test/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }