tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ContextMenu.test.jsx (7930B)


      1 import {
      2  ContextMenu,
      3  ContextMenuItem,
      4  _ContextMenuItem,
      5 } from "content-src/components/ContextMenu/ContextMenu";
      6 import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
      7 import { mount, shallow } from "enzyme";
      8 import React from "react";
      9 import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs";
     10 import { Provider } from "react-redux";
     11 import { combineReducers, createStore } from "redux";
     12 
     13 const DEFAULT_PROPS = {
     14  onUpdate: () => {},
     15  options: [],
     16  tabbableOptionsLength: 0,
     17 };
     18 
     19 const DEFAULT_MENU_OPTIONS = [
     20  "MoveUp",
     21  "MoveDown",
     22  "Separator",
     23  "ManageSection",
     24 ];
     25 
     26 const FakeMenu = props => {
     27  return <div>{props.children}</div>;
     28 };
     29 
     30 describe("<ContextMenuButton>", () => {
     31  // eslint-disable-next-line no-shadow
     32  function mountWithProps(options) {
     33    const store = createStore(combineReducers(reducers), INITIAL_STATE);
     34    return mount(
     35      <Provider store={store}>
     36        <ContextMenuButton>
     37          <ContextMenu options={options} />
     38        </ContextMenuButton>
     39      </Provider>
     40    );
     41  }
     42 
     43  let sandbox;
     44  beforeEach(() => {
     45    sandbox = sinon.createSandbox();
     46  });
     47  afterEach(() => {
     48    sandbox.restore();
     49  });
     50  it("should call onUpdate when clicked", () => {
     51    const onUpdate = sandbox.spy();
     52    const wrapper = mount(
     53      <ContextMenuButton onUpdate={onUpdate}>
     54        <FakeMenu />
     55      </ContextMenuButton>
     56    );
     57    wrapper.find(".context-menu-button").simulate("click");
     58    assert.calledOnce(onUpdate);
     59  });
     60  it("should call onUpdate when activated with Enter", () => {
     61    const onUpdate = sandbox.spy();
     62    const wrapper = mount(
     63      <ContextMenuButton onUpdate={onUpdate}>
     64        <FakeMenu />
     65      </ContextMenuButton>
     66    );
     67    wrapper.find(".context-menu-button").simulate("keydown", { key: "Enter" });
     68    assert.calledOnce(onUpdate);
     69  });
     70  it("should call onClick", () => {
     71    const onClick = sandbox.spy(ContextMenuButton.prototype, "onClick");
     72    const wrapper = mount(
     73      <ContextMenuButton>
     74        <FakeMenu />
     75      </ContextMenuButton>
     76    );
     77    wrapper.find("button").simulate("click");
     78    assert.calledOnce(onClick);
     79  });
     80  it("should have a default keyboardAccess prop of false", () => {
     81    const wrapper = mountWithProps(DEFAULT_MENU_OPTIONS);
     82    wrapper.find(ContextMenuButton).setState({ showContextMenu: true });
     83    assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), false);
     84  });
     85  it("should pass the keyboardAccess prop down to ContextMenu", () => {
     86    const wrapper = mountWithProps(DEFAULT_MENU_OPTIONS);
     87    wrapper
     88      .find(ContextMenuButton)
     89      .setState({ showContextMenu: true, contextMenuKeyboard: true });
     90    assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), true);
     91  });
     92  it("should call focusFirst when keyboardAccess is true", () => {
     93    // eslint-disable-next-line no-shadow
     94    const options = [{ label: "item1", first: true }];
     95    const wrapper = mountWithProps(options);
     96    const focusFirst = sandbox.spy(_ContextMenuItem.prototype, "focusFirst");
     97    wrapper
     98      .find(ContextMenuButton)
     99      .setState({ showContextMenu: true, contextMenuKeyboard: true });
    100    assert.calledOnce(focusFirst);
    101  });
    102 });
    103 
    104 describe("<ContextMenu>", () => {
    105  function mountWithProps(props) {
    106    const store = createStore(combineReducers(reducers), INITIAL_STATE);
    107    return mount(
    108      <Provider store={store}>
    109        <ContextMenu {...props} />
    110      </Provider>
    111    );
    112  }
    113 
    114  it("should render all the options provided", () => {
    115    // eslint-disable-next-line no-shadow
    116    const options = [
    117      { label: "item1" },
    118      { type: "separator" },
    119      { label: "item2" },
    120    ];
    121    const wrapper = shallow(
    122      <ContextMenu {...DEFAULT_PROPS} options={options} />
    123    );
    124    assert.lengthOf(wrapper.find(".context-menu-list").children(), 3);
    125  });
    126  it("should not add a link for a separator", () => {
    127    // eslint-disable-next-line no-shadow
    128    const options = [{ label: "item1" }, { type: "separator" }];
    129    const wrapper = shallow(
    130      <ContextMenu {...DEFAULT_PROPS} options={options} />
    131    );
    132    assert.lengthOf(wrapper.find(".separator"), 1);
    133  });
    134  it("should add a link for all types that are not separators", () => {
    135    // eslint-disable-next-line no-shadow
    136    const options = [{ label: "item1" }, { type: "separator" }];
    137    const wrapper = shallow(
    138      <ContextMenu {...DEFAULT_PROPS} options={options} />
    139    );
    140    assert.lengthOf(wrapper.find(ContextMenuItem), 1);
    141  });
    142  it("should not add an icon to any items", () => {
    143    const props = Object.assign({}, DEFAULT_PROPS, {
    144      options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
    145    });
    146    const wrapper = mountWithProps(props);
    147    assert.lengthOf(wrapper.find(".icon-icon1"), 0);
    148  });
    149  it("should be tabbable", () => {
    150    const props = {
    151      options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
    152    };
    153    const wrapper = mountWithProps(props);
    154    assert.equal(
    155      wrapper.find(".context-menu-item").props().role,
    156      "presentation"
    157    );
    158  });
    159  it("should call onUpdate with false when an option is clicked", () => {
    160    const onUpdate = sinon.spy();
    161    const onClick = sinon.spy();
    162    const props = Object.assign({}, DEFAULT_PROPS, {
    163      onUpdate,
    164      options: [{ label: "item1", onClick }],
    165    });
    166    const wrapper = mountWithProps(props);
    167    wrapper.find(".context-menu-item button").simulate("click");
    168    assert.calledOnce(onUpdate);
    169    assert.calledOnce(onClick);
    170  });
    171  it("should not have disabled className by default", () => {
    172    const props = Object.assign({}, DEFAULT_PROPS, {
    173      options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
    174    });
    175    const wrapper = mountWithProps(props);
    176    assert.lengthOf(wrapper.find(".context-menu-item a.disabled"), 0);
    177  });
    178  it("should add disabled className to any disabled options", () => {
    179    // eslint-disable-next-line no-shadow
    180    const options = [
    181      { label: "item1", icon: "icon1", disabled: true },
    182      { type: "separator" },
    183    ];
    184    const props = Object.assign({}, DEFAULT_PROPS, { options });
    185    const wrapper = mountWithProps(props);
    186    assert.lengthOf(wrapper.find(".context-menu-item button.disabled"), 1);
    187  });
    188  it("should have the context-menu-item class", () => {
    189    // eslint-disable-next-line no-shadow
    190    const options = [{ label: "item1", icon: "icon1" }];
    191    const props = Object.assign({}, DEFAULT_PROPS, { options });
    192    const wrapper = mountWithProps(props);
    193    assert.lengthOf(wrapper.find(".context-menu-item"), 1);
    194  });
    195  it("should call onClick when onKeyDown is called with Enter", () => {
    196    const onClick = sinon.spy();
    197    const props = Object.assign({}, DEFAULT_PROPS, {
    198      options: [{ label: "item1", onClick }],
    199    });
    200    const wrapper = mountWithProps(props);
    201    wrapper
    202      .find(".context-menu-item button")
    203      .simulate("keydown", { key: "Enter" });
    204    assert.calledOnce(onClick);
    205  });
    206  it("should call focusSibling when onKeyDown is called with ArrowUp", () => {
    207    const props = Object.assign({}, DEFAULT_PROPS, {
    208      options: [{ label: "item1" }],
    209    });
    210    const wrapper = mountWithProps(props);
    211    const focusSibling = sinon.stub(
    212      wrapper.find(_ContextMenuItem).instance(),
    213      "focusSibling"
    214    );
    215    wrapper
    216      .find(".context-menu-item button")
    217      .simulate("keydown", { key: "ArrowUp" });
    218    assert.calledOnce(focusSibling);
    219  });
    220  it("should call focusSibling when onKeyDown is called with ArrowDown", () => {
    221    const props = Object.assign({}, DEFAULT_PROPS, {
    222      options: [{ label: "item1" }],
    223    });
    224    const wrapper = mountWithProps(props);
    225    const focusSibling = sinon.stub(
    226      wrapper.find(_ContextMenuItem).instance(),
    227      "focusSibling"
    228    );
    229    wrapper
    230      .find(".context-menu-item button")
    231      .simulate("keydown", { key: "ArrowDown" });
    232    assert.calledOnce(focusSibling);
    233  });
    234 });