@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.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +3 -7
- package/CHANGELOG.md +17 -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
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, vi, beforeEach, it } from 'vitest';
|
|
3
|
+
import { useBook } from './useBook';
|
|
4
|
+
import { type BibleClient } from '@youversion/platform-core';
|
|
5
|
+
import { useBibleClient } from './useBibleClient';
|
|
6
|
+
import { createYVWrapper } from './test/utils';
|
|
7
|
+
import { createMockBook } from './__tests__/mocks/bibles';
|
|
8
|
+
|
|
9
|
+
vi.mock('./useBibleClient');
|
|
10
|
+
|
|
11
|
+
describe('useBook', () => {
|
|
12
|
+
const mockGetBook = vi.fn();
|
|
13
|
+
const mockBook = createMockBook();
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockGetBook.mockResolvedValue(mockBook);
|
|
17
|
+
|
|
18
|
+
const mockClient: Partial<BibleClient> = { getBook: mockGetBook };
|
|
19
|
+
vi.mocked(useBibleClient).mockReturnValue(mockClient as BibleClient);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('fetching book', () => {
|
|
23
|
+
it('should fetch book with versionId and book params', async () => {
|
|
24
|
+
const wrapper = createYVWrapper();
|
|
25
|
+
const { result } = renderHook(() => useBook(111, 'GEN'), { wrapper });
|
|
26
|
+
|
|
27
|
+
expect(result.current.loading).toBe(true);
|
|
28
|
+
expect(result.current.book).toBe(null);
|
|
29
|
+
|
|
30
|
+
await waitFor(() => {
|
|
31
|
+
expect(result.current.loading).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect.soft(mockGetBook).toHaveBeenCalledWith(111, 'GEN');
|
|
35
|
+
expect.soft(result.current.book).toEqual(mockBook);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it.each([
|
|
39
|
+
{
|
|
40
|
+
param: 'versionId',
|
|
41
|
+
HookFn: ({ val }: { val: number | string }) => useBook(val as number, 'GEN'),
|
|
42
|
+
initial: { val: 1 },
|
|
43
|
+
updated: { val: 111 },
|
|
44
|
+
expectedInitial: [1, 'GEN'],
|
|
45
|
+
expectedUpdated: [111, 'GEN'],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
param: 'book',
|
|
49
|
+
HookFn: ({ val }: { val: number | string }) => useBook(1, val as string),
|
|
50
|
+
initial: { val: 'GEN' },
|
|
51
|
+
updated: { val: 'EXO' },
|
|
52
|
+
expectedInitial: [1, 'GEN'],
|
|
53
|
+
expectedUpdated: [1, 'EXO'],
|
|
54
|
+
},
|
|
55
|
+
])(
|
|
56
|
+
'should refetch when $param changes',
|
|
57
|
+
async ({ HookFn, initial, updated, expectedInitial, expectedUpdated }) => {
|
|
58
|
+
const wrapper = createYVWrapper();
|
|
59
|
+
const { result, rerender } = renderHook(HookFn, {
|
|
60
|
+
wrapper,
|
|
61
|
+
initialProps: initial,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await waitFor(() => {
|
|
65
|
+
expect(result.current.loading).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect.soft(mockGetBook).toHaveBeenCalledTimes(1);
|
|
69
|
+
expect.soft(mockGetBook).toHaveBeenLastCalledWith(...expectedInitial);
|
|
70
|
+
|
|
71
|
+
act(() => {
|
|
72
|
+
rerender(updated);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
expect(result.current.loading).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect.soft(mockGetBook).toHaveBeenCalledTimes(2);
|
|
80
|
+
expect.soft(mockGetBook).toHaveBeenLastCalledWith(...expectedUpdated);
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
it('should not fetch when enabled is false', async () => {
|
|
85
|
+
const wrapper = createYVWrapper();
|
|
86
|
+
const { result } = renderHook(() => useBook(1, 'GEN', { enabled: false }), {
|
|
87
|
+
wrapper,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
expect(result.current.loading).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect.soft(mockGetBook).not.toHaveBeenCalled();
|
|
95
|
+
expect.soft(result.current.book).toBe(null);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle fetch errors', async () => {
|
|
99
|
+
const wrapper = createYVWrapper();
|
|
100
|
+
const error = new Error('Failed to fetch book');
|
|
101
|
+
mockGetBook.mockRejectedValueOnce(error);
|
|
102
|
+
|
|
103
|
+
const { result } = renderHook(() => useBook(1, 'GEN'), { wrapper });
|
|
104
|
+
|
|
105
|
+
await waitFor(() => {
|
|
106
|
+
expect(result.current.loading).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect.soft(result.current.error).toEqual(error);
|
|
110
|
+
expect.soft(result.current.book).toBe(null);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should clear error on successful refetch', async () => {
|
|
114
|
+
const wrapper = createYVWrapper();
|
|
115
|
+
const error = new Error('Failed to fetch book');
|
|
116
|
+
mockGetBook.mockRejectedValueOnce(error).mockResolvedValueOnce(mockBook);
|
|
117
|
+
|
|
118
|
+
const { result } = renderHook(() => useBook(1, 'GEN'), { wrapper });
|
|
119
|
+
|
|
120
|
+
await waitFor(() => {
|
|
121
|
+
expect(result.current.loading).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect.soft(result.current.error).toEqual(error);
|
|
125
|
+
expect.soft(result.current.book).toBe(null);
|
|
126
|
+
|
|
127
|
+
act(() => {
|
|
128
|
+
result.current.refetch();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await waitFor(() => {
|
|
132
|
+
expect(result.current.loading).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect.soft(result.current.error).toBe(null);
|
|
136
|
+
expect.soft(result.current.book).toEqual(mockBook);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should support manual refetch', async () => {
|
|
140
|
+
const wrapper = createYVWrapper();
|
|
141
|
+
const { result } = renderHook(() => useBook(1, 'GEN'), { wrapper });
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(result.current.loading).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(mockGetBook).toHaveBeenCalledTimes(1);
|
|
148
|
+
|
|
149
|
+
act(() => {
|
|
150
|
+
result.current.refetch();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await waitFor(() => {
|
|
154
|
+
expect(mockGetBook).toHaveBeenCalledTimes(2);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, vi, beforeEach, it } from 'vitest';
|
|
3
|
+
import { useBooks } from './useBooks';
|
|
4
|
+
import { type BibleClient, type BibleBook, type Collection } from '@youversion/platform-core';
|
|
5
|
+
import { useBibleClient } from './useBibleClient';
|
|
6
|
+
import { createYVWrapper } from './test/utils';
|
|
7
|
+
import { createMockBook } from './__tests__/mocks/bibles';
|
|
8
|
+
|
|
9
|
+
vi.mock('./useBibleClient');
|
|
10
|
+
|
|
11
|
+
describe('useBooks', () => {
|
|
12
|
+
const mockGetBooks = vi.fn();
|
|
13
|
+
|
|
14
|
+
const mockBooks: Collection<BibleBook> = {
|
|
15
|
+
data: [
|
|
16
|
+
createMockBook(),
|
|
17
|
+
createMockBook({
|
|
18
|
+
id: 'EXO',
|
|
19
|
+
title: 'Exodus',
|
|
20
|
+
full_title: 'The Second Book of Moses, Commonly Called Exodus',
|
|
21
|
+
abbreviation: 'Exod',
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
next_page_token: null,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mockGetBooks.mockResolvedValue(mockBooks);
|
|
29
|
+
|
|
30
|
+
const mockClient: Partial<BibleClient> = { getBooks: mockGetBooks };
|
|
31
|
+
vi.mocked(useBibleClient).mockReturnValue(mockClient as BibleClient);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('fetching books', () => {
|
|
35
|
+
it('should fetch all books for a version', async () => {
|
|
36
|
+
const wrapper = createYVWrapper();
|
|
37
|
+
const { result } = renderHook(() => useBooks(111), { wrapper });
|
|
38
|
+
|
|
39
|
+
expect(result.current.loading).toBe(true);
|
|
40
|
+
expect(result.current.books).toBe(null);
|
|
41
|
+
|
|
42
|
+
await waitFor(() => {
|
|
43
|
+
expect(result.current.loading).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect.soft(mockGetBooks).toHaveBeenCalledWith(111);
|
|
47
|
+
expect.soft(result.current.books).toEqual(mockBooks);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should refetch when versionId changes', async () => {
|
|
51
|
+
const wrapper = createYVWrapper();
|
|
52
|
+
const { result, rerender } = renderHook(({ versionId }) => useBooks(versionId), {
|
|
53
|
+
wrapper,
|
|
54
|
+
initialProps: { versionId: 111 },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await waitFor(() => {
|
|
58
|
+
expect(result.current.loading).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect.soft(mockGetBooks).toHaveBeenCalledTimes(1);
|
|
62
|
+
expect.soft(mockGetBooks).toHaveBeenLastCalledWith(111);
|
|
63
|
+
|
|
64
|
+
rerender({ versionId: 1 });
|
|
65
|
+
|
|
66
|
+
await waitFor(() => {
|
|
67
|
+
expect(result.current.loading).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect.soft(mockGetBooks).toHaveBeenCalledTimes(2);
|
|
71
|
+
expect.soft(mockGetBooks).toHaveBeenLastCalledWith(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not fetch when enabled is false', async () => {
|
|
75
|
+
const wrapper = createYVWrapper();
|
|
76
|
+
const { result } = renderHook(() => useBooks(111, { enabled: false }), {
|
|
77
|
+
wrapper,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
expect(result.current.loading).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
expect.soft(mockGetBooks).not.toHaveBeenCalled();
|
|
85
|
+
expect.soft(result.current.books).toBe(null);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should handle fetch errors', async () => {
|
|
89
|
+
const wrapper = createYVWrapper();
|
|
90
|
+
const error = new Error('Failed to fetch books');
|
|
91
|
+
mockGetBooks.mockRejectedValueOnce(error);
|
|
92
|
+
|
|
93
|
+
const { result } = renderHook(() => useBooks(111), { wrapper });
|
|
94
|
+
|
|
95
|
+
await waitFor(() => {
|
|
96
|
+
expect(result.current.loading).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect.soft(result.current.error).toEqual(error);
|
|
100
|
+
expect.soft(result.current.books).toBe(null);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should clear error on successful refetch', async () => {
|
|
104
|
+
const wrapper = createYVWrapper();
|
|
105
|
+
const error = new Error('Failed to fetch books');
|
|
106
|
+
mockGetBooks.mockRejectedValueOnce(error).mockResolvedValueOnce(mockBooks);
|
|
107
|
+
|
|
108
|
+
const { result } = renderHook(() => useBooks(111), { wrapper });
|
|
109
|
+
|
|
110
|
+
await waitFor(() => {
|
|
111
|
+
expect(result.current.loading).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect.soft(result.current.error).toEqual(error);
|
|
115
|
+
expect.soft(result.current.books).toBe(null);
|
|
116
|
+
|
|
117
|
+
act(() => {
|
|
118
|
+
result.current.refetch();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
expect(result.current.loading).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
expect.soft(result.current.error).toBe(null);
|
|
126
|
+
expect.soft(result.current.books).toEqual(mockBooks);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should support manual refetch', async () => {
|
|
130
|
+
const wrapper = createYVWrapper();
|
|
131
|
+
const { result } = renderHook(() => useBooks(111), { wrapper });
|
|
132
|
+
|
|
133
|
+
await waitFor(() => {
|
|
134
|
+
expect(result.current.loading).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(mockGetBooks).toHaveBeenCalledTimes(1);
|
|
138
|
+
|
|
139
|
+
act(() => {
|
|
140
|
+
result.current.refetch();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
expect(mockGetBooks).toHaveBeenCalledTimes(2);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
package/src/useChapter.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 { useChapter } from './useChapter';
|
|
5
|
-
import { YouVersionContext } from './context';
|
|
6
4
|
import { type BibleClient, type BibleChapter } 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('useChapter', () => {
|
|
12
|
-
const mockAppKey = 'test-app-key';
|
|
13
11
|
const mockGetChapter = vi.fn();
|
|
14
12
|
|
|
15
13
|
const mockChapter: BibleChapter = {
|
|
@@ -18,15 +16,7 @@ describe('useChapter', () => {
|
|
|
18
16
|
title: 'Matthew 1',
|
|
19
17
|
};
|
|
20
18
|
|
|
21
|
-
const createWrapper = (contextValue: { appKey: string }) => {
|
|
22
|
-
return ({ children }: { children: ReactNode }) => (
|
|
23
|
-
<YouVersionContext.Provider value={contextValue}>{children}</YouVersionContext.Provider>
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
19
|
beforeEach(() => {
|
|
28
|
-
vi.resetAllMocks();
|
|
29
|
-
|
|
30
20
|
mockGetChapter.mockResolvedValue(mockChapter);
|
|
31
21
|
|
|
32
22
|
const mockClient: Partial<BibleClient> = { getChapter: mockGetChapter };
|
|
@@ -35,10 +25,7 @@ describe('useChapter', () => {
|
|
|
35
25
|
|
|
36
26
|
describe('fetching chapter', () => {
|
|
37
27
|
it('should fetch chapter with versionId, book, chapter params', async () => {
|
|
38
|
-
const wrapper =
|
|
39
|
-
appKey: mockAppKey,
|
|
40
|
-
});
|
|
41
|
-
|
|
28
|
+
const wrapper = createYVWrapper();
|
|
42
29
|
const { result } = renderHook(() => useChapter(111, 'MAT', 1), { wrapper });
|
|
43
30
|
|
|
44
31
|
expect(result.current.loading).toBe(true);
|
|
@@ -48,102 +35,66 @@ describe('useChapter', () => {
|
|
|
48
35
|
expect(result.current.loading).toBe(false);
|
|
49
36
|
});
|
|
50
37
|
|
|
51
|
-
expect(mockGetChapter).toHaveBeenCalledWith(111, 'MAT', 1);
|
|
52
|
-
expect(result.current.chapter).toEqual(mockChapter);
|
|
38
|
+
expect.soft(mockGetChapter).toHaveBeenCalledWith(111, 'MAT', 1);
|
|
39
|
+
expect.soft(result.current.chapter).toEqual(mockChapter);
|
|
53
40
|
});
|
|
54
41
|
|
|
55
|
-
it(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
expect(mockGetChapter).toHaveBeenCalledTimes(2);
|
|
110
|
-
expect(mockGetChapter).toHaveBeenLastCalledWith(1, 'GEN', 1);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should refetch when chapter changes', async () => {
|
|
114
|
-
const wrapper = createWrapper({
|
|
115
|
-
appKey: mockAppKey,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const { result, rerender } = renderHook(({ chapter }) => useChapter(1, 'MAT', chapter), {
|
|
119
|
-
wrapper,
|
|
120
|
-
initialProps: { chapter: 1 },
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
await waitFor(() => {
|
|
124
|
-
expect(result.current.loading).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
expect(mockGetChapter).toHaveBeenCalledTimes(1);
|
|
128
|
-
expect(mockGetChapter).toHaveBeenLastCalledWith(1, 'MAT', 1);
|
|
129
|
-
|
|
130
|
-
act(() => {
|
|
131
|
-
rerender({ chapter: 5 });
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
await waitFor(() => {
|
|
135
|
-
expect(result.current.loading).toBe(false);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
expect(mockGetChapter).toHaveBeenCalledTimes(2);
|
|
139
|
-
expect(mockGetChapter).toHaveBeenLastCalledWith(1, 'MAT', 5);
|
|
140
|
-
});
|
|
42
|
+
it.each([
|
|
43
|
+
{
|
|
44
|
+
param: 'versionId',
|
|
45
|
+
HookFn: ({ val }: { val: number | string }) => useChapter(val as number, 'MAT', 1),
|
|
46
|
+
initial: { val: 1 },
|
|
47
|
+
updated: { val: 111 },
|
|
48
|
+
expectedInitial: [1, 'MAT', 1],
|
|
49
|
+
expectedUpdated: [111, 'MAT', 1],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
param: 'book',
|
|
53
|
+
HookFn: ({ val }: { val: number | string }) => useChapter(1, val as string, 1),
|
|
54
|
+
initial: { val: 'MAT' },
|
|
55
|
+
updated: { val: 'GEN' },
|
|
56
|
+
expectedInitial: [1, 'MAT', 1],
|
|
57
|
+
expectedUpdated: [1, 'GEN', 1],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
param: 'chapter',
|
|
61
|
+
HookFn: ({ val }: { val: number | string }) => useChapter(1, 'MAT', val as number),
|
|
62
|
+
initial: { val: 1 },
|
|
63
|
+
updated: { val: 5 },
|
|
64
|
+
expectedInitial: [1, 'MAT', 1],
|
|
65
|
+
expectedUpdated: [1, 'MAT', 5],
|
|
66
|
+
},
|
|
67
|
+
])(
|
|
68
|
+
'should refetch when $param changes',
|
|
69
|
+
async ({ HookFn, initial, updated, expectedInitial, expectedUpdated }) => {
|
|
70
|
+
const wrapper = createYVWrapper();
|
|
71
|
+
const { result, rerender } = renderHook(HookFn, {
|
|
72
|
+
wrapper,
|
|
73
|
+
initialProps: initial,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await waitFor(() => {
|
|
77
|
+
expect(result.current.loading).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect.soft(mockGetChapter).toHaveBeenCalledTimes(1);
|
|
81
|
+
expect.soft(mockGetChapter).toHaveBeenLastCalledWith(...expectedInitial);
|
|
82
|
+
|
|
83
|
+
act(() => {
|
|
84
|
+
rerender(updated);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await waitFor(() => {
|
|
88
|
+
expect(result.current.loading).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect.soft(mockGetChapter).toHaveBeenCalledTimes(2);
|
|
92
|
+
expect.soft(mockGetChapter).toHaveBeenLastCalledWith(...expectedUpdated);
|
|
93
|
+
},
|
|
94
|
+
);
|
|
141
95
|
|
|
142
96
|
it('should not fetch when enabled is false', async () => {
|
|
143
|
-
const wrapper =
|
|
144
|
-
appKey: mockAppKey,
|
|
145
|
-
});
|
|
146
|
-
|
|
97
|
+
const wrapper = createYVWrapper();
|
|
147
98
|
const { result } = renderHook(() => useChapter(1, 'MAT', 1, { enabled: false }), {
|
|
148
99
|
wrapper,
|
|
149
100
|
});
|
|
@@ -152,44 +103,38 @@ describe('useChapter', () => {
|
|
|
152
103
|
expect(result.current.loading).toBe(false);
|
|
153
104
|
});
|
|
154
105
|
|
|
155
|
-
expect(mockGetChapter).not.toHaveBeenCalled();
|
|
156
|
-
expect(result.current.chapter).toBe(null);
|
|
106
|
+
expect.soft(mockGetChapter).not.toHaveBeenCalled();
|
|
107
|
+
expect.soft(result.current.chapter).toBe(null);
|
|
157
108
|
});
|
|
158
109
|
|
|
159
110
|
it('should handle fetch errors', async () => {
|
|
111
|
+
const wrapper = createYVWrapper();
|
|
160
112
|
const error = new Error('Failed to fetch chapter');
|
|
161
113
|
mockGetChapter.mockRejectedValueOnce(error);
|
|
162
114
|
|
|
163
|
-
const wrapper = createWrapper({
|
|
164
|
-
appKey: mockAppKey,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
115
|
const { result } = renderHook(() => useChapter(1, 'MAT', 1), { wrapper });
|
|
168
116
|
|
|
169
117
|
await waitFor(() => {
|
|
170
118
|
expect(result.current.loading).toBe(false);
|
|
171
119
|
});
|
|
172
120
|
|
|
173
|
-
expect(result.current.error).toEqual(error);
|
|
174
|
-
expect(result.current.chapter).toBe(null);
|
|
121
|
+
expect.soft(result.current.error).toEqual(error);
|
|
122
|
+
expect.soft(result.current.chapter).toBe(null);
|
|
175
123
|
});
|
|
176
124
|
|
|
177
125
|
it('should clear error on successful refetch', async () => {
|
|
126
|
+
const wrapper = createYVWrapper();
|
|
178
127
|
const error = new Error('Failed to fetch chapter');
|
|
179
128
|
mockGetChapter.mockRejectedValueOnce(error).mockResolvedValueOnce(mockChapter);
|
|
180
129
|
|
|
181
|
-
const wrapper = createWrapper({
|
|
182
|
-
appKey: mockAppKey,
|
|
183
|
-
});
|
|
184
|
-
|
|
185
130
|
const { result } = renderHook(() => useChapter(1, 'MAT', 1), { wrapper });
|
|
186
131
|
|
|
187
132
|
await waitFor(() => {
|
|
188
133
|
expect(result.current.loading).toBe(false);
|
|
189
134
|
});
|
|
190
135
|
|
|
191
|
-
expect(result.current.error).toEqual(error);
|
|
192
|
-
expect(result.current.chapter).toBe(null);
|
|
136
|
+
expect.soft(result.current.error).toEqual(error);
|
|
137
|
+
expect.soft(result.current.chapter).toBe(null);
|
|
193
138
|
|
|
194
139
|
act(() => {
|
|
195
140
|
result.current.refetch();
|
|
@@ -199,15 +144,12 @@ describe('useChapter', () => {
|
|
|
199
144
|
expect(result.current.loading).toBe(false);
|
|
200
145
|
});
|
|
201
146
|
|
|
202
|
-
expect(result.current.error).toBe(null);
|
|
203
|
-
expect(result.current.chapter).toEqual(mockChapter);
|
|
147
|
+
expect.soft(result.current.error).toBe(null);
|
|
148
|
+
expect.soft(result.current.chapter).toEqual(mockChapter);
|
|
204
149
|
});
|
|
205
150
|
|
|
206
151
|
it('should support manual refetch', async () => {
|
|
207
|
-
const wrapper =
|
|
208
|
-
appKey: mockAppKey,
|
|
209
|
-
});
|
|
210
|
-
|
|
152
|
+
const wrapper = createYVWrapper();
|
|
211
153
|
const { result } = renderHook(() => useChapter(1, 'MAT', 1), { wrapper });
|
|
212
154
|
|
|
213
155
|
await waitFor(() => {
|
|
@@ -16,6 +16,9 @@ interface UseChapterNavigationResult {
|
|
|
16
16
|
/**
|
|
17
17
|
* Provides navigation functionality for chapters across book boundaries,
|
|
18
18
|
* including intro chapter support.
|
|
19
|
+
*
|
|
20
|
+
* @deprecated This hook will be removed in the next major version.
|
|
21
|
+
* Use `getAdjacentChapter` from `@youversion/platform-core` directly instead.
|
|
19
22
|
*/
|
|
20
23
|
export function useChapterNavigation(): UseChapterNavigationResult {
|
|
21
24
|
const { currentChapter, currentVersion, currentBook, setChapter, setBook } = useReaderContext();
|