/* eslint-disable no-shadow */
import { ContextMenu } from "content-src/components/ContextMenu/ContextMenu";
import { _LinkMenu as LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
import React from "react";
import { shallow } from "enzyme";

describe("<LinkMenu>", () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "" }}
        options={["CheckPinTopSite", "CheckBookmark", "OpenInNewWindow"]}
        dispatch={() => {}}
      />
    );
  });
  it("should render a ContextMenu element", () => {
    assert.ok(wrapper.find(ContextMenu).exists());
  });
  it("should pass onUpdate, and options to ContextMenu", () => {
    assert.ok(wrapper.find(ContextMenu).exists());
    const contextMenuProps = wrapper.find(ContextMenu).props();
    ["onUpdate", "options"].forEach(prop =>
      assert.property(contextMenuProps, prop)
    );
  });
  it("should give ContextMenu the correct tabbable options length for a11y", () => {
    const { options } = wrapper.find(ContextMenu).props();
    const [firstItem] = options;
    const lastItem = options[options.length - 1];

    // first item should have {first: true}
    assert.isTrue(firstItem.first);
    assert.ok(!firstItem.last);

    // last item should have {last: true}
    assert.isTrue(lastItem.last);
    assert.ok(!lastItem.first);

    // middle items should have neither
    for (let i = 1; i < options.length - 1; i++) {
      assert.ok(!options[i].first && !options[i].last);
    }
  });
  it("should show the correct options for default sites", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", isDefault: true }}
        options={["CheckBookmark"]}
        source={"TOP_SITES"}
        isPrivateBrowsingEnabled={true}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    let i = 0;
    assert.propertyVal(options[i++], "id", "newtab-menu-pin");
    assert.propertyVal(options[i++], "id", "newtab-menu-edit-topsites");
    assert.propertyVal(options[i++], "type", "separator");
    assert.propertyVal(options[i++], "id", "newtab-menu-open-new-window");
    assert.propertyVal(
      options[i++],
      "id",
      "newtab-menu-open-new-private-window"
    );
    assert.propertyVal(options[i++], "type", "separator");
    assert.propertyVal(options[i++], "id", "newtab-menu-dismiss");
    assert.propertyVal(options, "length", i);
    // Double check that delete options are not included for default top sites
    options
      .filter(o => o.type !== "separator")
      .forEach(o => {
        assert.notInclude(["newtab-menu-delete-history"], o.id);
      });
  });
  it("should show Unpin option for a pinned site if CheckPinTopSite in options list", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", isPinned: true }}
        source={"TOP_SITES"}
        options={["CheckPinTopSite"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(options.find(o => o.id && o.id === "newtab-menu-unpin"));
  });
  it("should show Pin option for an unpinned site if CheckPinTopSite in options list", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", isPinned: false }}
        source={"TOP_SITES"}
        options={["CheckPinTopSite"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(options.find(o => o.id && o.id === "newtab-menu-pin"));
  });
  it("should show Unbookmark option for a bookmarked site if CheckBookmark in options list", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", bookmarkGuid: 1234 }}
        source={"TOP_SITES"}
        options={["CheckBookmark"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-remove-bookmark")
    );
  });
  it("should show Bookmark option for an unbookmarked site if CheckBookmark in options list", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", bookmarkGuid: 0 }}
        source={"TOP_SITES"}
        options={["CheckBookmark"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-bookmark")
    );
  });
  it("should show Open File option for a downloaded item", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download", path: "foo" }}
        source={"HIGHLIGHTS"}
        options={["OpenFile"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-open-file")
    );
  });
  it("should show Show File option for a downloaded item on a default platform", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download", path: "foo" }}
        source={"HIGHLIGHTS"}
        options={["ShowFile"]}
        platform={"default"}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-show-file")
    );
  });
  it("should show Copy Downlad Link option for a downloaded item when CopyDownloadLink", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download" }}
        source={"HIGHLIGHTS"}
        options={["CopyDownloadLink"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-copy-download-link")
    );
  });
  it("should show Go To Download Page option for a downloaded item when GoToDownloadPage", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download", referrer: "foo" }}
        source={"HIGHLIGHTS"}
        options={["GoToDownloadPage"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-go-to-download-page")
    );
    assert.isFalse(options[0].disabled);
  });
  it("should show Go To Download Page option as disabled for a downloaded item when GoToDownloadPage if no referrer exists", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download", referrer: null }}
        source={"HIGHLIGHTS"}
        options={["GoToDownloadPage"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-go-to-download-page")
    );
    assert.isTrue(options[0].disabled);
  });
  it("should show Remove Download Link option for a downloaded item when RemoveDownload", () => {
    wrapper = shallow(
      <LinkMenu
        site={{ url: "", type: "download" }}
        source={"HIGHLIGHTS"}
        options={["RemoveDownload"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    assert.isDefined(
      options.find(o => o.id && o.id === "newtab-menu-remove-download")
    );
  });
  it("should show Edit option", () => {
    const props = { url: "foo", label: "label" };
    const index = 5;
    wrapper = shallow(
      <LinkMenu
        site={props}
        index={5}
        source={"TOP_SITES"}
        options={["EditTopSite"]}
        dispatch={() => {}}
      />
    );
    const { options } = wrapper.find(ContextMenu).props();
    const option = options.find(
      o => o.id && o.id === "newtab-menu-edit-topsites"
    );
    assert.isDefined(option);
    assert.equal(option.action.data.index, index);
  });
  describe(".onClick", () => {
    const FAKE_EVENT = {};
    const FAKE_INDEX = 3;
    const FAKE_SOURCE = "TOP_SITES";
    const FAKE_SITE = {
      bookmarkGuid: 1234,
      hostname: "foo",
      path: "foo",
      pocket_id: "1234",
      referrer: "https://foo.com/ref",
      title: "bar",
      type: "bookmark",
      typedBonus: true,
      url: "https://foo.com",
      sponsored_tile_id: 12345,
      card_type: "organic",
    };
    const dispatch = sinon.stub();
    const propOptions = [
      "ShowFile",
      "CopyDownloadLink",
      "GoToDownloadPage",
      "RemoveDownload",
      "Separator",
      "ShowPrivacyInfo",
      "RemoveBookmark",
      "AddBookmark",
      "OpenInNewWindow",
      "OpenInPrivateWindow",
      "BlockUrl",
      "DeleteUrl",
      "PinTopSite",
      "UnpinTopSite",
      "WebExtDismiss",
    ];
    const expectedActionData = {
      "newtab-menu-remove-bookmark": FAKE_SITE.bookmarkGuid,
      "newtab-menu-bookmark": {
        url: FAKE_SITE.url,
        title: FAKE_SITE.title,
        type: FAKE_SITE.type,
      },
      "newtab-menu-open-new-window": {
        card_type: FAKE_SITE.card_type,
        referrer: FAKE_SITE.referrer,
        typedBonus: FAKE_SITE.typedBonus,
        url: FAKE_SITE.url,
        sponsored_tile_id: FAKE_SITE.sponsored_tile_id,
        event_source: "CONTEXT_MENU",
        topic: undefined,
        firstVisibleTimestamp: undefined,
        tile_id: undefined,
        recommendation_id: undefined,
        scheduled_corpus_item_id: undefined,
        corpus_item_id: undefined,
        received_rank: undefined,
        recommended_at: undefined,
        format: undefined,
        is_pocket_card: false,
        is_list_card: undefined,
      },
      "newtab-menu-open-new-private-window": {
        url: FAKE_SITE.url,
        referrer: FAKE_SITE.referrer,
        event_source: "CONTEXT_MENU",
      },
      "newtab-menu-dismiss": [
        {
          url: FAKE_SITE.url,
          pocket_id: FAKE_SITE.pocket_id,
          tile_id: 12345,
          recommendation_id: undefined,
          scheduled_corpus_item_id: undefined,
          corpus_item_id: undefined,
          recommended_at: undefined,
          received_rank: undefined,
          isSponsoredTopSite: undefined,
          type: "bookmark",
          card_type: FAKE_SITE.card_type,
          position: 3,
          is_pocket_card: false,
          is_list_card: undefined,
        },
      ],
      menu_action_webext_dismiss: {
        source: "TOP_SITES",
        url: FAKE_SITE.url,
        action_position: 3,
      },
      "newtab-menu-delete-history": {
        url: FAKE_SITE.url,
        pocket_id: FAKE_SITE.pocket_id,
        forceBlock: FAKE_SITE.bookmarkGuid,
      },
      "newtab-menu-pin": { site: FAKE_SITE, index: FAKE_INDEX },
      "newtab-menu-unpin": { site: { url: FAKE_SITE.url } },
      "newtab-menu-show-file": { url: FAKE_SITE.url },
      "newtab-menu-copy-download-link": { url: FAKE_SITE.url },
      "newtab-menu-go-to-download-page": { url: FAKE_SITE.referrer },
      "newtab-menu-remove-download": { url: FAKE_SITE.url },
    };
    const { options } = shallow(
      <LinkMenu
        site={FAKE_SITE}
        siteInfo={{ value: { card_type: FAKE_SITE.type } }}
        dispatch={dispatch}
        index={FAKE_INDEX}
        isPrivateBrowsingEnabled={true}
        platform={"default"}
        options={propOptions}
        source={FAKE_SOURCE}
        shouldSendImpressionStats={true}
      />
    )
      .find(ContextMenu)
      .props();
    afterEach(() => dispatch.reset());
    options
      .filter(o => o.type !== "separator")
      .forEach(option => {
        it(`should fire a ${option.action.type} action for ${option.id} with the expected data`, () => {
          option.onClick(FAKE_EVENT);

          if (option.impression && option.userEvent) {
            assert.calledThrice(dispatch);
          } else if (option.impression || option.userEvent) {
            assert.calledTwice(dispatch);
          } else {
            assert.calledOnce(dispatch);
          }

          // option.action is dispatched
          assert.ok(dispatch.firstCall.calledWith(option.action));

          // option.action has correct data
          // (delete is a special case as it dispatches a nested DIALOG_OPEN-type action)
          // in the case of this FAKE_SITE, we send a bookmarkGuid therefore we also want
          // to block this if we delete it
          if (option.id === "newtab-menu-delete-history") {
            assert.deepEqual(
              option.action.data.onConfirm[0].data,
              expectedActionData[option.id]
            );
            // Test UserEvent send correct meta about item deleted
            assert.propertyVal(
              option.action.data.onConfirm[1].data,
              "action_position",
              FAKE_INDEX
            );
            assert.propertyVal(
              option.action.data.onConfirm[1].data,
              "source",
              FAKE_SOURCE
            );
          } else {
            assert.deepEqual(option.action.data, expectedActionData[option.id]);
          }
        });
        it(`should fire a UserEvent action for ${option.id} if configured`, () => {
          if (option.userEvent) {
            option.onClick(FAKE_EVENT);
            const [action] = dispatch.secondCall.args;
            assert.isUserEventAction(action);
            assert.propertyVal(action.data, "source", FAKE_SOURCE);
            assert.propertyVal(action.data, "action_position", FAKE_INDEX);
            assert.propertyVal(action.data.value, "card_type", FAKE_SITE.type);
          }
        });
        it(`should send impression stats for ${option.id}`, () => {
          if (option.impression) {
            option.onClick(FAKE_EVENT);
            const [action] = dispatch.thirdCall.args;
            assert.deepEqual(action, option.impression);
          }
        });
      });
    it(`should not send impression stats if not configured`, () => {
      const fakeOptions = shallow(
        <LinkMenu
          site={FAKE_SITE}
          dispatch={dispatch}
          index={FAKE_INDEX}
          options={propOptions}
          source={FAKE_SOURCE}
          shouldSendImpressionStats={false}
        />
      )
        .find(ContextMenu)
        .props().options;

      fakeOptions
        .filter(o => o.type !== "separator")
        .forEach(option => {
          if (option.impression) {
            option.onClick(FAKE_EVENT);
            assert.calledTwice(dispatch);
            assert.notEqual(dispatch.firstCall.args[0], option.impression);
            assert.notEqual(dispatch.secondCall.args[0], option.impression);
            dispatch.reset();
          }
        });
    });
    it(`should pin a SPOC with all of the site details sent`, () => {
      const pinSpocTopSite = "PinTopSite";
      const { options: spocOptions } = shallow(
        <LinkMenu
          site={FAKE_SITE}
          siteInfo={{ value: { card_type: FAKE_SITE.type } }}
          dispatch={dispatch}
          index={FAKE_INDEX}
          isPrivateBrowsingEnabled={true}
          platform={"default"}
          options={[pinSpocTopSite]}
          source={FAKE_SOURCE}
          shouldSendImpressionStats={true}
        />
      )
        .find(ContextMenu)
        .props();

      const [pinSpocOption] = spocOptions;
      pinSpocOption.onClick(FAKE_EVENT);

      if (pinSpocOption.impression && pinSpocOption.userEvent) {
        assert.calledThrice(dispatch);
      } else if (pinSpocOption.impression || pinSpocOption.userEvent) {
        assert.calledTwice(dispatch);
      } else {
        assert.calledOnce(dispatch);
      }

      // option.action is dispatched
      assert.ok(dispatch.firstCall.calledWith(pinSpocOption.action));

      assert.deepEqual(pinSpocOption.action.data, {
        site: FAKE_SITE,
        index: FAKE_INDEX,
      });
    });
    it(`should create a proper BLOCK_URL action for a sponsored tile`, () => {
      const site = {
        hostname: "foo",
        path: "foo",
        referrer: "https://foo.com/ref",
        title: "bar",
        type: "bookmark",
        typedBonus: true,
        url: "https://foo.com",
        sponsored_position: 1,
      };
      const { options: blockOptions } = shallow(
        <LinkMenu
          site={site}
          siteInfo={{ value: { card_type: site.type } }}
          dispatch={dispatch}
          index={FAKE_INDEX}
          isPrivateBrowsingEnabled={true}
          platform={"default"}
          options={["BlockUrl"]}
          source={FAKE_SOURCE}
          shouldSendImpressionStats={true}
        />
      )
        .find(ContextMenu)
        .props();
      const [blockUrlOption] = blockOptions;

      blockUrlOption.onClick(FAKE_EVENT);

      assert.calledThrice(dispatch);
      assert.ok(dispatch.firstCall.calledWith(blockUrlOption.action));
      const expected = {
        url: site.url,
        pocket_id: undefined,
        tile_id: undefined,
        recommendation_id: undefined,
        scheduled_corpus_item_id: undefined,
        corpus_item_id: undefined,
        recommended_at: undefined,
        received_rank: undefined,
        advertiser_name: site.hostname,
        isSponsoredTopSite: 1,
        type: "bookmark",
        card_type: undefined,
        position: 3,
        is_pocket_card: false,
        is_list_card: undefined,
      };
      assert.deepEqual(blockUrlOption.action.data[0], expected);
    });
    it(`should create a proper BLOCK_URL action for a pocket item`, () => {
      const site = {
        hostname: "foo",
        path: "foo",
        referrer: "https://foo.com/ref",
        title: "bar",
        type: "CardGrid",
        typedBonus: true,
        url: "https://foo.com",
      };
      const { options: blockOptions } = shallow(
        <LinkMenu
          site={site}
          siteInfo={{ value: { card_type: site.type } }}
          dispatch={dispatch}
          index={FAKE_INDEX}
          isPrivateBrowsingEnabled={true}
          platform={"default"}
          options={["BlockUrl"]}
          source={FAKE_SOURCE}
          shouldSendImpressionStats={true}
        />
      )
        .find(ContextMenu)
        .props();
      const [blockUrlOption] = blockOptions;

      blockUrlOption.onClick(FAKE_EVENT);

      assert.calledThrice(dispatch);
      assert.ok(dispatch.firstCall.calledWith(blockUrlOption.action));
      const expected = {
        url: site.url,
        pocket_id: undefined,
        tile_id: undefined,
        recommendation_id: undefined,
        scheduled_corpus_item_id: undefined,
        corpus_item_id: undefined,
        recommended_at: undefined,
        received_rank: undefined,
        isSponsoredTopSite: undefined,
        type: "CardGrid",
        card_type: undefined,
        position: 3,
        is_pocket_card: true,
        is_list_card: undefined,
      };
      assert.deepEqual(blockUrlOption.action.data[0], expected);
    });
  });
});
