musora-content-services 2.94.8 → 2.95.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.
- package/.claude/settings.local.json +18 -0
- package/CHANGELOG.md +18 -0
- package/CLAUDE.md +408 -0
- package/babel.config.cjs +10 -0
- package/jsdoc.json +2 -1
- package/package.json +2 -2
- package/src/constants/award-assets.js +35 -0
- package/src/filterBuilder.js +7 -2
- package/src/index.d.ts +26 -5
- package/src/index.js +26 -5
- package/src/services/awards/award-callbacks.js +165 -0
- package/src/services/awards/award-query.js +495 -0
- package/src/services/awards/internal/.indexignore +1 -0
- package/src/services/awards/internal/award-definitions.js +239 -0
- package/src/services/awards/internal/award-events.js +102 -0
- package/src/services/awards/internal/award-manager.js +162 -0
- package/src/services/awards/internal/certificate-builder.js +66 -0
- package/src/services/awards/internal/completion-data-generator.js +84 -0
- package/src/services/awards/internal/content-progress-observer.js +137 -0
- package/src/services/awards/internal/image-utils.js +62 -0
- package/src/services/awards/internal/message-generator.js +17 -0
- package/src/services/awards/internal/types.js +5 -0
- package/src/services/awards/types.d.ts +79 -0
- package/src/services/awards/types.js +101 -0
- package/src/services/config.js +24 -4
- package/src/services/content-org/learning-paths.ts +19 -15
- package/src/services/gamification/awards.ts +114 -83
- package/src/services/progress-events.js +58 -0
- package/src/services/progress-row/method-card.js +20 -5
- package/src/services/sanity.js +1 -1
- package/src/services/sync/fetch.ts +10 -2
- package/src/services/sync/manager.ts +6 -0
- package/src/services/sync/models/ContentProgress.ts +5 -6
- package/src/services/sync/models/UserAwardProgress.ts +55 -0
- package/src/services/sync/models/index.ts +1 -0
- package/src/services/sync/repositories/content-progress.ts +47 -25
- package/src/services/sync/repositories/index.ts +1 -0
- package/src/services/sync/repositories/practices.ts +16 -1
- package/src/services/sync/repositories/user-award-progress.ts +133 -0
- package/src/services/sync/repository-proxy.ts +6 -0
- package/src/services/sync/retry.ts +12 -11
- package/src/services/sync/schema/index.ts +18 -3
- package/src/services/sync/store/index.ts +53 -8
- package/src/services/sync/store/push-coalescer.ts +3 -3
- package/src/services/sync/store-configs.ts +7 -1
- package/src/services/userActivity.js +0 -1
- package/test/HttpClient.test.js +6 -6
- package/test/awards/award-alacarte-observer.test.js +196 -0
- package/test/awards/award-auto-refresh.test.js +83 -0
- package/test/awards/award-calculations.test.js +33 -0
- package/test/awards/award-certificate-display.test.js +328 -0
- package/test/awards/award-collection-edge-cases.test.js +210 -0
- package/test/awards/award-collection-filtering.test.js +285 -0
- package/test/awards/award-completion-flow.test.js +213 -0
- package/test/awards/award-exclusion-handling.test.js +273 -0
- package/test/awards/award-multi-lesson.test.js +241 -0
- package/test/awards/award-observer-integration.test.js +325 -0
- package/test/awards/award-query-messages.test.js +438 -0
- package/test/awards/award-user-collection.test.js +412 -0
- package/test/awards/duplicate-prevention.test.js +118 -0
- package/test/awards/helpers/completion-mock.js +54 -0
- package/test/awards/helpers/index.js +3 -0
- package/test/awards/helpers/mock-setup.js +69 -0
- package/test/awards/helpers/progress-emitter.js +39 -0
- package/test/awards/message-generator.test.js +162 -0
- package/test/initializeTests.js +6 -0
- package/test/mockData/award-definitions.js +171 -0
- package/test/sync/models/award-database-integration.test.js +519 -0
- package/tools/generate-index.cjs +9 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { mockAwardDefinitions } from '../mockData/award-definitions'
|
|
2
|
+
|
|
3
|
+
jest.mock('../../src/services/sanity', () => ({
|
|
4
|
+
default: {
|
|
5
|
+
fetch: jest.fn()
|
|
6
|
+
},
|
|
7
|
+
fetchSanity: jest.fn()
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
jest.mock('../../src/services/sync/repository-proxy', () => {
|
|
11
|
+
const mockFns = {
|
|
12
|
+
userAwardProgress: {
|
|
13
|
+
getAll: jest.fn(),
|
|
14
|
+
getByAwardId: jest.fn(),
|
|
15
|
+
getAwardsForContent: jest.fn()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { default: mockFns, ...mockFns }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
import sanityClient, { fetchSanity } from '../../src/services/sanity'
|
|
22
|
+
import db from '../../src/services/sync/repository-proxy'
|
|
23
|
+
import { awardDefinitions } from '../../src/services/awards/internal/award-definitions'
|
|
24
|
+
import { getCompletedAwards, getContentAwards, getInProgressAwards } from '../../src/services/awards/award-query'
|
|
25
|
+
|
|
26
|
+
describe('Award Query Message Generation', () => {
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
jest.clearAllMocks()
|
|
29
|
+
|
|
30
|
+
sanityClient.fetch = jest.fn().mockResolvedValue(mockAwardDefinitions)
|
|
31
|
+
fetchSanity.mockResolvedValue(mockAwardDefinitions)
|
|
32
|
+
|
|
33
|
+
await awardDefinitions.refresh()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
awardDefinitions.clear()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('getCompletedAwards', () => {
|
|
41
|
+
test('includes message in completionData for guided course awards', async () => {
|
|
42
|
+
const mockProgress = [{
|
|
43
|
+
award_id: 'award-1',
|
|
44
|
+
progress_percentage: 100,
|
|
45
|
+
completed_at: 1672531200,
|
|
46
|
+
completion_data: {
|
|
47
|
+
content_title: 'Blues Foundations',
|
|
48
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
49
|
+
days_user_practiced: 14,
|
|
50
|
+
practice_minutes: 180
|
|
51
|
+
}
|
|
52
|
+
}]
|
|
53
|
+
|
|
54
|
+
const mockDefinition = {
|
|
55
|
+
_id: 'award-1',
|
|
56
|
+
name: 'Blues Master',
|
|
57
|
+
badge: 'https://example.com/badge.png',
|
|
58
|
+
award: 'certificate',
|
|
59
|
+
brand: 'drumeo',
|
|
60
|
+
instructor_name: 'Mike Johnston',
|
|
61
|
+
content_type: 'guided-course'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
65
|
+
data: mockProgress
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
69
|
+
|
|
70
|
+
const awards = await getCompletedAwards()
|
|
71
|
+
|
|
72
|
+
expect(awards).toHaveLength(1)
|
|
73
|
+
expect(awards[0].completionData).toHaveProperty('message')
|
|
74
|
+
expect(awards[0].completionData.message).toContain('Blues Foundations')
|
|
75
|
+
expect(awards[0].completionData.message).toContain('14 days')
|
|
76
|
+
expect(awards[0].completionData.message).toContain('180 minutes')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('includes message in completionData for learning path awards', async () => {
|
|
80
|
+
const mockProgress = [{
|
|
81
|
+
award_id: 'award-2',
|
|
82
|
+
progress_percentage: 100,
|
|
83
|
+
completed_at: 1672531200,
|
|
84
|
+
completion_data: {
|
|
85
|
+
content_title: 'Jazz Essentials',
|
|
86
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
87
|
+
days_user_practiced: 30,
|
|
88
|
+
practice_minutes: 450
|
|
89
|
+
}
|
|
90
|
+
}]
|
|
91
|
+
|
|
92
|
+
const mockDefinition = {
|
|
93
|
+
_id: 'award-2',
|
|
94
|
+
name: 'Jazz Journey',
|
|
95
|
+
badge: 'https://example.com/badge2.png',
|
|
96
|
+
award: 'certificate',
|
|
97
|
+
brand: 'pianote',
|
|
98
|
+
instructor_name: 'Lisa Witt',
|
|
99
|
+
content_type: 'learning-path-v2'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
103
|
+
data: mockProgress
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
107
|
+
|
|
108
|
+
const awards = await getCompletedAwards()
|
|
109
|
+
|
|
110
|
+
expect(awards).toHaveLength(1)
|
|
111
|
+
expect(awards[0].completionData).toHaveProperty('message')
|
|
112
|
+
expect(awards[0].completionData.message).toContain('Jazz Essentials')
|
|
113
|
+
expect(awards[0].completionData.message).toContain('30 days')
|
|
114
|
+
expect(awards[0].completionData.message).toContain('450 minutes')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('uses correct practice statistics in message', async () => {
|
|
118
|
+
const mockProgress = [{
|
|
119
|
+
award_id: 'award-3',
|
|
120
|
+
progress_percentage: 100,
|
|
121
|
+
completed_at: 1672531200,
|
|
122
|
+
completion_data: {
|
|
123
|
+
content_title: 'Rock Foundations',
|
|
124
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
125
|
+
days_user_practiced: 7,
|
|
126
|
+
practice_minutes: 90
|
|
127
|
+
}
|
|
128
|
+
}]
|
|
129
|
+
|
|
130
|
+
const mockDefinition = {
|
|
131
|
+
_id: 'award-3',
|
|
132
|
+
name: 'Rock Star',
|
|
133
|
+
badge: 'https://example.com/badge3.png',
|
|
134
|
+
award: 'badge',
|
|
135
|
+
brand: 'guitareo',
|
|
136
|
+
instructor_name: 'Steve Stine',
|
|
137
|
+
content_type: 'guided-course'
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
141
|
+
data: mockProgress
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
145
|
+
|
|
146
|
+
const awards = await getCompletedAwards()
|
|
147
|
+
|
|
148
|
+
expect(awards[0].completionData.message).toContain('7 days')
|
|
149
|
+
expect(awards[0].completionData.message).toContain('90 minutes')
|
|
150
|
+
expect(awards[0].completionData.message).not.toContain('180 minutes')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('handles awards with zero practice minutes', async () => {
|
|
154
|
+
const mockProgress = [{
|
|
155
|
+
award_id: 'award-4',
|
|
156
|
+
progress_percentage: 100,
|
|
157
|
+
completed_at: 1672531200,
|
|
158
|
+
completion_data: {
|
|
159
|
+
content_title: 'Quick Course',
|
|
160
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
161
|
+
days_user_practiced: 1,
|
|
162
|
+
practice_minutes: 0
|
|
163
|
+
}
|
|
164
|
+
}]
|
|
165
|
+
|
|
166
|
+
const mockDefinition = {
|
|
167
|
+
_id: 'award-4',
|
|
168
|
+
name: 'Quick Learner',
|
|
169
|
+
badge: 'https://example.com/badge4.png',
|
|
170
|
+
award: 'badge',
|
|
171
|
+
brand: 'singeo',
|
|
172
|
+
instructor_name: 'Camille van Niekerk',
|
|
173
|
+
content_type: 'guided-course'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
177
|
+
data: mockProgress
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
181
|
+
|
|
182
|
+
const awards = await getCompletedAwards()
|
|
183
|
+
|
|
184
|
+
expect(awards[0].completionData.message).toContain('0 minutes')
|
|
185
|
+
expect(awards[0].completionData.message).toContain('1 day')
|
|
186
|
+
expect(awards[0].completionData.message).not.toContain('1 days')
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('getContentAwards', () => {
|
|
191
|
+
test('includes message in completionData when award is completed', async () => {
|
|
192
|
+
const mockDefinitions = [{
|
|
193
|
+
_id: 'award-5',
|
|
194
|
+
name: 'Content Award',
|
|
195
|
+
badge: 'https://example.com/badge5.png',
|
|
196
|
+
award: 'certificate',
|
|
197
|
+
brand: 'drumeo',
|
|
198
|
+
instructor_name: 'Jared Falk',
|
|
199
|
+
content_type: 'guided-course'
|
|
200
|
+
}]
|
|
201
|
+
|
|
202
|
+
const mockProgress = new Map()
|
|
203
|
+
mockProgress.set('award-5', {
|
|
204
|
+
progress_percentage: 100,
|
|
205
|
+
isCompleted: true,
|
|
206
|
+
completed_at: 1672531200,
|
|
207
|
+
completion_data: {
|
|
208
|
+
content_title: 'Advanced Techniques',
|
|
209
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
210
|
+
days_user_practiced: 21,
|
|
211
|
+
practice_minutes: 300
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
jest.spyOn(db.userAwardProgress, 'getAwardsForContent').mockResolvedValue({
|
|
216
|
+
definitions: mockDefinitions,
|
|
217
|
+
progress: mockProgress
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
jest.spyOn(awardDefinitions, 'hasAwards').mockResolvedValue(true)
|
|
221
|
+
|
|
222
|
+
const result = await getContentAwards(12345)
|
|
223
|
+
|
|
224
|
+
expect(result.hasAwards).toBe(true)
|
|
225
|
+
expect(result.awards).toHaveLength(1)
|
|
226
|
+
expect(result.awards[0].completionData).toHaveProperty('message')
|
|
227
|
+
expect(result.awards[0].completionData.message).toContain('21 days')
|
|
228
|
+
expect(result.awards[0].completionData.message).toContain('300 minutes')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('handles awards without completion data gracefully', async () => {
|
|
232
|
+
const mockDefinitions = [{
|
|
233
|
+
_id: 'award-6',
|
|
234
|
+
name: 'In Progress Award',
|
|
235
|
+
badge: 'https://example.com/badge6.png',
|
|
236
|
+
award: 'badge',
|
|
237
|
+
brand: 'pianote',
|
|
238
|
+
instructor_name: 'Lisa Witt',
|
|
239
|
+
content_type: 'learning-path-v2'
|
|
240
|
+
}]
|
|
241
|
+
|
|
242
|
+
const mockProgress = new Map()
|
|
243
|
+
mockProgress.set('award-6', {
|
|
244
|
+
progress_percentage: 50,
|
|
245
|
+
isCompleted: false,
|
|
246
|
+
completed_at: null,
|
|
247
|
+
completion_data: null
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
jest.spyOn(db.userAwardProgress, 'getAwardsForContent').mockResolvedValue({
|
|
251
|
+
definitions: mockDefinitions,
|
|
252
|
+
progress: mockProgress
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
jest.spyOn(awardDefinitions, 'hasAwards').mockResolvedValue(true)
|
|
256
|
+
|
|
257
|
+
const result = await getContentAwards(12345)
|
|
258
|
+
|
|
259
|
+
expect(result.hasAwards).toBe(true)
|
|
260
|
+
expect(result.awards).toHaveLength(1)
|
|
261
|
+
expect(result.awards[0].completionData).toBeNull()
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
describe('getInProgressAwards', () => {
|
|
266
|
+
test('handles in-progress awards with partial completion data', async () => {
|
|
267
|
+
const mockProgress = [{
|
|
268
|
+
award_id: 'award-7',
|
|
269
|
+
progress_percentage: 75,
|
|
270
|
+
completed_at: null,
|
|
271
|
+
completion_data: {
|
|
272
|
+
content_title: 'Intermediate Skills',
|
|
273
|
+
completed_at: null,
|
|
274
|
+
days_user_practiced: 10,
|
|
275
|
+
practice_minutes: 150
|
|
276
|
+
}
|
|
277
|
+
}]
|
|
278
|
+
|
|
279
|
+
const mockDefinition = {
|
|
280
|
+
_id: 'award-7',
|
|
281
|
+
name: 'Progress Champion',
|
|
282
|
+
badge: 'https://example.com/badge7.png',
|
|
283
|
+
award: 'badge',
|
|
284
|
+
brand: 'guitareo',
|
|
285
|
+
instructor_name: 'Anders Mouridsen',
|
|
286
|
+
content_type: 'guided-course'
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
290
|
+
data: mockProgress
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
294
|
+
|
|
295
|
+
const awards = await getInProgressAwards()
|
|
296
|
+
|
|
297
|
+
expect(awards).toHaveLength(1)
|
|
298
|
+
expect(awards[0].isCompleted).toBe(false)
|
|
299
|
+
expect(awards[0].completionData).toHaveProperty('message')
|
|
300
|
+
expect(awards[0].completionData.message).toContain('10 days')
|
|
301
|
+
expect(awards[0].completionData.message).toContain('150 minutes')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test('handles in-progress awards without completion data', async () => {
|
|
305
|
+
const mockProgress = [{
|
|
306
|
+
award_id: 'award-8',
|
|
307
|
+
progress_percentage: 25,
|
|
308
|
+
completed_at: null,
|
|
309
|
+
completion_data: null
|
|
310
|
+
}]
|
|
311
|
+
|
|
312
|
+
const mockDefinition = {
|
|
313
|
+
_id: 'award-8',
|
|
314
|
+
name: 'Just Started',
|
|
315
|
+
badge: 'https://example.com/badge8.png',
|
|
316
|
+
award: 'badge',
|
|
317
|
+
brand: 'singeo',
|
|
318
|
+
instructor_name: 'Ramsey Voice Studio',
|
|
319
|
+
content_type: 'learning-path-v2'
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
323
|
+
data: mockProgress
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
327
|
+
|
|
328
|
+
const awards = await getInProgressAwards()
|
|
329
|
+
|
|
330
|
+
expect(awards).toHaveLength(1)
|
|
331
|
+
expect(awards[0].completionData).toBeNull()
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
describe('Award type determination', () => {
|
|
336
|
+
test('generates message with content title for guided-course type', async () => {
|
|
337
|
+
const mockProgress = [{
|
|
338
|
+
award_id: 'gc-award',
|
|
339
|
+
progress_percentage: 100,
|
|
340
|
+
completed_at: 1672531200,
|
|
341
|
+
completion_data: {
|
|
342
|
+
content_title: 'Test Course',
|
|
343
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
344
|
+
days_user_practiced: 5,
|
|
345
|
+
practice_minutes: 60
|
|
346
|
+
}
|
|
347
|
+
}]
|
|
348
|
+
|
|
349
|
+
const mockDefinition = {
|
|
350
|
+
_id: 'gc-award',
|
|
351
|
+
name: 'Guided Course Award',
|
|
352
|
+
badge: 'https://example.com/gc-badge.png',
|
|
353
|
+
award: 'certificate',
|
|
354
|
+
brand: 'drumeo',
|
|
355
|
+
instructor_name: 'Test Instructor',
|
|
356
|
+
content_type: 'guided-course'
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
360
|
+
data: mockProgress
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
364
|
+
|
|
365
|
+
const awards = await getCompletedAwards()
|
|
366
|
+
|
|
367
|
+
expect(awards[0].completionData.message).toContain('Test Course')
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
test('generates message with content title for learning-path-v2 type', async () => {
|
|
371
|
+
const mockProgress = [{
|
|
372
|
+
award_id: 'lp-award',
|
|
373
|
+
progress_percentage: 100,
|
|
374
|
+
completed_at: 1672531200,
|
|
375
|
+
completion_data: {
|
|
376
|
+
content_title: 'Test Path',
|
|
377
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
378
|
+
days_user_practiced: 5,
|
|
379
|
+
practice_minutes: 60
|
|
380
|
+
}
|
|
381
|
+
}]
|
|
382
|
+
|
|
383
|
+
const mockDefinition = {
|
|
384
|
+
_id: 'lp-award',
|
|
385
|
+
name: 'Learning Path Award',
|
|
386
|
+
badge: 'https://example.com/lp-badge.png',
|
|
387
|
+
award: 'certificate',
|
|
388
|
+
brand: 'pianote',
|
|
389
|
+
instructor_name: 'Test Instructor',
|
|
390
|
+
content_type: 'learning-path-v2'
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
394
|
+
data: mockProgress
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
398
|
+
|
|
399
|
+
const awards = await getCompletedAwards()
|
|
400
|
+
|
|
401
|
+
expect(awards[0].completionData.message).toContain('Test Path')
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
test('generates message for unknown content types', async () => {
|
|
405
|
+
const mockProgress = [{
|
|
406
|
+
award_id: 'unknown-award',
|
|
407
|
+
progress_percentage: 100,
|
|
408
|
+
completed_at: 1672531200,
|
|
409
|
+
completion_data: {
|
|
410
|
+
content_title: 'Test Content',
|
|
411
|
+
completed_at: '2023-01-01T00:00:00.000Z',
|
|
412
|
+
days_user_practiced: 5,
|
|
413
|
+
practice_minutes: 60
|
|
414
|
+
}
|
|
415
|
+
}]
|
|
416
|
+
|
|
417
|
+
const mockDefinition = {
|
|
418
|
+
_id: 'unknown-award',
|
|
419
|
+
name: 'Unknown Type Award',
|
|
420
|
+
badge: 'https://example.com/unknown-badge.png',
|
|
421
|
+
award: 'certificate',
|
|
422
|
+
brand: 'guitareo',
|
|
423
|
+
instructor_name: 'Test Instructor',
|
|
424
|
+
content_type: 'some-unknown-type'
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
jest.spyOn(db.userAwardProgress, 'getAll').mockResolvedValue({
|
|
428
|
+
data: mockProgress
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
jest.spyOn(awardDefinitions, 'getById').mockResolvedValue(mockDefinition)
|
|
432
|
+
|
|
433
|
+
const awards = await getCompletedAwards()
|
|
434
|
+
|
|
435
|
+
expect(awards[0].completionData.message).toContain('Test Content')
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
})
|