summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/components/test/Outline.spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/components/test/Outline.spec.js')
-rw-r--r--devtools/client/debugger/src/components/test/Outline.spec.js302
1 files changed, 302 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/components/test/Outline.spec.js b/devtools/client/debugger/src/components/test/Outline.spec.js
new file mode 100644
index 0000000000..621f0bacfc
--- /dev/null
+++ b/devtools/client/debugger/src/components/test/Outline.spec.js
@@ -0,0 +1,302 @@
+/* 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 <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+
+import React from "react";
+import { shallow } from "enzyme";
+import Outline from "../../components/PrimaryPanes/Outline";
+import { makeSymbolDeclaration } from "../../utils/test-head";
+import { mockcx } from "../../utils/test-mockup";
+import { showMenu } from "../../context-menu/menu";
+import { copyToTheClipboard } from "../../utils/clipboard";
+
+jest.mock("../../context-menu/menu", () => ({ showMenu: jest.fn() }));
+jest.mock("../../utils/clipboard", () => ({ copyToTheClipboard: jest.fn() }));
+
+const sourceId = "id";
+const mockFunctionText = "mock function text";
+
+function generateDefaults(overrides) {
+ return {
+ cx: mockcx,
+ selectLocation: jest.fn(),
+ selectedSource: { id: sourceId },
+ getFunctionText: jest.fn().mockReturnValue(mockFunctionText),
+ flashLineRange: jest.fn(),
+ isHidden: false,
+ symbols: {},
+ selectedLocation: { sourceId },
+ onAlphabetizeClick: jest.fn(),
+ ...overrides,
+ };
+}
+
+function render(overrides = {}) {
+ const props = generateDefaults(overrides);
+ // $FlowIgnore
+ const component = shallow(<Outline.WrappedComponent {...props} />);
+ const instance = component.instance();
+ return { component, props, instance };
+}
+
+describe("Outline", () => {
+ afterEach(() => {
+ (copyToTheClipboard: any).mockClear();
+ showMenu.mockClear();
+ });
+
+ it("renders a list of functions when properties change", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("my_example_function1", 21),
+ makeSymbolDeclaration("my_example_function2", 22),
+ ],
+ };
+
+ const { component } = render({ symbols });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("selects a line of code in the current file on click", async () => {
+ const startLine = 12;
+ const symbols = {
+ functions: [makeSymbolDeclaration("my_example_function", startLine)],
+ };
+
+ const { component, props } = render({ symbols });
+
+ const { selectLocation } = props;
+ const listItem = component.find("li").first();
+ listItem.simulate("click");
+ expect(selectLocation).toHaveBeenCalledWith(mockcx, {
+ line: startLine,
+ sourceId,
+ });
+ });
+
+ describe("renders outline", () => {
+ describe("renders loading", () => {
+ it("if symbols is not defined", () => {
+ const { component } = render({
+ symbols: (null: any),
+ });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("if symbols are loading", () => {
+ const { component } = render({
+ symbols: {
+ loading: true,
+ },
+ });
+ expect(component).toMatchSnapshot();
+ });
+ });
+
+ it("renders ignore anonymous functions", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("my_example_function1", 21),
+ makeSymbolDeclaration("anonymous", 25),
+ ],
+ };
+
+ const { component } = render({ symbols });
+ expect(component).toMatchSnapshot();
+ });
+ describe("renders placeholder", () => {
+ it("`No File Selected` if selectedSource is not defined", async () => {
+ const { component } = render({
+ selectedSource: (null: any),
+ });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("`No functions` if all func are anonymous", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("anonymous", 25),
+ makeSymbolDeclaration("anonymous", 30),
+ ],
+ };
+
+ const { component } = render({ symbols });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("`No functions` if symbols has no func", async () => {
+ const symbols = {
+ functions: [],
+ };
+ const { component } = render({ symbols });
+ expect(component).toMatchSnapshot();
+ });
+ });
+
+ it("sorts functions alphabetically by function name", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("c_function", 25),
+ makeSymbolDeclaration("x_function", 30),
+ makeSymbolDeclaration("a_function", 70),
+ ],
+ };
+
+ const { component } = render({
+ symbols,
+ alphabetizeOutline: true,
+ });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("calls onAlphabetizeClick when sort button is clicked", async () => {
+ const symbols = {
+ functions: [makeSymbolDeclaration("example_function", 25)],
+ };
+
+ const { component, props } = render({ symbols });
+
+ await component
+ .find(".outline-footer")
+ .find("button")
+ .simulate("click", {});
+
+ expect(props.onAlphabetizeClick).toHaveBeenCalled();
+ });
+
+ it("renders functions by function class", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("x_function", 25, 26, "x_klass"),
+ makeSymbolDeclaration("a2_function", 30, 31, "a_klass"),
+ makeSymbolDeclaration("a1_function", 70, 71, "a_klass"),
+ ],
+ classes: [
+ makeSymbolDeclaration("x_klass", 24, 27),
+ makeSymbolDeclaration("a_klass", 29, 72),
+ ],
+ };
+
+ const { component } = render({ symbols });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("renders functions by function class, alphabetically", async () => {
+ const symbols = {
+ functions: [
+ makeSymbolDeclaration("x_function", 25, 26, "x_klass"),
+ makeSymbolDeclaration("a2_function", 30, 31, "a_klass"),
+ makeSymbolDeclaration("a1_function", 70, 71, "a_klass"),
+ ],
+ classes: [
+ makeSymbolDeclaration("x_klass", 24, 27),
+ makeSymbolDeclaration("a_klass", 29, 72),
+ ],
+ };
+
+ const { component } = render({
+ symbols,
+ alphabetizeOutline: true,
+ });
+ expect(component).toMatchSnapshot();
+ });
+
+ it("selects class on click on class headline", async () => {
+ const symbols = {
+ functions: [makeSymbolDeclaration("x_function", 25, 26, "x_klass")],
+ classes: [makeSymbolDeclaration("x_klass", 24, 27)],
+ };
+
+ const { component, props } = render({ symbols });
+
+ await component.find("h2").simulate("click", {});
+
+ expect(props.selectLocation).toHaveBeenCalledWith(mockcx, {
+ line: 24,
+ sourceId,
+ });
+ });
+
+ it("does not select an item if selectedSource is not defined", async () => {
+ const { instance, props } = render({ selectedSource: (null: any) });
+ await instance.selectItem({});
+ expect(props.selectLocation).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("onContextMenu of Outline", () => {
+ it("is called onContextMenu for each item", async () => {
+ const event = { event: "oncontextmenu" };
+ const fn = makeSymbolDeclaration("exmple_function", 2);
+ const symbols = {
+ functions: [fn],
+ };
+
+ const { component, instance } = render({ symbols });
+ instance.onContextMenu = jest.fn(() => {});
+ await component
+ .find(".outline-list__element")
+ .simulate("contextmenu", event);
+
+ expect(instance.onContextMenu).toHaveBeenCalledWith(event, fn);
+ });
+
+ it("does not show menu with no selected source", async () => {
+ const mockEvent = {
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn(),
+ };
+ const { instance } = render({
+ selectedSource: (null: any),
+ });
+ await instance.onContextMenu(mockEvent, {});
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
+ expect(mockEvent.stopPropagation).toHaveBeenCalled();
+ expect(showMenu).not.toHaveBeenCalled();
+ });
+
+ it("shows menu to copy func, copies to clipboard on click", async () => {
+ const startLine = 12;
+ const endLine = 21;
+ const func = makeSymbolDeclaration(
+ "my_example_function",
+ startLine,
+ endLine
+ );
+ const symbols = {
+ functions: [func],
+ };
+ const mockEvent = {
+ preventDefault: jest.fn(),
+ stopPropagation: jest.fn(),
+ };
+ const { instance, props } = render({ symbols });
+ await instance.onContextMenu(mockEvent, func);
+
+ expect(mockEvent.preventDefault).toHaveBeenCalled();
+ expect(mockEvent.stopPropagation).toHaveBeenCalled();
+
+ const expectedMenuOptions = [
+ {
+ accesskey: "F",
+ click: expect.any(Function),
+ disabled: false,
+ id: "node-menu-copy-function",
+ label: "Copy function",
+ },
+ ];
+ expect(props.getFunctionText).toHaveBeenCalledWith(12);
+ expect(showMenu).toHaveBeenCalledWith(mockEvent, expectedMenuOptions);
+
+ showMenu.mock.calls[0][1][0].click();
+ expect(copyToTheClipboard).toHaveBeenCalledWith(mockFunctionText);
+ expect(props.flashLineRange).toHaveBeenCalledWith({
+ end: endLine,
+ sourceId,
+ start: startLine,
+ });
+ });
+ });
+});