UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

574 lines (482 loc) • 15.9 kB
import * as R from "ramda"; import { HooksManager } from "../index"; import { HOOKS_EVENTS } from "../constants"; import riversWithHooks from "./fixtures/riverWithHooks.json"; const simpleRivers = { A1234: { id: "A1234", hooks: { preload_plugins: [ { identifier: "hook_plugin", type: "general", weight: 1, }, ], }, }, }; const payload = { foo: "bar" }; const getHookPlugin = (identifier, mock_response = {}) => ({ identifier, type: "general", module: { run: jest.fn((payload, callback) => { callback( R.merge( { success: true, payload: { ...payload, identifier } }, mock_response ) ); }), isFlowBlocker: () => true, }, configuration: { name: identifier }, }); describe("HooksManager", () => { describe("when there are no hooks", () => { const riverWithNoHook = [ { id: "A1234", hooks: { preload_plugins: [], }, }, ]; const plugins = []; const targetScreen = { id: "A1234" }; const hooksManager = HooksManager({ rivers: riverWithNoHook, targetScreen, plugins, }); const successHandler = jest.fn(); it("invokes the success handler directly", () => { hooksManager .on(HOOKS_EVENTS.COMPLETE, successHandler) .handleHooks(payload); expect(successHandler).toHaveBeenCalledTimes(1); expect(successHandler).toHaveBeenCalledWith( { payload, hookPlugin: { lastHook: true } }, expect.any(Function) ); }); }); describe("when target route is playable", () => { const rivers = simpleRivers; const targetScreen = { screenType: "playable", id: "1234" }; const headlessPlayerHook = { identifier: "headless_player_hook", module: { hasPlayerHook: true, run: jest.fn((payload, callback) => { callback({ success: true, payload }); }), }, configuration: { config_field: "bar" }, }; const plugins = [headlessPlayerHook]; const hooksManager = HooksManager({ rivers, targetScreen, plugins, }); const successHandler = jest.fn(); const completeHandler = jest.fn(); const presentScreenHandler = jest.fn(); it("executes the player hook", () => { hooksManager .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, presentScreenHandler) .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .handleHooks(payload); expect(headlessPlayerHook.module.run).toHaveBeenCalledWith( payload, expect.any(Function), headlessPlayerHook.configuration ); expect(successHandler).toHaveBeenCalledWith( { hookPlugin: expect.objectContaining(headlessPlayerHook), payload }, expect.any(Function) ); expect(completeHandler).toHaveBeenCalledWith( { hookPlugin: expect.objectContaining(headlessPlayerHook), payload }, expect.any(Function) ); }); }); describe("when there are preload hooks", () => { describe("hook with screen", () => { const hookScreenId = "6e15fcbf-9414-4ea8-b7db-a287593ab6bd"; const pluginScreenId = "1bb8ad39-a42e-4096-a134-56aadbd35c83"; const rivers = R.zipObj( R.map(R.prop("id"), riversWithHooks), riversWithHooks ); const targetScreen = rivers[hookScreenId]; const hookScreen = getHookPlugin("hook_screen"); const plugins = [hookScreen]; const hooksManager = HooksManager({ rivers, targetScreen, plugins, }); const successHandler = jest.fn(); const completeHandler = jest.fn(); const presentScreenHandler = jest.fn( ({ payload: { payload, callback } }) => { callback({ success: true, payload }); } ); it("executes the hook", () => { hooksManager .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, presentScreenHandler) .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .handleHooks(payload); expect(presentScreenHandler).toHaveBeenCalledWith( { route: `/hooks/${pluginScreenId}`, hookPlugin: expect.objectContaining(hookScreen), payload: { payload, hookPlugin: expect.objectContaining(hookScreen), callback: expect.any(Function), }, }, expect.any(Function) ); expect(successHandler).toHaveBeenCalledWith( { payload, hookPlugin: expect.objectContaining(hookScreen) }, expect.any(Function) ); expect(completeHandler).toHaveBeenCalledWith( { payload, hookPlugin: expect.objectContaining(hookScreen) }, expect.any(Function) ); }); }); }); describe("when there are multiple hooks", () => { const hookScreenId = "6e15fcbf-multiple-hooks"; const pluginScreenId = "1bb8ad39-a42e-4096-a134-56aadbd35c83"; const pluginScreenId2 = "1bb8ad39-hook_screen_2"; const rivers = R.zipObj( R.map(R.prop("id"), riversWithHooks), riversWithHooks ); const targetScreen = rivers[hookScreenId]; const hookScreen = getHookPlugin("hook_screen"); const hookScreen2 = getHookPlugin("hook_screen_2"); const plugins = [hookScreen, hookScreen2]; const hooksManager = HooksManager({ rivers, targetScreen, plugins, }); const successHandler = jest.fn(); const completeHandler = jest.fn(); const presentScreenHandler = jest.fn(({ route, payload }) => { const modified_by = R.path(["payload", "modified_by"], payload) || []; const hookPayload = R.merge(payload.payload, { modified_by: R.append(route, modified_by), }); payload.callback({ success: true, payload: hookPayload }); }); it("executes the hook", () => { hooksManager .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, presentScreenHandler) .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .handleHooks(payload); expect(presentScreenHandler).toHaveBeenCalledTimes(2); expect(presentScreenHandler).toHaveBeenNthCalledWith( 1, { route: `/hooks/${pluginScreenId}`, hookPlugin: expect.objectContaining(hookScreen), payload: { callback: expect.any(Function), payload, hookPlugin: expect.objectContaining(hookScreen), }, }, expect.any(Function) ); expect(presentScreenHandler).toHaveBeenNthCalledWith( 2, { route: `/hooks/${pluginScreenId2}`, hookPlugin: expect.objectContaining(hookScreen2), payload: { payload: expect.objectContaining({ ...payload, modified_by: [`/hooks/${pluginScreenId}`], }), hookPlugin: expect.objectContaining(hookScreen2), callback: expect.any(Function), }, }, expect.any(Function) ); expect(successHandler).toHaveBeenCalledTimes(2); expect(successHandler).toHaveBeenNthCalledWith( 1, expect.objectContaining({ hookPlugin: expect.objectContaining(hookScreen), payload: expect.objectContaining({ ...payload, modified_by: [`/hooks/${pluginScreenId}`], }), }), expect.any(Function) ); expect(successHandler).toHaveBeenNthCalledWith( 2, expect.objectContaining({ hookPlugin: expect.objectContaining(hookScreen2), payload: expect.objectContaining({ ...payload, modified_by: [ `/hooks/${pluginScreenId}`, `/hooks/${pluginScreenId2}`, ], }), }), expect.any(Function) ); expect(completeHandler).toHaveBeenCalledTimes(1); expect(completeHandler).toHaveBeenNthCalledWith( 1, expect.objectContaining({ payload: { ...payload, modified_by: [ `/hooks/${pluginScreenId}`, `/hooks/${pluginScreenId2}`, ], }, }), expect.any(Function) ); }); }); describe("hook with an failure and flow_blocker", () => { const hookScreenId = "6e15fcbf-9414-4ea8-b7db-a287593ab6bd"; const rivers = R.zipObj( R.map(R.prop("id"), riversWithHooks), riversWithHooks ); const targetScreen = rivers[hookScreenId]; const hookScreen = getHookPlugin("hook_screen"); const plugins = [hookScreen]; const hooksManager = HooksManager({ rivers, targetScreen, plugins, }); const successHandler = jest.fn(); const completeHandler = jest.fn(); const cancelHandler = jest.fn(); const presentScreenHandler = jest.fn( ({ payload: { payload, callback } }) => { callback({ success: false, payload }); } ); it("stops the flow", () => { hooksManager .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, presentScreenHandler) .on(HOOKS_EVENTS.CANCEL, cancelHandler) .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .handleHooks(payload); expect(cancelHandler).toHaveBeenCalledWith( { hookPlugin: expect.any(Object), payload }, expect.any(Function) ); expect(successHandler).not.toHaveBeenCalled(); expect(completeHandler).not.toHaveBeenCalled(); }); }); describe("headless hook", () => { const rivers = simpleRivers; const targetScreen = rivers.A1234; const hookPlugin = getHookPlugin("hook_plugin"); const plugins = [hookPlugin]; const successHandler = jest.fn(); const completeHandler = jest.fn(); const hooksManager = HooksManager({ rivers, targetScreen, plugins }); const payload = { foo: "bar" }; it("executes the hook", () => { hooksManager .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .handleHooks(payload); expect(successHandler).toHaveBeenCalledWith( { payload: { ...payload, identifier: "hook_plugin" }, hookPlugin: expect.objectContaining(hookPlugin), }, expect.any(Function) ); expect(completeHandler).toHaveBeenCalledWith( { payload: { ...payload, identifier: "hook_plugin" }, hookPlugin: expect.objectContaining(hookPlugin), }, expect.any(Function) ); }); }); describe("headless hook without implementation", () => { const rivers = simpleRivers; const targetScreen = rivers.A1234; const hookPlugin = { module: {}, identifier: "hook_plugin", type: "general", }; const plugins = [hookPlugin]; const successHandler = jest.fn(); const completeHandler = jest.fn(); const errorHandler = jest.fn(); const hooksManager = HooksManager({ rivers, targetScreen, plugins }); it("invokes the error handler but not the success handler", () => { hooksManager .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .on(HOOKS_EVENTS.ERROR, errorHandler) .handleHooks(payload); expect(successHandler).not.toHaveBeenCalled(); expect(completeHandler).not.toHaveBeenCalled(); expect(errorHandler).toHaveBeenCalledWith( { error: new TypeError("hookPlugin.module.run is not a function"), hookPlugin: expect.any(Object), payload, }, expect.any(Function) ); }); }); describe("hook with an error", () => { const rivers = simpleRivers; const targetScreen = rivers.A1234; const hookPlugin = getHookPlugin("hook_plugin", { error: new Error("error"), }); const plugins = [hookPlugin]; const successHandler = jest.fn(); const completeHandler = jest.fn(); const errorHandler = jest.fn(); const cancelHandler = jest.fn(); const hooksManager = HooksManager({ rivers, targetScreen, plugins }); it("invokes the error handler, and stops the flow", () => { hooksManager .on(HOOKS_EVENTS.SUCCESS, successHandler) .on(HOOKS_EVENTS.COMPLETE, completeHandler) .on(HOOKS_EVENTS.ERROR, errorHandler) .on(HOOKS_EVENTS.CANCEL, cancelHandler) .handleHooks(payload); expect(errorHandler).toHaveBeenCalledWith( { error: new Error("error"), hookPlugin: expect.any(Object), payload: { ...payload, identifier: "hook_plugin" }, }, expect.any(Function) ); expect(cancelHandler).not.toHaveBeenCalled(); expect(completeHandler).not.toHaveBeenCalled(); expect(successHandler).not.toHaveBeenCalled(); }); }); describe("multiple hooks", () => { const rivers = { A1234: { id: "A1234", hooks: { preload_plugins: [ { identifier: "hook_plugin", type: "general", weight: 1, }, { identifier: "hook_plugin_2", type: "general", weight: 1, }, { identifier: "hook_plugin_3", type: "general", weight: 2, }, ], }, }, }; const targetScreen = rivers.A1234; const hookPlugin = getHookPlugin("hook_plugin"); const hookPlugin2 = getHookPlugin("hook_plugin_2"); const hookPlugin3 = getHookPlugin("hook_plugin_3"); const plugins = [hookPlugin, hookPlugin2, hookPlugin3]; const successHandler = jest.fn(); const completeHandler = jest.fn(); const hooksManager = HooksManager({ rivers, targetScreen, plugins }); it("executes the hook", () => { hooksManager .on(HOOKS_EVENTS.COMPLETE, completeHandler) .on(HOOKS_EVENTS.SUCCESS, successHandler) .handleHooks(payload); expect(successHandler).toHaveBeenCalledTimes(3); expect(successHandler).toHaveBeenNthCalledWith( 1, { payload: { ...payload, identifier: "hook_plugin" }, hookPlugin: expect.objectContaining(hookPlugin), }, expect.any(Function) ); expect(successHandler).toHaveBeenNthCalledWith( 2, { payload: { ...payload, identifier: "hook_plugin_2" }, hookPlugin: expect.objectContaining(hookPlugin2), }, expect.any(Function) ); expect(successHandler).toHaveBeenNthCalledWith( 3, { payload: { ...payload, identifier: "hook_plugin_3" }, hookPlugin: expect.objectContaining(hookPlugin3), }, expect.any(Function) ); expect(completeHandler).toHaveBeenCalledTimes(1); expect(completeHandler).toHaveBeenCalledWith( { payload: { ...payload, identifier: "hook_plugin_3" }, hookPlugin: expect.objectContaining(hookPlugin3), }, expect.any(Function) ); expect(hookPlugin.module.run).toHaveBeenCalledWith( payload, expect.any(Function), hookPlugin.configuration ); expect(hookPlugin2.module.run).toHaveBeenCalledWith( { ...payload, identifier: "hook_plugin" }, expect.any(Function), hookPlugin2.configuration ); expect(hookPlugin3.module.run).toHaveBeenCalledWith( { ...payload, identifier: "hook_plugin_2" }, expect.any(Function), hookPlugin3.configuration ); }); }); });