@applicaster/zapp-react-native-utils 14.0.0-alpha.5219335081 → 14.0.0-alpha.5234792518

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 (37) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +60 -84
  2. package/actionsExecutor/ScreenActions.ts +164 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/analyticsUtils/AnalyticsEvents/helper.ts +1 -1
  7. package/analyticsUtils/__tests__/analyticsUtils.test.js +0 -11
  8. package/analyticsUtils/playerAnalyticsTracker.ts +2 -1
  9. package/appUtils/HooksManager/index.ts +10 -10
  10. package/appUtils/contextKeysManager/contextResolver.ts +42 -1
  11. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
  12. package/appUtils/focusManager/index.ios.ts +10 -0
  13. package/appUtils/focusManager/index.ts +25 -16
  14. package/appUtils/focusManagerAux/utils/index.ts +18 -2
  15. package/configurationUtils/__tests__/manifestKeyParser.test.ts +0 -1
  16. package/navigationUtils/index.ts +1 -1
  17. package/package.json +2 -2
  18. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  19. package/reactHooks/cell-click/index.ts +8 -1
  20. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  21. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
  22. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +71 -31
  23. package/reactHooks/feed/index.ts +2 -0
  24. package/reactHooks/feed/useBatchLoading.ts +14 -9
  25. package/reactHooks/feed/useFeedLoader.tsx +36 -38
  26. package/reactHooks/feed/useLoadPipesDataDispatch.ts +63 -0
  27. package/reactHooks/navigation/useRoute.ts +7 -2
  28. package/reactHooks/navigation/useScreenStateStore.ts +8 -0
  29. package/reactHooks/state/index.ts +1 -7
  30. package/reactHooks/state/useHomeRiver.ts +0 -31
  31. package/screenPickerUtils/index.ts +3 -6
  32. package/storage/ScreenSingleValueProvider.ts +204 -0
  33. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  34. package/storage/StorageMultiSelectProvider.ts +192 -0
  35. package/storage/StorageSingleSelectProvider.ts +108 -0
  36. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +72 -0
  37. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.ts +59 -0
