qlik-oss/sn-action-button

View on GitHub
src/utils/__tests__/actions.spec.js

Summary

Maintainability
F
2 wks
Test Coverage
import actions, { checkShowAction, getActionsList, getValueList } from "../actions";

describe("actions", () => {
  const qStateName = "someState";
  let app;
  let multiUserAutomation;
  let createdBookmark;
  let createdTemporaryBookmark;
  let fieldObject;
  let fieldInfoObject;
  let variableObject;
  let field;
  let bookmark;
  let variable;
  let automationId;
  let inputs = [];
  let automationPostData = false;
  let automationTriggered = false;
  const value = "someValue";
  const softLock = true;
  const resourceId = "fakeResourceId";
  const id = "fakeId";
  const sheetId = "fakeSheetId";
  const senseNavigation = { getCurrentSheetId: () => sheetId };
  const tenantId = "fakeTenantId";
  const spaceId = "fakeSpaceId";
  const csrfToken = "fakeCsrfToken";
  const automationExecutionToken = "fakeExecutionToken";
  const blocks = [{ displayName: "Inputs", form: [{ label: "blockLabel" }, { label: "blockLabel" }] }];
  const translator = { get: () => "" };

  describe("all actions", () => {
    beforeEach(() => {
      field = "someField";
      bookmark = "someBookmark";
      variable = "someVariable";
      automationId = "someAutomation";
      fieldObject = {
        clear: jest.fn(),
        clearAllButThis: jest.fn(),
        lock: jest.fn(),
        unlock: jest.fn(),
        select: jest.fn(),
        selectAll: jest.fn(),
        selectValues: jest.fn(),
        selectAlternative: jest.fn(),
        selectExcluded: jest.fn(),
        selectPossible: jest.fn(),
        toggleSelect: jest.fn(),
      };
      fieldInfoObject = {
        qTags: [],
      };
      variableObject = {
        setStringValue: jest.fn(),
      };
      createdBookmark = {
        getLayout: jest.fn(() => Promise.resolve({ qInfo: { qId: "bmId" } })),
      };
      app = {
        id: "fakeAppId",
        applyBookmark: jest.fn(),
        clearAll: jest.fn(),
        createBookmark: jest.fn(() => Promise.resolve(createdBookmark)),
        createTemporaryBookmark: jest.fn(() => Promise.resolve(createdTemporaryBookmark)),
        back: jest.fn(),
        forward: jest.fn(),
        getField: jest.fn(() => Promise.resolve(fieldObject)),
        getFieldDescription: jest.fn(() => Promise.resolve(fieldInfoObject)),
        getVariableByName: jest.fn(() => Promise.resolve(variableObject)),
        lockAll: jest.fn(),
        unlockAll: jest.fn(),
        clearAllSoftPatches: jest.fn(),
        getBookmarkList: () => [
          {
            qData: { title: "findMyBookmark" },
            qInfo: { qId: "myBookmarkId" },
            qMeta: { isExtended: false },
          },
          {
            qData: { title: "findMyBookmarkExtended" },
            qInfo: { qId: "myBookmarkExtendedId" },
            qMeta: { isExtended: true },
          },
        ],
        evaluate: () => "43850;43881",
        doReload: jest.fn(() => true),
        doSave: jest.fn(),
        saveObjects: jest.fn(),
      };
      global.fetch = jest.fn(() =>
        Promise.resolve({
          json: () => ({
            resourceId,
            id,
            automationExecutionToken,
            inputs,
            blocks,
            tenantId,
            headers: { get: () => csrfToken },
            attributes: { spaceId },
          }),
          headers: { get: () => csrfToken },
        })
      );
    });

    afterEach(() => {
      inputs = [];
      automationPostData = false;
      jest.resetAllMocks();
    });

    it("should call applyBookmark", async () => {
      const actionObj = actions.find((action) => action.value === "applyBookmark");
      await actionObj.getActionCall({ app, bookmark })();
      expect(app.applyBookmark).toHaveBeenCalledWith(bookmark);
    });

    it("should NOT call applyBookmark when no bookmark", async () => {
      const actionObj = actions.find((action) => action.value === "applyBookmark");
      bookmark = null;
      await actionObj.getActionCall({ app, bookmark })();
      expect(app.applyBookmark).toHaveBeenCalledTimes(0);
    });

    it("should call applyBookmark with bookmark from bookmark list", async () => {
      const actionObj = actions.find((action) => action.value === "applyBookmark");
      await actionObj.getActionCall({ app, bookmark: "findMyBookmark" })();
      expect(app.applyBookmark).toHaveBeenCalledWith("myBookmarkId");
    });

    it("should call clearallsoftpatches if bookmark is extended", async () => {
      const actionObj = actions.find((action) => action.value === "applyBookmark");
      await actionObj.getActionCall({ app, bookmark: "myBookmarkExtendedId" })();
      expect(app.clearAllSoftPatches).toHaveBeenCalledTimes(1);
    });

    it("should not call clearallsoftpatches if bookmark is not extended", async () => {
      const actionObj = actions.find((action) => action.value === "applyBookmark");
      await actionObj.getActionCall({ app, bookmark: "myBookmarkId" })();
      expect(app.clearAllSoftPatches).toHaveBeenCalledTimes(0);
    });

    it("should call back", async () => {
      const actionObject = actions.find((action) => action.value === "back");
      await actionObject.getActionCall({ app })();
      expect(app.back).toHaveBeenCalled();
    });

    it("should call forward", async () => {
      const actionObject = actions.find((action) => action.value === "forward");
      await actionObject.getActionCall({ app })();
      expect(app.forward).toHaveBeenCalled();
    });

    it("should call clearAll", async () => {
      const actionObject = actions.find((action) => action.value === "clearAll");
      await actionObject.getActionCall({ app, softLock })();
      expect(app.clearAll).toHaveBeenCalledWith(softLock);
    });

    it("should call clearAllButThis", async () => {
      const actionObject = actions.find((action) => action.value === "clearAllButThis");
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.clearAllButThis).toHaveBeenCalledWith(false);
    });

    it("should NOT call clearAllButThis when no field", async () => {
      const actionObject = actions.find((action) => action.value === "clearAllButThis");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field, softLock })();
      expect(fieldObject.clearAllButThis).toHaveBeenCalledTimes(0);
    });

    it("should call clearField", async () => {
      const actionObject = actions.find((action) => action.value === "clearField");
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.clear).toHaveBeenCalled();
    });

    it("should NOT call clearField when no field", async () => {
      const actionObject = actions.find((action) => action.value === "clearField");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.clear).toHaveBeenCalledTimes(0);
    });

    it("should call selectAll", async () => {
      const actionObject = actions.find((action) => action.value === "selectAll");
      await actionObject.getActionCall({ app, qStateName, field, softLock })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.selectAll).toHaveBeenCalledWith(softLock);
    });

    it("should NOT call selectAll when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectAll");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectAll).toHaveBeenCalledTimes(0);
    });

    it("should call toggleSelect", async () => {
      const actionObject = actions.find((action) => action.value === "toggleSelect");
      await actionObject.getActionCall({ app, qStateName, field, value })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.toggleSelect).toHaveBeenCalledWith(value, false);
    });

    it("should NOT call toggleSelect when no field", async () => {
      const actionObject = actions.find((action) => action.value === "toggleSelect");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field, value })();
      expect(fieldObject.toggleSelect).toHaveBeenCalledTimes(0);
    });

    it("should call selectValues", async () => {
      const actionObject = actions.find((action) => action.value === "selectValues");
      await actionObject.getActionCall({ app, qStateName, field, value, softLock })();
      const valueList = await getValueList(app, value, false);
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.selectValues).toHaveBeenCalledWith(valueList, false, softLock);
    });

    it("should NOT call selectValues when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectValues");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectValues).toHaveBeenCalledTimes(0);
    });

    it("should call selectMatchingValues", async () => {
      const actionObject = actions.find((action) => action.value === "selectMatchingValues");
      await actionObject.getActionCall({ app, qStateName, field, value })();
      expect(fieldObject.select).toHaveBeenCalledWith(value, false, false);
    });

    it("should NOT call selectMatchingValues when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectMatchingValues");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectValues).toHaveBeenCalledTimes(0);
    });

    it("should call selectAlternative", async () => {
      const actionObject = actions.find((action) => action.value === "selectAlternative");
      await actionObject.getActionCall({ app, qStateName, field, softLock })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.selectAlternative).toHaveBeenCalledWith(softLock);
    });

    it("should NOT call selectAlternative when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectAlternative");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectAlternative).toHaveBeenCalledTimes(0);
    });

    it("should call selectExcluded", async () => {
      const actionObject = actions.find((action) => action.value === "selectExcluded");
      await actionObject.getActionCall({ app, qStateName, field, softLock })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.selectExcluded).toHaveBeenCalledWith(softLock);
    });

    it("should NOT call selectExcluded when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectExcluded");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectExcluded).toHaveBeenCalledTimes(0);
    });

    it("should call selectPossible", async () => {
      const actionObject = actions.find((action) => action.value === "selectPossible");
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.selectPossible).toHaveBeenCalledWith(false);
    });

    it("should NOT call selectPossible when no field", async () => {
      const actionObject = actions.find((action) => action.value === "selectPossible");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.selectPossible).toHaveBeenCalledTimes(0);
    });

    it("should call lockAll", async () => {
      const actionObject = actions.find((action) => action.value === "lockAll");
      await actionObject.getActionCall({ app })();
      expect(app.lockAll).toHaveBeenCalled();
    });

    it("should call lockField", async () => {
      const actionObject = actions.find((action) => action.value === "lockField");
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.lock).toHaveBeenCalled();
    });

    it("should NOT call lockField when no field", async () => {
      const actionObject = actions.find((action) => action.value === "lockField");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.lock).toHaveBeenCalledTimes(0);
    });

    it("should call unlockAll", async () => {
      const actionObject = actions.find((action) => action.value === "unlockAll");
      await actionObject.getActionCall({ app })();
      expect(app.unlockAll).toHaveBeenCalled();
    });

    it("should call unlockField", async () => {
      const actionObject = actions.find((action) => action.value === "unlockField");
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(app.getField).toHaveBeenCalledWith(field, qStateName);
      expect(fieldObject.unlock).toHaveBeenCalled();
    });

    it("should NOT call unlockField when no field", async () => {
      const actionObject = actions.find((action) => action.value === "unlockField");
      field = null;
      await actionObject.getActionCall({ app, qStateName, field })();
      expect(fieldObject.unlock).toHaveBeenCalledTimes(0);
    });

    it("should call setVariable", async () => {
      const actionObject = actions.find((action) => action.value === "setVariable");
      await actionObject.getActionCall({ app, variable, value })();
      expect(variableObject.setStringValue).toHaveBeenCalledWith(value);
    });

    it("should NOT call setVariable when no variable", async () => {
      const actionObject = actions.find((action) => action.value === "setVariable");
      variable = null;
      await actionObject.getActionCall({ app, variable, value })();
      expect(variableObject.setStringValue).toHaveBeenCalledTimes(0);
    });

    it("should do a reload and save", async () => {
      const actionObject = actions.find((action) => action.value === "doReload");
      await actionObject.getActionCall({ app, partial: true })();
      expect(app.doReload).toHaveBeenCalledWith(0, true, false);
      expect(app.doSave).toHaveBeenCalled();
    });

    it("should not save on failed reload", async () => {
      const actionObject = actions.find((action) => action.value === "doReload");
      app.doReload = () => false;
      await actionObject.getActionCall({ app, partial: true })();
      expect(app.doSave).toHaveBeenCalledTimes(0);
    });

    it("should do step cycle", async () => {
      const actionObject = actions.find((action) => action.value === "cyclicDimension");
      const stepCycle = jest.fn();
      app.getDimension = () => ({ stepCycle });
      await actionObject.getActionCall({ app, partial: true })();
      expect(stepCycle).toHaveBeenCalledTimes(1);
    });

    it("should call executeAutomation when automationTriggered is true", async () => {
      inputs = [];
      const appId = "fakeAppId";
      let automation;
      automationPostData = false;
      automationTriggered = true;
      multiUserAutomation = true;
      const time = new Date(2022, 10, 1);
      jest.useFakeTimers("modern");
      jest.setSystemTime(time);
      const actionObject = actions.find((action) => action.value === "executeAutomation");
      await actionObject.getActionCall({
        app,
        automation,
        automationId,
        automationTriggered,
        automationExecutionToken,
        automationPostData,
        senseNavigation,
        multiUserAutomation,
        translator,
      })();
      expect(global.fetch).toHaveBeenCalledTimes(4);
      expect(global.fetch).toHaveBeenCalledWith(`../api/v1/users/me`);
      expect(global.fetch).toHaveBeenCalledWith(`../api/v1/apps/${appId}`);
      expect(global.fetch).toHaveBeenCalledWith("../api/v1/csrf-token");
      const headers = {
        "Content-Type": "application/json",
        "qlik-csrf-token": csrfToken,
        "X-Execution-Token": automationExecutionToken,
      };
      const postOptions = {
        method: "POST",
        headers,
        body: JSON.stringify({
          app: appId,
          sheet: sheetId,
          user: id,
          space: spaceId,
          tenant: tenantId,
          time,
        }),
      };
      expect(global.fetch).toHaveBeenCalledWith(
        `../api/v1/automations/${automationId}/actions/execute?X-Execution-Token=${automationExecutionToken}`,
        postOptions
      );
      jest.useRealTimers();
    });

    it("should call automation run when automationTriggered is false", async () => {
      inputs = [];
      let automation;
      automationPostData = false;
      automationTriggered = false;
      const time = new Date(2022, 10, 1);
      jest.useFakeTimers("modern");
      jest.setSystemTime(time);
      const actionObject = actions.find((action) => action.value === "executeAutomation");
      await actionObject.getActionCall({
        app,
        automation,
        automationId,
        automationTriggered,
        automationExecutionToken,
        automationPostData,
        multiUserAutomation,
        translator,
        senseNavigation,
      })();
      expect(global.fetch).toHaveBeenCalledTimes(4);
      expect(global.fetch).toHaveBeenCalledWith(`../api/v1/users/me`);
      expect(global.fetch).toHaveBeenCalledWith(`../api/v1/apps/${app.id}`);
      expect(global.fetch).toHaveBeenCalledWith("../api/v1/csrf-token");
      const automationData = {
        id: automationId,
        inputs: {
          app: app.id,
          sheet: sheetId,
          user: id,
          space: spaceId,
          tenant: tenantId,
          time,
        },
        context: "qsbutton",
      };
      const headers = {
        "Content-Type": "application/json",
        "qlik-csrf-token": csrfToken,
      };
      const postOptions = {
        method: "POST",
        headers,
        body: JSON.stringify(automationData),
      };
      expect(global.fetch).toHaveBeenCalledWith(`../api/v1/automations/${automationId}/runs`, postOptions);
      jest.useRealTimers();
    });

    it("should NOT call executeAutomation when no automation", async () => {
      const actionObject = actions.find((action) => action.value === "executeAutomation");
      automationId = "";
      await actionObject.getActionCall({ app, automationId, automationPostData, multiUserAutomation, translator })();
      expect(global.fetch).toHaveBeenCalledTimes(0);
    });

    it("should call executeAutomation with creation of temp bookmark", async () => {
      inputs = [];
      automationPostData = true;
      let automation;
      const actionObject = actions.find((action) => action.value === "executeAutomation");
      await actionObject.getActionCall({
        app,
        automation,
        automationId,
        automationTriggered,
        automationExecutionToken,
        automationPostData,
        multiUserAutomation,
        translator,
        senseNavigation,
      })();
      expect(app.createTemporaryBookmark).toHaveBeenCalled();
    });

    it("should call executeAutomation WITHOUT creation of temp bookmark", async () => {
      inputs = [];
      automationPostData = false;
      let automation;
      const actionObject = actions.find((action) => action.value === "executeAutomation");
      await actionObject.getActionCall({
        app,
        automation,
        automationId,
        automationTriggered,
        automationExecutionToken,
        automationPostData,
        multiUserAutomation,
        translator,
        senseNavigation,
      })();
      expect(app.createTemporaryBookmark).toHaveBeenCalledTimes(0);
    });

    it("should call refreshDynamicViews", async () => {
      senseNavigation.refreshDynamicViews = jest.fn();
      const actionObject = actions.find((action) => action.value === "refreshDynamicViews");
      await actionObject.getActionCall({ senseNavigation })();
      expect(senseNavigation.refreshDynamicViews).toHaveBeenCalled();
    });
  });

  describe("getValueList", () => {
    let valueString = "valueOne;valueTwo";
    let ExpectedList = [{ qText: "valueOne" }, { qText: "valueTwo" }];

    it("should return array with values in an array", async () => {
      const valueList = await getValueList(app, valueString, false);
      expect(valueList).toEqual(ExpectedList);
    });

    it("should return array with numbers in value string", async () => {
      valueString = "1;2";
      ExpectedList = [
        { qNumber: 1, qIsNumeric: true },
        { qNumber: 2, qIsNumeric: true },
      ];
      const valueList = await getValueList(app, valueString, false);
      expect(valueList).toEqual(ExpectedList);
    });

    it("should return array with converted dates", async () => {
      valueString = "2020-01-20;2020-02-20";
      ExpectedList = [
        { qNumber: 43850, qIsNumeric: true },
        { qNumber: 43881, qIsNumeric: true },
      ];
      const valueList = await getValueList(app, valueString, true);
      expect(valueList).toEqual(ExpectedList);
    });
  });

  describe("checkShowAction", () => {
    let data;

    beforeEach(() => {
      data = { actionType: "applyBookmark" };
    });

    it("should return true when field required", () => {
      const result = checkShowAction(data, "bookmark");
      expect(result).toBe(true);
    });

    it("should return false when field is not required", () => {
      const result = checkShowAction(data, "notTheField");
      expect(result).toBe(false);
    });

    it("should return undefined when action is not found", () => {
      data.actionType = "notAnAction";
      const result = checkShowAction(data, "bookmark");
      expect(result).toEqual(undefined);
    });
  });

  describe("getActionsList", () => {
    it("should return all but not FF disabled actions", () => {
      const shouldHide = {
        isUnsupportedFeature: jest.fn().mockReturnValue(false),
        isFeatureBlacklisted: jest.fn().mockReturnValue(false),
        isEnabled: jest.fn().mockReturnValue(false),
      };
      const result = getActionsList(shouldHide);
      expect(result.length).toBe(20);
    });
    it("should return all but not unsupported feature navigations", () => {
      const shouldHide = {
        isUnsupportedFeature: jest.fn().mockReturnValue(true),
        isFeatureBlacklisted: jest.fn().mockReturnValue(false),
        isEnabled: jest.fn().mockReturnValue(true),
      };
      const result = getActionsList(shouldHide);
      expect(result.length).toBe(21);
    });
    it("should return all but not feature blacklisted navigations", () => {
      const shouldHide = {
        isUnsupportedFeature: jest.fn().mockReturnValue(false),
        isFeatureBlacklisted: jest.fn().mockReturnValue(true),
        isEnabled: jest.fn().mockReturnValue(true),
      };
      const result = getActionsList(shouldHide);
      expect(result.length).toBe(14);
    });
    it("should return all", () => {
      const shouldHide = {
        isUnsupportedFeature: jest.fn().mockReturnValue(false),
        isFeatureBlacklisted: jest.fn().mockReturnValue(false),
        isEnabled: jest.fn().mockReturnValue(true),
      };
      const result = getActionsList(shouldHide);
      expect(result.length).toBe(22);
    });
  });
});