@youversion/platform-react-hooks 1.11.0 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,202 @@
1
+ import { renderHook, waitFor, act } from '@testing-library/react';
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import type { ReactNode } from 'react';
4
+ import { useVerses } from './useVerses';
5
+ import { YouVersionContext } from './context';
6
+ import { type BibleClient, type BibleVerse, type Collection } from '@youversion/platform-core';
7
+ import { useBibleClient } from './useBibleClient';
8
+
9
+ vi.mock('./useBibleClient');
10
+
11
+ describe('useVerses', () => {
12
+ const mockAppKey = 'test-app-key';
13
+ const mockGetVerses = vi.fn();
14
+
15
+ const mockVerses: Collection<BibleVerse> = {
16
+ data: [
17
+ { id: '1', passage_id: 'MAT.1.1', title: '1' },
18
+ { id: '2', passage_id: 'MAT.1.2', title: '2' },
19
+ { id: '3', passage_id: 'MAT.1.3', title: '3' },
20
+ ],
21
+ next_page_token: null,
22
+ };
23
+
24
+ const createWrapper = (contextValue: { appKey: string }) => {
25
+ return ({ children }: { children: ReactNode }) => (
26
+ <YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
27
+ );
28
+ };
29
+
30
+ beforeEach(() => {
31
+ vi.resetAllMocks();
32
+
33
+ mockGetVerses.mockResolvedValue(mockVerses);
34
+
35
+ const mockClient: Partial<BibleClient> = { getVerses: mockGetVerses };
36
+ vi.mocked(useBibleClient).mockReturnValue(mockClient as BibleClient);
37
+ });
38
+
39
+ describe('fetching verses', () => {
40
+ it('should fetch verses with all 3 parameters', async () => {
41
+ const wrapper = createWrapper({
42
+ appKey: mockAppKey,
43
+ });
44
+
45
+ const { result } = renderHook(() => useVerses(111, 'MAT', 1), { wrapper });
46
+
47
+ expect(result.current.loading).toBe(true);
48
+ expect(result.current.verses).toBe(null);
49
+
50
+ await waitFor(() => {
51
+ expect(result.current.loading).toBe(false);
52
+ });
53
+
54
+ expect(mockGetVerses).toHaveBeenCalledWith(111, 'MAT', 1);
55
+ expect(result.current.verses).toEqual(mockVerses);
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 }) => useVerses(versionId, 'MAT', 1), {
64
+ wrapper,
65
+ initialProps: { versionId: 1 },
66
+ });
67
+
68
+ await waitFor(() => {
69
+ expect(result.current.loading).toBe(false);
70
+ });
71
+
72
+ expect(mockGetVerses).toHaveBeenCalledTimes(1);
73
+ expect(mockGetVerses).toHaveBeenLastCalledWith(1, 'MAT', 1);
74
+
75
+ act(() => {
76
+ rerender({ versionId: 111 });
77
+ });
78
+
79
+ await waitFor(() => {
80
+ expect(result.current.loading).toBe(false);
81
+ });
82
+
83
+ expect(mockGetVerses).toHaveBeenCalledTimes(2);
84
+ expect(mockGetVerses).toHaveBeenLastCalledWith(111, 'MAT', 1);
85
+ });
86
+
87
+ it('should refetch when book changes', async () => {
88
+ const wrapper = createWrapper({
89
+ appKey: mockAppKey,
90
+ });
91
+
92
+ const { result, rerender } = renderHook(({ book }) => useVerses(1, book, 1), {
93
+ wrapper,
94
+ initialProps: { book: 'MAT' },
95
+ });
96
+
97
+ await waitFor(() => {
98
+ expect(result.current.loading).toBe(false);
99
+ });
100
+
101
+ expect(mockGetVerses).toHaveBeenCalledTimes(1);
102
+ expect(mockGetVerses).toHaveBeenLastCalledWith(1, 'MAT', 1);
103
+
104
+ act(() => {
105
+ rerender({ book: 'GEN' });
106
+ });
107
+
108
+ await waitFor(() => {
109
+ expect(result.current.loading).toBe(false);
110
+ });
111
+
112
+ expect(mockGetVerses).toHaveBeenCalledTimes(2);
113
+ expect(mockGetVerses).toHaveBeenLastCalledWith(1, 'GEN', 1);
114
+ });
115
+
116
+ it('should refetch when chapter changes', async () => {
117
+ const wrapper = createWrapper({
118
+ appKey: mockAppKey,
119
+ });
120
+
121
+ const { result, rerender } = renderHook(({ chapter }) => useVerses(1, 'MAT', chapter), {
122
+ wrapper,
123
+ initialProps: { chapter: 1 },
124
+ });
125
+
126
+ await waitFor(() => {
127
+ expect(result.current.loading).toBe(false);
128
+ });
129
+
130
+ expect(mockGetVerses).toHaveBeenCalledTimes(1);
131
+ expect(mockGetVerses).toHaveBeenLastCalledWith(1, 'MAT', 1);
132
+
133
+ act(() => {
134
+ rerender({ chapter: 5 });
135
+ });
136
+
137
+ await waitFor(() => {
138
+ expect(result.current.loading).toBe(false);
139
+ });
140
+
141
+ expect(mockGetVerses).toHaveBeenCalledTimes(2);
142
+ expect(mockGetVerses).toHaveBeenLastCalledWith(1, 'MAT', 5);
143
+ });
144
+
145
+ it('should not fetch when enabled is false', async () => {
146
+ const wrapper = createWrapper({
147
+ appKey: mockAppKey,
148
+ });
149
+
150
+ const { result } = renderHook(() => useVerses(1, 'MAT', 1, { enabled: false }), {
151
+ wrapper,
152
+ });
153
+
154
+ await waitFor(() => {
155
+ expect(result.current.loading).toBe(false);
156
+ });
157
+
158
+ expect(mockGetVerses).not.toHaveBeenCalled();
159
+ expect(result.current.verses).toBe(null);
160
+ });
161
+
162
+ it('should handle fetch errors', async () => {
163
+ const error = new Error('Failed to fetch verses');
164
+ mockGetVerses.mockRejectedValueOnce(error);
165
+
166
+ const wrapper = createWrapper({
167
+ appKey: mockAppKey,
168
+ });
169
+
170
+ const { result } = renderHook(() => useVerses(1, 'MAT', 1), { wrapper });
171
+
172
+ await waitFor(() => {
173
+ expect(result.current.loading).toBe(false);
174
+ });
175
+
176
+ expect(result.current.error).toEqual(error);
177
+ expect(result.current.verses).toBe(null);
178
+ });
179
+
180
+ it('should support manual refetch', async () => {
181
+ const wrapper = createWrapper({
182
+ appKey: mockAppKey,
183
+ });
184
+
185
+ const { result } = renderHook(() => useVerses(1, 'MAT', 1), { wrapper });
186
+
187
+ await waitFor(() => {
188
+ expect(result.current.loading).toBe(false);
189
+ });
190
+
191
+ expect(mockGetVerses).toHaveBeenCalledTimes(1);
192
+
193
+ act(() => {
194
+ result.current.refetch();
195
+ });
196
+
197
+ await waitFor(() => {
198
+ expect(mockGetVerses).toHaveBeenCalledTimes(2);
199
+ });
200
+ });
201
+ });
202
+ });
@@ -0,0 +1,242 @@
1
+ import { renderHook, waitFor, act } from '@testing-library/react';
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import type { ReactNode } from 'react';
4
+ import { useVersion } from './useVersion';
5
+ import { YouVersionContext } from './context';
6
+ import { type BibleClient, type BibleVersion } from '@youversion/platform-core';
7
+ import { useBibleClient } from './useBibleClient';
8
+
9
+ vi.mock('./useBibleClient');
10
+
11
+ describe('useVersion', () => {
12
+ const mockAppKey = 'test-app-key';
13
+ const mockGetVersion = vi.fn();
14
+
15
+ const mockVersion: BibleVersion = {
16
+ id: 111,
17
+ title: 'New International Version',
18
+ abbreviation: 'NIV',
19
+ localized_title: 'New International Version',
20
+ localized_abbreviation: 'NIV',
21
+ language_tag: 'en',
22
+ books: ['GEN', 'EXO', 'LEV'],
23
+ youversion_deep_link: 'https://bible.com/versions/111',
24
+ };
25
+
26
+ const createWrapper = (contextValue: { appKey: string }) => {
27
+ return ({ children }: { children: ReactNode }) => (
28
+ <YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
29
+ );
30
+ };
31
+
32
+ beforeEach(() => {
33
+ vi.resetAllMocks();
34
+
35
+ mockGetVersion.mockResolvedValue(mockVersion);
36
+
37
+ const mockClient: Partial<BibleClient> = { getVersion: mockGetVersion };
38
+ vi.mocked(useBibleClient).mockReturnValue(mockClient as BibleClient);
39
+ });
40
+
41
+ describe('fetching version', () => {
42
+ it('should fetch version by ID', async () => {
43
+ const wrapper = createWrapper({
44
+ appKey: mockAppKey,
45
+ });
46
+
47
+ const { result } = renderHook(() => useVersion(111), { wrapper });
48
+
49
+ expect(result.current.loading).toBe(true);
50
+ expect(result.current.version).toBe(null);
51
+
52
+ await waitFor(() => {
53
+ expect(result.current.loading).toBe(false);
54
+ });
55
+
56
+ expect(mockGetVersion).toHaveBeenCalledWith(111);
57
+ expect(result.current.version).toEqual(mockVersion);
58
+ });
59
+
60
+ it('should fetch different version by ID', async () => {
61
+ const mockKJV: BibleVersion = {
62
+ id: 1,
63
+ title: 'King James Version',
64
+ abbreviation: 'KJV',
65
+ localized_title: 'King James Version',
66
+ localized_abbreviation: 'KJV',
67
+ language_tag: 'en',
68
+ books: ['GEN', 'EXO', 'LEV'],
69
+ youversion_deep_link: 'https://bible.com/versions/1',
70
+ };
71
+ mockGetVersion.mockResolvedValueOnce(mockKJV);
72
+
73
+ const wrapper = createWrapper({
74
+ appKey: mockAppKey,
75
+ });
76
+
77
+ const { result } = renderHook(() => useVersion(1), { wrapper });
78
+
79
+ await waitFor(() => {
80
+ expect(result.current.loading).toBe(false);
81
+ });
82
+
83
+ expect(mockGetVersion).toHaveBeenCalledWith(1);
84
+ expect(result.current.version).toEqual(mockKJV);
85
+ });
86
+ });
87
+
88
+ describe('refetch behavior', () => {
89
+ it('should refetch when versionId changes', async () => {
90
+ const wrapper = createWrapper({
91
+ appKey: mockAppKey,
92
+ });
93
+
94
+ const { result, rerender } = renderHook(({ versionId }) => useVersion(versionId), {
95
+ wrapper,
96
+ initialProps: { versionId: 111 },
97
+ });
98
+
99
+ await waitFor(() => {
100
+ expect(result.current.loading).toBe(false);
101
+ });
102
+
103
+ expect(mockGetVersion).toHaveBeenCalledTimes(1);
104
+ expect(mockGetVersion).toHaveBeenNthCalledWith(1, 111);
105
+
106
+ rerender({ versionId: 1 });
107
+
108
+ await waitFor(() => {
109
+ expect(result.current.loading).toBe(false);
110
+ });
111
+
112
+ expect(mockGetVersion).toHaveBeenCalledTimes(2);
113
+ expect(mockGetVersion).toHaveBeenNthCalledWith(2, 1);
114
+ });
115
+ });
116
+
117
+ describe('enabled option', () => {
118
+ it('should not fetch when enabled is false', async () => {
119
+ const wrapper = createWrapper({
120
+ appKey: mockAppKey,
121
+ });
122
+
123
+ const { result } = renderHook(() => useVersion(111, { enabled: false }), {
124
+ wrapper,
125
+ });
126
+
127
+ await waitFor(() => {
128
+ expect(result.current.loading).toBe(false);
129
+ });
130
+
131
+ expect(mockGetVersion).not.toHaveBeenCalled();
132
+ expect(result.current.version).toBe(null);
133
+ });
134
+
135
+ it('should fetch when enabled is true', async () => {
136
+ const wrapper = createWrapper({
137
+ appKey: mockAppKey,
138
+ });
139
+
140
+ const { result } = renderHook(() => useVersion(111, { enabled: true }), {
141
+ wrapper,
142
+ });
143
+
144
+ await waitFor(() => {
145
+ expect(result.current.loading).toBe(false);
146
+ });
147
+
148
+ expect(mockGetVersion).toHaveBeenCalled();
149
+ expect(result.current.version).toEqual(mockVersion);
150
+ });
151
+
152
+ it('should fetch when enabled is not specified', async () => {
153
+ const wrapper = createWrapper({
154
+ appKey: mockAppKey,
155
+ });
156
+
157
+ const { result } = renderHook(() => useVersion(111), {
158
+ wrapper,
159
+ });
160
+
161
+ await waitFor(() => {
162
+ expect(result.current.loading).toBe(false);
163
+ });
164
+
165
+ expect(mockGetVersion).toHaveBeenCalled();
166
+ });
167
+ });
168
+
169
+ describe('error handling', () => {
170
+ it('should handle fetch errors', async () => {
171
+ const error = new Error('Failed to fetch version');
172
+ mockGetVersion.mockRejectedValueOnce(error);
173
+
174
+ const wrapper = createWrapper({
175
+ appKey: mockAppKey,
176
+ });
177
+
178
+ const { result } = renderHook(() => useVersion(111), { wrapper });
179
+
180
+ await waitFor(() => {
181
+ expect(result.current.loading).toBe(false);
182
+ });
183
+
184
+ expect(result.current.error).toEqual(error);
185
+ expect(result.current.version).toBe(null);
186
+ });
187
+
188
+ it('should clear error on successful refetch', async () => {
189
+ const error = new Error('Failed to fetch version');
190
+ mockGetVersion.mockRejectedValueOnce(error).mockResolvedValueOnce(mockVersion);
191
+
192
+ const wrapper = createWrapper({
193
+ appKey: mockAppKey,
194
+ });
195
+
196
+ const { result } = renderHook(() => useVersion(111), { wrapper });
197
+
198
+ await waitFor(() => {
199
+ expect(result.current.loading).toBe(false);
200
+ });
201
+
202
+ expect(result.current.error).toEqual(error);
203
+
204
+ act(() => {
205
+ result.current.refetch();
206
+ });
207
+
208
+ await waitFor(() => {
209
+ expect(result.current.error).toBe(null);
210
+ });
211
+
212
+ expect(result.current.version).toEqual(mockVersion);
213
+ });
214
+ });
215
+
216
+ describe('manual refetch', () => {
217
+ it('should support manual refetch', async () => {
218
+ const wrapper = createWrapper({
219
+ appKey: mockAppKey,
220
+ });
221
+
222
+ const { result } = renderHook(() => useVersion(111), { wrapper });
223
+
224
+ await waitFor(() => {
225
+ expect(result.current.loading).toBe(false);
226
+ });
227
+
228
+ expect(mockGetVersion).toHaveBeenCalledTimes(1);
229
+ expect(mockGetVersion).toHaveBeenNthCalledWith(1, 111);
230
+
231
+ act(() => {
232
+ result.current.refetch();
233
+ });
234
+
235
+ await waitFor(() => {
236
+ expect(mockGetVersion).toHaveBeenCalledTimes(2);
237
+ });
238
+
239
+ expect(mockGetVersion).toHaveBeenNthCalledWith(2, 111);
240
+ });
241
+ });
242
+ });