@applicaster/zapp-react-native-utils 15.0.0-rc.9 → 15.0.0-rc.90

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 (107) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +3 -6
  2. package/actionsExecutor/feedDecorator.ts +6 -6
  3. package/adsUtils/index.ts +2 -2
  4. package/analyticsUtils/README.md +1 -1
  5. package/analyticsUtils/analyticsMapper.ts +10 -2
  6. package/appUtils/HooksManager/index.ts +10 -10
  7. package/appUtils/RiverFocusManager/{index.js → index.ts} +25 -18
  8. package/appUtils/accessibilityManager/__tests__/utils.test.ts +360 -0
  9. package/appUtils/accessibilityManager/const.ts +4 -0
  10. package/appUtils/accessibilityManager/hooks.ts +20 -13
  11. package/appUtils/accessibilityManager/index.ts +28 -1
  12. package/appUtils/accessibilityManager/utils.ts +59 -8
  13. package/appUtils/contextKeysManager/__tests__/getKeys/failure.test.ts +7 -2
  14. package/appUtils/contextKeysManager/__tests__/getKeys/success.test.ts +48 -0
  15. package/appUtils/contextKeysManager/contextResolver.ts +51 -22
  16. package/appUtils/contextKeysManager/index.ts +65 -10
  17. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +4 -0
  18. package/appUtils/focusManager/index.ios.ts +59 -3
  19. package/appUtils/focusManagerAux/utils/index.ios.ts +122 -0
  20. package/appUtils/focusManagerAux/utils/index.ts +19 -1
  21. package/appUtils/focusManagerAux/utils/utils.ios.ts +231 -0
  22. package/appUtils/keyCodes/keys/keys.web.ts +1 -4
  23. package/appUtils/orientationHelper.ts +2 -4
  24. package/appUtils/platform/platformUtils.ts +117 -18
  25. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +94 -4
  26. package/appUtils/playerManager/OverlayObserver/utils.ts +32 -20
  27. package/appUtils/playerManager/player.ts +4 -0
  28. package/appUtils/playerManager/playerNative.ts +29 -16
  29. package/appUtils/playerManager/usePlayerState.tsx +14 -2
  30. package/arrayUtils/__tests__/allTruthy.test.ts +24 -0
  31. package/arrayUtils/__tests__/anyThruthy.test.ts +24 -0
  32. package/arrayUtils/index.ts +5 -0
  33. package/cellUtils/index.ts +32 -0
  34. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +38 -0
  35. package/configurationUtils/__tests__/manifestKeyParser.test.ts +26 -26
  36. package/configurationUtils/index.ts +17 -11
  37. package/focusManager/aux/index.ts +1 -1
  38. package/manifestUtils/defaultManifestConfigurations/player.js +96 -11
  39. package/manifestUtils/keys.js +21 -0
  40. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +1 -0
  41. package/manifestUtils/tvAction/container/index.js +1 -1
  42. package/navigationUtils/index.ts +15 -5
  43. package/package.json +4 -4
  44. package/playerUtils/usePlayerTTS.ts +8 -3
  45. package/pluginUtils/index.ts +4 -0
  46. package/reactHooks/advertising/index.ts +2 -2
  47. package/reactHooks/app/__tests__/useAppState.test.ts +1 -1
  48. package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
  49. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -2
  50. package/reactHooks/cell-click/__tests__/index.test.js +1 -3
  51. package/reactHooks/configuration/__tests__/index.test.tsx +1 -1
  52. package/reactHooks/connection/__tests__/index.test.js +1 -1
  53. package/reactHooks/debugging/__tests__/index.test.js +4 -4
  54. package/reactHooks/device/useMemoizedIsTablet.ts +3 -3
  55. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +32 -23
  56. package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +19 -19
  57. package/reactHooks/feed/__tests__/useEntryScreenId.test.tsx +4 -1
  58. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +42 -30
  59. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +1 -1
  60. package/reactHooks/feed/__tests__/{useInflatedUrl.test.ts → useInflatedUrl.test.tsx} +62 -7
  61. package/reactHooks/feed/useBatchLoading.ts +7 -1
  62. package/reactHooks/feed/useEntryScreenId.ts +2 -2
  63. package/reactHooks/feed/useInflatedUrl.ts +43 -17
  64. package/reactHooks/feed/usePipesCacheReset.ts +3 -1
  65. package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +13 -16
  66. package/reactHooks/hookModal/hooks/useHookModalScreenData.ts +12 -8
  67. package/reactHooks/layout/__tests__/index.test.tsx +1 -1
  68. package/reactHooks/layout/__tests__/useLayoutVersion.test.tsx +1 -1
  69. package/reactHooks/layout/index.ts +1 -1
  70. package/reactHooks/layout/useDimensions/__tests__/{useDimensions.test.ts → useDimensions.test.tsx} +105 -25
  71. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -2
  72. package/reactHooks/navigation/__tests__/index.test.tsx +40 -9
  73. package/reactHooks/navigation/index.ts +27 -11
  74. package/reactHooks/navigation/useRoute.ts +11 -7
  75. package/reactHooks/player/TVSeekControlller/TVSeekController.ts +27 -10
  76. package/reactHooks/player/__tests__/useAutoSeek._test.tsx +1 -1
  77. package/reactHooks/player/__tests__/useTapSeek._test.ts +1 -1
  78. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +1 -1
  79. package/reactHooks/resolvers/__tests__/useComponentResolver.test.tsx +1 -1
  80. package/reactHooks/resolvers/useCellResolver.ts +6 -2
  81. package/reactHooks/resolvers/useComponentResolver.ts +8 -2
  82. package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +2 -2
  83. package/reactHooks/screen/__tests__/useScreenBackgroundColor.test.tsx +1 -1
  84. package/reactHooks/screen/__tests__/useScreenData.test.tsx +1 -1
  85. package/reactHooks/screen/__tests__/useTargetScreenData.test.tsx +12 -4
  86. package/reactHooks/screen/useTargetScreenData.ts +4 -2
  87. package/reactHooks/state/useRefWithInitialValue.ts +10 -0
  88. package/reactHooks/state/useRivers.ts +1 -1
  89. package/reactHooks/usePluginConfiguration.ts +2 -2
  90. package/reactHooks/utils/__tests__/index.test.js +1 -1
  91. package/screenState/__tests__/index.test.ts +1 -1
  92. package/searchUtils/const.ts +7 -0
  93. package/searchUtils/index.ts +3 -0
  94. package/services/storageServiceSync.web.ts +1 -1
  95. package/stringUtils/index.ts +1 -1
  96. package/testUtils/index.tsx +30 -21
  97. package/utils/__tests__/mapAccum.test.ts +73 -0
  98. package/utils/__tests__/mergeRight.test.ts +48 -0
  99. package/utils/__tests__/selectors.test.ts +124 -0
  100. package/utils/index.ts +20 -0
  101. package/utils/mapAccum.ts +23 -0
  102. package/utils/mergeRight.ts +5 -0
  103. package/utils/path.ts +6 -3
  104. package/utils/pathOr.ts +5 -1
  105. package/utils/selectors.ts +46 -0
  106. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +49 -12
  107. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +1 -1
