@applicaster/zapp-react-native-ui-components 14.0.0-rc.8 → 15.0.0-rc.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.
Files changed (163) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  4. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  5. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  6. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  7. package/Components/AudioPlayer/{Artwork.tsx → tv/Artwork.tsx} +3 -2
  8. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
  9. package/Components/AudioPlayer/tv/Layout.tsx +168 -0
  10. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +7 -1
  11. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +6 -2
  12. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +6 -2
  13. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
  15. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -2
  17. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -2
  18. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  19. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +11 -5
  20. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +17 -58
  21. package/Components/AudioPlayer/types.ts +40 -0
  22. package/Components/BaseFocusable/index.tsx +23 -12
  23. package/Components/Cell/Cell.tsx +91 -64
  24. package/Components/Cell/CellWithFocusable.tsx +3 -0
  25. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  26. package/Components/Cell/index.js +7 -3
  27. package/Components/ComponentResolver/index.ts +1 -1
  28. package/Components/FeedLoader/FeedLoader.tsx +7 -16
  29. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  30. package/Components/FeedLoader/index.js +2 -8
  31. package/Components/Focusable/Focusable.tsx +12 -3
  32. package/Components/Focusable/FocusableTvOS.tsx +5 -5
  33. package/Components/Focusable/FocusableiOS.tsx +2 -2
  34. package/Components/Focusable/Touchable.tsx +5 -3
  35. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  36. package/Components/Focusable/index.android.tsx +19 -11
  37. package/Components/Focusable/index.tsx +1 -1
  38. package/Components/FocusableGroup/FocusableTvOS.tsx +1 -1
  39. package/Components/FocusableList/FocusableItem.tsx +4 -3
  40. package/Components/FocusableList/FocusableListItemWrapper.tsx +2 -1
  41. package/Components/FocusableList/hooks/useCellState.android.ts +13 -3
  42. package/Components/FocusableList/index.tsx +20 -9
  43. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  44. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  45. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
  46. package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
  47. package/Components/HookRenderer/HookRenderer.tsx +5 -1
  48. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  49. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  50. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +6 -2
  51. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  52. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -39
  53. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  54. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  55. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
  56. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
  57. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
  58. package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
  59. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  60. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  61. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  62. package/Components/MasterCell/elementMapper.tsx +1 -2
  63. package/Components/MasterCell/index.tsx +1 -1
  64. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  65. package/Components/MasterCell/utils/index.ts +11 -5
  66. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  67. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  68. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  69. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  70. package/Components/PlayerContainer/PlayerContainer.tsx +46 -33
  71. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  72. package/Components/PlayerContainer/index.ts +1 -1
  73. package/Components/PlayerImageBackground/index.tsx +1 -1
  74. package/Components/River/ComponentsMap/ComponentsMap.tsx +1 -6
  75. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  76. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  77. package/Components/River/RefreshControl.tsx +11 -17
  78. package/Components/River/RiverItem.tsx +11 -8
  79. package/Components/River/TV/River.tsx +2 -17
  80. package/Components/River/TV/index.tsx +3 -1
  81. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  82. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  83. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  84. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -6
  85. package/Components/River/__tests__/river.test.js +12 -26
  86. package/Components/River/index.tsx +1 -1
  87. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  88. package/Components/Screen/__tests__/navigationHandler.test.ts +133 -22
  89. package/Components/Screen/navigationHandler.ts +20 -2
  90. package/Components/ScreenResolver/index.tsx +15 -0
  91. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  92. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  93. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  94. package/Components/ScreenRevealManager/index.ts +1 -0
  95. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  96. package/Components/Tabs/TV/Tabs.android.tsx +1 -3
  97. package/Components/Tabs/Tabs.tsx +2 -3
  98. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
  99. package/Components/TextInputTv/index.tsx +11 -0
  100. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  101. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  102. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  103. package/Components/VideoLive/animationUtils.ts +3 -3
  104. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
  105. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +294 -0
  106. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +93 -0
  107. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +73 -29
  108. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  109. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  110. package/Components/VideoModal/VideoModal.tsx +3 -17
  111. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  112. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  113. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -180
  114. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +21 -51
  115. package/Components/VideoModal/hooks/index.ts +0 -2
  116. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +15 -1
  117. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  118. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
  119. package/Components/VideoModal/hooks/utils/index.ts +4 -0
  120. package/Components/VideoModal/utils.ts +6 -0
  121. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  122. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  123. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  124. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  125. package/Contexts/CellFocusedStateContext/index.tsx +27 -0
  126. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  127. package/Contexts/ScreenContext/index.tsx +46 -6
  128. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  129. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  130. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  131. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  132. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  133. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  134. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  135. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  136. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  137. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  138. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  139. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  140. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  141. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  142. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  143. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  144. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  145. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  146. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  147. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  148. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  149. package/events/index.ts +3 -0
  150. package/package.json +5 -10
  151. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  152. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  153. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  154. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  155. package/Components/AudioPlayer/index.ts +0 -1
  156. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  157. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  158. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  159. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  160. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  161. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  162. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  163. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { NullFeedResolver } from "../resolvers/NullFeedResolver";
