@youversion/platform-react-hooks 1.18.1 → 1.20.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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +3 -7
- package/CHANGELOG.md +32 -0
- package/dist/__tests__/mocks/bibles.d.ts +6 -1
- package/dist/__tests__/mocks/bibles.d.ts.map +1 -1
- package/dist/__tests__/mocks/bibles.js +10 -0
- package/dist/__tests__/mocks/bibles.js.map +1 -1
- package/dist/__tests__/mocks/core-mock-factory.d.ts +83 -0
- package/dist/__tests__/mocks/core-mock-factory.d.ts.map +1 -0
- package/dist/__tests__/mocks/core-mock-factory.js +138 -0
- package/dist/__tests__/mocks/core-mock-factory.js.map +1 -0
- package/dist/context/ReaderContext.d.ts +6 -0
- package/dist/context/ReaderContext.d.ts.map +1 -1
- package/dist/context/ReaderContext.js +6 -0
- package/dist/context/ReaderContext.js.map +1 -1
- package/dist/context/ReaderProvider.d.ts +3 -0
- package/dist/context/ReaderProvider.d.ts.map +1 -1
- package/dist/context/ReaderProvider.js +3 -0
- package/dist/context/ReaderProvider.js.map +1 -1
- package/dist/context/VerseSelectionContext.d.ts +6 -0
- package/dist/context/VerseSelectionContext.d.ts.map +1 -1
- package/dist/context/VerseSelectionContext.js +3 -0
- package/dist/context/VerseSelectionContext.js.map +1 -1
- package/dist/context/VerseSelectionProvider.d.ts +3 -0
- package/dist/context/VerseSelectionProvider.d.ts.map +1 -1
- package/dist/context/VerseSelectionProvider.js +3 -0
- package/dist/context/VerseSelectionProvider.js.map +1 -1
- package/dist/test/utils.d.ts +7 -0
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +7 -0
- package/dist/test/utils.js.map +1 -0
- package/dist/useChapterNavigation.d.ts +3 -0
- package/dist/useChapterNavigation.d.ts.map +1 -1
- package/dist/useChapterNavigation.js +3 -0
- package/dist/useChapterNavigation.js.map +1 -1
- package/dist/useInitData.d.ts +4 -0
- package/dist/useInitData.d.ts.map +1 -1
- package/dist/useInitData.js +4 -0
- package/dist/useInitData.js.map +1 -1
- package/dist/useVerseSelection.d.ts +3 -0
- package/dist/useVerseSelection.d.ts.map +1 -1
- package/dist/useVerseSelection.js +3 -0
- package/dist/useVerseSelection.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/mocks/bibles.ts +18 -1
- package/src/__tests__/mocks/core-mock-factory.ts +226 -0
- package/src/context/ReaderContext.tsx +6 -0
- package/src/context/ReaderProvider.tsx +3 -0
- package/src/context/VerseSelectionContext.tsx +6 -0
- package/src/context/VerseSelectionProvider.tsx +3 -0
- package/src/context/YouVersionAuthProvider.test.tsx +14 -131
- package/src/test/utils.tsx +12 -0
- package/src/useBibleClient.test.tsx +8 -37
- package/src/useBook.test.tsx +158 -0
- package/src/useBooks.test.tsx +148 -0
- package/src/useChapter.test.tsx +70 -128
- package/src/useChapterNavigation.ts +3 -0
- package/src/useChapters.test.tsx +80 -150
- package/src/useHighlights.test.tsx +33 -104
- package/src/useInitData.ts +4 -0
- package/src/useLanguage.test.tsx +8 -10
- package/src/useLanguageClient.test.tsx +9 -25
- package/src/useLanguages.test.tsx +27 -64
- package/src/usePassage.test.tsx +304 -0
- package/src/useTheme.test.tsx +32 -0
- package/src/useVOTD.test.tsx +28 -67
- package/src/useVerse.test.tsx +73 -149
- package/src/useVerseSelection.ts +3 -0
- package/src/useVerses.test.tsx +37 -104
- package/src/useVersion.test.tsx +29 -66
- package/src/useVersions.test.tsx +72 -154
- package/src/useYVAuth.test.tsx +26 -134
- package/src/utility/getDayOfYear.test.ts +48 -0
- package/vitest.config.ts +12 -0
- package/src/context/ReaderProvider.test.tsx +0 -264
- package/src/context/VerseSelectionProvider.test.tsx +0 -362
- package/src/useChapterNavigation.test.tsx +0 -160
- package/src/useVerseSelection.test.tsx +0 -33
- package/vitest.setup.ts +0 -1
package/src/useChapters.test.tsx
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
2
|
-
import { describe,
|
|
3
|
-
import type { ReactNode } from 'react';
|
|
2
|
+
import { describe, expect, vi, beforeEach, it } from 'vitest';
|
|
4
3
|
import { useChapters } from './useChapters';
|
|
5
|
-
import { YouVersionContext } from './context';
|
|
6
4
|
import { type BibleClient, type BibleChapter, type Collection } from '@youversion/platform-core';
|
|
7
5
|
import { useBibleClient } from './useBibleClient';
|
|
6
|
+
import { createYVWrapper } from './test/utils';
|
|
8
7
|
|
|
9
8
|
vi.mock('./useBibleClient');
|
|
10
9
|
|
|
11
10
|
describe('useChapters', () => {
|
|
12
|
-
const mockAppKey = 'test-app-key';
|
|
13
11
|
const mockGetChapters = vi.fn();
|
|
14
12
|
|
|
15
13
|
const mockChapters: Collection<BibleChapter> = {
|
|
@@ -21,15 +19,7 @@ describe('useChapters', () => {
|
|
|
21
19
|
next_page_token: null,
|
|
22
20
|
};
|
|
23
21
|
|
|
24
|
-
const createWrapper = (contextValue: { appKey: string }) => {
|
|
25
|
-
return ({ children }: { children: ReactNode }) => (
|
|
26
|
-
<YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
|
|
27
|
-
);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
22
|
beforeEach(() => {
|
|
31
|
-
vi.resetAllMocks();
|
|
32
|
-
|
|
33
23
|
mockGetChapters.mockResolvedValue(mockChapters);
|
|
34
24
|
|
|
35
25
|
const mockClient: Partial<BibleClient> = { getChapters: mockGetChapters };
|
|
@@ -38,10 +28,7 @@ describe('useChapters', () => {
|
|
|
38
28
|
|
|
39
29
|
describe('fetching chapters', () => {
|
|
40
30
|
it('should fetch chapters with versionId, book params', async () => {
|
|
41
|
-
const wrapper =
|
|
42
|
-
appKey: mockAppKey,
|
|
43
|
-
});
|
|
44
|
-
|
|
31
|
+
const wrapper = createYVWrapper();
|
|
45
32
|
const { result } = renderHook(() => useChapters(111, 'MAT'), { wrapper });
|
|
46
33
|
|
|
47
34
|
expect(result.current.loading).toBe(true);
|
|
@@ -51,73 +38,58 @@ describe('useChapters', () => {
|
|
|
51
38
|
expect(result.current.loading).toBe(false);
|
|
52
39
|
});
|
|
53
40
|
|
|
54
|
-
expect(mockGetChapters).toHaveBeenCalledWith(111, 'MAT');
|
|
55
|
-
expect(result.current.chapters).toEqual(mockChapters);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should refetch when versionId changes', async () => {
|
|
59
|
-
const wrapper = createWrapper({
|
|
60
|
-
appKey: mockAppKey,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const { result, rerender } = renderHook(({ versionId }) => useChapters(versionId, 'MAT'), {
|
|
64
|
-
wrapper,
|
|
65
|
-
initialProps: { versionId: 1 },
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
await waitFor(() => {
|
|
69
|
-
expect(result.current.loading).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(mockGetChapters).toHaveBeenCalledTimes(1);
|
|
73
|
-
expect(mockGetChapters).toHaveBeenLastCalledWith(1, 'MAT');
|
|
74
|
-
|
|
75
|
-
act(() => {
|
|
76
|
-
rerender({ versionId: 111 });
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
await waitFor(() => {
|
|
80
|
-
expect(result.current.loading).toBe(false);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
expect(mockGetChapters).toHaveBeenCalledTimes(2);
|
|
84
|
-
expect(mockGetChapters).toHaveBeenLastCalledWith(111, 'MAT');
|
|
41
|
+
expect.soft(mockGetChapters).toHaveBeenCalledWith(111, 'MAT');
|
|
42
|
+
expect.soft(result.current.chapters).toEqual(mockChapters);
|
|
85
43
|
});
|
|
86
44
|
|
|
87
|
-
it(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
45
|
+
it.each([
|
|
46
|
+
{
|
|
47
|
+
param: 'versionId',
|
|
48
|
+
HookFn: ({ val }: { val: number | string }) => useChapters(val as number, 'MAT'),
|
|
49
|
+
initial: { val: 1 },
|
|
50
|
+
updated: { val: 111 },
|
|
51
|
+
expectedInitial: [1, 'MAT'],
|
|
52
|
+
expectedUpdated: [111, 'MAT'],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
param: 'book',
|
|
56
|
+
HookFn: ({ val }: { val: number | string }) => useChapters(1, val as string),
|
|
57
|
+
initial: { val: 'MAT' },
|
|
58
|
+
updated: { val: 'GEN' },
|
|
59
|
+
expectedInitial: [1, 'MAT'],
|
|
60
|
+
expectedUpdated: [1, 'GEN'],
|
|
61
|
+
},
|
|
62
|
+
])(
|
|
63
|
+
'should refetch when $param changes',
|
|
64
|
+
async ({ HookFn, initial, updated, expectedInitial, expectedUpdated }) => {
|
|
65
|
+
const wrapper = createYVWrapper();
|
|
66
|
+
const { result, rerender } = renderHook(HookFn, {
|
|
67
|
+
wrapper,
|
|
68
|
+
initialProps: initial,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(result.current.loading).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect.soft(mockGetChapters).toHaveBeenCalledTimes(1);
|
|
76
|
+
expect.soft(mockGetChapters).toHaveBeenLastCalledWith(...expectedInitial);
|
|
77
|
+
|
|
78
|
+
act(() => {
|
|
79
|
+
rerender(updated);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await waitFor(() => {
|
|
83
|
+
expect(result.current.loading).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect.soft(mockGetChapters).toHaveBeenCalledTimes(2);
|
|
87
|
+
expect.soft(mockGetChapters).toHaveBeenLastCalledWith(...expectedUpdated);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
115
90
|
|
|
116
91
|
it('should not fetch when enabled is false', async () => {
|
|
117
|
-
const wrapper =
|
|
118
|
-
appKey: mockAppKey,
|
|
119
|
-
});
|
|
120
|
-
|
|
92
|
+
const wrapper = createYVWrapper();
|
|
121
93
|
const { result } = renderHook(() => useChapters(1, 'MAT', { enabled: false }), {
|
|
122
94
|
wrapper,
|
|
123
95
|
});
|
|
@@ -126,44 +98,38 @@ describe('useChapters', () => {
|
|
|
126
98
|
expect(result.current.loading).toBe(false);
|
|
127
99
|
});
|
|
128
100
|
|
|
129
|
-
expect(mockGetChapters).not.toHaveBeenCalled();
|
|
130
|
-
expect(result.current.chapters).toBe(null);
|
|
101
|
+
expect.soft(mockGetChapters).not.toHaveBeenCalled();
|
|
102
|
+
expect.soft(result.current.chapters).toBe(null);
|
|
131
103
|
});
|
|
132
104
|
|
|
133
105
|
it('should handle fetch errors', async () => {
|
|
106
|
+
const wrapper = createYVWrapper();
|
|
134
107
|
const error = new Error('Failed to fetch chapters');
|
|
135
108
|
mockGetChapters.mockRejectedValueOnce(error);
|
|
136
109
|
|
|
137
|
-
const wrapper = createWrapper({
|
|
138
|
-
appKey: mockAppKey,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
110
|
const { result } = renderHook(() => useChapters(1, 'MAT'), { wrapper });
|
|
142
111
|
|
|
143
112
|
await waitFor(() => {
|
|
144
113
|
expect(result.current.loading).toBe(false);
|
|
145
114
|
});
|
|
146
115
|
|
|
147
|
-
expect(result.current.error).toEqual(error);
|
|
148
|
-
expect(result.current.chapters).toBe(null);
|
|
116
|
+
expect.soft(result.current.error).toEqual(error);
|
|
117
|
+
expect.soft(result.current.chapters).toBe(null);
|
|
149
118
|
});
|
|
150
119
|
|
|
151
120
|
it('should clear error on successful refetch', async () => {
|
|
121
|
+
const wrapper = createYVWrapper();
|
|
152
122
|
const error = new Error('Failed to fetch chapters');
|
|
153
123
|
mockGetChapters.mockRejectedValueOnce(error).mockResolvedValueOnce(mockChapters);
|
|
154
124
|
|
|
155
|
-
const wrapper = createWrapper({
|
|
156
|
-
appKey: mockAppKey,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
125
|
const { result } = renderHook(() => useChapters(1, 'MAT'), { wrapper });
|
|
160
126
|
|
|
161
127
|
await waitFor(() => {
|
|
162
128
|
expect(result.current.loading).toBe(false);
|
|
163
129
|
});
|
|
164
130
|
|
|
165
|
-
expect(result.current.error).toEqual(error);
|
|
166
|
-
expect(result.current.chapters).toBe(null);
|
|
131
|
+
expect.soft(result.current.error).toEqual(error);
|
|
132
|
+
expect.soft(result.current.chapters).toBe(null);
|
|
167
133
|
|
|
168
134
|
act(() => {
|
|
169
135
|
result.current.refetch();
|
|
@@ -173,15 +139,12 @@ describe('useChapters', () => {
|
|
|
173
139
|
expect(result.current.loading).toBe(false);
|
|
174
140
|
});
|
|
175
141
|
|
|
176
|
-
expect(result.current.error).toBe(null);
|
|
177
|
-
expect(result.current.chapters).toEqual(mockChapters);
|
|
142
|
+
expect.soft(result.current.error).toBe(null);
|
|
143
|
+
expect.soft(result.current.chapters).toEqual(mockChapters);
|
|
178
144
|
});
|
|
179
145
|
|
|
180
146
|
it('should support manual refetch', async () => {
|
|
181
|
-
const wrapper =
|
|
182
|
-
appKey: mockAppKey,
|
|
183
|
-
});
|
|
184
|
-
|
|
147
|
+
const wrapper = createYVWrapper();
|
|
185
148
|
const { result } = renderHook(() => useChapters(1, 'MAT'), { wrapper });
|
|
186
149
|
|
|
187
150
|
await waitFor(() => {
|
|
@@ -201,56 +164,23 @@ describe('useChapters', () => {
|
|
|
201
164
|
});
|
|
202
165
|
|
|
203
166
|
describe('book validation', () => {
|
|
204
|
-
it(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
it('should skip fetch when book is "null" string', async () => {
|
|
220
|
-
const wrapper = createWrapper({
|
|
221
|
-
appKey: mockAppKey,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const { result } = renderHook(() => useChapters(1, 'null'), { wrapper });
|
|
225
|
-
|
|
226
|
-
await waitFor(() => {
|
|
227
|
-
expect(result.current.loading).toBe(false);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
expect(mockGetChapters).not.toHaveBeenCalled();
|
|
231
|
-
expect(result.current.chapters).toBe(null);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('should skip fetch when book is empty string', async () => {
|
|
235
|
-
const wrapper = createWrapper({
|
|
236
|
-
appKey: mockAppKey,
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const { result } = renderHook(() => useChapters(1, ''), { wrapper });
|
|
240
|
-
|
|
241
|
-
await waitFor(() => {
|
|
242
|
-
expect(result.current.loading).toBe(false);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
expect(mockGetChapters).not.toHaveBeenCalled();
|
|
246
|
-
expect(result.current.chapters).toBe(null);
|
|
247
|
-
});
|
|
167
|
+
it.each([{ invalidBook: 'undefined' }, { invalidBook: 'null' }, { invalidBook: '' }])(
|
|
168
|
+
'should skip fetch when book is "$invalidBook"',
|
|
169
|
+
async ({ invalidBook }) => {
|
|
170
|
+
const wrapper = createYVWrapper();
|
|
171
|
+
const { result } = renderHook(() => useChapters(1, invalidBook), { wrapper });
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(result.current.loading).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect.soft(mockGetChapters).not.toHaveBeenCalled();
|
|
178
|
+
expect.soft(result.current.chapters).toBe(null);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
248
181
|
|
|
249
182
|
it('should fetch when book changes from invalid to valid', async () => {
|
|
250
|
-
const wrapper =
|
|
251
|
-
appKey: mockAppKey,
|
|
252
|
-
});
|
|
253
|
-
|
|
183
|
+
const wrapper = createYVWrapper();
|
|
254
184
|
const { result, rerender } = renderHook(({ book }) => useChapters(1, book), {
|
|
255
185
|
wrapper,
|
|
256
186
|
initialProps: { book: 'undefined' },
|
|
@@ -270,9 +200,9 @@ describe('useChapters', () => {
|
|
|
270
200
|
expect(result.current.loading).toBe(false);
|
|
271
201
|
});
|
|
272
202
|
|
|
273
|
-
expect(mockGetChapters).toHaveBeenCalledTimes(1);
|
|
274
|
-
expect(mockGetChapters).toHaveBeenCalledWith(1, 'MAT');
|
|
275
|
-
expect(result.current.chapters).toEqual(mockChapters);
|
|
203
|
+
expect.soft(mockGetChapters).toHaveBeenCalledTimes(1);
|
|
204
|
+
expect.soft(mockGetChapters).toHaveBeenCalledWith(1, 'MAT');
|
|
205
|
+
expect.soft(result.current.chapters).toEqual(mockChapters);
|
|
276
206
|
});
|
|
277
207
|
});
|
|
278
208
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
2
|
-
import { describe,
|
|
2
|
+
import { describe, expect, vi, beforeEach, it, type Mock } from 'vitest';
|
|
3
3
|
import type { ReactNode } from 'react';
|
|
4
4
|
import { useHighlights } from './useHighlights';
|
|
5
5
|
import { YouVersionContext } from './context';
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
type Highlight,
|
|
11
11
|
type CreateHighlight,
|
|
12
12
|
} from '@youversion/platform-core';
|
|
13
|
+
import { createYVWrapper } from './test/utils';
|
|
13
14
|
|
|
14
|
-
// Mock the core package
|
|
15
15
|
vi.mock('@youversion/platform-core', async () => {
|
|
16
16
|
const actual = await vi.importActual('@youversion/platform-core');
|
|
17
17
|
return {
|
|
@@ -26,8 +26,6 @@ vi.mock('@youversion/platform-core', async () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
describe('useHighlights', () => {
|
|
29
|
-
const mockAppKey = 'test-app-key';
|
|
30
|
-
|
|
31
29
|
const mockHighlights: Collection<Highlight> = {
|
|
32
30
|
data: [
|
|
33
31
|
{
|
|
@@ -54,15 +52,7 @@ describe('useHighlights', () => {
|
|
|
54
52
|
let mockCreateHighlight: Mock;
|
|
55
53
|
let mockDeleteHighlight: Mock;
|
|
56
54
|
|
|
57
|
-
const createWrapper = (contextValue: { appKey: string }) => {
|
|
58
|
-
return ({ children }: { children: ReactNode }) => (
|
|
59
|
-
<YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
|
|
60
|
-
);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
55
|
beforeEach(() => {
|
|
64
|
-
vi.clearAllMocks();
|
|
65
|
-
|
|
66
56
|
mockGetHighlights = vi.fn().mockResolvedValue(mockHighlights);
|
|
67
57
|
mockCreateHighlight = vi.fn().mockResolvedValue(mockHighlight);
|
|
68
58
|
mockDeleteHighlight = vi.fn().mockResolvedValue(undefined);
|
|
@@ -88,47 +78,30 @@ describe('useHighlights', () => {
|
|
|
88
78
|
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
89
79
|
);
|
|
90
80
|
});
|
|
91
|
-
|
|
92
|
-
it('should throw error when appKey is missing', () => {
|
|
93
|
-
const wrapper = createWrapper({
|
|
94
|
-
appKey: '',
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
expect(() => renderHook(() => useHighlights(), { wrapper })).toThrow(
|
|
98
|
-
'YouVersion context not found. Make sure your component is wrapped with YouVersionProvider and an API key is provided.',
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
81
|
});
|
|
102
82
|
|
|
103
83
|
describe('client creation', () => {
|
|
104
84
|
it('should create HighlightsClient with correct ApiClient config', () => {
|
|
105
|
-
const wrapper =
|
|
106
|
-
appKey: mockAppKey,
|
|
107
|
-
});
|
|
108
|
-
|
|
85
|
+
const wrapper = createYVWrapper();
|
|
109
86
|
renderHook(() => useHighlights(), { wrapper });
|
|
110
87
|
|
|
111
88
|
expect(ApiClient).toHaveBeenCalledWith({
|
|
112
|
-
appKey:
|
|
89
|
+
appKey: 'test-app-key',
|
|
113
90
|
});
|
|
114
91
|
expect(HighlightsClient).toHaveBeenCalledWith(expect.objectContaining({ isApiClient: true }));
|
|
115
92
|
});
|
|
116
93
|
|
|
117
94
|
it('should memoize HighlightsClient instance', () => {
|
|
118
|
-
const wrapper =
|
|
119
|
-
appKey: mockAppKey,
|
|
120
|
-
});
|
|
121
|
-
|
|
95
|
+
const wrapper = createYVWrapper();
|
|
122
96
|
const { rerender } = renderHook(() => useHighlights(), { wrapper });
|
|
123
97
|
|
|
124
98
|
rerender();
|
|
125
99
|
|
|
126
|
-
// If client is memoized, refetch should be stable (though useCallback might still create new refs)
|
|
127
100
|
expect(HighlightsClient).toHaveBeenCalledTimes(1);
|
|
128
101
|
});
|
|
129
102
|
|
|
130
103
|
it('should create new HighlightsClient when context values change', () => {
|
|
131
|
-
let currentAppKey =
|
|
104
|
+
let currentAppKey = 'test-app-key';
|
|
132
105
|
|
|
133
106
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
134
107
|
<YouVersionContext.Provider
|
|
@@ -154,10 +127,7 @@ describe('useHighlights', () => {
|
|
|
154
127
|
|
|
155
128
|
describe('fetching highlights', () => {
|
|
156
129
|
it('should fetch highlights with no options', async () => {
|
|
157
|
-
const wrapper =
|
|
158
|
-
appKey: mockAppKey,
|
|
159
|
-
});
|
|
160
|
-
|
|
130
|
+
const wrapper = createYVWrapper();
|
|
161
131
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
162
132
|
|
|
163
133
|
expect(result.current.loading).toBe(true);
|
|
@@ -167,45 +137,36 @@ describe('useHighlights', () => {
|
|
|
167
137
|
expect(result.current.loading).toBe(false);
|
|
168
138
|
});
|
|
169
139
|
|
|
170
|
-
expect(mockGetHighlights).toHaveBeenCalledWith(undefined);
|
|
171
|
-
expect(result.current.highlights).toEqual(mockHighlights);
|
|
140
|
+
expect.soft(mockGetHighlights).toHaveBeenCalledWith(undefined);
|
|
141
|
+
expect.soft(result.current.highlights).toEqual(mockHighlights);
|
|
172
142
|
});
|
|
173
143
|
|
|
174
144
|
it('should fetch highlights with version_id option', async () => {
|
|
175
|
-
const wrapper =
|
|
176
|
-
appKey: mockAppKey,
|
|
177
|
-
});
|
|
178
|
-
|
|
145
|
+
const wrapper = createYVWrapper();
|
|
179
146
|
const { result } = renderHook(() => useHighlights({ version_id: 111 }), { wrapper });
|
|
180
147
|
|
|
181
148
|
await waitFor(() => {
|
|
182
149
|
expect(result.current.loading).toBe(false);
|
|
183
150
|
});
|
|
184
151
|
|
|
185
|
-
expect(mockGetHighlights).toHaveBeenCalledWith({ version_id: 111 });
|
|
186
|
-
expect(result.current.highlights).toEqual(mockHighlights);
|
|
152
|
+
expect.soft(mockGetHighlights).toHaveBeenCalledWith({ version_id: 111 });
|
|
153
|
+
expect.soft(result.current.highlights).toEqual(mockHighlights);
|
|
187
154
|
});
|
|
188
155
|
|
|
189
156
|
it('should fetch highlights with passage_id option', async () => {
|
|
190
|
-
const wrapper =
|
|
191
|
-
appKey: mockAppKey,
|
|
192
|
-
});
|
|
193
|
-
|
|
157
|
+
const wrapper = createYVWrapper();
|
|
194
158
|
const { result } = renderHook(() => useHighlights({ passage_id: 'MAT.1.1' }), { wrapper });
|
|
195
159
|
|
|
196
160
|
await waitFor(() => {
|
|
197
161
|
expect(result.current.loading).toBe(false);
|
|
198
162
|
});
|
|
199
163
|
|
|
200
|
-
expect(mockGetHighlights).toHaveBeenCalledWith({ passage_id: 'MAT.1.1' });
|
|
201
|
-
expect(result.current.highlights).toEqual(mockHighlights);
|
|
164
|
+
expect.soft(mockGetHighlights).toHaveBeenCalledWith({ passage_id: 'MAT.1.1' });
|
|
165
|
+
expect.soft(result.current.highlights).toEqual(mockHighlights);
|
|
202
166
|
});
|
|
203
167
|
|
|
204
168
|
it('should fetch highlights with both options', async () => {
|
|
205
|
-
const wrapper =
|
|
206
|
-
appKey: mockAppKey,
|
|
207
|
-
});
|
|
208
|
-
|
|
169
|
+
const wrapper = createYVWrapper();
|
|
209
170
|
const { result } = renderHook(
|
|
210
171
|
() => useHighlights({ version_id: 111, passage_id: 'MAT.1.1' }),
|
|
211
172
|
{
|
|
@@ -217,18 +178,15 @@ describe('useHighlights', () => {
|
|
|
217
178
|
expect(result.current.loading).toBe(false);
|
|
218
179
|
});
|
|
219
180
|
|
|
220
|
-
expect(mockGetHighlights).toHaveBeenCalledWith({
|
|
181
|
+
expect.soft(mockGetHighlights).toHaveBeenCalledWith({
|
|
221
182
|
version_id: 111,
|
|
222
183
|
passage_id: 'MAT.1.1',
|
|
223
184
|
});
|
|
224
|
-
expect(result.current.highlights).toEqual(mockHighlights);
|
|
185
|
+
expect.soft(result.current.highlights).toEqual(mockHighlights);
|
|
225
186
|
});
|
|
226
187
|
|
|
227
188
|
it('should refetch when options change', async () => {
|
|
228
|
-
const wrapper =
|
|
229
|
-
appKey: mockAppKey,
|
|
230
|
-
});
|
|
231
|
-
|
|
189
|
+
const wrapper = createYVWrapper();
|
|
232
190
|
const { result, rerender } = renderHook(({ options }) => useHighlights(options), {
|
|
233
191
|
wrapper,
|
|
234
192
|
initialProps: { options: { version_id: 111 } },
|
|
@@ -246,15 +204,12 @@ describe('useHighlights', () => {
|
|
|
246
204
|
expect(result.current.loading).toBe(false);
|
|
247
205
|
});
|
|
248
206
|
|
|
249
|
-
expect(mockGetHighlights).toHaveBeenCalledTimes(2);
|
|
250
|
-
expect(mockGetHighlights).toHaveBeenLastCalledWith({ version_id: 1 });
|
|
207
|
+
expect.soft(mockGetHighlights).toHaveBeenCalledTimes(2);
|
|
208
|
+
expect.soft(mockGetHighlights).toHaveBeenLastCalledWith({ version_id: 1 });
|
|
251
209
|
});
|
|
252
210
|
|
|
253
211
|
it('should not fetch when enabled is false', async () => {
|
|
254
|
-
const wrapper =
|
|
255
|
-
appKey: mockAppKey,
|
|
256
|
-
});
|
|
257
|
-
|
|
212
|
+
const wrapper = createYVWrapper();
|
|
258
213
|
const { result } = renderHook(() => useHighlights(undefined, { enabled: false }), {
|
|
259
214
|
wrapper,
|
|
260
215
|
});
|
|
@@ -263,33 +218,27 @@ describe('useHighlights', () => {
|
|
|
263
218
|
expect(result.current.loading).toBe(false);
|
|
264
219
|
});
|
|
265
220
|
|
|
266
|
-
expect(mockGetHighlights).not.toHaveBeenCalled();
|
|
267
|
-
expect(result.current.highlights).toBe(null);
|
|
221
|
+
expect.soft(mockGetHighlights).not.toHaveBeenCalled();
|
|
222
|
+
expect.soft(result.current.highlights).toBe(null);
|
|
268
223
|
});
|
|
269
224
|
|
|
270
225
|
it('should handle fetch errors', async () => {
|
|
226
|
+
const wrapper = createYVWrapper();
|
|
271
227
|
const error = new Error('Failed to fetch highlights');
|
|
272
228
|
mockGetHighlights.mockRejectedValueOnce(error);
|
|
273
229
|
|
|
274
|
-
const wrapper = createWrapper({
|
|
275
|
-
appKey: mockAppKey,
|
|
276
|
-
});
|
|
277
|
-
|
|
278
230
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
279
231
|
|
|
280
232
|
await waitFor(() => {
|
|
281
233
|
expect(result.current.loading).toBe(false);
|
|
282
234
|
});
|
|
283
235
|
|
|
284
|
-
expect(result.current.error).toEqual(error);
|
|
285
|
-
expect(result.current.highlights).toBe(null);
|
|
236
|
+
expect.soft(result.current.error).toEqual(error);
|
|
237
|
+
expect.soft(result.current.highlights).toBe(null);
|
|
286
238
|
});
|
|
287
239
|
|
|
288
240
|
it('should support manual refetch', async () => {
|
|
289
|
-
const wrapper =
|
|
290
|
-
appKey: mockAppKey,
|
|
291
|
-
});
|
|
292
|
-
|
|
241
|
+
const wrapper = createYVWrapper();
|
|
293
242
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
294
243
|
|
|
295
244
|
await waitFor(() => {
|
|
@@ -308,10 +257,7 @@ describe('useHighlights', () => {
|
|
|
308
257
|
|
|
309
258
|
describe('createHighlight mutation', () => {
|
|
310
259
|
it('should create highlight and refetch', async () => {
|
|
311
|
-
const wrapper =
|
|
312
|
-
appKey: mockAppKey,
|
|
313
|
-
});
|
|
314
|
-
|
|
260
|
+
const wrapper = createYVWrapper();
|
|
315
261
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
316
262
|
|
|
317
263
|
await waitFor(() => {
|
|
@@ -333,20 +279,16 @@ describe('useHighlights', () => {
|
|
|
333
279
|
const created = await createPromise;
|
|
334
280
|
expect(created).toEqual(mockHighlight);
|
|
335
281
|
|
|
336
|
-
// Should refetch after creation
|
|
337
282
|
await waitFor(() => {
|
|
338
283
|
expect(mockGetHighlights).toHaveBeenCalledTimes(2);
|
|
339
284
|
});
|
|
340
285
|
});
|
|
341
286
|
|
|
342
287
|
it('should handle create error', async () => {
|
|
288
|
+
const wrapper = createYVWrapper();
|
|
343
289
|
const error = new Error('Failed to create highlight');
|
|
344
290
|
mockCreateHighlight.mockRejectedValueOnce(error);
|
|
345
291
|
|
|
346
|
-
const wrapper = createWrapper({
|
|
347
|
-
appKey: mockAppKey,
|
|
348
|
-
});
|
|
349
|
-
|
|
350
292
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
351
293
|
|
|
352
294
|
await waitFor(() => {
|
|
@@ -363,17 +305,13 @@ describe('useHighlights', () => {
|
|
|
363
305
|
'Failed to create highlight',
|
|
364
306
|
);
|
|
365
307
|
|
|
366
|
-
// Should not refetch on error
|
|
367
308
|
expect(mockGetHighlights).toHaveBeenCalledTimes(1);
|
|
368
309
|
});
|
|
369
310
|
});
|
|
370
311
|
|
|
371
312
|
describe('deleteHighlight mutation', () => {
|
|
372
313
|
it('should delete highlight and refetch', async () => {
|
|
373
|
-
const wrapper =
|
|
374
|
-
appKey: mockAppKey,
|
|
375
|
-
});
|
|
376
|
-
|
|
314
|
+
const wrapper = createYVWrapper();
|
|
377
315
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
378
316
|
|
|
379
317
|
await waitFor(() => {
|
|
@@ -388,17 +326,13 @@ describe('useHighlights', () => {
|
|
|
388
326
|
|
|
389
327
|
await deletePromise;
|
|
390
328
|
|
|
391
|
-
// Should refetch after deletion
|
|
392
329
|
await waitFor(() => {
|
|
393
330
|
expect(mockGetHighlights).toHaveBeenCalledTimes(2);
|
|
394
331
|
});
|
|
395
332
|
});
|
|
396
333
|
|
|
397
334
|
it('should delete highlight with options', async () => {
|
|
398
|
-
const wrapper =
|
|
399
|
-
appKey: mockAppKey,
|
|
400
|
-
});
|
|
401
|
-
|
|
335
|
+
const wrapper = createYVWrapper();
|
|
402
336
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
403
337
|
|
|
404
338
|
await waitFor(() => {
|
|
@@ -413,18 +347,14 @@ describe('useHighlights', () => {
|
|
|
413
347
|
|
|
414
348
|
await deletePromise;
|
|
415
349
|
|
|
416
|
-
// Should refetch after deletion
|
|
417
350
|
expect(mockGetHighlights).toHaveBeenCalledTimes(2);
|
|
418
351
|
});
|
|
419
352
|
|
|
420
353
|
it('should handle delete error', async () => {
|
|
354
|
+
const wrapper = createYVWrapper();
|
|
421
355
|
const error = new Error('Failed to delete highlight');
|
|
422
356
|
mockDeleteHighlight.mockRejectedValueOnce(error);
|
|
423
357
|
|
|
424
|
-
const wrapper = createWrapper({
|
|
425
|
-
appKey: mockAppKey,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
358
|
const { result } = renderHook(() => useHighlights(), { wrapper });
|
|
429
359
|
|
|
430
360
|
await waitFor(() => {
|
|
@@ -435,7 +365,6 @@ describe('useHighlights', () => {
|
|
|
435
365
|
'Failed to delete highlight',
|
|
436
366
|
);
|
|
437
367
|
|
|
438
|
-
// Should not refetch on error
|
|
439
368
|
expect(mockGetHighlights).toHaveBeenCalledTimes(1);
|
|
440
369
|
});
|
|
441
370
|
});
|
package/src/useInitData.ts
CHANGED
|
@@ -22,6 +22,10 @@ interface InitData {
|
|
|
22
22
|
chapter: BibleChapter;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated This hook will be removed in the next major version.
|
|
27
|
+
* Use `useVersion`, `useBook`, and `useChapter` directly instead.
|
|
28
|
+
*/
|
|
25
29
|
export function useInitData(
|
|
26
30
|
{ version, book, chapter }: Props = {
|
|
27
31
|
version: DEFAULT.VERSION,
|