@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,368 @@
1
+ import React from "react";
2
+ import * as useFeedLoaderModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
3
+ import * as useFeedRefreshModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh";
4
+ import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
5
+ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
6
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
7
+ import * as ZappPipesModule from "@applicaster/zapp-pipes-v2-client";
8
+
9
+ const mockSubscribeForKeyChanges = jest.fn();
10
+
11
+ jest
12
+ .spyOn(ZappPipesModule, "subscribeForKeyChanges")
13
+ .mockImplementation(mockSubscribeForKeyChanges);
14
+
15
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader");
16
+
17
+ jest.mock(
18
+ "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh"
19
+ );
20
+
21
+ jest.mock("@applicaster/zapp-pipes-v2-client");
22
+
23
+ jest.mock("@applicaster/zapp-react-native-bridge/Favorites", () => ({
24
+ favoritesListener: {
25
+ on: jest.fn(),
26
+ removeHandler: jest.fn(),
27
+ },
28
+ }));
29
+
30
+ jest.mock("../utils", () => ({
31
+ isVerticalListOrGrid: jest.fn(
32
+ (component) =>
33
+ component?.type === "vertical_list" || component?.type === "vertical_grid"
34
+ ),
35
+ }));
36
+
37
+ const componentRequiredKeys = { rules: { item_limit: 10 } };
38
+
39
+ describe("UrlFeedResolver", () => {
40
+ const mockChildren = jest.fn(() => <div data-testid="mock-children" />);
41
+ const mockReloadData = jest.fn();
42
+ const mockLoadNext = jest.fn();
43
+
44
+ const mockAddDataSourceListener = jest.fn().mockReturnValue(jest.fn());
45
+
46
+ beforeEach(() => {
47
+ jest.clearAllMocks();
48
+
49
+ (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
50
+ reloadData: mockReloadData,
51
+ loadNext: mockLoadNext,
52
+ error: null,
53
+ loading: false,
54
+ url: "test-url",
55
+ data: { entry: [{ id: "1" }] },
56
+ });
57
+ });
58
+
59
+ it("should use feedUrl directly when provided", () => {
60
+ const props = {
61
+ feedUrl: "https://example.com/feed",
62
+ component: { ...componentRequiredKeys } as any,
63
+ children: mockChildren,
64
+ riverId: "test-river",
65
+ };
66
+
67
+ renderWithProviders(<UrlFeedResolver {...props} />);
68
+
69
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
70
+ feedUrl: "https://example.com/feed",
71
+ pipesOptions: expect.any(Object),
72
+ });
73
+ });
74
+
75
+ it("should use component.data.source when no feedUrl is provided", () => {
76
+ const props = {
77
+ component: {
78
+ data: {
79
+ source: "https://example.com/source",
80
+ },
81
+ ...componentRequiredKeys,
82
+ } as any,
83
+ children: mockChildren,
84
+ riverId: "test-river",
85
+ };
86
+
87
+ renderWithProviders(<UrlFeedResolver {...props} />);
88
+
89
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
90
+ feedUrl: "https://example.com/source",
91
+ pipesOptions: expect.any(Object),
92
+ });
93
+ });
94
+
95
+ it("should handle FAVOURITES type correctly", () => {
96
+ const props = {
97
+ component: {
98
+ data: {
99
+ source: "favorites-source",
100
+ type: "FAVOURITES",
101
+ },
102
+ ...componentRequiredKeys,
103
+ } as any,
104
+ children: mockChildren,
105
+ riverId: "test-river",
106
+ };
107
+
108
+ renderWithProviders(<UrlFeedResolver {...props} />);
109
+
110
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
111
+ feedUrl: "favorites-source",
112
+ pipesOptions: expect.objectContaining({
113
+ clearCache: true,
114
+ loadLocalFavorites: true,
115
+ }),
116
+ });
117
+
118
+ expect(favoritesListener.on).toHaveBeenCalledWith(
119
+ "FAVORITES_CHANGED",
120
+ mockReloadData
121
+ );
122
+ });
123
+
124
+ it("should handle APPLICASTER_COLLECTION type correctly", () => {
125
+ const props = {
126
+ component: {
127
+ data: {
128
+ source: "collection-id",
129
+ type: "APPLICASTER_COLLECTION",
130
+ },
131
+ ...componentRequiredKeys,
132
+ } as any,
133
+ children: mockChildren,
134
+ riverId: "test-river",
135
+ };
136
+
137
+ renderWithProviders(<UrlFeedResolver {...props} />);
138
+
139
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
140
+ feedUrl:
141
+ "applicaster://fetchData?type=APPLICASTER_COLLECTION&collectionId=collection-id",
142
+ pipesOptions: expect.any(Object),
143
+ });
144
+ });
145
+
146
+ it("should handle APPLICASTER_CATEGORY type correctly", () => {
147
+ const props = {
148
+ component: {
149
+ data: {
150
+ source: "category-id",
151
+ type: "APPLICASTER_CATEGORY",
152
+ },
153
+ ...componentRequiredKeys,
154
+ } as any,
155
+ children: mockChildren,
156
+ riverId: "test-river",
157
+ plugins: [],
158
+ };
159
+
160
+ renderWithProviders(<UrlFeedResolver {...props} />);
161
+
162
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
163
+ feedUrl: expect.stringContaining(
164
+ "applicaster://fetchData?type=APPLICASTER_CATEGORY&categoryId=category-id"
165
+ ),
166
+ pipesOptions: expect.any(Object),
167
+ });
168
+ });
169
+
170
+ it("should set up URL context key changes listener", () => {
171
+ const props = {
172
+ feedUrl: "https://example.com/feed",
173
+ component: { ...componentRequiredKeys } as any,
174
+ children: mockChildren,
175
+ riverId: "test-river",
176
+ };
177
+
178
+ renderWithProviders(<UrlFeedResolver {...props} />);
179
+
180
+ expect(mockSubscribeForKeyChanges.mock.calls[0][0]).toMatchObject({
181
+ url: "https://example.com/feed",
182
+ callback: mockReloadData,
183
+ });
184
+ });
185
+
186
+ it("should set up pipesv2 URL listener from plugin if available", () => {
187
+ const props = {
188
+ feedUrl: "pipesv2://test-plugin/endpoint",
189
+ component: { ...componentRequiredKeys } as any,
190
+ children: mockChildren,
191
+ riverId: "test-river",
192
+ plugins: [
193
+ {
194
+ identifier: "test-plugin",
195
+ module: {
196
+ addDataSourceListener: mockAddDataSourceListener,
197
+ },
198
+ },
199
+ ] as any,
200
+ };
201
+
202
+ renderWithProviders(<UrlFeedResolver {...props} />);
203
+
204
+ expect(mockAddDataSourceListener).toHaveBeenCalledWith(mockReloadData);
205
+ expect(mockSubscribeForKeyChanges).not.toHaveBeenCalled();
206
+ });
207
+
208
+ it("should apply item limit from component rules", () => {
209
+ const props = {
210
+ feedUrl: "https://example.com/feed",
211
+ component: {
212
+ rules: {
213
+ item_limit: "2",
214
+ },
215
+ } as any,
216
+ children: mockChildren,
217
+ riverId: "test-river",
218
+ };
219
+
220
+ (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
221
+ reloadData: mockReloadData,
222
+ loadNext: mockLoadNext,
223
+ error: null,
224
+ loading: false,
225
+ url: "test-url",
226
+ data: { entry: [{ id: "1" }, { id: "2" }, { id: "3" }] },
227
+ });
228
+
229
+ renderWithProviders(<UrlFeedResolver {...props} />);
230
+
231
+ expect(mockChildren).toHaveBeenCalledWith(
232
+ expect.objectContaining({
233
+ zappPipesData: expect.objectContaining({
234
+ data: expect.objectContaining({
235
+ entry: [{ id: "1" }, { id: "2" }],
236
+ }),
237
+ }),
238
+ })
239
+ );
240
+ });
241
+
242
+ it("should conditionally include loadNextData based on isLast and component type", () => {
243
+ // Case 1: isLast=true, should not include loadNextData regardless of component type
244
+ const props1 = {
245
+ feedUrl: "https://example.com/feed",
246
+ component: {
247
+ type: "vertical_list",
248
+ ...componentRequiredKeys,
249
+ } as any,
250
+ children: mockChildren,
251
+ riverId: "test-river",
252
+ isLast: true,
253
+ };
254
+
255
+ renderWithProviders(<UrlFeedResolver {...props1} />);
256
+
257
+ expect(mockChildren).toHaveBeenCalledWith(
258
+ expect.objectContaining({
259
+ loadNextData: mockLoadNext,
260
+ })
261
+ );
262
+
263
+ mockChildren.mockClear();
264
+
265
+ // Case 2: isLast=false, vertical_list component, should not include loadNextData
266
+ const props2 = {
267
+ feedUrl: "https://example.com/feed",
268
+ component: {
269
+ type: "vertical_list",
270
+ ...componentRequiredKeys,
271
+ } as any,
272
+ children: mockChildren,
273
+ riverId: "test-river",
274
+ isLast: false,
275
+ };
276
+
277
+ renderWithProviders(<UrlFeedResolver {...props2} />);
278
+
279
+ expect(mockChildren).toHaveBeenCalledWith(
280
+ expect.objectContaining({
281
+ loadNextData: undefined,
282
+ })
283
+ );
284
+
285
+ mockChildren.mockClear();
286
+
287
+ // Case 3: isLast=false, non-vertical component, should include loadNextData
288
+ const props3 = {
289
+ feedUrl: "https://example.com/feed",
290
+ component: {
291
+ type: "horizontal_list",
292
+ ...componentRequiredKeys,
293
+ } as any,
294
+ children: mockChildren,
295
+ riverId: "test-river",
296
+ isLast: false,
297
+ };
298
+
299
+ renderWithProviders(<UrlFeedResolver {...props3} />);
300
+
301
+ expect(mockChildren).toHaveBeenCalledWith(
302
+ expect.objectContaining({
303
+ loadNextData: mockLoadNext,
304
+ })
305
+ );
306
+ });
307
+
308
+ it("should pass correct zappPipesDataProps to children", () => {
309
+ const props = {
310
+ feedUrl: "https://example.com/feed",
311
+ component: { ...componentRequiredKeys } as any,
312
+ children: mockChildren,
313
+ riverId: "test-river",
314
+ };
315
+
316
+ renderWithProviders(<UrlFeedResolver {...props} />);
317
+
318
+ expect(mockChildren).toHaveBeenCalledWith({
319
+ zappPipesData: {
320
+ url: "test-url",
321
+ loading: false,
322
+ data: { entry: [{ id: "1" }] },
323
+ error: null,
324
+ },
325
+ reloadData: mockReloadData,
326
+ loadNextData: mockLoadNext,
327
+ });
328
+ });
329
+
330
+ it("should apply feed refresh hook", () => {
331
+ const props = {
332
+ feedUrl: "https://example.com/feed",
333
+ component: { ...componentRequiredKeys } as any,
334
+ children: mockChildren,
335
+ riverId: "test-river",
336
+ };
337
+
338
+ renderWithProviders(<UrlFeedResolver {...props} />);
339
+
340
+ expect(useFeedRefreshModule.useFeedRefresh).toHaveBeenCalledWith({
341
+ reloadData: mockReloadData,
342
+ component: props.component,
343
+ });
344
+ });
345
+
346
+ it("should clean up listeners on unmount", () => {
347
+ const props = {
348
+ component: {
349
+ data: {
350
+ source: "favorites-source",
351
+ type: "FAVOURITES",
352
+ },
353
+ ...componentRequiredKeys,
354
+ } as any,
355
+ children: mockChildren,
356
+ riverId: "test-river",
357
+ };
358
+
359
+ const { unmount } = renderWithProviders(<UrlFeedResolver {...props} />);
360
+
361
+ unmount();
362
+
363
+ expect(favoritesListener.removeHandler).toHaveBeenCalledWith(
364
+ "FAVORITES_CHANGED",
365
+ mockReloadData
366
+ );
367
+ });
368
+ });
@@ -0,0 +1,39 @@
1
+ import { isVerticalListOrGrid } from "../utils";
2
+
3
+ describe("ZappPipesDataConnector utils", () => {
4
+ describe("isVerticalListOrGrid", () => {
5
+ it("should return true for list-qb components", () => {
6
+ const component = {
7
+ component_type: "list-qb",
8
+ } as any;
9
+
10
+ expect(isVerticalListOrGrid(component)).toBe(true);
11
+ });
12
+
13
+ it("should return true for grid-qb components", () => {
14
+ const component = {
15
+ component_type: "grid-qb",
16
+ } as any;
17
+
18
+ expect(isVerticalListOrGrid(component)).toBe(true);
19
+ });
20
+
21
+ it("should return false for other component types", () => {
22
+ const component = {
23
+ component_type: "horizontal-list",
24
+ } as any;
25
+
26
+ expect(isVerticalListOrGrid(component)).toBe(false);
27
+ });
28
+
29
+ it("should return false for undefined component", () => {
30
+ expect(isVerticalListOrGrid(undefined)).toBe(false);
31
+ });
32
+
33
+ it("should return false for component without type", () => {
34
+ const component = {} as any;
35
+
36
+ expect(isVerticalListOrGrid(component)).toBe(false);
37
+ });
38
+ });
39
+ });