5
+ import { ReloadDataFunction } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
6
+
7
+ type MockChildrenArgs = {
8
+ zappPipesData?: {
9
+ loading: boolean;
10
+ data?: any;
11
+ error?: Error | null;
12
+ };
13
+ reloadData?: ReloadDataFunction;
14
+ loadNextData?: () => void;
15
+ };
16
+
17
+ describe("NullFeedResolver", () => {
18
+ it("should render children with correct default props", () => {
19
+ const mockChildren = jest.fn((_args: MockChildrenArgs) => (
20
+ <View testID="mock-children" />
21
+ ));
22
+
23
+ render(<NullFeedResolver>{mockChildren}</NullFeedResolver>);
24
+
25
+ expect(mockChildren).toHaveBeenCalledWith(
26
+ expect.objectContaining({
27
+ zappPipesData: {
28
+ url: "",
29
+ loading: false,
30
+ data: null,
31
+ error: null,
32
+ },
33
+ reloadData: expect.any(Function),
34
+ loadNextData: undefined,
35
+ })
36
+ );
37
+ });
38
+
39
+ it("should render children component", () => {
40
+ const { getByTestId } = render(
41
+ <NullFeedResolver>{() => <View testID="test-child" />}</NullFeedResolver>
42
+ );
43
+
44
+ expect(getByTestId("test-child")).toBeTruthy();
45
+ });
46
+
47
+ it("should create a memoized data object that doesn't change on re-renders", () => {
48
+ const mockChildren = jest.fn(() => <View testID="mock-children" />);
49
+
50
+ const { rerender } = render(
51
+ <NullFeedResolver>{mockChildren}</NullFeedResolver>
52
+ );
53
+
54
+ expect(mockChildren).toHaveBeenCalled();
55
+ // @ts-ignore
56
+ const firstCallDataProps = mockChildren.mock.calls[0][0];
57
+
58
+ rerender(<NullFeedResolver>{mockChildren}</NullFeedResolver>);
59
+
60
+ // @ts-ignore
61
+ const secondCallDataProps = mockChildren.mock.calls[1][0];
62
+
63
+ expect(firstCallDataProps).toBe(secondCallDataProps);
64
+ });
65
+
66
+ it("should have reloadData function that returns null", () => {
67
+ const mockChildren = jest.fn(() => <View testID="mock-children" />);
68
+
69
+ render(<NullFeedResolver>{mockChildren}</NullFeedResolver>);
70
+
71
+ expect(mockChildren).toHaveBeenCalled();
72
+
73
+ // @ts-ignore
74
+ const { reloadData } = mockChildren.mock.calls[0][0];
75
+
76
+ expect(reloadData()).toBeNull();
77
+ });
78
+ });
@@ -0,0 +1,205 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { ResolverSelector } from "../ResolverSelector";
5
+ import { StaticFeedResolver } from "../resolvers/StaticFeedResolver";
6
+ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
7
+ import { NullFeedResolver } from "../resolvers/NullFeedResolver";
8
+
9
+ jest.mock("../resolvers/StaticFeedResolver", () => ({
10
+ StaticFeedResolver: jest.fn(() => null),
11
+ }));
12
+
13
+ jest.mock("../resolvers/UrlFeedResolver", () => ({
14
+ UrlFeedResolver: jest.fn(() => null),
15
+ }));
16
+
17
+ jest.mock("../resolvers/NullFeedResolver", () => ({
18
+ NullFeedResolver: jest.fn(() => null),
19
+ }));
20
+
21
+ const testPlugin = {
22
+ identifier: "test-plugin",
23
+ name: "test",
24
+ type: "general" as PluginType,
25
+ module: jest.fn(),
26
+ };
27
+
28
+ describe("ResolverSelector", () => {
29
+ const mockChildren = jest.fn(() => <View testID="mock-children" />);
30
+
31
+ const mockComponent = {
32
+ id: "test-component",
33
+ type: "vertical_list",
34
+ styles: {},
35
+ } as any;
36
+
37
+ beforeEach(() => {
38
+ jest.clearAllMocks();
39
+ });
40
+
41
+ it("should render StaticFeedResolver when getStaticComponentFeed is provided", () => {
42
+ const props = {
43
+ getStaticComponentFeed: jest.fn(),
44
+ component: mockComponent,
45
+ children: mockChildren,
46
+ riverId: "test-river",
47
+ };
48
+
49
+ render(<ResolverSelector {...props} />);
50
+
51
+ expect(StaticFeedResolver).toHaveBeenCalledWith(
52
+ expect.objectContaining({
53
+ getStaticComponentFeed: props.getStaticComponentFeed,
54
+ component: mockComponent,
55
+ children: mockChildren,
56
+ }),
57
+ expect.anything()
58
+ );
59
+
60
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
61
+ expect(NullFeedResolver).not.toHaveBeenCalled();
62
+ });
63
+
64
+ it("should render UrlFeedResolver when feedUrl is provided", () => {
65
+ const props = {
66
+ feedUrl: "https://example.com/feed",
67
+ component: mockComponent,
68
+ children: mockChildren,
69
+ riverId: "test-river",
70
+ };
71
+
72
+ render(<ResolverSelector {...props} />);
73
+
74
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
75
+ expect.objectContaining({
76
+ feedUrl: props.feedUrl,
77
+ component: mockComponent,
78
+ children: mockChildren,
79
+ }),
80
+ expect.anything()
81
+ );
82
+
83
+ expect(StaticFeedResolver).not.toHaveBeenCalled();
84
+ expect(NullFeedResolver).not.toHaveBeenCalled();
85
+ });
86
+
87
+ it("should render UrlFeedResolver when component.data.source is provided", () => {
88
+ const componentWithSource = {
89
+ ...mockComponent,
90
+ data: {
91
+ source: "data-source",
92
+ },
93
+ };
94
+
95
+ const props = {
96
+ component: componentWithSource,
97
+ children: mockChildren,
98
+ riverId: "test-river",
99
+ };
100
+
101
+ render(<ResolverSelector {...props} />);
102
+
103
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
104
+ expect.objectContaining({
105
+ component: componentWithSource,
106
+ children: mockChildren,
107
+ }),
108
+ expect.anything()
109
+ );
110
+
111
+ expect(StaticFeedResolver).not.toHaveBeenCalled();
112
+ expect(NullFeedResolver).not.toHaveBeenCalled();
113
+ });
114
+
115
+ it("should render NullFeedResolver when no data source is provided", () => {
116
+ const props = {
117
+ component: mockComponent,
118
+ children: mockChildren,
119
+ riverId: "test-river",
120
+ };
121
+
122
+ render(<ResolverSelector {...props} />);
123
+
124
+ expect(NullFeedResolver).toHaveBeenCalledWith(
125
+ expect.objectContaining({
126
+ children: mockChildren,
127
+ }),
128
+ expect.anything()
129
+ );
130
+
131
+ expect(StaticFeedResolver).not.toHaveBeenCalled();
132
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
133
+ });
134
+
135
+ it("should pass all props to StaticFeedResolver", () => {
136
+ const props = {
137
+ getStaticComponentFeed: jest.fn(),
138
+ component: mockComponent,
139
+ children: mockChildren,
140
+ riverId: "test-river",
141
+ isLast: true,
142
+ componentIndex: 1,
143
+ isScreenWrappedInContainer: true,
144
+ entryContext: { entry: "test" },
145
+ screenContext: { screen: "test" },
146
+ searchContext: "search",
147
+ screenData: { data: "test" },
148
+ plugins: [testPlugin],
149
+ };
150
+
151
+ render(<ResolverSelector {...props} />);
152
+
153
+ expect(StaticFeedResolver).toHaveBeenCalledWith(
154
+ expect.objectContaining(props),
155
+ expect.anything()
156
+ );
157
+ });
158
+
159
+ it("should pass all props to UrlFeedResolver", () => {
160
+ const props = {
161
+ feedUrl: "https://example.com/feed",
162
+ component: mockComponent,
163
+ children: mockChildren,
164
+ riverId: "test-river",
165
+ isLast: true,
166
+ componentIndex: 1,
167
+ isScreenWrappedInContainer: true,
168
+ entryContext: { entry: "test" },
169
+ screenContext: { screen: "test" },
170
+ searchContext: "search",
171
+ screenData: { data: "test" },
172
+ plugins: [testPlugin],
173
+ };
174
+
175
+ render(<ResolverSelector {...props} />);
176
+
177
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
178
+ expect.objectContaining(props),
179
+ expect.anything()
180
+ );
181
+ });
182
+
183
+ it("should prioritize StaticFeedResolver over UrlFeedResolver when both conditions are met", () => {
184
+ const componentWithSource = {
185
+ ...mockComponent,
186
+ data: {
187
+ source: "data-source",
188
+ },
189
+ };
190
+
191
+ const props = {
192
+ getStaticComponentFeed: jest.fn(),
193
+ feedUrl: "https://example.com/feed",
194
+ component: componentWithSource,
195
+ children: mockChildren,
196
+ riverId: "test-river",
197
+ };
198
+
199
+ render(<ResolverSelector {...props} />);
200
+
201
+ expect(StaticFeedResolver).toHaveBeenCalled();
202
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
203
+ expect(NullFeedResolver).not.toHaveBeenCalled();
204
+ });
205
+ });
@@ -0,0 +1,251 @@
1
+ import React from "react";
2
+ import { render, act, waitFor } from "@testing-library/react-native";
3
+ import { StaticFeedResolver } from "../resolvers/StaticFeedResolver";
4
+ import { View } from "react-native";
5
+ import { ReloadDataFunction } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
6
+
7
+ type MockChildrenArgs = {
8
+ zappPipesData?: {
9
+ loading: boolean;
10
+ data?: any;
11
+ error?: Error | null;
12
+ };
13
+ reloadData?: ReloadDataFunction;
14
+ };
15
+
16
+ describe("StaticFeedResolver", () => {
17
+ const mockChildren = jest.fn((_args: MockChildrenArgs) => (
18
+ <View data-testid="mock-children" />
19
+ ));
20
+
21
+ const mockComponent = {
22
+ id: "test-component",
23
+ position: 1,
24
+ } as any;
25
+
26
+ const mockGetStaticComponentFeed = jest.fn();
27
+
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ mockGetStaticComponentFeed.mockResolvedValue({ entry: [{ id: "1" }] });
31
+ });
32
+
33
+ it("should generate correct static URL based on component", () => {
34
+ render(
35
+ <StaticFeedResolver
36
+ getStaticComponentFeed={mockGetStaticComponentFeed}
37
+ component={mockComponent}
38
+ componentIndex={0}
39
+ riverId="test-river"
40
+ >
41
+ {mockChildren}
42
+ </StaticFeedResolver>
43
+ );
44
+
45
+ expect(mockChildren).toHaveBeenCalledWith(
46
+ expect.objectContaining({
47
+ zappPipesData: expect.objectContaining({
48
+ url: "static://component?id=test-component&position=1",
49
+ }),
50
+ })
51
+ );
52
+ });
53
+
54
+ it("should start with loading state", () => {
55
+ render(
56
+ <StaticFeedResolver
57
+ getStaticComponentFeed={mockGetStaticComponentFeed}
58
+ component={mockComponent}
59
+ componentIndex={0}
60
+ riverId="test-river"
61
+ >
62
+ {mockChildren}
63
+ </StaticFeedResolver>
64
+ );
65
+
66
+ expect(mockChildren).toHaveBeenCalledWith(
67
+ expect.objectContaining({
68
+ zappPipesData: expect.objectContaining({
69
+ loading: true,
70
+ data: null,
71
+ error: null,
72
+ }),
73
+ })
74
+ );
75
+ });
76
+
77
+ it("should fetch data when mounted", async () => {
78
+ render(
79
+ <StaticFeedResolver
80
+ getStaticComponentFeed={mockGetStaticComponentFeed}
81
+ component={mockComponent}
82
+ componentIndex={0}
83
+ riverId="test-river"
84
+ >
85
+ {mockChildren}
86
+ </StaticFeedResolver>
87
+ );
88
+
89
+ expect(mockGetStaticComponentFeed).toHaveBeenCalledWith({
90
+ index: 0,
91
+ component: mockComponent,
92
+ });
93
+
94
+ await waitFor(() => {
95
+ expect(mockChildren).toHaveBeenLastCalledWith(
96
+ expect.objectContaining({
97
+ zappPipesData: expect.objectContaining({
98
+ loading: false,
99
+ data: { entry: [{ id: "1" }] },
100
+ }),
101
+ })
102
+ );
103
+ });
104
+ });
105
+
106
+ it("should handle errors from getStaticComponentFeed", async () => {
107
+ const error = new Error("Test error");
108
+ mockGetStaticComponentFeed.mockRejectedValue(error);
109
+
110
+ render(
111
+ <StaticFeedResolver
112
+ getStaticComponentFeed={mockGetStaticComponentFeed}
113
+ component={mockComponent}
114
+ componentIndex={0}
115
+ riverId="test-river"
116
+ >
117
+ {mockChildren}
118
+ </StaticFeedResolver>
119
+ );
120
+
121
+ await waitFor(() => {
122
+ expect(mockChildren).toHaveBeenLastCalledWith(
123
+ expect.objectContaining({
124
+ zappPipesData: expect.objectContaining({
125
+ loading: false,
126
+ error,
127
+ }),
128
+ })
129
+ );
130
+ });
131
+ });
132
+
133
+ it("should provide a reloadData function that refreshes the data", async () => {
134
+ render(
135
+ <StaticFeedResolver
136
+ getStaticComponentFeed={mockGetStaticComponentFeed}
137
+ component={mockComponent}
138
+ componentIndex={0}
139
+ riverId="test-river"
140
+ >
141
+ {mockChildren}
142
+ </StaticFeedResolver>
143
+ );
144
+
145
+ await waitFor(() => {
146
+ expect(mockChildren).toHaveBeenLastCalledWith(
147
+ expect.objectContaining({
148
+ zappPipesData: expect.objectContaining({
149
+ loading: false,
150
+ }),
151
+ })
152
+ );
153
+ });
154
+
155
+ const { reloadData } =
156
+ mockChildren.mock.calls[mockChildren.mock.calls.length - 1][0];
157
+
158
+ mockGetStaticComponentFeed.mockClear();
159
+ mockGetStaticComponentFeed.mockResolvedValue({ entry: [{ id: "2" }] });
160
+
161
+ await act(async () => {
162
+ await reloadData();
163
+ });
164
+
165
+ expect(mockGetStaticComponentFeed).toHaveBeenCalledWith({
166
+ index: 0,
167
+ component: mockComponent,
168
+ });
169
+
170
+ await waitFor(() => {
171
+ expect(mockChildren).toHaveBeenLastCalledWith(
172
+ expect.objectContaining({
173
+ zappPipesData: expect.objectContaining({
174
+ data: { entry: [{ id: "2" }] },
175
+ }),
176
+ })
177
+ );
178
+ });
179
+ });
180
+
181
+ it("should handle errors in reloadData", async () => {
182
+ render(
183
+ <StaticFeedResolver
184
+ getStaticComponentFeed={mockGetStaticComponentFeed}
185
+ component={mockComponent}
186
+ componentIndex={0}
187
+ riverId="test-river"
188
+ >
189
+ {mockChildren}
190
+ </StaticFeedResolver>
191
+ );
192
+
193
+ await waitFor(() => {
194
+ expect(mockChildren).toHaveBeenLastCalledWith(
195
+ expect.objectContaining({
196
+ zappPipesData: expect.objectContaining({
197
+ loading: false,
198
+ }),
199
+ })
200
+ );
201
+ });
202
+
203
+ const { reloadData } =
204
+ mockChildren.mock.calls[mockChildren.mock.calls.length - 1][0];
205
+
206
+ const error = new Error("Reload error");
207
+ mockGetStaticComponentFeed.mockRejectedValue(error);
208
+
209
+ await expect(reloadData()).rejects.toEqual(error);
210
+
211
+ await waitFor(() => {
212
+ expect(mockChildren).toHaveBeenLastCalledWith(
213
+ expect.objectContaining({
214
+ zappPipesData: expect.objectContaining({
215
+ error,
216
+ }),
217
+ })
218
+ );
219
+ });
220
+ });
221
+
222
+ it("should not fetch data if getStaticComponentFeed is not provided", async () => {
223
+ render(
224
+ <StaticFeedResolver
225
+ component={mockComponent}
226
+ componentIndex={0}
227
+ riverId="test-river"
228
+ >
229
+ {mockChildren}
230
+ </StaticFeedResolver>
231
+ );
232
+
233
+ expect(mockGetStaticComponentFeed).not.toHaveBeenCalled();
234
+ });
235
+
236
+ it("should provide reloadData function that resolves immediately if getStaticComponentFeed is not provided", async () => {
237
+ render(
238
+ <StaticFeedResolver
239
+ component={mockComponent}
240
+ componentIndex={0}
241
+ riverId="test-river"
242
+ >
243
+ {mockChildren}
244
+ </StaticFeedResolver>
245
+ );
246
+
247
+ const { reloadData } = mockChildren.mock.calls[0][0];
248
+
249
+ await expect(reloadData()).resolves.toBeUndefined();
250
+ });
251
+ });