musora-content-services 2.155.7 → 2.155.9
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/.agent/decisions/2026-04-21-tma-239-mcs-sanity-add-a-v2-query-para.md +26 -0
- package/.github/workflows/automated-testing.yml +4 -1
- package/CHANGELOG.md +10 -0
- package/codecov.yml +7 -1
- package/package.json +1 -1
- package/src/services/offline/activities.ts +8 -4
- package/src/services/offline/practices.ts +4 -3
- package/src/services/sanity.js +4 -4
- package/test/unit/{HttpClient.test.js → HttpClient.test.ts} +7 -13
- package/test/unit/awards/{award-alacarte-observer.test.js → award-alacarte-observer.test.ts} +13 -11
- package/test/unit/awards/{award-certificate-display.test.js → award-certificate-display.test.ts} +2 -1
- package/test/unit/awards/{award-collection-edge-cases.test.js → award-collection-edge-cases.test.ts} +4 -3
- package/test/unit/awards/{award-collection-filtering.test.js → award-collection-filtering.test.ts} +4 -3
- package/test/unit/awards/{award-completion-flow.test.js → award-completion-flow.test.ts} +4 -3
- package/test/unit/awards/{award-exclusion-handling.test.js → award-exclusion-handling.test.ts} +4 -3
- package/test/unit/awards/{award-multi-lesson.test.js → award-multi-lesson.test.ts} +4 -3
- package/test/unit/awards/{award-observer-integration.test.js → award-observer-integration.test.ts} +5 -4
- package/test/unit/awards/{award-query-messages.test.js → award-query-messages.test.ts} +2 -1
- package/test/unit/awards/{award-user-collection.test.js → award-user-collection.test.ts} +2 -1
- package/test/unit/awards/{duplicate-prevention.test.js → duplicate-prevention.test.ts} +5 -4
- package/test/unit/awards/helpers/{completion-mock.js → completion-mock.ts} +9 -7
- package/test/unit/awards/helpers/index.ts +3 -0
- package/test/unit/awards/helpers/{mock-setup.js → mock-setup.ts} +12 -2
- package/test/unit/awards/helpers/{progress-emitter.js → progress-emitter.ts} +10 -4
- package/test/unit/{contentLikes.test.js → contentLikes.test.ts} +1 -1
- package/test/unit/{contentProgress.test.js → contentProgress.test.ts} +8 -2
- package/test/unit/{dataContext.test.js → dataContext.test.ts} +1 -1
- package/test/unit/{dateUtils.test.js → dateUtils.test.ts} +2 -2
- package/test/unit/{imageSRCBuilder.test.js → imageSRCBuilder.test.ts} +1 -1
- package/test/unit/{imageSRCVerify.test.js → imageSRCVerify.test.ts} +1 -6
- package/test/unit/{notifications.test.js → notifications.test.ts} +1 -4
- package/test/unit/{progressRows.test.js → progressRows.test.ts} +19 -13
- package/test/unit/{sanityQueryService.test.js → sanityQueryService.test.ts} +7 -8
- package/test/unit/{streakMessage.test.js → streakMessage.test.ts} +4 -7
- package/test/unit/{userActivity.test.js → userActivity.test.ts} +5 -2
- package/test/unit/awards/helpers/index.js +0 -3
- /package/test/unit/awards/{award-auto-refresh.test.js → award-auto-refresh.test.ts} +0 -0
- /package/test/unit/awards/{award-calculations.test.js → award-calculations.test.ts} +0 -0
- /package/test/unit/awards/{message-generator.test.js → message-generator.test.ts} +0 -0
- /package/test/unit/lib/{lastUpdated.test.js → lastUpdated.test.ts} +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
date: 2026-04-21
|
|
3
|
+
pr: railroadmedia/musora-content-services#933
|
|
4
|
+
branch: TMA-239-mcs-sanity-add-a-v2-query-parameter-to-all-sanity-
|
|
5
|
+
status: open
|
|
6
|
+
tags: [[jira]], [[bug-fix]], [[sanity]], [[cache]]
|
|
7
|
+
components: [[fetchSanity]]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Add v=2 query parameter to all Sanity requests to force cache refresh
|
|
11
|
+
|
|
12
|
+
## Context
|
|
13
|
+
All Sanity API requests from musora-content-services were being served with cached responses. Adding a `v=2` query parameter forces a cache refresh, ensuring clients receive up-to-date content.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
Appended `&v=2` to the `baseUrl` string inside `fetchSanity()` in `src/services/sanity.js`. This is the single location where all Sanity request URLs are constructed, so one targeted change covers every request made through the library.
|
|
17
|
+
|
|
18
|
+
## Alternatives Considered
|
|
19
|
+
- Adding the parameter per call-site: There are dozens of call sites and they all funnel through `fetchSanity`, so modifying the base URL there is cleaner and less error-prone.
|
|
20
|
+
- Making it configurable via `sanityConfig`: The ticket does not ask for configurability — it asks for the parameter to be added to all requests unconditionally.
|
|
21
|
+
|
|
22
|
+
## Process Notes
|
|
23
|
+
The `fetchSanity` function at line 1477 of `src/services/sanity.js` is the sole entry point for all Sanity API calls. It builds `baseUrl` on line 1488 and then appends `&query=...` for GET requests or uses it as the POST URL. Appending `&v=2` to `baseUrl` ensures the parameter is present in both cases.
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
All future Sanity requests from this library will include `v=2`, bypassing any CDN or server-side cache and ensuring fresh content is returned.
|
|
@@ -19,6 +19,9 @@ jobs:
|
|
|
19
19
|
- name: Run unit tests
|
|
20
20
|
run: npm test -- --coverage
|
|
21
21
|
- name: Upload coverage to Codecov
|
|
22
|
-
uses: codecov/codecov-action@
|
|
22
|
+
uses: codecov/codecov-action@v5
|
|
23
23
|
with:
|
|
24
24
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
25
|
+
slug: railroadmedia/musora-content-services
|
|
26
|
+
flags: unit
|
|
27
|
+
fail_ci_if_error: true
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.155.9](https://github.com/railroadmedia/musora-content-services/compare/v2.155.8...v2.155.9) (2026-04-28)
|
|
6
|
+
|
|
7
|
+
### [2.155.8](https://github.com/railroadmedia/musora-content-services/compare/v2.155.7...v2.155.8) (2026-04-28)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* revert activity timestamp changes ([#942](https://github.com/railroadmedia/musora-content-services/issues/942)) ([bd9f97d](https://github.com/railroadmedia/musora-content-services/commit/bd9f97d6547409b7f2856bf716c90d6723306c36))
|
|
13
|
+
* **TMA-239:** MCS - Sanity - Add a v=2 query parameter to all sanity requests to force a cache refresh ([#933](https://github.com/railroadmedia/musora-content-services/issues/933)) ([eac5a2c](https://github.com/railroadmedia/musora-content-services/commit/eac5a2c6848aee80c462ba0ccbeba52a92652fa9))
|
|
14
|
+
|
|
5
15
|
### [2.155.7](https://github.com/railroadmedia/musora-content-services/compare/v2.155.6...v2.155.7) (2026-04-23)
|
|
6
16
|
|
|
7
17
|
|
package/codecov.yml
CHANGED
package/package.json
CHANGED
|
@@ -14,13 +14,16 @@ interface Activity {
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param offlineTimestamp - Minimum `updated_at` epoch ms to include
|
|
17
|
+
* @param page
|
|
18
|
+
* @param limit
|
|
19
|
+
* @param tabName
|
|
17
20
|
* @param options.page - Page number (default 1)
|
|
18
21
|
* @param options.limit - Results per page (default 5)
|
|
19
22
|
* @param options.tabName - Restrict to `'lessons'`, `'songs'`, or both when `null`
|
|
20
|
-
* @returns
|
|
23
|
+
* @returns {Promise<{currentPage: number, totalPages: number, data: Activity[]}>}
|
|
21
24
|
*/
|
|
22
25
|
export async function getRecentActivityOffline(
|
|
23
|
-
|
|
26
|
+
offlineTimestamp: number,
|
|
24
27
|
{
|
|
25
28
|
page = 1,
|
|
26
29
|
limit = 5,
|
|
@@ -34,7 +37,7 @@ export async function getRecentActivityOffline(
|
|
|
34
37
|
// because setting up watermelon user activities table is extremely complicated.
|
|
35
38
|
// Note: this implementation does not persist "activities" beyond when the corresponding record is deleted. That's ok right now.
|
|
36
39
|
|
|
37
|
-
const clauses = getClauses(tabName)
|
|
40
|
+
const clauses = getClauses(offlineTimestamp, tabName)
|
|
38
41
|
|
|
39
42
|
const query = await db.contentProgress.queryAll(...clauses)
|
|
40
43
|
const progress = query.data
|
|
@@ -54,8 +57,9 @@ export async function getRecentActivityOffline(
|
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
function getClauses(tabName: string|null) {
|
|
60
|
+
function getClauses(offlineTimestamp: number, tabName: string|null) {
|
|
58
61
|
let clauses: Q.Clause[] = [
|
|
62
|
+
Q.where('updated_at', Q.gte(offlineTimestamp)),
|
|
59
63
|
Q.sortBy('created_at', 'desc'),
|
|
60
64
|
]
|
|
61
65
|
|
|
@@ -6,12 +6,13 @@ import { calculateLongestStreaks } from '../userActivity.js'
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param offlineTimestamp - Minimum `updated_at` epoch ms to include
|
|
9
|
+
* @param day
|
|
9
10
|
* @param options.day - Date in YYYY-MM-DD format, defaults to today
|
|
10
|
-
* @returns
|
|
11
|
+
* @returns {Promise<{data: {practices: object[], practiceDuration: number}}>}
|
|
11
12
|
*/
|
|
12
13
|
export async function getPracticeSessionsOffline(
|
|
13
|
-
offlineTimestamp: number,
|
|
14
|
-
|
|
14
|
+
offlineTimestamp: number, {
|
|
15
|
+
day = dayjs().format('YYYY-MM-DD') }: { day?: string } = {}
|
|
15
16
|
) {
|
|
16
17
|
|
|
17
18
|
const query = await db.practices.queryAll(
|
package/src/services/sanity.js
CHANGED
|
@@ -1296,8 +1296,8 @@ export async function fetchTopLevelParentId(railcontentId) {
|
|
|
1296
1296
|
* Ignores learning-path-v2 parents.
|
|
1297
1297
|
* ex: if railcontentId is of type 'skill-pack-lesson', return the corresponding 'skill-pack' railcontent_id
|
|
1298
1298
|
*
|
|
1299
|
-
* @param {int[]}
|
|
1300
|
-
* @returns {Promise<
|
|
1299
|
+
* @param {int[]} railcontentIds
|
|
1300
|
+
* @returns {Promise<Object.<number, object>|null>}
|
|
1301
1301
|
*/
|
|
1302
1302
|
async function fetchTopLevelParentIds(railcontentIds) {
|
|
1303
1303
|
const idsString = railcontentIds.join(',')
|
|
@@ -1460,7 +1460,7 @@ async function fetchALaCarteHierarchyData(railcontentId) {
|
|
|
1460
1460
|
/**
|
|
1461
1461
|
* returns a map of railcontentId to hierarchy data.
|
|
1462
1462
|
* @param {int[]} railcontentIds
|
|
1463
|
-
* @returns {Promise<
|
|
1463
|
+
* @returns {Promise<Object.<number, object>|null>}
|
|
1464
1464
|
*/
|
|
1465
1465
|
async function fetchALaCarteHierarchyDataForIds(railcontentIds) {
|
|
1466
1466
|
const topLevelIds = await fetchTopLevelParentIds(railcontentIds)
|
|
@@ -1567,7 +1567,7 @@ export async function fetchSanity(
|
|
|
1567
1567
|
}
|
|
1568
1568
|
const perspective = globalConfig.sanityConfig.perspective ?? 'published'
|
|
1569
1569
|
const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api'
|
|
1570
|
-
const baseUrl = `https://sanity.musora.com/${globalConfig.sanityConfig.projectId}/${api}/v${globalConfig.sanityConfig.version}/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`
|
|
1570
|
+
const baseUrl = `https://sanity.musora.com/${globalConfig.sanityConfig.projectId}/${api}/v${globalConfig.sanityConfig.version}/${globalConfig.sanityConfig.dataset}?perspective=${perspective}&v=2`
|
|
1571
1571
|
|
|
1572
1572
|
try {
|
|
1573
1573
|
const encodedQuery = encodeURIComponent(query)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { HttpClient } from '../../src/infrastructure/http
|
|
2
|
-
import { HeaderProvider } from '../../src/infrastructure/http/interfaces/HeaderProvider.ts'
|
|
3
|
-
import { RequestExecutor } from '../../src/infrastructure/http/interfaces/RequestExecutor.ts'
|
|
1
|
+
import { HeaderProvider, HttpClient, RequestExecutor } from '../../src/infrastructure/http'
|
|
4
2
|
|
|
5
3
|
describe('HttpClient', () => {
|
|
6
|
-
let httpClient
|
|
7
|
-
let mockHeaderProvider
|
|
8
|
-
let mockRequestExecutor
|
|
4
|
+
let httpClient: HttpClient
|
|
5
|
+
let mockHeaderProvider: jest.Mocked<HeaderProvider>
|
|
6
|
+
let mockRequestExecutor: jest.Mocked<RequestExecutor>
|
|
9
7
|
const baseUrl = 'https://api.example.com'
|
|
10
8
|
const token = 'test-token'
|
|
11
9
|
const headers = { 'Content-Type': 'application/json', Accept: 'application/json' }
|
|
@@ -190,23 +188,21 @@ describe('HttpClient', () => {
|
|
|
190
188
|
})
|
|
191
189
|
|
|
192
190
|
test('should not add Data-Version header when not provided', async () => {
|
|
193
|
-
// Log what's happening for debugging
|
|
194
|
-
console.log('Starting test: should not add Data-Version header when not provided')
|
|
195
191
|
|
|
196
192
|
const url = '/test'
|
|
197
193
|
|
|
198
194
|
// Make sure we're using exact null here, not undefined
|
|
199
|
-
const dataVersion = null
|
|
195
|
+
const dataVersion: string | null = null
|
|
200
196
|
|
|
201
197
|
// Create separate mocks for this test to avoid state bleeding
|
|
202
|
-
const testHeaderProvider = {
|
|
198
|
+
const testHeaderProvider: jest.Mocked<HeaderProvider> = {
|
|
203
199
|
getHeaders: jest.fn().mockReturnValue({
|
|
204
200
|
'Content-Type': 'application/json',
|
|
205
201
|
Accept: 'application/json',
|
|
206
202
|
}),
|
|
207
203
|
}
|
|
208
204
|
|
|
209
|
-
const testRequestExecutor = {
|
|
205
|
+
const testRequestExecutor: jest.Mocked<RequestExecutor> = {
|
|
210
206
|
execute: jest.fn().mockResolvedValue(responseData),
|
|
211
207
|
}
|
|
212
208
|
|
|
@@ -215,8 +211,6 @@ describe('HttpClient', () => {
|
|
|
215
211
|
|
|
216
212
|
await testClient.get(url, { dataVersion })
|
|
217
213
|
|
|
218
|
-
console.log('Headers used in request:', testRequestExecutor.execute.mock.calls[0][1].headers)
|
|
219
|
-
|
|
220
214
|
// Direct check on the mock to validate Data-Version absence
|
|
221
215
|
const actualHeaders = testRequestExecutor.execute.mock.calls[0][1].headers
|
|
222
216
|
const hasDataVersion = Object.prototype.hasOwnProperty.call(actualHeaders, 'Data-Version')
|
package/test/unit/awards/{award-alacarte-observer.test.js → award-alacarte-observer.test.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { contentProgressObserver } from '../../../src/services/awards/internal/content-progress-observer.js'
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
4
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
5
|
-
import { mockCompletionStates, mockAllCompleted } from './helpers/completion-mock
|
|
6
|
-
import { COLLECTION_TYPE, emitAlaCarteProgress, emitProgress, waitForDebounce } from './helpers/progress-emitter
|
|
4
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
5
|
+
import { mockCompletionStates, mockAllCompleted } from './helpers/completion-mock'
|
|
6
|
+
import { COLLECTION_TYPE, emitAlaCarteProgress, emitProgress, waitForDebounce } from './helpers/progress-emitter'
|
|
7
7
|
|
|
8
8
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
9
9
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -32,9 +32,11 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
35
|
-
import db from '../../../src/services/sync/repository-proxy
|
|
35
|
+
import db from '../../../src/services/sync/repository-proxy'
|
|
36
36
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
37
37
|
|
|
38
|
+
const mockDb = db as jest.MockedObjectDeep<typeof db>
|
|
39
|
+
|
|
38
40
|
describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
39
41
|
let listeners
|
|
40
42
|
|
|
@@ -43,7 +45,7 @@ describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
|
43
45
|
awardEvents.removeAllListeners()
|
|
44
46
|
|
|
45
47
|
sanityClient.fetch = jest.fn().mockResolvedValue(mockAwardDefinitions)
|
|
46
|
-
setupDefaultMocks(
|
|
48
|
+
setupDefaultMocks(mockDb, fetchSanity)
|
|
47
49
|
|
|
48
50
|
await awardDefinitions.refresh()
|
|
49
51
|
|
|
@@ -71,7 +73,7 @@ describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
|
71
73
|
})
|
|
72
74
|
|
|
73
75
|
test('emits awardProgress for partial completion with null collection', async () => {
|
|
74
|
-
mockCompletionStates(
|
|
76
|
+
mockCompletionStates(mockDb, [417045])
|
|
75
77
|
|
|
76
78
|
emitAlaCarteProgress(417045)
|
|
77
79
|
await waitForDebounce()
|
|
@@ -114,18 +116,18 @@ describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
|
114
116
|
test('finds all awards containing the child content id', async () => {
|
|
115
117
|
const sharedChildId = 418003
|
|
116
118
|
|
|
117
|
-
mockAllCompleted(
|
|
119
|
+
mockAllCompleted(mockDb)
|
|
118
120
|
|
|
119
121
|
emitAlaCarteProgress(sharedChildId)
|
|
120
122
|
await waitForDebounce()
|
|
121
123
|
|
|
122
|
-
expect(
|
|
124
|
+
expect(mockDb.userAwardProgress.recordAwardProgress).toHaveBeenCalled()
|
|
123
125
|
})
|
|
124
126
|
})
|
|
125
127
|
|
|
126
128
|
describe('A la carte debouncing', () => {
|
|
127
129
|
beforeEach(() => {
|
|
128
|
-
mockCompletionStates(
|
|
130
|
+
mockCompletionStates(mockDb, [])
|
|
129
131
|
})
|
|
130
132
|
|
|
131
133
|
test('debounces multiple rapid a la carte updates', async () => {
|
|
@@ -135,7 +137,7 @@ describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
|
135
137
|
|
|
136
138
|
await waitForDebounce()
|
|
137
139
|
|
|
138
|
-
expect(
|
|
140
|
+
expect(mockDb.userAwardProgress.recordAwardProgress).toHaveBeenCalledTimes(1)
|
|
139
141
|
})
|
|
140
142
|
})
|
|
141
143
|
|
|
@@ -151,7 +153,7 @@ describe('Award Observer - A La Carte Progress (null collection)', () => {
|
|
|
151
153
|
|
|
152
154
|
describe('A la carte already completed award', () => {
|
|
153
155
|
beforeEach(() => {
|
|
154
|
-
|
|
156
|
+
mockDb.userAwardProgress.hasCompletedAward.mockResolvedValue(true)
|
|
155
157
|
})
|
|
156
158
|
|
|
157
159
|
test('does not re-grant already completed award', async () => {
|
package/test/unit/awards/{award-certificate-display.test.js → award-certificate-display.test.ts}
RENAMED
|
@@ -25,7 +25,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
25
25
|
|
|
26
26
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
27
27
|
import { getUserData } from '../../../src/services/user/management.js'
|
|
28
|
-
import
|
|
28
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
29
|
+
const db = db_ as any
|
|
29
30
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
30
31
|
|
|
31
32
|
describe('Award Certificate Display - E2E Scenarios', () => {
|
package/test/unit/awards/{award-collection-edge-cases.test.js → award-collection-edge-cases.test.ts}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { contentProgressObserver } from '../../../src/services/awards/internal/content-progress-observer.js'
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { mockAwardDefinitions } from '../../mockData/award-definitions.js'
|
|
4
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
5
|
-
import { COLLECTION_TYPE, emitProgress, waitForDebounce } from './helpers/progress-emitter
|
|
4
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
5
|
+
import { COLLECTION_TYPE, emitProgress, waitForDebounce } from './helpers/progress-emitter'
|
|
6
6
|
|
|
7
7
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
8
8
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -31,7 +31,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
34
|
-
import
|
|
34
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
35
|
+
const db = db_ as any
|
|
35
36
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
36
37
|
|
|
37
38
|
describe('Award Collection Filtering - Edge Cases', () => {
|
package/test/unit/awards/{award-collection-filtering.test.js → award-collection-filtering.test.ts}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { contentProgressObserver } from '../../../src/services/awards/internal/content-progress-observer.js'
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { mockAwardDefinitions } from '../../mockData/award-definitions.js'
|
|
4
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
5
|
-
import { COLLECTION_TYPE, emitProgress, waitForDebounce } from './helpers/progress-emitter
|
|
4
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
5
|
+
import { COLLECTION_TYPE, emitProgress, waitForDebounce } from './helpers/progress-emitter'
|
|
6
6
|
|
|
7
7
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
8
8
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -31,7 +31,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
34
|
-
import
|
|
34
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
35
|
+
const db = db_ as any
|
|
35
36
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
36
37
|
|
|
37
38
|
describe('Award Collection Filtering', () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { awardManager } from '../../../src/services/awards/internal/award-manager.js'
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
4
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
5
|
-
import { mockCompletionStates, mockAllCompleted } from './helpers/completion-mock
|
|
4
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
5
|
+
import { mockCompletionStates, mockAllCompleted } from './helpers/completion-mock'
|
|
6
6
|
|
|
7
7
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
8
8
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -31,7 +31,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
34
|
-
import
|
|
34
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
35
|
+
const db = db_ as any
|
|
35
36
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
36
37
|
|
|
37
38
|
describe('Award Completion Flow - E2E Scenarios', () => {
|
package/test/unit/awards/{award-exclusion-handling.test.js → award-exclusion-handling.test.ts}
RENAMED
|
@@ -3,8 +3,8 @@ import { awardEvents } from '../../../src/services/awards/internal/award-events.
|
|
|
3
3
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
4
4
|
import { globalConfig } from '../../../src/services/config.js'
|
|
5
5
|
import { LocalStorageMock } from '../../localStorageMock.js'
|
|
6
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
7
|
-
import { mockCompletionStates, mockAllCompleted, mockNoneCompleted } from './helpers/completion-mock
|
|
6
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
7
|
+
import { mockCompletionStates, mockAllCompleted, mockNoneCompleted } from './helpers/completion-mock'
|
|
8
8
|
|
|
9
9
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
10
10
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -39,7 +39,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
42
|
-
import
|
|
42
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
43
|
+
const db = db_ as any
|
|
43
44
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
44
45
|
|
|
45
46
|
describe('Award Content Exclusion Handling - E2E Scenarios', () => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { awardManager } from '../../../src/services/awards/internal/award-manager.js'
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
4
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
5
|
-
import { mockCompletionStates, mockAllCompleted, mockNoneCompleted } from './helpers/completion-mock
|
|
4
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
5
|
+
import { mockCompletionStates, mockAllCompleted, mockNoneCompleted } from './helpers/completion-mock'
|
|
6
6
|
|
|
7
7
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
8
8
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -31,7 +31,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
34
|
-
import
|
|
34
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
35
|
+
const db = db_ as any
|
|
35
36
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
36
37
|
|
|
37
38
|
describe('Award Progress Calculation', () => {
|
package/test/unit/awards/{award-observer-integration.test.js → award-observer-integration.test.ts}
RENAMED
|
@@ -2,9 +2,9 @@ import { contentProgressObserver } from '../../../src/services/awards/internal/c
|
|
|
2
2
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
3
3
|
import { emitProgressSaved } from '../../../src/services/progress-events.js'
|
|
4
4
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
5
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
6
|
-
import { mockCompletionStates } from './helpers/completion-mock
|
|
7
|
-
import { COLLECTION_TYPE, waitForDebounce } from './helpers/progress-emitter
|
|
5
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
6
|
+
import { mockCompletionStates } from './helpers/completion-mock'
|
|
7
|
+
import { COLLECTION_TYPE, waitForDebounce } from './helpers/progress-emitter'
|
|
8
8
|
|
|
9
9
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
10
10
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -33,7 +33,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
33
33
|
})
|
|
34
34
|
|
|
35
35
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
36
|
-
import
|
|
36
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
37
|
+
const db = db_ as any
|
|
37
38
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
38
39
|
|
|
39
40
|
describe('Award Observer Integration - E2E Scenarios', () => {
|
|
@@ -21,7 +21,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
24
|
-
import
|
|
24
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
25
|
+
const db = db_ as any
|
|
25
26
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
26
27
|
import { getCompletedAwards, getContentAwards, getInProgressAwards } from '../../../src/services/awards/award-query.js'
|
|
27
28
|
|
|
@@ -20,7 +20,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
23
|
-
import
|
|
23
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
24
|
+
const db = db_ as any
|
|
24
25
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
25
26
|
import { getCompletedAwards, getInProgressAwards, getAwardStatistics } from '../../../src/services/awards/award-query.js'
|
|
26
27
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mockAwardDefinitions, getAwardByContentId } from '../../mockData/award-definitions.js'
|
|
2
|
-
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index
|
|
3
|
-
import { COLLECTION_TYPE, emitLearningPathProgress, emitAlaCarteProgress, waitForDebounce } from './helpers/progress-emitter
|
|
4
|
-
import { mockCollectionAwareCompletion } from './helpers/completion-mock
|
|
2
|
+
import { setupDefaultMocks, setupAwardEventListeners } from './helpers/index'
|
|
3
|
+
import { COLLECTION_TYPE, emitLearningPathProgress, emitAlaCarteProgress, waitForDebounce } from './helpers/progress-emitter'
|
|
4
|
+
import { mockCollectionAwareCompletion } from './helpers/completion-mock'
|
|
5
5
|
|
|
6
6
|
jest.mock('../../../src/services/sanity.js', () => ({
|
|
7
7
|
...jest.requireActual('../../../src/services/sanity'),
|
|
@@ -30,7 +30,8 @@ jest.mock('../../../src/services/sync/repository-proxy.ts', () => {
|
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
import sanityClient, { fetchSanity } from '../../../src/services/sanity.js'
|
|
33
|
-
import
|
|
33
|
+
import db_ from '../../../src/services/sync/repository-proxy'
|
|
34
|
+
const db = db_ as any
|
|
34
35
|
import { awardDefinitions } from '../../../src/services/awards/internal/award-definitions.js'
|
|
35
36
|
import { awardEvents } from '../../../src/services/awards/internal/award-events.js'
|
|
36
37
|
import { contentProgressObserver } from '../../../src/services/awards/internal/content-progress-observer.js'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
type Collection = { type: string; id: number } | null
|
|
2
|
+
|
|
3
|
+
export const mockCompletionStates = (db: any, completedIds: number[] = [], collection: Collection = null) => {
|
|
4
|
+
db.contentProgress.getSomeProgressByContentIds.mockImplementation((contentIds: number[], requestedCollection: Collection) => {
|
|
3
5
|
const collectionMatches = !collection || (
|
|
4
6
|
requestedCollection?.type === collection.type &&
|
|
5
7
|
requestedCollection?.id === collection.id
|
|
@@ -17,8 +19,8 @@ export const mockCompletionStates = (db, completedIds = [], collection = null) =
|
|
|
17
19
|
})
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export const mockAllCompleted = (db) => {
|
|
21
|
-
db.contentProgress.getSomeProgressByContentIds.mockImplementation((contentIds) => {
|
|
22
|
+
export const mockAllCompleted = (db: any) => {
|
|
23
|
+
db.contentProgress.getSomeProgressByContentIds.mockImplementation((contentIds: number[]) => {
|
|
22
24
|
const completedRecords = contentIds.map(id => ({
|
|
23
25
|
content_id: id,
|
|
24
26
|
state: 'completed',
|
|
@@ -29,12 +31,12 @@ export const mockAllCompleted = (db) => {
|
|
|
29
31
|
})
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
export const mockNoneCompleted = (db) => {
|
|
34
|
+
export const mockNoneCompleted = (db: any) => {
|
|
33
35
|
db.contentProgress.getSomeProgressByContentIds.mockResolvedValue({ data: [] })
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
export const mockCollectionAwareCompletion = (db, completionMap) => {
|
|
37
|
-
db.contentProgress.getSomeProgressByContentIds.mockImplementation((contentIds, collection) => {
|
|
38
|
+
export const mockCollectionAwareCompletion = (db: any, completionMap: Record<string, boolean>) => {
|
|
39
|
+
db.contentProgress.getSomeProgressByContentIds.mockImplementation((contentIds: number[], collection: Collection) => {
|
|
38
40
|
const completedRecords = contentIds
|
|
39
41
|
.filter(id => {
|
|
40
42
|
const key = collection
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { mockAwardDefinitions } from '../../../mockData/award-definitions.js'
|
|
2
2
|
|
|
3
|
+
export type RepositoryProxyMock = ReturnType<typeof createRepositoryProxyMock>
|
|
4
|
+
|
|
3
5
|
export const createRepositoryProxyMock = () => ({
|
|
4
6
|
contentProgress: {
|
|
5
7
|
getOneProgressByContentId: jest.fn(),
|
|
@@ -19,7 +21,15 @@ export const createRepositoryProxyMock = () => ({
|
|
|
19
21
|
}
|
|
20
22
|
})
|
|
21
23
|
|
|
22
|
-
export const setupDefaultMocks = (
|
|
24
|
+
export const setupDefaultMocks = (
|
|
25
|
+
db: any,
|
|
26
|
+
fetchSanity: jest.Mock,
|
|
27
|
+
options: {
|
|
28
|
+
definitions?: typeof mockAwardDefinitions,
|
|
29
|
+
practiceMinutes?: number,
|
|
30
|
+
hasCompleted?: boolean
|
|
31
|
+
} = {}
|
|
32
|
+
) => {
|
|
23
33
|
const {
|
|
24
34
|
definitions = mockAwardDefinitions,
|
|
25
35
|
practiceMinutes = 200,
|
|
@@ -56,7 +66,7 @@ export const setupDefaultMocks = (db, fetchSanity, options = {}) => {
|
|
|
56
66
|
})
|
|
57
67
|
}
|
|
58
68
|
|
|
59
|
-
export const setupAwardEventListeners = (awardEvents) => {
|
|
69
|
+
export const setupAwardEventListeners = (awardEvents: any) => {
|
|
60
70
|
const listeners = {
|
|
61
71
|
progress: jest.fn(),
|
|
62
72
|
granted: jest.fn()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { emitProgressSaved } from '../../../../src/services/progress-events.js'
|
|
2
|
-
import { COLLECTION_TYPE } from '../../../../src/services/sync/models/ContentProgress
|
|
2
|
+
import { COLLECTION_TYPE } from '../../../../src/services/sync/models/ContentProgress'
|
|
3
3
|
|
|
4
4
|
export { COLLECTION_TYPE }
|
|
5
5
|
|
|
@@ -9,6 +9,12 @@ export const emitProgress = ({
|
|
|
9
9
|
collectionId = null,
|
|
10
10
|
progressPercent = 100,
|
|
11
11
|
userId = 123
|
|
12
|
+
}: {
|
|
13
|
+
contentId: number
|
|
14
|
+
collectionType?: string | null
|
|
15
|
+
collectionId?: number | null
|
|
16
|
+
progressPercent?: number
|
|
17
|
+
userId?: number
|
|
12
18
|
}) => {
|
|
13
19
|
emitProgressSaved({
|
|
14
20
|
userId,
|
|
@@ -23,7 +29,7 @@ export const emitProgress = ({
|
|
|
23
29
|
})
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
export const emitLearningPathProgress = (contentId, learningPathId, progressPercent = 100) => {
|
|
32
|
+
export const emitLearningPathProgress = (contentId: number, learningPathId: number, progressPercent = 100) => {
|
|
27
33
|
emitProgress({
|
|
28
34
|
contentId,
|
|
29
35
|
collectionType: COLLECTION_TYPE.LEARNING_PATH,
|
|
@@ -32,8 +38,8 @@ export const emitLearningPathProgress = (contentId, learningPathId, progressPerc
|
|
|
32
38
|
})
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
export const emitAlaCarteProgress = (contentId, progressPercent = 100) => {
|
|
41
|
+
export const emitAlaCarteProgress = (contentId: number, progressPercent = 100) => {
|
|
36
42
|
emitProgress({ contentId, progressPercent })
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
export const waitForDebounce = (ms = 100) => new Promise(resolve => setTimeout(resolve, ms))
|
|
45
|
+
export const waitForDebounce = (ms = 100): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from '../../src/services/contentLikes.js'
|
|
6
6
|
import { initializeTestService } from '../initializeTests.js'
|
|
7
7
|
|
|
8
|
-
let mockLikedIds = new Set()
|
|
8
|
+
let mockLikedIds: Set<number> = new Set()
|
|
9
9
|
|
|
10
10
|
jest.mock('../../src/services/sync/repository-proxy.ts', () => {
|
|
11
11
|
const mockFns = {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { initializeTestService } from '../initializeTests.js'
|
|
2
|
-
import { getAllStarted, getAllStartedOrCompleted, getProgressState } from '../../src/
|
|
2
|
+
import { getAllStarted, getAllStartedOrCompleted, getProgressState } from '../../src/services/contentProgress.js'
|
|
3
3
|
|
|
4
|
-
let mockProgressRecords
|
|
4
|
+
let mockProgressRecords: {
|
|
5
|
+
content_id: number;
|
|
6
|
+
state: string;
|
|
7
|
+
progress_percent: number;
|
|
8
|
+
updated_at: number;
|
|
9
|
+
last_interacted_a_la_carte?: number
|
|
10
|
+
}[] = []
|
|
5
11
|
|
|
6
12
|
jest.mock('../../src/services/sync/repository-proxy', () => {
|
|
7
13
|
const mockFns = {
|
|
@@ -2,7 +2,7 @@ import { initializeTestService } from '../initializeTests.js'
|
|
|
2
2
|
import { DataContext, verifyLocalDataContext } from '../../src/services/dataContext.js'
|
|
3
3
|
|
|
4
4
|
describe('dataContext', function () {
|
|
5
|
-
let mock
|
|
5
|
+
let mock: jest.SpyInstance
|
|
6
6
|
let testVersion = 1
|
|
7
7
|
const dataVersionKey = 1
|
|
8
8
|
const dataContext = new DataContext(dataVersionKey, null)
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
getToday,
|
|
9
9
|
} from '../../src/services/dateUtils.js'
|
|
10
10
|
|
|
11
|
-
const mockTimezone = (tz) => {
|
|
11
|
+
const mockTimezone = (tz: string): void => {
|
|
12
12
|
const RealDateTimeFormat = Intl.DateTimeFormat
|
|
13
13
|
jest.spyOn(Intl, 'DateTimeFormat').mockImplementation((...args) => {
|
|
14
14
|
const instance = new RealDateTimeFormat(...args)
|
|
@@ -16,7 +16,7 @@ const mockTimezone = (tz) => {
|
|
|
16
16
|
format: instance.format.bind(instance),
|
|
17
17
|
formatToParts: instance.formatToParts.bind(instance),
|
|
18
18
|
resolvedOptions: () => ({ ...instance.resolvedOptions(), timeZone: tz }),
|
|
19
|
-
}
|
|
19
|
+
} as unknown as Intl.DateTimeFormat
|
|
20
20
|
})
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Mock console.warn to avoid cluttering test output and to verify warnings
|
|
2
1
|
import { extractSanityUrl, isBucketUrl, verifyImageSRC } from '../../src/services/imageSRCVerify.js'
|
|
3
2
|
|
|
4
3
|
const originalConsoleWarn = console.warn
|
|
@@ -6,20 +5,16 @@ const originalConsoleError = console.error
|
|
|
6
5
|
const originalNodeEnv = process.env.NODE_ENV
|
|
7
6
|
|
|
8
7
|
describe('Image URL Verification', () => {
|
|
9
|
-
let consoleWarnMock
|
|
8
|
+
let consoleWarnMock: any
|
|
10
9
|
|
|
11
10
|
beforeEach(() => {
|
|
12
|
-
// Mock console.warn and console.error
|
|
13
11
|
consoleWarnMock = jest.fn()
|
|
14
12
|
console.warn = consoleWarnMock
|
|
15
13
|
console.error = jest.fn()
|
|
16
|
-
|
|
17
|
-
// Set NODE_ENV to development for all tests
|
|
18
14
|
process.env.NODE_ENV = 'development'
|
|
19
15
|
})
|
|
20
16
|
|
|
21
17
|
afterEach(() => {
|
|
22
|
-
// Restore the original console methods and NODE_ENV
|
|
23
18
|
console.warn = originalConsoleWarn
|
|
24
19
|
console.error = originalConsoleError
|
|
25
20
|
process.env.NODE_ENV = originalNodeEnv
|
|
@@ -22,16 +22,13 @@ jest.mock('../../src/services/eventsAPI.js', () => ({
|
|
|
22
22
|
}
|
|
23
23
|
}))
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
import { GET, PUT, DELETE } from '../../src/infrastructure/http/HttpClient'
|
|
26
26
|
|
|
27
27
|
const baseUrl = `/api/notifications`
|
|
28
28
|
|
|
29
29
|
describe('UserNotifications module', function () {
|
|
30
30
|
beforeEach(() => {
|
|
31
31
|
initializeTestService()
|
|
32
|
-
GET.mockReset()
|
|
33
|
-
PUT.mockReset()
|
|
34
|
-
DELETE.mockReset()
|
|
35
32
|
})
|
|
36
33
|
|
|
37
34
|
describe('fetchNotifications', () => {
|
|
@@ -81,22 +81,28 @@ describe('getProgressRows', () => {
|
|
|
81
81
|
},
|
|
82
82
|
];
|
|
83
83
|
|
|
84
|
-
fetchUserPlaylists.mockResolvedValue({ data: mockPlaylists })
|
|
85
|
-
fetchByRailContentIds.mockResolvedValue(mockPlaylistContents)
|
|
86
|
-
getAllStartedOrCompleted.mockResolvedValue([201])
|
|
87
|
-
getProgressStateByIds.mockResolvedValue({ '201': 'in_progress' })
|
|
84
|
+
fetchUserPlaylists.mockResolvedValue({ data: mockPlaylists })
|
|
85
|
+
fetchByRailContentIds.mockResolvedValue(mockPlaylistContents)
|
|
86
|
+
getAllStartedOrCompleted.mockResolvedValue([201])
|
|
87
|
+
getProgressStateByIds.mockResolvedValue({ '201': 'in_progress' })
|
|
88
88
|
|
|
89
|
-
const result = await getProgressRows({ brand: 'brand1', limit: 8 })
|
|
89
|
+
const result = await getProgressRows({ brand: 'brand1', limit: 8 })
|
|
90
90
|
|
|
91
|
-
expect(result).toHaveProperty('type', 'progress_rows')
|
|
91
|
+
expect(result).toHaveProperty('type', 'progress_rows')
|
|
92
92
|
expect(result).toHaveProperty('data');
|
|
93
|
-
expect(Array.isArray(result.data)).toBe(true)
|
|
94
|
-
expect(result.data.length).toBe(2)
|
|
95
|
-
expect(result.data[0]).toHaveProperty('id'
|
|
96
|
-
expect(result.data[0]
|
|
97
|
-
expect(result.data[0]
|
|
98
|
-
expect(result.data[0]
|
|
99
|
-
expect(result.data[0]
|
|
93
|
+
expect(Array.isArray(result.data)).toBe(true)
|
|
94
|
+
expect(result.data.length).toBe(2)
|
|
95
|
+
expect(result.data[0]).toHaveProperty('id')
|
|
96
|
+
expect(result.data[0]).toHaveProperty('progressType')
|
|
97
|
+
expect(result.data[0]).toHaveProperty('header')
|
|
98
|
+
expect(result.data[0]).toHaveProperty('body')
|
|
99
|
+
expect(result.data[0]).toHaveProperty('cta')
|
|
100
|
+
expect(result.data[0].progressType).toBe('playlist')
|
|
101
|
+
expect(result.data[0].body).toHaveProperty('first_items_thumbnail_url')
|
|
102
|
+
expect(result.data[0].body).toHaveProperty('title')
|
|
103
|
+
expect(result.data[0].body).toHaveProperty('subtitle')
|
|
104
|
+
expect(result.data[0].cta).toHaveProperty('text')
|
|
105
|
+
expect(result.data[0].cta).toHaveProperty('action')
|
|
100
106
|
|
|
101
107
|
});
|
|
102
108
|
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { initializeTestService } from '../initializeTests.js'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
getSortOrder
|
|
5
|
-
} from '../../src/index.js'
|
|
2
|
+
import { getSortOrder } from '../../src/services/sanity.js'
|
|
6
3
|
import { processMetadata } from '../../src/contentMetaData.js'
|
|
7
|
-
|
|
8
|
-
const { FilterBuilder } = require('../../src/filterBuilder.js')
|
|
4
|
+
import { FilterBuilder } from '../../src/filterBuilder.js'
|
|
9
5
|
|
|
10
6
|
jest.mock('../../src/services/permissions/index.ts', () => ({
|
|
11
7
|
...jest.requireActual('../../src/services/permissions/index.ts'),
|
|
@@ -137,7 +133,11 @@ describe('Filter Builder', function() {
|
|
|
137
133
|
expect(clauses[3].operator).toBe('>=')
|
|
138
134
|
})
|
|
139
135
|
|
|
140
|
-
function spliceFilterForAnds(filter) {
|
|
136
|
+
function spliceFilterForAnds(filter: string): {
|
|
137
|
+
phrase: string;
|
|
138
|
+
field: string;
|
|
139
|
+
operator: string;
|
|
140
|
+
condition: string }[] {
|
|
141
141
|
// this will not correctly split complex filters with && and || conditions.
|
|
142
142
|
let phrases = filter.split(' && ')
|
|
143
143
|
let clauses = []
|
|
@@ -152,7 +152,6 @@ describe('Filter Builder', function() {
|
|
|
152
152
|
})
|
|
153
153
|
return clauses
|
|
154
154
|
}
|
|
155
|
-
|
|
156
155
|
})
|
|
157
156
|
|
|
158
157
|
describe('Sanity Queries', function() {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { initializeTestService } from '../initializeTests.js'
|
|
2
2
|
import {getUserWeeklyStats, userActivityContext} from '../../src/services/userActivity.js'
|
|
3
|
-
import { streakCalculator } from '../../src/services/user/streakCalculator
|
|
3
|
+
import { streakCalculator } from '../../src/services/user/streakCalculator'
|
|
4
4
|
import {log} from '../log.js'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let mockPracticeData = []
|
|
6
|
+
let mockPracticeData: { date: string; duration_seconds: number }[] = []
|
|
9
7
|
|
|
10
8
|
jest.mock('../../src/services/sync/repository-proxy.ts', () => {
|
|
11
9
|
const mockFns = {
|
|
@@ -200,15 +198,14 @@ describe('Streak Messages', function () {
|
|
|
200
198
|
})
|
|
201
199
|
})
|
|
202
200
|
|
|
203
|
-
function incrementFakeDate(nDays = 1){
|
|
201
|
+
function incrementFakeDate(nDays = 1) {
|
|
204
202
|
let today = new Date();
|
|
205
203
|
today.setFullYear(today.getFullYear(), today.getMonth(), (today.getDate() + nDays));
|
|
206
204
|
jest.useFakeTimers();
|
|
207
205
|
jest.setSystemTime(today);
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
function sliceExampleData(startDate, nDays, includeToday, activeDays)
|
|
211
|
-
{
|
|
208
|
+
function sliceExampleData(startDate, nDays, includeToday, activeDays) {
|
|
212
209
|
if (nDays === 0 && !includeToday) {
|
|
213
210
|
return []
|
|
214
211
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { initializeTestService } from '../initializeTests.js'
|
|
2
2
|
import { getUserMonthlyStats, getUserWeeklyStats, recordUserPractice } from '../../src/services/userActivity.js'
|
|
3
3
|
|
|
4
|
-
let mockPracticesData
|
|
4
|
+
let mockPracticesData: {
|
|
5
|
+
date: string;
|
|
6
|
+
duration_seconds: number
|
|
7
|
+
}[] = []
|
|
5
8
|
|
|
6
9
|
jest.mock('../../src/services/sync/repository-proxy.ts', () => {
|
|
7
10
|
const mockFns = {
|
|
@@ -33,7 +36,7 @@ jest.mock('../../src/services/railcontent.js', () => ({
|
|
|
33
36
|
fetchUserPermissionsData: jest.fn(() => ({ permissions: [78, 91, 92], isAdmin: false }))
|
|
34
37
|
}))
|
|
35
38
|
|
|
36
|
-
const repositoryProxy = require('../../src/services/sync/repository-proxy.ts')
|
|
39
|
+
const repositoryProxy: any = require('../../src/services/sync/repository-proxy.ts')
|
|
37
40
|
|
|
38
41
|
describe('User Activity API Tests', function () {
|
|
39
42
|
beforeEach(() => {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|