/* eslint max-nested-callbacks: ["error", 4] */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at . */
// @flow
import React from "react";
import lodash from "lodash";
import { shallow, mount } from "enzyme";
import { QuickOpenModal } from "../QuickOpenModal";
import { mockcx } from "../../utils/test-mockup";
jest.mock("fuzzaldrin-plus");
jest.unmock("lodash");
import { filter } from "fuzzaldrin-plus";
// $FlowIgnore
lodash.throttle = jest.fn(fn => fn);
function generateModal(propOverrides, renderType = "shallow") {
const props = {
cx: mockcx,
enabled: false,
query: "",
searchType: "sources",
displayedSources: [],
tabs: [],
selectSpecificLocation: jest.fn(),
setQuickOpenQuery: jest.fn(),
highlightLineRange: jest.fn(),
clearHighlightLineRange: jest.fn(),
closeQuickOpen: jest.fn(),
shortcutsModalEnabled: false,
symbols: { functions: [] },
symbolsLoading: false,
toggleShortcutsModal: jest.fn(),
isOriginal: false,
thread: "FakeThread",
...propOverrides
};
return {
wrapper:
renderType === "shallow"
? shallow()
: mount(),
props
};
}
function generateTab(url) {
return {
url,
isOriginal: false,
thread: "FakeThread"
};
}
function generateQuickOpenResult(title) {
return {
id: "qor",
value: "",
title
};
}
describe("QuickOpenModal", () => {
beforeEach(() => {
filter.mockClear();
});
test("Doesn't render when disabled", () => {
const { wrapper } = generateModal();
expect(wrapper).toMatchSnapshot();
});
test("Renders when enabled", () => {
const { wrapper } = generateModal({ enabled: true });
expect(wrapper).toMatchSnapshot();
});
test("Basic render with mount", () => {
const { wrapper } = generateModal({ enabled: true }, "mount");
expect(wrapper).toMatchSnapshot();
});
test("Basic render with mount & searchType = functions", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "@",
searchType: "functions",
symbols: {
functions: [],
variables: []
}
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
test("toggles shortcut modal if enabled", () => {
const { props } = generateModal(
{
enabled: true,
query: "test",
shortcutsModalEnabled: true,
toggleShortcutsModal: jest.fn()
},
"shallow"
);
expect(props.toggleShortcutsModal).toHaveBeenCalled();
});
test("shows top sources", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "",
displayedSources: [
// $FlowIgnore
{ url: "mozilla.com", relativeUrl: true }
],
tabs: [generateTab("mozilla.com")]
},
"shallow"
);
expect(wrapper.state("results")).toEqual([
{
id: undefined,
icon: "tab result-item-icon",
subtitle: "true",
title: "mozilla.com",
url: "mozilla.com",
value: "true"
}
]);
});
describe("shows loading", () => {
it("loads with function type search", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "",
searchType: "functions",
symbolsLoading: true
},
"shallow"
);
expect(wrapper).toMatchSnapshot();
});
});
test("Ensure anonymous functions do not render in QuickOpenModal", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "@",
searchType: "functions",
symbols: {
functions: [
generateQuickOpenResult("anonymous"),
generateQuickOpenResult("c"),
generateQuickOpenResult("anonymous")
],
variables: []
}
},
"mount"
);
expect(wrapper.find("ResultList")).toHaveLength(1);
expect(wrapper.find("li")).toHaveLength(1);
});
test("Basic render with mount & searchType = variables", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "#",
searchType: "variables",
symbols: {
functions: [],
variables: []
}
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
test("Basic render with mount & searchType = shortcuts", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "?",
searchType: "shortcuts",
symbols: {
functions: [],
variables: []
}
},
"mount"
);
expect(wrapper.find("ResultList")).toHaveLength(1);
expect(wrapper.find("li")).toHaveLength(3);
});
test("updateResults on enable", () => {
const { wrapper } = generateModal({}, "mount");
expect(wrapper).toMatchSnapshot();
wrapper.setProps({ enabled: true });
expect(wrapper).toMatchSnapshot();
});
test("basic source search", () => {
const { wrapper } = generateModal(
{
enabled: true,
symbols: {
functions: [],
variables: []
}
},
"mount"
);
wrapper.find("input").simulate("change", { target: { value: "somefil" } });
expect(filter).toHaveBeenCalledWith([], "somefil", {
key: "value",
maxResults: 100
});
});
test("basic gotoSource search", () => {
const { wrapper } = generateModal(
{
enabled: true,
searchType: "gotoSource",
symbols: {
functions: [],
variables: []
}
},
"mount"
);
wrapper
.find("input")
.simulate("change", { target: { value: "somefil:33" } });
expect(filter).toHaveBeenCalledWith([], "somefil", {
key: "value",
maxResults: 100
});
});
describe("empty symbol search", () => {
it("basic symbol search", () => {
const { wrapper } = generateModal(
{
enabled: true,
searchType: "functions",
symbols: {
functions: [],
variables: []
},
// symbol searching relies on a source being selected.
// So we dummy out the source and the API.
selectedSource: { id: "foo", text: "yo" },
selectedContentLoaded: true
},
"mount"
);
wrapper
.find("input")
.simulate("change", { target: { value: "@someFunc" } });
expect(filter).toHaveBeenCalledWith([], "someFunc", {
key: "value",
maxResults: 100
});
});
it("does not do symbol search if no selected source", () => {
const { wrapper } = generateModal(
{
enabled: true,
searchType: "functions",
symbols: {
functions: [],
variables: []
},
// symbol searching relies on a source being selected.
// So we dummy out the source and the API.
selectedSource: null,
selectedContentLoaded: false
},
"mount"
);
wrapper
.find("input")
.simulate("change", { target: { value: "@someFunc" } });
expect(filter).not.toHaveBeenCalled();
});
});
test("Simple goto search query = :abc & searchType = goto", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: ":abc",
searchType: "goto",
symbols: {
functions: [],
variables: []
}
},
"mount"
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.state().results).toEqual(null);
});
describe("onEnter", () => {
it("on Enter go to location", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: ":34:12",
searchType: "goto"
},
"shallow"
);
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
column: 12,
line: 34,
sourceId: ""
});
});
it("on Enter go to location with sourceId", () => {
const sourceId = "source_id";
const { wrapper, props } = generateModal(
{
enabled: true,
query: ":34:12",
searchType: "goto",
selectedSource: { id: sourceId },
selectedContentLoaded: true
},
"shallow"
);
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
column: 12,
line: 34,
sourceId: sourceId
});
});
it("on Enter with no location, does no action", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: ":",
searchType: "goto"
},
"shallow"
);
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.highlightLineRange).not.toHaveBeenCalled();
});
it("on Enter with empty results, handle no item", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "",
searchType: "shortcuts"
},
"shallow"
);
wrapper.setState(() => ({
results: [],
selectedIndex: 0
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.highlightLineRange).not.toHaveBeenCalled();
});
it("on Enter with results, handle symbol shortcut", () => {
const symbols: Object = [":", "#", "@"];
let key;
let symbol;
for (key in symbols) {
symbol = symbols[key];
const { wrapper, props } = generateModal(
{
enabled: true,
query: "",
searchType: "shortcuts"
},
"shallow"
);
wrapper.setState(() => ({
results: [{ id: symbol }],
selectedIndex: 0
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.setQuickOpenQuery).toHaveBeenCalledWith(symbol);
}
});
it("on Enter, returns the result with the selected index", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "@test",
searchType: "shortcuts"
},
"shallow"
);
wrapper.setState(() => ({
results: [{ id: "@" }, { id: ":" }, { id: "#" }],
selectedIndex: 1
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.setQuickOpenQuery).toHaveBeenCalledWith(":");
});
it("on Enter with results, handle result item", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "@test",
searchType: "other"
},
"shallow"
);
const id = "test_id";
wrapper.setState(() => ({
results: [{}, { id }],
selectedIndex: 1
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
column: undefined,
sourceId: id,
line: 0
});
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
});
it("on Enter with results, handle functions result item", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "@test",
searchType: "functions",
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const id = "test_id";
wrapper.setState(() => ({
results: [{}, { id }],
selectedIndex: 1
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
column: undefined,
line: 0,
sourceId: ""
});
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
});
it("on Enter with results, handle gotoSource search", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: ":3:4",
searchType: "gotoSource",
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const id = "test_id";
wrapper.setState(() => ({
results: [{}, { id }],
selectedIndex: 1
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).toHaveBeenCalledWith(mockcx, {
column: 4,
line: 3,
sourceId: id
});
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
});
it("on Enter with results, handle shortcuts search", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "@",
searchType: "shortcuts",
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const id = "#";
wrapper.setState(() => ({
results: [{}, { id }],
selectedIndex: 1
}));
const event = {
key: "Enter"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.setQuickOpenQuery).toHaveBeenCalledWith(id);
});
});
describe("onKeyDown", () => {
it("does nothing if search type is not goto", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "other"
},
"shallow"
);
wrapper.find("SearchInput").simulate("keydown", {});
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.setQuickOpenQuery).not.toHaveBeenCalled();
});
it("on Tab, close modal", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: ":34:12",
searchType: "goto"
},
"shallow"
);
const event = {
key: "Tab"
};
wrapper.find("SearchInput").simulate("keydown", event);
expect(props.closeQuickOpen).toHaveBeenCalled();
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
});
});
describe("with arrow keys", () => {
it("on ArrowUp, traverse results up with functions", () => {
const sourceId = "sourceId";
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "functions",
selectedSource: { id: sourceId },
selectedContentLoaded: true,
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const event = {
preventDefault: jest.fn(),
key: "ArrowUp"
};
const location = {
start: {
line: 1
},
end: {
line: 3
}
};
wrapper.setState(() => ({
results: [{ id: "0", location }, { id: "1" }, { id: "2" }],
selectedIndex: 1
}));
wrapper.find("SearchInput").simulate("keydown", event);
expect(event.preventDefault).toHaveBeenCalled();
expect(wrapper.state().selectedIndex).toEqual(0);
expect(props.highlightLineRange).toHaveBeenCalledWith({
end: 3,
sourceId: sourceId,
start: 1
});
});
it("on ArrowDown, traverse down with no results", () => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "goto"
},
"shallow"
);
const event = {
preventDefault: jest.fn(),
key: "ArrowDown"
};
wrapper.setState(() => ({
results: null,
selectedIndex: 1
}));
wrapper.find("SearchInput").simulate("keydown", event);
expect(event.preventDefault).toHaveBeenCalled();
expect(wrapper.state().selectedIndex).toEqual(0);
expect(props.selectSpecificLocation).not.toHaveBeenCalledWith();
expect(props.highlightLineRange).not.toHaveBeenCalled();
});
it("on ArrowUp, traverse results up to function with no location", () => {
const sourceId = "sourceId";
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "functions",
selectedSource: { id: sourceId },
selectedContentLoaded: true,
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const event = {
preventDefault: jest.fn(),
key: "ArrowUp"
};
wrapper.setState(() => ({
results: [{ id: "0", location: null }, { id: "1" }, { id: "2" }],
selectedIndex: 1
}));
wrapper.find("SearchInput").simulate("keydown", event);
expect(event.preventDefault).toHaveBeenCalled();
expect(wrapper.state().selectedIndex).toEqual(0);
expect(props.highlightLineRange).toHaveBeenCalledWith({
sourceId: "sourceId"
});
});
it(
"on ArrowDown, traverse down results, without " +
"taking action if no selectedSource",
() => {
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "variables",
selectedSource: null,
selectedContentLoaded: true,
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const event = {
preventDefault: jest.fn(),
key: "ArrowDown"
};
const location = {
start: {
line: 7
}
};
wrapper.setState(() => ({
results: [{ id: "0", location }, { id: "1" }, { id: "2" }],
selectedIndex: 1
}));
wrapper.find("SearchInput").simulate("keydown", event);
expect(event.preventDefault).toHaveBeenCalled();
expect(wrapper.state().selectedIndex).toEqual(2);
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.highlightLineRange).not.toHaveBeenCalled();
}
);
it(
"on ArrowUp, traverse up results, without taking action if " +
"the query is not for variables or functions",
() => {
const sourceId = "sourceId";
const { wrapper, props } = generateModal(
{
enabled: true,
query: "test",
searchType: "other",
selectedSource: { id: sourceId },
selectedContentLoaded: true,
symbols: {
functions: [],
variables: {}
}
},
"shallow"
);
const event = {
preventDefault: jest.fn(),
key: "ArrowUp"
};
const location = {
start: {
line: 7
}
};
wrapper.setState(() => ({
results: [{ id: "0", location }, { id: "1" }, { id: "2" }],
selectedIndex: 1
}));
wrapper.find("SearchInput").simulate("keydown", event);
expect(event.preventDefault).toHaveBeenCalled();
expect(wrapper.state().selectedIndex).toEqual(0);
expect(props.selectSpecificLocation).not.toHaveBeenCalled();
expect(props.highlightLineRange).not.toHaveBeenCalled();
}
);
});
describe("showErrorEmoji", () => {
it("true when no count + query", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "test",
searchType: ""
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
it("false when count + query", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "dasdasdas"
},
"mount"
);
wrapper.setState(() => ({
results: [1, 2]
}));
expect(wrapper).toMatchSnapshot();
});
it("false when no query", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: "",
searchType: ""
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
it("false when goto numeric ':2222'", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: ":2222",
searchType: "goto"
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
it("true when goto not numeric ':22k22'", () => {
const { wrapper } = generateModal(
{
enabled: true,
query: ":22k22",
searchType: "goto"
},
"mount"
);
expect(wrapper).toMatchSnapshot();
});
});
});