@@ -1,4 +1,4 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook } from "@testing-library/react-native";
2
2
 
3
3
  jest.mock(
4
4
  "@applicaster/zapp-react-native-ui-components/Contexts/ScreenTrackedViewPositionsContext",
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { renderHook } from "@testing-library/react-hooks";
4
- import { act, waitFor } from "@testing-library/react-native";
3
+ import { renderHook, act, waitFor } from "@testing-library/react-native";
5
4
  import { Provider } from "react-redux";
6
5
  import configureStore from "redux-mock-store";
7
6
  import { useTrackedView } from "../useTrackedView";
@@ -1,10 +1,8 @@
1
1
  import * as React from "react";
2
- import { cleanup, renderHook } from "@testing-library/react-hooks";
2
+ import { cleanup, renderHook, waitFor } from "@testing-library/react-native";
3
3
  import { CellTapContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellTapContext";
4
4
  import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutorContext";
5
5
 
6
- import { waitFor } from "@testing-library/react-native";
7
-
8
6
  const mockNavigator = {
9
7
  getPathname: () => "pathnameMock",
10
8
  push: jest.fn(),
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable react/prop-types */
2
2
  import React from "react";
3
- import { renderHook, cleanup } from "@testing-library/react-hooks";
3
+ import { renderHook, cleanup } from "@testing-library/react-native";
4
4
  import { ConfigurationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfigutaionContext";
5
5
  import { useConfiguration } from "@applicaster/zapp-react-native-utils/reactHooks/configuration";
6
6
 
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { renderHook, cleanup } from "@testing-library/react-hooks";
2
+ import { renderHook, cleanup } from "@testing-library/react-native";
3
3
  import { NetworkStatusContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NetworkStatusContext";
4
4
 
5
5
  const MockNetInfo = {
@@ -27,20 +27,20 @@ describe("Debug utils", () => {
27
27
  it("should create new timer if timer with a label does not exist", () => {
28
28
  performanceNowMock.mockReturnValue(1000);
29
29
  time("timer1");
30
- expect(timers["timer1"]).toBeDefined();
31
- expect(timers["timer1"].startTime).toBe(1000);
30
+ expect(timers.timer1).toBeDefined();
31
+ expect(timers.timer1.startTime).toBe(1000);
32
32
  });
33
33
 
34
34
  it("should update start time of timer if timer with a label exists", () => {
35
35
  // First call to time()
36
36
  performanceNowMock.mockReturnValue(1000);
37
37
  time("timer1");
38
- const previousStartTime = timers["timer1"].startTime;
38
+ const previousStartTime = timers.timer1.startTime;
39
39
 
40
40
  // Second call to time()
41
41
  performanceNowMock.mockReturnValue(2000);
42
42
  time("timer1");
43
- const currentStartTime = timers["timer1"].startTime;
43
+ const currentStartTime = timers.timer1.startTime;
44
44
 
45
45
  expect(previousStartTime).toBe(1000);
46
46
  expect(currentStartTime).toBe(2000);
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
3
2
  import { useIsTablet } from "./useIsTablet";
3
+ import { useAppData } from "@applicaster/zapp-react-native-redux";
4
4
 
5
5
  export const useMemoizedIsTablet = () => {
6
6
  const isTablet = useIsTablet();
@@ -14,9 +14,9 @@ export const useMemoizedIsTablet = () => {
14
14
  };
15
15
 
16
16
  export const useIsTabletLandscape = (): boolean => {
17
- const { appData } = usePickFromState(["appData"]);
17
+ const { isTabletPortrait } = useAppData();
18
18
 
19
19
  const isTablet = useIsTablet();
20
20
 
21
- return isTablet && !appData.isTabletPortrait;
21
+ return isTablet && !isTabletPortrait;
22
22
  };
@@ -1,8 +1,7 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook, waitFor } from "@testing-library/react-native";
2
2
  import { allFeedsIsReady, useBatchLoading } from "../useBatchLoading";
3
3
  import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
4
  import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
5
- import { waitFor } from "@testing-library/react-native";
6
5
 
7
6
  jest.mock("../../navigation");
8
7
 
@@ -16,7 +15,11 @@ jest.mock(
16
15
  })
17
16
  );
18
17
 
19
- const wrapper = WrappedWithProviders;
18
+ const getWrapper =
19
+ (store) =>
20
+ ({ children }) => (
21
+ <WrappedWithProviders store={store}>{children}</WrappedWithProviders>
22
+ );
20
23
 
21
24
  describe("useBatchLoading", () => {
22
25
  const data = [
@@ -63,8 +66,7 @@ describe("useBatchLoading", () => {
63
66
  const riverId = "123";
64
67
 
65
68
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
66
- wrapper,
67
- initialProps: { store },
69
+ wrapper: getWrapper(store),
68
70
  });
69
71
 
70
72
  const actions = (appStore.getStore() as any).getActions();
@@ -84,7 +86,7 @@ describe("useBatchLoading", () => {
84
86
  });
85
87
  });
86
88
 
87
- it("loadPipesData start loading new feed when 1 feed is done loading and 1 is in loading state", () => {
89
+ it("loadPipesData start loading new feed when 1 feed is done loading and 1 is in loading state", async () => {
88
90
  const store = {
89
91
  zappPipes: {
90
92
  url1: {
@@ -110,13 +112,14 @@ describe("useBatchLoading", () => {
110
112
  const riverId = "123";
111
113
 
112
114
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
113
- wrapper,
114
- initialProps: { store },
115
+ wrapper: getWrapper(store),
115
116
  });
116
117
 
117
118
  const actions = (appStore.getStore() as any).getActions();
118
119
 
119
- expect(actions).toHaveLength(1);
120
+ await waitFor(() => {
121
+ expect(actions).toHaveLength(1);
122
+ });
120
123
 
121
124
  expect(actions[0]).toMatchObject({
122
125
  type: "ZAPP_PIPES_REQUEST_START",
@@ -124,7 +127,7 @@ describe("useBatchLoading", () => {
124
127
  });
125
128
  });
126
129
 
127
- it("loadPipesData has been called when no data cached", () => {
130
+ it("loadPipesData has been called when no data cached", async () => {
128
131
  const store = {
129
132
  zappPipes: {},
130
133
  test: "true",
@@ -134,16 +137,17 @@ describe("useBatchLoading", () => {
134
137
  const riverId = "123";
135
138
 
136
139
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
137
- wrapper,
138
- initialProps: { store },
140
+ wrapper: getWrapper(store),
139
141
  });
140
142
 
141
143
  const actions = (appStore.getStore() as any).getActions();
142
144
 
143
- expect(actions).toHaveLength(3);
145
+ await waitFor(() => {
146
+ expect(actions).toHaveLength(3);
147
+ });
144
148
  });
145
149
 
146
- it("initial batch ready when all initial items loaded", () => {
150
+ it("initial batch ready when all initial items loaded", async () => {
147
151
  const store = {
148
152
  zappPipes: {
149
153
  url1: {
@@ -169,13 +173,15 @@ describe("useBatchLoading", () => {
169
173
 
170
174
  const { result } = renderHook(
171
175
  () => useBatchLoading(data, { initialBatchSize, riverId }),
172
- { wrapper, initialProps: { store } }
176
+ { wrapper: getWrapper(store) }
173
177
  );
174
178
 
175
- expect(result.current).toBe(true);
179
+ await waitFor(() => {
180
+ expect(result.current).toBe(true);
181
+ });
176
182
  });
177
183
 
178
- it("gallery-qb: loadPipesData should be called only once for first component in the gallery", () => {
184
+ it("gallery-qb: loadPipesData should be called only once for first component in the gallery", async () => {
179
185
  const store = {
180
186
  zappPipes: {},
181
187
  test: "true",
@@ -198,16 +204,17 @@ describe("useBatchLoading", () => {
198
204
  ];
199
205
 
200
206
  renderHook(() => useBatchLoading(data, { initialBatchSize, riverId }), {
201
- wrapper,
202
- initialProps: { store },
207
+ wrapper: getWrapper(store),
203
208
  });
204
209
 
205
210
  const actions = (appStore.getStore() as any).getActions();
206
211
 
207
- expect(actions).toHaveLength(1);
212
+ await waitFor(() => {
213
+ expect(actions).toHaveLength(1);
214
+ });
208
215
  });
209
216
 
210
- it("gallery-qb: initial batch ready when all initial items loaded", () => {
217
+ it("gallery-qb: initial batch ready when all initial items loaded", async () => {
211
218
  const store = {
212
219
  zappPipes: {
213
220
  url1: {
@@ -233,10 +240,12 @@ describe("useBatchLoading", () => {
233
240
 
234
241
  const { result } = renderHook(
235
242
  () => useBatchLoading(data, { initialBatchSize, riverId }),
236
- { wrapper, initialProps: { store } }
243
+ { wrapper: getWrapper(store) }
237
244
  );
238
245
 
239
- expect(result.current).toBe(true);
246
+ await waitFor(() => {
247
+ expect(result.current).toBe(true);
248
+ });
240
249
  });
241
250
  });
242
251
 
@@ -1,4 +1,4 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook, waitFor } from "@testing-library/react-native";
2
2
  import { encodeParams } from "@applicaster/zapp-pipes-v2-client/src/utils";
3
3
 
4
4
  const appData = {
@@ -42,26 +42,26 @@ describe("useBuildPipesUrl", () => {
42
42
  build: jest.fn(() => Promise.resolve(buildResult)),
43
43
  };
44
44
 
45
- const { result, waitForNextUpdate } = renderHook(() =>
45
+ const { result } = renderHook(() =>
46
46
  useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
47
47
  );
48
48
 
49
49
  expect(result.current.url).toBe("http://foo.com/show/show-1");
50
50
  expect(result.current.requestParams).toBeNull();
51
51
 
52
- await waitForNextUpdate();
53
-
54
- expect(result.current.requestParams).toEqual(
55
- expect.objectContaining({
56
- params: {
57
- version: appData.version_name,
58
- ctx: encodeParams({ platform: appData.platform }),
59
- },
60
- headers: {
61
- SDK: appData.sdk_version,
62
- },
63
- })
64
- );
52
+ await waitFor(() => {
53
+ expect(result.current.requestParams).toEqual(
54
+ expect.objectContaining({
55
+ params: {
56
+ version: appData.version_name,
57
+ ctx: encodeParams({ platform: appData.platform }),
58
+ },
59
+ headers: {
60
+ SDK: appData.sdk_version,
61
+ },
62
+ })
63
+ );
64
+ });
65
65
  });
66
66
 
67
67
  it("returns empty object if url is not in endpoints", async () => {
@@ -81,15 +81,15 @@ describe("useBuildPipesUrl", () => {
81
81
  build: jest.fn(() => Promise.resolve(buildResult)),
82
82
  };
83
83
 
84
- const { result, waitForNextUpdate } = renderHook(() =>
84
+ const { result } = renderHook(() =>
85
85
  useBuildPipesUrl({ url, mapping, requestBuilder: mockedRequestBuilder })
86
86
  );
87
87
 
88
88
  expect(result.current.url).toBe("http://foobar.com/show-1");
89
89
  expect(result.current.requestParams).toBeNull();
90
90
 
91
- await waitForNextUpdate();
92
-
93
- expect(result.current.requestParams).toEqual({});
91
+ await waitFor(() => {
92
+ expect(result.current.requestParams).toEqual({});
93
+ });
94
94
  });
95
95
  });
@@ -1,6 +1,6 @@
1
1
  import * as R from "ramda";
2
2
  import React from "react";
3
- import { renderHook } from "@testing-library/react-hooks";
3
+ import { renderHook } from "@testing-library/react-native";
4
4
  import { Provider } from "react-redux";
5
5
  import { createStore } from "redux";
6
6
 
@@ -10,6 +10,9 @@ describe("useEntryScreenId", () => {
10
10
  const mappedScreenId = "mapped-id";
11
11
 
12
12
  const initialState = {
13
+ rivers: {
14
+ [mappedScreenId]: { id: mappedScreenId, type: "any" },
15
+ },
13
16
  contentTypes: { mappedEntry: { screen_id: mappedScreenId } },
14
17
  };
15
18
 
@@ -1,4 +1,4 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook } from "@testing-library/react-native";
2
2
  import * as R from "ramda";
3
3
  import * as zappPipesModule from "@applicaster/zapp-react-native-redux/ZappPipes";
4
4
  import * as reactReduxModules from "react-redux";
@@ -50,6 +50,16 @@ const mockZappPipesData = {
50
50
  url: "test://testfakeurl",
51
51
  };
52
52
 
53
+ const createWrapper = (
54
+ getStore: () => any
55
+ ): React.FC<{ children: React.ReactNode }> => {
56
+ const Wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
57
+ <WrappedWithProviders store={getStore()}>{children}</WrappedWithProviders>
58
+ );
59
+
60
+ return Wrapper;
61
+ };
62
+
53
63
  describe("useFeedLoader", () => {
54
64
  describe("with cached feed url", () => {
55
65
  const store = {
@@ -57,11 +67,7 @@ describe("useFeedLoader", () => {
57
67
  zappPipes: { "test://testfakeurl": mockZappPipesData },
58
68
  };
59
69
 
60
- const wrapper: React.FC<any> = ({ children, ...props }) => (
61
- <WrappedWithProviders store={props.store || store}>
62
- {children}
63
- </WrappedWithProviders>
64
- );
70
+ const wrapper = createWrapper(() => store);
65
71
 
66
72
  it("returns cached feed", () => {
67
73
  const { result } = renderHook(
@@ -109,12 +115,6 @@ describe("useFeedLoader", () => {
109
115
  describe("without cached feeds", () => {
110
116
  const feedUrl = "test://testfakeurl2";
111
117
 
112
- const wrapper: React.FC<any> = ({ children, ...props }) => (
113
- <WrappedWithProviders store={props.store}>
114
- {children}
115
- </WrappedWithProviders>
116
- );
117
-
118
118
  it("It loads data for new url and returns it", () => {
119
119
  const useDispatchSpy = jest
120
120
  .spyOn(reactReduxModules, "useDispatch")
@@ -129,9 +129,12 @@ describe("useFeedLoader", () => {
129
129
  zappPipes: { "test://testfakeurl": "foobar" },
130
130
  };
131
131
 
132
+ let store = initialStore;
133
+ const wrapper = createWrapper(() => store);
134
+
132
135
  const { result, rerender } = renderHook(
133
- () => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
134
- { wrapper, initialProps: { store: initialStore } }
136
+ ({ feedUrl: url }) => useFeedLoader({ feedUrl: url }),
137
+ { wrapper, initialProps: { feedUrl } }
135
138
  );
136
139
 
137
140
  expect(result.current.data).toBeNull();
@@ -150,7 +153,8 @@ describe("useFeedLoader", () => {
150
153
  zappPipes: { "test://testfakeurl2": mockZappPipesData },
151
154
  };
152
155
 
153
- rerender({ store: store2 });
156
+ store = store2;
157
+ rerender({ feedUrl });
154
158
 
155
159
  expect(R.omit(["reloadData", "loadNext"], result.current)).toEqual(
156
160
  mockZappPipesData
@@ -174,9 +178,12 @@ describe("useFeedLoader", () => {
174
178
  zappPipes: { "test://testfakeurl": "foobar" },
175
179
  };
176
180
 
181
+ let store = initialStore;
182
+ const wrapper = createWrapper(() => store);
183
+
177
184
  const { result, rerender } = renderHook(
178
- () => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
179
- { wrapper, initialProps: { store: initialStore } }
185
+ ({ feedUrl: url }) => useFeedLoader({ feedUrl: url }),
186
+ { wrapper, initialProps: { feedUrl } }
180
187
  );
181
188
 
182
189
  expect(result.current.data).toBeNull();
@@ -198,7 +205,8 @@ describe("useFeedLoader", () => {
198
205
  zappPipes: { "test://testfakeurl2": mockZappPipesData },
199
206
  };
200
207
 
201
- rerender({ store: store2 });
208
+ store = store2;
209
+ rerender({ feedUrl });
202
210
 
203
211
  loadPipesDataSpy.mockRestore();
204
212
  useDispatchSpy.mockRestore();
@@ -209,12 +217,6 @@ describe("useFeedLoader", () => {
209
217
  const feedUrl = "test://testfakeurl";
210
218
  const feedUrlWithNext = "test://withnexttestfakeurl";
211
219
 
212
- const wrapper: React.FC<any> = ({ children, ...props }) => (
213
- <WrappedWithProviders store={props.store || {}}>
214
- {children}
215
- </WrappedWithProviders>
216
- );
217
-
218
220
  describe("reloadData", () => {
219
221
  it("Tries to reload data in the redux store", () => {
220
222
  const loadPipesDataSpy = jest
@@ -230,10 +232,17 @@ describe("useFeedLoader", () => {
230
232
  zappPipes: { [feedUrl]: "foobar" },
231
233
  };
232
234
 
233
- const { result } = renderHook(() => useFeedLoader({ feedUrl }), {
234
- wrapper,
235
- initialProps: { store: initialStore },
236
- });
235
+ const store = initialStore;
236
+
237
+ const wrapper = createWrapper(() => store);
238
+
239
+ const { result } = renderHook(
240
+ ({ feedUrl: url }) => useFeedLoader({ feedUrl: url }),
241
+ {
242
+ wrapper,
243
+ initialProps: { feedUrl },
244
+ }
245
+ );
237
246
 
238
247
  const { reloadData } = result.current;
239
248
 
@@ -279,11 +288,14 @@ describe("useFeedLoader", () => {
279
288
  zappPipes: { [feedUrlWithNext]: { data: { next: nextUrl } } },
280
289
  };
281
290
 
291
+ const store = initialStore;
292
+ const wrapper = createWrapper(() => store);
293
+
282
294
  const { result } = renderHook(
283
- () => useFeedLoader({ feedUrl: feedUrlWithNext }),
295
+ ({ feedUrl: url }) => useFeedLoader({ feedUrl: url }),
284
296
  {
285
297
  wrapper,
286
- initialProps: { store: initialStore },
298
+ initialProps: { feedUrl: feedUrlWithNext },
287
299
  }
288
300
  );
289
301
 
@@ -1,4 +1,4 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook } from "@testing-library/react-native";
2
2
 
3
3
  jest.useFakeTimers();
4
4
 
@@ -1,3 +1,34 @@
1
+ import React from "react";
2
+
3
+ import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
4
+
5
+ import {
6
+ getInflatedDataSourceUrl,
7
+ getSearchContext,
8
+ useGetUrlInflater,
9
+ } from "../useInflatedUrl";
10
+
11
+ import { reactHooksLogger } from "../../logger";
12
+
13
+ jest.mock("../../navigation", () => ({
14
+ useRoute: () => ({ pathname: "/mock/path" }),
15
+ }));
16
+
17
+ // mock contexts hooks
18
+ jest.mock("@applicaster/zapp-react-native-ui-components/Contexts", () => ({
19
+ ZappPipesEntryContext: {
20
+ useZappPipesContext: () => [
21
+ { id: "entry-1", extensions: { showId: "A123" } },
22
+ ],
23
+ },
24
+ ZappPipesSearchContext: {
25
+ useZappPipesContext: () => ["user%20query"],
26
+ },
27
+ ZappPipesScreenContext: {
28
+ useZappPipesContext: () => [{ id: "screen-1" }],
29
+ },
30
+ }));
31
+
1
32
  jest.mock("../../logger", () => ({
2
33
  reactHooksLogger: {
3
34
  warning: jest.fn(),
@@ -5,13 +36,6 @@ jest.mock("../../logger", () => ({
5
36
  },
6
37
  }));
7
38
 
8
- const {
9
- getInflatedDataSourceUrl,
10
- getSearchContext,
11
- } = require("../useInflatedUrl");
12
-
13
- const { reactHooksLogger } = require("../../logger");
14
-
15
39
  let mockStore;
16
40
 
17
41
  describe("getInflatedDataSourceUrl", () => {
@@ -188,3 +212,34 @@ describe("getSearchContext", () => {
188
212
  expect(result).toHaveProperty(mapping.queryParam.property, searchContext);
189
213
  });
190
214
  });
215
+
216
+ describe("useGetUrlInflater", () => {
217
+ const { renderHook } = require("@testing-library/react-native");
218
+
219
+ it("returns original url when mapping is not provided", () => {
220
+ const { result } = renderHook(() => useGetUrlInflater(), {
221
+ wrapper: WrappedWithProviders,
222
+ });
223
+
224
+ const src = "https://foo.com/feed";
225
+ expect(result.current(src)).toBe(src);
226
+ });
227
+
228
+ it("inflates url using entry/search/screen contexts when mapping provided", () => {
229
+ const { result } = renderHook(() => useGetUrlInflater(), {
230
+ wrapper: ({ children }) => (
231
+ <WrappedWithProviders>{children}</WrappedWithProviders>
232
+ ),
233
+ });
234
+
235
+ const source = "https://foo.com/shows/{{showId}}?q={{q}}";
236
+
237
+ const mapping = {
238
+ showId: { source: "entry", property: "extensions.showId" },
239
+ q: { source: "search", property: "q" },
240
+ };
241
+
242
+ const url = result.current(source, mapping);
243
+ expect(url).toBe("https://foo.com/shows/A123?q=user%20query");
244
+ });
245
+ });
@@ -151,7 +151,13 @@ export const useBatchLoading = (
151
151
  }
152
152
  }
153
153
  });
154
- }, [feedUrls, feeds, loadPipesDataDispatcher]);
154
+ }, [
155
+ batchComponents,
156
+ feeds,
157
+ getUrl,
158
+ loadPipesDataDispatcher,
159
+ options.riverId,
160
+ ]);
155
161
 
156
162
  React.useEffect(() => {
157
163
  runBatchLoading();
@@ -1,7 +1,7 @@
1
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
1
+ import { useContentTypes } from "@applicaster/zapp-react-native-redux";
2
2
 
3
3
  export const useEntryScreenId = (entry?: ZappEntry): string | undefined => {
4
- const { contentTypes } = usePickFromState("contentTypes");
4
+ const contentTypes = useContentTypes();
5
5
 
6
6
  if (!entry) {
7
7
  return;
@@ -1,4 +1,4 @@
1
- import { useMemo } from "react";
1
+ import { useCallback, useMemo, useRef } from "react";
2
2
  import * as R from "ramda";
3
3
 
4
4
  import {
@@ -169,7 +169,7 @@ const encodeIfNeeded: (string) => string = R.tryCatch(
169
169
 
170
170
  export function getSearchContext(
171
171
  searchContext: string,
172
- mapping: ZappTypeMapping
172
+ mapping: Nullable<ZappTypeMapping>
173
173
  ) {
174
174
  if (!mapping) {
175
175
  return {};
@@ -183,31 +183,57 @@ export function getSearchContext(
183
183
  return { [property]: encodeIfNeeded(searchContext) };
184
184
  }
185
185
 
186
- export function useInflatedUrl({
187
- feedUrl,
188
- mapping,
189
- }: {
190
- feedUrl?: string;
191
- mapping?: ZappTypeMapping;
192
- }) {
186
+ /**
187
+ * Hook returns a function that replace placeholders with context values
188
+ * @returns function that inflates urls based on contexts
189
+ */
190
+ export const useGetUrlInflater = () => {
193
191
  const { pathname } = useRoute();
194
192
 
195
193
  const [entryContext] = ZappPipesEntryContext.useZappPipesContext(pathname);
196
194
  const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
197
195
  const [screenContext] = ZappPipesScreenContext.useZappPipesContext();
198
196
 
199
- const url = useMemo(
200
- () =>
201
- getInflatedDataSourceUrl({
197
+ const entryContextRef = useRef(entryContext);
198
+ entryContextRef.current = entryContext;
199
+
200
+ const screenContextRef = useRef(screenContext);
201
+ screenContextRef.current = screenContext;
202
+
203
+ const searchContextRef = useRef(searchContext);
204
+ searchContextRef.current = searchContext;
205
+
206
+ return useCallback(
207
+ (
208
+ feedUrl: Nullable<string>,
209
+ mapping?: Nullable<ZappTypeMapping>
210
+ ): Nullable<string> => {
211
+ return getInflatedDataSourceUrl({
202
212
  source: feedUrl,
203
213
  contexts: {
204
- entry: entryContext,
205
- screen: screenContext,
206
- search: getSearchContext(searchContext, mapping),
214
+ entry: entryContextRef.current,
215
+ screen: screenContextRef.current,
216
+ search: getSearchContext(searchContextRef.current, mapping),
207
217
  },
208
218
  mapping,
209
- }),
210
- [entryContext, feedUrl, mapping, screenContext, searchContext]
219
+ });
220
+ },
221
+ []
222
+ );
223
+ };
224
+
225
+ export function useInflatedUrl({
226
+ feedUrl,
227
+ mapping,
228
+ }: {
229
+ feedUrl?: string;
230
+ mapping?: ZappTypeMapping;
231
+ }) {
232
+ const urlInflater = useGetUrlInflater();
233
+
234
+ const url = useMemo(
235
+ () => urlInflater(feedUrl, mapping),
236
+ [urlInflater, feedUrl, mapping]
211
237
  );
212
238
 
213
239
  return url;