import { renderHook } from "@testing-library/react-hooks"; import * as R from "ramda"; import * as zappPipesModule from "@applicaster/zapp-react-native-redux/ZappPipes"; import * as reactReduxModules from "react-redux"; import * as React from "react"; import * as useRouteHook from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute"; import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation"; import { useFeedLoader } from "../useFeedLoader"; import { WrappedWithProviders } from "../../../testUtils"; import { ScreenStateResolver } from "../../../appUtils/contextKeysManager/contextResolver"; jest.useFakeTimers({ legacyFakeTimers: true }); const mockLogger = { warning: jest.fn(), log: jest.fn(), info: jest.fn(), }; const mockNavigator: any = { currentRoute: "/river/A1234", getStack: jest.fn(() => []), }; const mockRouteData: any = { screenData: {}, pathname: "/river/A1234", }; jest.doMock("../../logger", () => ({ reactHooksLogger: { addSubsystem: jest.fn(() => mockLogger), }, })); jest .spyOn(useNavigationHooks, "useNavigation") .mockImplementation(() => mockNavigator); jest.spyOn(useRouteHook, "useRoute").mockImplementation(() => mockRouteData); const mockZappPipesData = { data: { id: 1, entry: [{ title: "test", type: { value: "feed" } }], type: { value: "feed" }, title: "Winning Programs", }, loading: "false", url: "test://testfakeurl", }; describe("useFeedLoader", () => { describe("with cached feed url", () => { const store = { plugins: [], zappPipes: { "test://testfakeurl": mockZappPipesData }, }; const wrapper: React.FC = ({ children, ...props }) => ( {children} ); it("returns cached feed", () => { const { result } = renderHook( () => useFeedLoader({ feedUrl: "test://testfakeurl" }), { wrapper, } ); expect(R.omit(["reloadData", "loadNext"], result.current)).toEqual( mockZappPipesData ); }); it("Tries to load newFeed it feedUrl changes", () => { const useDispatchSpy = jest .spyOn(reactReduxModules, "useDispatch") .mockImplementation(() => jest.fn()); const loadPipesDataSpy = jest .spyOn(zappPipesModule, "loadPipesData") .mockImplementation(jest.fn()); const { rerender } = renderHook( () => useFeedLoader({ feedUrl: "test://testfakeurl" }), { wrapper, } ); expect(loadPipesDataSpy).not.toBeCalled(); rerender({ feedUrl: "test" }); expect(loadPipesDataSpy).not.toBeCalledWith({ feedUrl: "test", pipesOptions: {}, }); loadPipesDataSpy.mockRestore(); useDispatchSpy.mockRestore(); }); }); describe("without cached feeds", () => { const feedUrl = "test://testfakeurl2"; const wrapper: React.FC = ({ children, ...props }) => ( {children} ); it("It loads data for new url and returns it", () => { const useDispatchSpy = jest .spyOn(reactReduxModules, "useDispatch") .mockImplementation(() => jest.fn()); const loadPipesDataSpy = jest .spyOn(zappPipesModule, "loadPipesData") .mockImplementation(jest.fn()); const initialStore = { plugins: [], zappPipes: { "test://testfakeurl": "foobar" }, }; const { result, rerender } = renderHook( () => useFeedLoader({ feedUrl: "test://testfakeurl2" }), { wrapper, initialProps: { store: initialStore } } ); expect(result.current.data).toBeNull(); expect(loadPipesDataSpy).toHaveBeenCalledWith(feedUrl, { clearCache: true, riverId: undefined, callback: expect.any(Function), resolvers: { screen: expect.any(ScreenStateResolver), }, }); const store2 = { plugins: [], zappPipes: { "test://testfakeurl2": mockZappPipesData }, }; rerender({ store: store2 }); expect(R.omit(["reloadData", "loadNext"], result.current)).toEqual( mockZappPipesData ); loadPipesDataSpy.mockRestore(); useDispatchSpy.mockRestore(); }); it("It loads data for new url and calls onFeedLoaded if provided", () => { const loadPipesDataSpy = jest .spyOn(zappPipesModule, "loadPipesData") .mockImplementation(jest.fn()); const useDispatchSpy = jest .spyOn(reactReduxModules, "useDispatch") .mockImplementation(() => jest.fn()); const initialStore = { plugins: [], zappPipes: { "test://testfakeurl": "foobar" }, }; const { result, rerender } = renderHook( () => useFeedLoader({ feedUrl: "test://testfakeurl2" }), { wrapper, initialProps: { store: initialStore } } ); expect(result.current.data).toBeNull(); expect(loadPipesDataSpy.mock.calls[0][0]).toBe(feedUrl); expect(loadPipesDataSpy.mock.calls[0][1]).toMatchObject({ clearCache: true, riverId: undefined, resolvers: { screen: { screenStateStore: expect.any(Function), }, }, }); const store2 = { plugins: [], zappPipes: { "test://testfakeurl2": mockZappPipesData }, }; rerender({ store: store2 }); loadPipesDataSpy.mockRestore(); useDispatchSpy.mockRestore(); }); }); describe("on callbacks called", () => { const feedUrl = "test://testfakeurl"; const feedUrlWithNext = "test://withnexttestfakeurl"; const wrapper: React.FC = ({ children, ...props }) => ( {children} ); describe("reloadData", () => { it("Tries to reload data in the redux store", () => { const loadPipesDataSpy = jest .spyOn(zappPipesModule, "loadPipesData") .mockImplementation(jest.fn()); const useDispatchSpy = jest .spyOn(reactReduxModules, "useDispatch") .mockImplementation(() => jest.fn()); const initialStore = { plugins: [], zappPipes: { [feedUrl]: "foobar" }, }; const { result } = renderHook(() => useFeedLoader({ feedUrl }), { wrapper, initialProps: { store: initialStore }, }); const { reloadData } = result.current; reloadData?.(); expect(loadPipesDataSpy).toHaveBeenCalled(); expect( loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0] ).toBe(feedUrl); expect( loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1] ).toMatchObject({ clearCache: true, silentRefresh: true, resolvers: { screen: { screenStateStore: expect.any(Function), }, }, }); loadPipesDataSpy.mockRestore(); useDispatchSpy.mockRestore(); }); }); describe("loadNext", () => { it("Tries to load next data for the feed", () => { const nextUrl = "test://nextUrl"; const loadPipesDataSpy = jest .spyOn(zappPipesModule, "loadPipesData") .mockImplementation(jest.fn()); const useDispatchSpy = jest .spyOn(reactReduxModules, "useDispatch") .mockImplementation(() => jest.fn()); const initialStore = { plugins: [], zappPipes: { [feedUrlWithNext]: { data: { next: nextUrl } } }, }; const { result } = renderHook( () => useFeedLoader({ feedUrl: feedUrlWithNext }), { wrapper, initialProps: { store: initialStore }, } ); const { loadNext } = result.current; loadNext?.(); expect(loadPipesDataSpy).toHaveBeenCalled(); expect( loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][0] ).toBe(nextUrl); expect( loadPipesDataSpy.mock.calls[loadPipesDataSpy.mock.calls.length - 1][1] ).toMatchObject({ parentFeed: feedUrlWithNext, silentRefresh: true, resolvers: { screen: { screenStateStore: expect.any(Function), }, }, }); loadPipesDataSpy.mockRestore(); useDispatchSpy.mockRestore(); }); }); }); });