@@ -230,7 +230,7 @@ export function HooksManager({
230
230
  function completeHook(hookPlugin, payload, callback) {
231
231
  logHookEvent(
232
232
  hooksManagerLogger.info,
233
- `completeHook: hook sequence completed successfully: ${hookPlugin["identifier"]}`,
233
+ `completeHook: hook sequence completed successfully: ${hookPlugin.identifier}`,
234
234
  {
235
235
  payload,
236
236
  hook: hookPlugin,
@@ -276,7 +276,7 @@ export function HooksManager({
276
276
  if (hookPlugin.isCancelled()) {
277
277
  logHookEvent(
278
278
  hooksManagerLogger.info,
279
- `hookCallback: hook was cancelled: ${hookPlugin["identifier"]}`,
279
+ `hookCallback: hook was cancelled: ${hookPlugin.identifier}`,
280
280
  {}
281
281
  );
282
282
 
@@ -305,7 +305,7 @@ export function HooksManager({
305
305
  if (!success) {
306
306
  logHookEvent(
307
307
  hooksManagerLogger.info,
308
- `hookCallback: hook was cancelled: ${hookPlugin["identifier"]}`,
308
+ `hookCallback: hook was cancelled: ${hookPlugin.identifier}`,
309
309
  {
310
310
  payload,
311
311
  hook: hookPlugin,
@@ -334,7 +334,7 @@ export function HooksManager({
334
334
  if (isHookInHomescreen && isHookFlowBlocker && cancelled) {
335
335
  logHookEvent(
336
336
  hooksManagerLogger.info,
337
- `hookCallback: send app to background, cancelled flow blocker hook ${hookPlugin["identifier"]} on home screen`,
337
+ `hookCallback: send app to background, cancelled flow blocker hook ${hookPlugin.identifier} on home screen`,
338
338
  {
339
339
  payload,
340
340
  hook: hookPlugin,
@@ -349,7 +349,7 @@ export function HooksManager({
349
349
  } else {
350
350
  logHookEvent(
351
351
  hooksManagerLogger.info,
352
- `hookCallback: hook successfully finished: ${hookPlugin["identifier"]}`,
352
+ `hookCallback: hook successfully finished: ${hookPlugin.identifier}`,
353
353
  {
354
354
  payload,
355
355
  hook: hookPlugin,
@@ -359,7 +359,7 @@ export function HooksManager({
359
359
  if (!callback) {
360
360
  logHookEvent(
361
361
  hooksManagerLogger.warn,
362
- `hookCallback: ${hookPlugin["identifier"]} is missing \`callback\`, using hookCallback(default one)`,
362
+ `hookCallback: ${hookPlugin.identifier} is missing \`callback\`, using hookCallback(default one)`,
363
363
  {
364
364
  hookPlugin,
365
365
  }
@@ -401,7 +401,7 @@ export function HooksManager({
401
401
 
402
402
  logHookEvent(
403
403
  hooksManagerLogger.info,
404
- `presentScreenHook: Presenting screen hook: ${hookPlugin["identifier"]}`,
404
+ `presentScreenHook: Presenting screen hook: ${hookPlugin.identifier}`,
405
405
  {
406
406
  hook: hookPlugin,
407
407
  payload,
@@ -421,7 +421,7 @@ export function HooksManager({
421
421
  hooksManager.executeHook = function (hookPlugin, payload, callback) {
422
422
  logHookEvent(
423
423
  hooksManagerLogger.info,
424
- `executeHook: ${hookPlugin["identifier"]}`,
424
+ `executeHook: ${hookPlugin.identifier}`,
425
425
  {
426
426
  hook: hookPlugin,
427
427
  payload,
@@ -433,7 +433,7 @@ export function HooksManager({
433
433
  } catch (error) {
434
434
  logHookEvent(
435
435
  hooksManagerLogger.error,
436
- `executeHook: error executing hook: ${hookPlugin["identifier"]} error: ${error.message}`,
436
+ `executeHook: error executing hook: ${hookPlugin.identifier} error: ${error.message}`,
437
437
  {
438
438
  hook: hookPlugin,
439
439
  payload,
@@ -460,7 +460,7 @@ export function HooksManager({
460
460
  try {
461
461
  logHookEvent(
462
462
  hooksManagerLogger.info,
463
- `runInBackground: Executing hook: ${hookPlugin["identifier"]}`,
463
+ `runInBackground: Executing hook: ${hookPlugin.identifier}`,
464
464
  {
465
465
  hook: hookPlugin,
466
466
  payload,
@@ -1,10 +1,13 @@
1
1
  import { ContextKeysManager } from "./index";
2
2
  import * as R from "ramda";
3
+ import * as _ from "lodash";
4
+ import { useScreenStateStore } from "../../reactHooks/navigation/useScreenStateStore";
3
5
 
4
- interface IResolver {
6
+ export interface IResolver {
5
7
  resolve: (string) => Promise<string | number | object>;
6
8
  }
7
9
 
10
+ // TODO: Rename to ObjectKeyResolver or similar
8
11
  export class EntryResolver implements IResolver {
9
12
  entry: ZappEntry;
10
13
 
@@ -21,6 +24,28 @@ export class EntryResolver implements IResolver {
21
24
  }
22
25
  }
23
26
 
27
+ // TODO: Move to proper place
28
+
29
+ export class ScreenStateResolver implements IResolver {
30
+ constructor(
31
+ private screenStateStore: ReturnType<typeof useScreenStateStore>
32
+ ) {}
33
+
34
+ async resolve(key: string) {
35
+ const screenState = this.screenStateStore.getState().data;
36
+
37
+ if (!key || key.length === 0) {
38
+ return screenState;
39
+ }
40
+
41
+ if (key.includes(".")) {
42
+ return R.view(R.lensPath(key.split(".")), screenState);
43
+ }
44
+
45
+ return screenState?.[key];
46
+ }
47
+ }
48
+
24
49
  export class ContextResolver implements IResolver {
25
50
  resolve = async (compositeKey: string) =>
26
51
  ContextKeysManager.instance.getKey(compositeKey);
@@ -64,3 +89,19 @@ export const resolveObjectValues = async (
64
89
 
65
90
  return Object.fromEntries(resolvedEntries);
66
91
  };
92
+
93
+ export const extractAtValues = _.memoize((input: any): string[] => {
94
+ return _.flatMapDeep(input, (value: any) => {
95
+ if (_.isString(value)) {
96
+ const matches = value.match(/@\{([^}]*)\}/g);
97
+
98
+ return matches ? matches.map((match) => match.slice(2, -1)) : [];
99
+ }
100
+
101
+ if (_.isObject(value)) {
102
+ return extractAtValues(_.values(value));
103
+ }
104
+
105
+ return [];
106
+ });
107
+ });
@@ -25,6 +25,7 @@ exports[`focusManager should be defined 1`] = `
25
25
  "invokeHandler": [Function],
26
26
  "isCurrentFocusOnTheTopScreen": [Function],
27
27
  "isFocusDisabled": [Function],
28
+ "isFocusOn": [Function],
28
29
  "isFocusOnContent": [Function],
29
30
  "isFocusOnMenu": [Function],
30
31
  "isGroupItemFocused": [Function],
@@ -66,6 +67,7 @@ exports[`focusManagerIOS should be defined 1`] = `
66
67
  "getGroupRootById": [Function],
67
68
  "getPreferredFocusChild": [Function],
68
69
  "invokeHandler": [Function],
70
+ "isFocusOn": [Function],
69
71
  "isGroupItemFocused": [Function],
70
72
  "moveFocus": [Function],
71
73
  "on": [Function],
@@ -1,6 +1,7 @@
1
1
  import { NativeModules } from "react-native";
2
2
  import * as R from "ramda";
3
3
 
4
+ import { isCurrentFocusOn } from "../focusManagerAux/utils";
4
5
  import { Tree } from "./treeDataStructure/Tree";
5
6
  import { findFocusableNode } from "./treeDataStructure/Utils";
6
7
  import { subscriber } from "../../functionUtils";
@@ -391,6 +392,14 @@ export const focusManager = (function () {
391
392
  return node;
392
393
  }
393
394
 
395
+ function isFocusOn(id): boolean {
396
+ const currentFocusNode = focusableTree.findInTree(
397
+ getCurrentFocus()?.props?.id
398
+ );
399
+
400
+ return id && isCurrentFocusOn(id, currentFocusNode);
401
+ }
402
+
394
403
  return {
395
404
  on,
396
405
  invokeHandler,
@@ -412,5 +421,6 @@ export const focusManager = (function () {
412
421
  getGroupRootById,
413
422
  isGroupItemFocused,
414
423
  getPreferredFocusChild,
424
+ isFocusOn,
415
425
  };
416
426
  })();
@@ -20,6 +20,7 @@ import {
20
20
  isTabsScreenContentFocused,
21
21
  isCurrentFocusOnContent,
22
22
  isCurrentFocusOnMenu,
23
+ isCurrentFocusOn,
23
24
  } from "../focusManagerAux/utils";
24
25
 
25
26
  const logger = coreLogger.addSubsystem("focusManager");
@@ -299,25 +300,25 @@ export const focusManager = (function () {
299
300
  return isCurrentFocusOnMenu(currentFocusNode);
300
301
  }
301
302
 
302
- function landFocusTo(id) {
303
- if (id) {
304
- // set focus on selected menu item
305
- const direction = undefined;
303
+ // Move focus to appropriate top navigation tab with context
304
+ function focusTopNavigation(isTabsScreen: boolean, item: ZappEntry) {
305
+ const landFocusTo = (id) => {
306
+ if (id) {
307
+ // set focus on selected menu item
308
+ const direction = undefined;
306
309
 
307
- const context: FocusManager.FocusContext = {
308
- source: "back",
309
- preserveScroll: true,
310
- };
310
+ const context: FocusManager.FocusContext = {
311
+ source: "back",
312
+ preserveScroll: true,
313
+ };
311
314
 
312
- logger.log({ message: "landFocusTo", data: { id } });
315
+ logger.log({ message: "landFocusTo", data: { id } });
313
316
 
314
- blur(direction);
315
- setFocus(id, direction, context);
316
- }
317
- }
317
+ blur(direction);
318
+ setFocus(id, direction, context);
319
+ }
320
+ };
318
321
 
319
- // Move focus to appropriate top navigation tab with context
320
- function focusTopNavigation(isTabsScreen: boolean, item: ZappEntry) {
321
322
  if (isTabsScreen && isTabsScreenContentFocused(currentFocusNode)) {
322
323
  const selectedTabId = findSelectedTabId(item);
323
324
 
@@ -604,6 +605,14 @@ export const focusManager = (function () {
604
605
  return preferredFocus[0];
605
606
  }
606
607
 
608
+ function isFocusOn(id): boolean {
609
+ return (
610
+ id &&
611
+ isCurrentFocusOnTheTopScreen() &&
612
+ isCurrentFocusOn(id, currentFocusNode)
613
+ );
614
+ }
615
+
607
616
  /**
608
617
  * this is the list of the functions available externally
609
618
  * when importing the focus manager
@@ -634,9 +643,9 @@ export const focusManager = (function () {
634
643
  recoverFocus,
635
644
  isCurrentFocusOnTheTopScreen,
636
645
  findPreferredFocusChild,
637
-
638
646
  focusTopNavigation,
639
647
  isFocusOnContent,
640
648
  isFocusOnMenu,
649
+ isFocusOn,
641
650
  };
642
651
  })();
@@ -12,7 +12,7 @@ import {
12
12
  } from "@applicaster/quick-brick-core/const";
13
13
 
14
14
  import {
15
- generateFocusableId,
15
+ getFocusableId,
16
16
  SCREEN_PICKER_CONTAINER,
17
17
  } from "@applicaster/zapp-react-native-utils/screenPickerUtils";
18
18
 
@@ -118,7 +118,7 @@ export const waitForContent = (focusableTree) => {
118
118
  };
119
119
 
120
120
  export const findSelectedTabId = (item: ZappEntry): string => {
121
- const selectedTabId = generateFocusableId(item.id);
121
+ const selectedTabId = getFocusableId(item.id);
122
122
 
123
123
  return selectedTabId;
124
124
  };
@@ -186,3 +186,19 @@ export const isCurrentFocusOnContent = (node) => {
186
186
 
187
187
  return isCurrentFocusOnContent(node.parent);
188
188
  };
189
+
190
+ export const isCurrentFocusOn = (id, node) => {
191
+ if (!node) {
192
+ return false;
193
+ }
194
+
195
+ if (isRoot(node)) {
196
+ return false;
197
+ }
198
+
199
+ if (node?.id === id) {
200
+ return true;
201
+ }
202
+
203
+ return isCurrentFocusOn(id, node.parent);
204
+ };
@@ -1,6 +1,5 @@
1
1
  import { getAllSpecificStyles } from "../manifestKeyParser";
2
2
 
3
- // Mock the dependencies
4
3
  jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
5
4
  platformSelect: jest.fn((platforms) => platforms.samsung_tv), // Default to samsung for tests
6
5
  }));
@@ -42,7 +42,7 @@ export function getNavigationType(
42
42
  R.unless(R.isNil, R.prop("navigation_type")),
43
43
  R.defaultTo(undefined),
44
44
  R.find(R.propEq("category", category))
45
- )(navigations);
45
+ )(navigations || []);
46
46
  }
47
47
 
48
48
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "14.0.0-alpha.5219335081",
3
+ "version": "14.0.0-alpha.5234792518",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "14.0.0-alpha.5219335081",
30
+ "@applicaster/applicaster-types": "14.0.0-alpha.5234792518",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -26,6 +26,9 @@ jest.mock("@applicaster/zapp-react-native-utils/analyticsUtils/", () => ({
26
26
  }));
27
27
 
28
28
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen", () => ({
29
+ ...jest.requireActual(
30
+ "@applicaster/zapp-react-native-utils/reactHooks/screen"
31
+ ),
29
32
  useTargetScreenData: jest.fn(() => ({})),
30
33
  useCurrentScreenData: jest.fn(() => ({})),
31
34
  }));
@@ -16,7 +16,8 @@ import { ActionExecutorContext } from "@applicaster/zapp-react-native-utils/acti
16
16
  import { isFunction, noop } from "../../functionUtils";
17
17
  import { useSendAnalyticsOnPress } from "../analytics";
18
18
  import { logOnPress, warnEmptyContentType } from "./helpers";
19
- import { useCurrentScreenData } from "../screen";
19
+ import { useCurrentScreenData, useScreenContext } from "../screen";
20
+ import { useScreenStateStore } from "../navigation/useScreenStateStore";
20
21
 
21
22
  /**
22
23
  * If onCellTap is defined execute the function and
@@ -42,10 +43,12 @@ export const useCellClick = ({
42
43
  }: Props): onPressReturnFn => {
43
44
  const { push, currentRoute } = useNavigation();
44
45
  const { pathname } = useRoute();
46
+ const screenStateStore = useScreenStateStore();
45
47
 
46
48
  const onCellTap: Option<Function> = React.useContext(CellTapContext);
47
49
  const actionExecutor = React.useContext(ActionExecutorContext);
48
50
  const screenData = useCurrentScreenData();
51
+ const screenState = useScreenContext()?.options;
49
52
 
50
53
  const cellSelectable = toBooleanWithDefaultTrue(
51
54
  component?.rules?.component_cells_selectable
@@ -83,6 +86,9 @@ export const useCellClick = ({
83
86
  await actionExecutor?.handleEntryActions(selectedItem, {
84
87
  component,
85
88
  screenData,
89
+ screenState,
90
+ screenRoute: pathname,
91
+ screenStateStore,
86
92
  });
87
93
  }
88
94
 
@@ -117,6 +123,7 @@ export const useCellClick = ({
117
123
  push,
118
124
  sendAnalyticsOnPress,
119
125
  screenData,
126
+ screenState,
120
127
  ]
121
128
  );
122
129
 
@@ -12,7 +12,6 @@ describe("Debug utils", () => {
12
12
  // Clear the timers object
13
13
  Object.keys(timers).forEach((key) => delete timers[key]);
14
14
 
15
- // Mock performance.now()
16
15
  // eslint-disable-next-line no-undef
17
16
  performanceNowMock = jest.spyOn(performance, "now");
18
17
  performanceNowMock.mockReturnValue(0); // Initial value
@@ -2,12 +2,16 @@ import { renderHook } from "@testing-library/react-hooks";
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";
5
6
 
6
7
  jest.mock("../../navigation");
7
8
 
8
9
  jest.mock(
9
10
  "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext",
10
11
  () => ({
12
+ ...jest.requireActual(
13
+ "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext"
14
+ ),
11
15
  useScreenContext: jest.fn().mockReturnValue({ screen: {}, entry: {} }),
12
16
  })
13
17
  );
@@ -33,7 +37,7 @@ describe("useBatchLoading", () => {
33
37
  jest.clearAllMocks();
34
38
  });
35
39
 
36
- it("loadPipesData start loading not started requests", () => {
40
+ it("loadPipesData start loading not started requests", async () => {
37
41
  const store = {
38
42
  zappPipes: {
39
43
  url1: {
@@ -65,7 +69,9 @@ describe("useBatchLoading", () => {
65
69
 
66
70
  const actions = (appStore.getStore() as any).getActions();
67
71
 
68
- expect(actions).toHaveLength(2);
72
+ await waitFor(() => {
73
+ expect(actions).toHaveLength(2);
74
+ });
69
75
 
70
76
  expect(actions[0]).toMatchObject({
71
77
  type: "ZAPP_PIPES_REQUEST_START",
@@ -2,15 +2,12 @@ import { renderHook } from "@testing-library/react-hooks";
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";
5
- import { Provider } from "react-redux";
6
5
  import * as React from "react";
7
- import configureStore from "redux-mock-store";
8
- import thunk from "redux-thunk";
9
6
  import * as useRouteHook from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
10
7
  import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
11
8
  import { useFeedLoader } from "../useFeedLoader";
12
-
13
- const mockStore = configureStore([thunk]);
9
+ import { WrappedWithProviders } from "../../../testUtils";
10
+ import { ScreenStateResolver } from "../../../appUtils/contextKeysManager/contextResolver";
14
11
 
15
12
  jest.useFakeTimers({ legacyFakeTimers: true });
16
13
 
@@ -55,13 +52,15 @@ const mockZappPipesData = {
55
52
 
56
53
  describe("useFeedLoader", () => {
57
54
  describe("with cached feed url", () => {
58
- const store = mockStore({
55
+ const store = {
59
56
  plugins: [],
60
57
  zappPipes: { "test://testfakeurl": mockZappPipesData },
61
- });
58
+ };
62
59
 
63
- const wrapper: React.FC<any> = ({ children }) => (
64
- <Provider store={store}>{children}</Provider>
60
+ const wrapper: React.FC<any> = ({ children, ...props }) => (
61
+ <WrappedWithProviders store={props.store || store}>
62
+ {children}
63
+ </WrappedWithProviders>
65
64
  );
66
65
 
67
66
  it("returns cached feed", () => {
@@ -110,8 +109,10 @@ describe("useFeedLoader", () => {
110
109
  describe("without cached feeds", () => {
111
110
  const feedUrl = "test://testfakeurl2";
112
111
 
113
- const wrapper: React.FC<any> = ({ children, store }) => (
114
- <Provider store={store}>{children}</Provider>
112
+ const wrapper: React.FC<any> = ({ children, ...props }) => (
113
+ <WrappedWithProviders store={props.store}>
114
+ {children}
115
+ </WrappedWithProviders>
115
116
  );
116
117
 
117
118
  it("It loads data for new url and returns it", () => {
@@ -123,10 +124,10 @@ describe("useFeedLoader", () => {
123
124
  .spyOn(zappPipesModule, "loadPipesData")
124
125
  .mockImplementation(jest.fn());
125
126
 
126
- const initialStore = mockStore({
127
+ const initialStore = {
127
128
  plugins: [],
128
129
  zappPipes: { "test://testfakeurl": "foobar" },
129
- });
130
+ };
130
131
 
131
132
  const { result, rerender } = renderHook(
132
133
  () => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
@@ -135,15 +136,19 @@ describe("useFeedLoader", () => {
135
136
 
136
137
  expect(result.current.data).toBeNull();
137
138
 
138
- expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
139
+ expect(loadPipesDataSpy).toHaveBeenCalledWith(feedUrl, {
139
140
  clearCache: true,
140
141
  riverId: undefined,
142
+ callback: expect.any(Function),
143
+ resolvers: {
144
+ screen: expect.any(ScreenStateResolver),
145
+ },
141
146
  });
142
147
 
143
- const store2 = mockStore({
148
+ const store2 = {
144
149
  plugins: [],
145
150
  zappPipes: { "test://testfakeurl2": mockZappPipesData },
146
- });
151
+ };
147
152
 
148
153
  rerender({ store: store2 });
149
154
 
@@ -164,10 +169,10 @@ describe("useFeedLoader", () => {
164
169
  .spyOn(reactReduxModules, "useDispatch")
165
170
  .mockImplementation(() => jest.fn());
166
171
 
167
- const initialStore = mockStore({
172
+ const initialStore = {
168
173
  plugins: [],
169
174
  zappPipes: { "test://testfakeurl": "foobar" },
170
- });
175
+ };
171
176
 
172
177
  const { result, rerender } = renderHook(
173
178
  () => useFeedLoader({ feedUrl: "test://testfakeurl2" }),
@@ -176,15 +181,22 @@ describe("useFeedLoader", () => {
176
181
 
177
182
  expect(result.current.data).toBeNull();
178
183
 
179
- expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
184
+ expect(loadPipesDataSpy.mock.calls[0][0]).toBe(feedUrl);
185
+
186
+ expect(loadPipesDataSpy.mock.calls[0][1]).toMatchObject({
180
187
  clearCache: true,
181
188
  riverId: undefined,
189
+ resolvers: {
190
+ screen: {
191
+ screenStateStore: expect.any(Function),
192
+ },
193
+ },
182
194
  });
183
195
 
184
- const store2 = mockStore({
196
+ const store2 = {
185
197
  plugins: [],
186
198
  zappPipes: { "test://testfakeurl2": mockZappPipesData },
187
- });
199
+ };
188
200
 
189
201
  rerender({ store: store2 });
190
202
 
@@ -197,8 +209,10 @@ describe("useFeedLoader", () => {
197
209
  const feedUrl = "test://testfakeurl";
198
210
  const feedUrlWithNext = "test://withnexttestfakeurl";
199
211
 
200
- const wrapper: React.FC<any> = ({ children, store }) => (
201
- <Provider store={store}>{children}</Provider>
212
+ const wrapper: React.FC<any> = ({ children, ...props }) => (
213
+ <WrappedWithProviders store={props.store || {}}>
214
+ {children}
215
+ </WrappedWithProviders>
202
216
  );
203
217
 
204
218
  describe("reloadData", () => {
@@ -211,10 +225,10 @@ describe("useFeedLoader", () => {
211
225
  .spyOn(reactReduxModules, "useDispatch")
212
226
  .mockImplementation(() => jest.fn());
213
227
 
214
- const initialStore = mockStore({
228
+ const initialStore = {
215
229
  plugins: [],
216
230
  zappPipes: { [feedUrl]: "foobar" },
217
- });
231
+ };
218
232
 
219
233
  const { result } = renderHook(() => useFeedLoader({ feedUrl }), {
220
234
  wrapper,
@@ -223,11 +237,24 @@ describe("useFeedLoader", () => {
223
237
 
224
238
  const { reloadData } = result.current;
225
239
 
226
- reloadData();
240
+ reloadData?.();
241
+
242
+ expect(loadPipesDataSpy).toHaveBeenCalled();
243
+
244
+ expect(
245
+ loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0]
246
+ ).toBe(feedUrl);
227
247
 
228
- expect(loadPipesDataSpy).toBeCalledWith(feedUrl, {
248
+ expect(
249
+ loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1]
250
+ ).toMatchObject({
229
251
  clearCache: true,
230
252
  silentRefresh: true,
253
+ resolvers: {
254
+ screen: {
255
+ screenStateStore: expect.any(Function),
256
+ },
257
+ },
231
258
  });
232
259
 
233
260
  loadPipesDataSpy.mockRestore();
@@ -247,10 +274,10 @@ describe("useFeedLoader", () => {
247
274
  .spyOn(reactReduxModules, "useDispatch")
248
275
  .mockImplementation(() => jest.fn());
249
276
 
250
- const initialStore = mockStore({
277
+ const initialStore = {
251
278
  plugins: [],
252
279
  zappPipes: { [feedUrlWithNext]: { data: { next: nextUrl } } },
253
- });
280
+ };
254
281
 
255
282
  const { result } = renderHook(
256
283
  () => useFeedLoader({ feedUrl: feedUrlWithNext }),
@@ -262,11 +289,24 @@ describe("useFeedLoader", () => {
262
289
 
263
290
  const { loadNext } = result.current;
264
291
 
265
- loadNext();
292
+ loadNext?.();
293
+
294
+ expect(loadPipesDataSpy).toHaveBeenCalled();
295
+
296
+ expect(
297
+ loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0]
298
+ ).toBe(nextUrl);
266
299
 
267
- expect(loadPipesDataSpy).toBeCalledWith(nextUrl, {
300
+ expect(
301
+ loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1]
302
+ ).toMatchObject({
268
303
  parentFeed: feedUrlWithNext,
269
304
  silentRefresh: true,
305
+ resolvers: {
306
+ screen: {
307
+ screenStateStore: expect.any(Function),
308
+ },
309
+ },
270
310
  });
271
311
 
272
312
  loadPipesDataSpy.mockRestore();
@@ -11,3 +11,5 @@ export { useBuildPipesUrl } from "./useBuildPipesUrl";
11
11
  export { usePipesCacheReset } from "./usePipesCacheReset";
12
12
 
13
13
  export { useBatchLoading } from "./useBatchLoading";
14
+
15
+ export { useLoadPipesDataDispatch } from "./useLoadPipesDataDispatch";