@youversion/platform-react-hooks 1.18.1 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +3 -7
  3. package/CHANGELOG.md +17 -0
  4. package/dist/__tests__/mocks/bibles.d.ts +6 -1
  5. package/dist/__tests__/mocks/bibles.d.ts.map +1 -1
  6. package/dist/__tests__/mocks/bibles.js +10 -0
  7. package/dist/__tests__/mocks/bibles.js.map +1 -1
  8. package/dist/__tests__/mocks/core-mock-factory.d.ts +83 -0
  9. package/dist/__tests__/mocks/core-mock-factory.d.ts.map +1 -0
  10. package/dist/__tests__/mocks/core-mock-factory.js +138 -0
  11. package/dist/__tests__/mocks/core-mock-factory.js.map +1 -0
  12. package/dist/context/ReaderContext.d.ts +6 -0
  13. package/dist/context/ReaderContext.d.ts.map +1 -1
  14. package/dist/context/ReaderContext.js +6 -0
  15. package/dist/context/ReaderContext.js.map +1 -1
  16. package/dist/context/ReaderProvider.d.ts +3 -0
  17. package/dist/context/ReaderProvider.d.ts.map +1 -1
  18. package/dist/context/ReaderProvider.js +3 -0
  19. package/dist/context/ReaderProvider.js.map +1 -1
  20. package/dist/context/VerseSelectionContext.d.ts +6 -0
  21. package/dist/context/VerseSelectionContext.d.ts.map +1 -1
  22. package/dist/context/VerseSelectionContext.js +3 -0
  23. package/dist/context/VerseSelectionContext.js.map +1 -1
  24. package/dist/context/VerseSelectionProvider.d.ts +3 -0
  25. package/dist/context/VerseSelectionProvider.d.ts.map +1 -1
  26. package/dist/context/VerseSelectionProvider.js +3 -0
  27. package/dist/context/VerseSelectionProvider.js.map +1 -1
  28. package/dist/test/utils.d.ts +7 -0
  29. package/dist/test/utils.d.ts.map +1 -0
  30. package/dist/test/utils.js +7 -0
  31. package/dist/test/utils.js.map +1 -0
  32. package/dist/useChapterNavigation.d.ts +3 -0
  33. package/dist/useChapterNavigation.d.ts.map +1 -1
  34. package/dist/useChapterNavigation.js +3 -0
  35. package/dist/useChapterNavigation.js.map +1 -1
  36. package/dist/useInitData.d.ts +4 -0
  37. package/dist/useInitData.d.ts.map +1 -1
  38. package/dist/useInitData.js +4 -0
  39. package/dist/useInitData.js.map +1 -1
  40. package/dist/useVerseSelection.d.ts +3 -0
  41. package/dist/useVerseSelection.d.ts.map +1 -1
  42. package/dist/useVerseSelection.js +3 -0
  43. package/dist/useVerseSelection.js.map +1 -1
  44. package/package.json +2 -2
  45. package/src/__tests__/mocks/bibles.ts +18 -1
  46. package/src/__tests__/mocks/core-mock-factory.ts +226 -0
  47. package/src/context/ReaderContext.tsx +6 -0
  48. package/src/context/ReaderProvider.tsx +3 -0
  49. package/src/context/VerseSelectionContext.tsx +6 -0
  50. package/src/context/VerseSelectionProvider.tsx +3 -0
  51. package/src/context/YouVersionAuthProvider.test.tsx +14 -131
  52. package/src/test/utils.tsx +12 -0
  53. package/src/useBibleClient.test.tsx +8 -37
  54. package/src/useBook.test.tsx +158 -0
  55. package/src/useBooks.test.tsx +148 -0
  56. package/src/useChapter.test.tsx +70 -128
  57. package/src/useChapterNavigation.ts +3 -0
  58. package/src/useChapters.test.tsx +80 -150
  59. package/src/useHighlights.test.tsx +33 -104
  60. package/src/useInitData.ts +4 -0
  61. package/src/useLanguage.test.tsx +8 -10
  62. package/src/useLanguageClient.test.tsx +9 -25
  63. package/src/useLanguages.test.tsx +27 -64
  64. package/src/usePassage.test.tsx +304 -0
  65. package/src/useTheme.test.tsx +32 -0
  66. package/src/useVOTD.test.tsx +28 -67
  67. package/src/useVerse.test.tsx +73 -149
  68. package/src/useVerseSelection.ts +3 -0
  69. package/src/useVerses.test.tsx +37 -104
  70. package/src/useVersion.test.tsx +29 -66
  71. package/src/useVersions.test.tsx +72 -154
  72. package/src/useYVAuth.test.tsx +26 -134
  73. package/src/utility/getDayOfYear.test.ts +48 -0
  74. package/vitest.config.ts +12 -0
  75. package/src/context/ReaderProvider.test.tsx +0 -264
  76. package/src/context/VerseSelectionProvider.test.tsx +0 -362
  77. package/src/useChapterNavigation.test.tsx +0 -160
  78. package/src/useVerseSelection.test.tsx +0 -33
  79. package/vitest.setup.ts +0 -1
@@ -1,15 +1,13 @@
1
1
  import { renderHook, waitFor, act } from '@testing-library/react';
2
- import { describe, it, expect, vi, beforeEach } from 'vitest';
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 = createWrapper({
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('should refetch when book changes', async () => {
88
- const wrapper = createWrapper({
89
- appKey: mockAppKey,
90
- });
91
-
92
- const { result, rerender } = renderHook(({ book }) => useChapters(1, book), {
93
- wrapper,
94
- initialProps: { book: 'MAT' },
95
- });
96
-
97
- await waitFor(() => {
98
- expect(result.current.loading).toBe(false);
99
- });
100
-
101
- expect(mockGetChapters).toHaveBeenCalledTimes(1);
102
- expect(mockGetChapters).toHaveBeenLastCalledWith(1, 'MAT');
103
-
104
- act(() => {
105
- rerender({ book: 'GEN' });
106
- });
107
-
108
- await waitFor(() => {
109
- expect(result.current.loading).toBe(false);
110
- });
111
-
112
- expect(mockGetChapters).toHaveBeenCalledTimes(2);
113
- expect(mockGetChapters).toHaveBeenLastCalledWith(1, 'GEN');
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 = createWrapper({
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 = createWrapper({
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('should skip fetch when book is "undefined" string', async () => {
205
- const wrapper = createWrapper({
206
- appKey: mockAppKey,
207
- });
208
-
209
- const { result } = renderHook(() => useChapters(1, 'undefined'), { wrapper });
210
-
211
- await waitFor(() => {
212
- expect(result.current.loading).toBe(false);
213
- });
214
-
215
- expect(mockGetChapters).not.toHaveBeenCalled();
216
- expect(result.current.chapters).toBe(null);
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 = createWrapper({
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, it, expect, vi, beforeEach, type Mock } from 'vitest';
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 = createWrapper({
106
- appKey: mockAppKey,
107
- });
108
-
85
+ const wrapper = createYVWrapper();
109
86
  renderHook(() => useHighlights(), { wrapper });
110
87
 
111
88
  expect(ApiClient).toHaveBeenCalledWith({
112
- appKey: mockAppKey,
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 = createWrapper({
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 = mockAppKey;
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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 = createWrapper({
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
  });
@@ -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,