summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx')
-rw-r--r--browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx227
1 files changed, 227 insertions, 0 deletions
diff --git a/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx b/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
new file mode 100644
index 0000000000..4f7edadc41
--- /dev/null
+++ b/browser/components/newtab/test/unit/content-src/components/ContextMenu.test.jsx
@@ -0,0 +1,227 @@
+import {
+ ContextMenu,
+ ContextMenuItem,
+ _ContextMenuItem,
+} from "content-src/components/ContextMenu/ContextMenu";
+import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
+import { mount, shallow } from "enzyme";
+import React from "react";
+import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs";
+import { Provider } from "react-redux";
+import { combineReducers, createStore } from "redux";
+
+const DEFAULT_PROPS = {
+ onUpdate: () => {},
+ options: [],
+ tabbableOptionsLength: 0,
+};
+
+const DEFAULT_MENU_OPTIONS = [
+ "MoveUp",
+ "MoveDown",
+ "Separator",
+ "ManageSection",
+];
+
+const FakeMenu = props => {
+ return <div>{props.children}</div>;
+};
+
+describe("<ContextMenuButton>", () => {
+ function mountWithProps(options) {
+ const store = createStore(combineReducers(reducers), INITIAL_STATE);
+ return mount(
+ <Provider store={store}>
+ <ContextMenuButton>
+ <ContextMenu options={options} />
+ </ContextMenuButton>
+ </Provider>
+ );
+ }
+
+ let sandbox;
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ });
+ afterEach(() => {
+ sandbox.restore();
+ });
+ it("should call onUpdate when clicked", () => {
+ const onUpdate = sandbox.spy();
+ const wrapper = mount(
+ <ContextMenuButton onUpdate={onUpdate}>
+ <FakeMenu />
+ </ContextMenuButton>
+ );
+ wrapper.find(".context-menu-button").simulate("click");
+ assert.calledOnce(onUpdate);
+ });
+ it("should call onUpdate when activated with Enter", () => {
+ const onUpdate = sandbox.spy();
+ const wrapper = mount(
+ <ContextMenuButton onUpdate={onUpdate}>
+ <FakeMenu />
+ </ContextMenuButton>
+ );
+ wrapper.find(".context-menu-button").simulate("keydown", { key: "Enter" });
+ assert.calledOnce(onUpdate);
+ });
+ it("should call onClick", () => {
+ const onClick = sandbox.spy(ContextMenuButton.prototype, "onClick");
+ const wrapper = mount(
+ <ContextMenuButton>
+ <FakeMenu />
+ </ContextMenuButton>
+ );
+ wrapper.find("button").simulate("click");
+ assert.calledOnce(onClick);
+ });
+ it("should have a default keyboardAccess prop of false", () => {
+ const wrapper = mountWithProps(DEFAULT_MENU_OPTIONS);
+ wrapper.find(ContextMenuButton).setState({ showContextMenu: true });
+ assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), false);
+ });
+ it("should pass the keyboardAccess prop down to ContextMenu", () => {
+ const wrapper = mountWithProps(DEFAULT_MENU_OPTIONS);
+ wrapper
+ .find(ContextMenuButton)
+ .setState({ showContextMenu: true, contextMenuKeyboard: true });
+ assert.equal(wrapper.find(ContextMenu).prop("keyboardAccess"), true);
+ });
+ it("should call focusFirst when keyboardAccess is true", () => {
+ const options = [{ label: "item1", first: true }];
+ const wrapper = mountWithProps(options);
+ const focusFirst = sandbox.spy(_ContextMenuItem.prototype, "focusFirst");
+ wrapper
+ .find(ContextMenuButton)
+ .setState({ showContextMenu: true, contextMenuKeyboard: true });
+ assert.calledOnce(focusFirst);
+ });
+});
+
+describe("<ContextMenu>", () => {
+ function mountWithProps(props) {
+ const store = createStore(combineReducers(reducers), INITIAL_STATE);
+ return mount(
+ <Provider store={store}>
+ <ContextMenu {...props} />
+ </Provider>
+ );
+ }
+
+ it("should render all the options provided", () => {
+ const options = [
+ { label: "item1" },
+ { type: "separator" },
+ { label: "item2" },
+ ];
+ const wrapper = shallow(
+ <ContextMenu {...DEFAULT_PROPS} options={options} />
+ );
+ assert.lengthOf(wrapper.find(".context-menu-list").children(), 3);
+ });
+ it("should not add a link for a separator", () => {
+ const options = [{ label: "item1" }, { type: "separator" }];
+ const wrapper = shallow(
+ <ContextMenu {...DEFAULT_PROPS} options={options} />
+ );
+ assert.lengthOf(wrapper.find(".separator"), 1);
+ });
+ it("should add a link for all types that are not separators", () => {
+ const options = [{ label: "item1" }, { type: "separator" }];
+ const wrapper = shallow(
+ <ContextMenu {...DEFAULT_PROPS} options={options} />
+ );
+ assert.lengthOf(wrapper.find(ContextMenuItem), 1);
+ });
+ it("should not add an icon to any items", () => {
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
+ });
+ const wrapper = mountWithProps(props);
+ assert.lengthOf(wrapper.find(".icon-icon1"), 0);
+ });
+ it("should be tabbable", () => {
+ const props = {
+ options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
+ };
+ const wrapper = mountWithProps(props);
+ assert.equal(
+ wrapper.find(".context-menu-item").props().role,
+ "presentation"
+ );
+ });
+ it("should call onUpdate with false when an option is clicked", () => {
+ const onUpdate = sinon.spy();
+ const onClick = sinon.spy();
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ onUpdate,
+ options: [{ label: "item1", onClick }],
+ });
+ const wrapper = mountWithProps(props);
+ wrapper.find(".context-menu-item button").simulate("click");
+ assert.calledOnce(onUpdate);
+ assert.calledOnce(onClick);
+ });
+ it("should not have disabled className by default", () => {
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ options: [{ label: "item1", icon: "icon1" }, { type: "separator" }],
+ });
+ const wrapper = mountWithProps(props);
+ assert.lengthOf(wrapper.find(".context-menu-item a.disabled"), 0);
+ });
+ it("should add disabled className to any disabled options", () => {
+ const options = [
+ { label: "item1", icon: "icon1", disabled: true },
+ { type: "separator" },
+ ];
+ const props = Object.assign({}, DEFAULT_PROPS, { options });
+ const wrapper = mountWithProps(props);
+ assert.lengthOf(wrapper.find(".context-menu-item button.disabled"), 1);
+ });
+ it("should have the context-menu-item class", () => {
+ const options = [{ label: "item1", icon: "icon1" }];
+ const props = Object.assign({}, DEFAULT_PROPS, { options });
+ const wrapper = mountWithProps(props);
+ assert.lengthOf(wrapper.find(".context-menu-item"), 1);
+ });
+ it("should call onClick when onKeyDown is called with Enter", () => {
+ const onClick = sinon.spy();
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ options: [{ label: "item1", onClick }],
+ });
+ const wrapper = mountWithProps(props);
+ wrapper
+ .find(".context-menu-item button")
+ .simulate("keydown", { key: "Enter" });
+ assert.calledOnce(onClick);
+ });
+ it("should call focusSibling when onKeyDown is called with ArrowUp", () => {
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ options: [{ label: "item1" }],
+ });
+ const wrapper = mountWithProps(props);
+ const focusSibling = sinon.stub(
+ wrapper.find(_ContextMenuItem).instance(),
+ "focusSibling"
+ );
+ wrapper
+ .find(".context-menu-item button")
+ .simulate("keydown", { key: "ArrowUp" });
+ assert.calledOnce(focusSibling);
+ });
+ it("should call focusSibling when onKeyDown is called with ArrowDown", () => {
+ const props = Object.assign({}, DEFAULT_PROPS, {
+ options: [{ label: "item1" }],
+ });
+ const wrapper = mountWithProps(props);
+ const focusSibling = sinon.stub(
+ wrapper.find(_ContextMenuItem).instance(),
+ "focusSibling"
+ );
+ wrapper
+ .find(".context-menu-item button")
+ .simulate("keydown", { key: "ArrowDown" });
+ assert.calledOnce(focusSibling);
+ });
+});