@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
299 lines (237 loc) • 8.08 kB
JavaScript
import * as R from "ramda";
import { Hook } from "../Hook";
import { HOOKS_EVENTS, HOOKS_TRANSITION_TYPE } from "../constants";
const hook = R.merge({
screen_id: "A1234",
identifier: "hook_screen",
weight: 1,
id: "A1234",
name: "Hook Screen",
type: "hook_screen",
styles: {},
advertising: {},
general: {
is_flow_blocker: true,
allow_screen_plugin_presentation: true,
},
navigations: [],
hooks: [],
});
const hooks = (options, otherHook = null) =>
R.reject(R.isNil, [
R.pick(["screen_id", "identifier", "type", "weight"], hook(options)),
otherHook,
]);
const manager = {
presentScreenHook: jest.fn(),
executeHook: jest.fn(),
invokeHandler: jest.fn(),
};
const getHook = (options, otherHook) =>
new Hook({
hook: hook(options),
hooks: hooks(options, otherHook),
manager,
});
function clearMocks() {
manager.presentScreenHook.mockClear();
manager.executeHook.mockClear();
manager.invokeHandler.mockClear();
}
describe("Hook Properties", () => {
const hookPlugin = getHook({});
beforeEach(clearMocks);
it("has all the hook properties", () => {
expect(hookPlugin).toMatchSnapshot();
});
});
describe("getModalContainer", () => {
it("returns ModalContainer", () => {
const ModalContainer = jest.fn();
const hookPlugin = getHook({
general: {},
module: { ModalContainer },
});
expect(hookPlugin.getModalContainer()).toBe(ModalContainer);
});
});
describe("isFlowBlocker", () => {
it("returns true if the hook is flow blocker", () => {
const hookPlugin = getHook({ general: { is_flow_blocker: true } });
const hookWithoutScreen = getHook({
general: {},
module: { isFlowBlocker: () => true },
});
expect(hookPlugin.isFlowBlocker()).toBe(true);
expect(hookWithoutScreen.isFlowBlocker()).toBe(true);
});
it("returns false otherwise", () => {
const hookPlugin = getHook({ general: { is_flow_blocker: false } });
const hookWithoutScreen = getHook({
general: {},
module: { isFlowBlocker: () => false },
});
expect(hookPlugin.isFlowBlocker()).toBeFalsy();
expect(hookWithoutScreen.isFlowBlocker()).toBeFalsy();
});
});
describe("skipHook", () => {
it("returns true if the hooks module skipHook method returns true", () => {
const hookPlugin = getHook({ module: { skipHook: () => true } });
expect(hookPlugin.skipHook()).toBe(true);
});
it("returns false otherwise", () => {
const hookPlugin = getHook({ module: { skipHook: false } });
const hookPluginWithUndefinedSkipHook = getHook({ module: {} });
expect(hookPlugin.skipHook()).toBeFalsy();
expect(hookPluginWithUndefinedSkipHook.skipHook()).toBeFalsy();
});
});
describe("lastHook", () => {
it("returns true if the current hook is the last one", () => {
const hookPlugin = getHook({});
expect(hookPlugin.lastHook).toBe(true);
});
it("returns false otherwise", () => {
const hookPlugin = getHook(
{},
{
screen_id: "B4567",
type: "general",
identifier: "some_hook",
weight: 2,
}
);
expect(hookPlugin.lastHook).toBe(false);
});
});
describe("setStateAndNotify", () => {
let hookPlugin;
const args = ["foo", { bar: 42 }];
beforeEach(() => {
clearMocks();
hookPlugin = getHook({});
});
it("changes the hook's state", () => {
hookPlugin.setStateAndNotify(HOOKS_EVENTS.START, ...args);
expect(hookPlugin.state).toEqual(1);
});
it("calls invokes the handler on the hook manager", () => {
hookPlugin.setStateAndNotify(HOOKS_EVENTS.START, ...args);
expect(manager.invokeHandler).toHaveBeenCalledWith(
HOOKS_EVENTS.START,
...args
);
});
});
describe("shouldPresentScreenHook", () => {
it("returns true if the hook has a screenId and allows screen presentation", () => {
const hookPlugin = getHook({});
const hookWithoutRiver = getHook({
screen_id: undefined,
module: { presentFullScreen: true },
});
expect(hookPlugin.shouldPresentScreenHook()).toBe(true);
expect(hookWithoutRiver.shouldPresentScreenHook()).toBe(true);
});
it("returns true if the hook is modal presentation", () => {
const hookPlugin = getHook({
general: { transition_type: HOOKS_TRANSITION_TYPE.MODAL },
});
expect(hookPlugin.shouldPresentScreenHook()).toBe(true);
});
it("returns false otherwise", () => {
const hookWithoutScreen = getHook({ screen_id: undefined });
const hookWithoutRiver = getHook({
screen_id: undefined,
module: { presentFullScreen: false },
});
const hookWithoutAllowScreen = getHook({
general: { allow_screen_plugin_presentation: false },
});
const hookWithoutScreenAndAllow = getHook({
general: { allow_screen_plugin_presentation: false },
screen_id: null,
});
expect(hookWithoutScreen.shouldPresentScreenHook()).toBeFalsy();
expect(hookWithoutAllowScreen.shouldPresentScreenHook()).toBeFalsy();
expect(hookWithoutScreenAndAllow.shouldPresentScreenHook()).toBeFalsy();
expect(hookWithoutRiver.shouldPresentScreenHook()).toBeFalsy();
});
});
describe("shouldStartNexHook", () => {
const hookPlugin = getHook({});
const hookWithoutScreen = getHook({ screen_id: undefined });
it("returns true if the nextHook has the same weight and is not a screen", () => {
expect(
hookWithoutScreen.shouldStartNextHook({
weight: hookWithoutScreen.weight,
})
).toBe(true);
});
it("returns false otherwise", () => {
// even if weight if the same, since hookPlugin should present a screen,
// it cannot run in parallel
expect(hookPlugin.shouldStartNextHook({ weight: hookPlugin })).toBeFalsy();
expect(
hookWithoutScreen.shouldStartNextHook({
weight: hookWithoutScreen.weight + 1,
})
).toBeFalsy();
});
});
describe("execute", () => {
const payload = { foo: "bar" };
const next = jest.fn();
beforeEach(clearMocks);
describe("when the hook execution should be skipped", () => {
it("doesn't execute the hook, and goes to the next", () => {
const hookPlugin = getHook({ module: { skipHook: () => true } });
hookPlugin.execute(payload, next);
expect(manager.presentScreenHook).not.toHaveBeenCalled();
expect(manager.executeHook).not.toHaveBeenCalled();
expect(next).toHaveBeenCalledWith({ payload, success: true });
});
});
describe("when the hook is not in pending state", () => {
it("doesn't execute the hook, and goes to the next", () => {
const hookPlugin = getHook({});
hookPlugin.setStateAndNotify(HOOKS_EVENTS.START);
hookPlugin.execute(payload, next);
const hookStateSpy = jest.spyOn(hookPlugin, "setStateAndNotify");
expect(hookStateSpy).not.toHaveBeenCalled();
expect(manager.presentScreenHook).not.toHaveBeenCalled();
expect(manager.executeHook).not.toHaveBeenCalled();
expect(next).toHaveBeenCalledWith({ payload });
hookStateSpy.mockRestore();
});
});
describe("when the screen should be presented", () => {
it("executes the hook", () => {
const hookPlugin = getHook({});
const hookStateSpy = jest.spyOn(hookPlugin, "setStateAndNotify");
hookPlugin.execute(payload, next);
expect(hookStateSpy).toHaveBeenCalledWith(HOOKS_EVENTS.START, hookPlugin);
expect(manager.presentScreenHook).toHaveBeenCalledWith(
hookPlugin,
payload,
next
);
});
});
describe("when the hook is headless", () => {
it("executes the hook", () => {
const hookPlugin = getHook({
general: { allow_screen_plugin_presentation: false },
});
const hookStateSpy = jest.spyOn(hookPlugin, "setStateAndNotify");
hookPlugin.execute(payload, next);
expect(hookStateSpy).toHaveBeenCalledWith(HOOKS_EVENTS.START, hookPlugin);
expect(manager.executeHook).toHaveBeenCalledWith(
hookPlugin,
payload,
next
);
});
});
});