/* 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 { shallow } from "enzyme";
import { showMenu } from "devtools-contextmenu";
import SourcesTree from "../SourcesTree";
import { makeMockSource, mockcx } from "../../../utils/test-mockup";
import { copyToTheClipboard } from "../../../utils/clipboard";
jest.mock("devtools-contextmenu", () => ({ showMenu: jest.fn() }));
jest.mock("../../../utils/clipboard", () => ({
copyToTheClipboard: jest.fn()
}));
describe("SourcesTree", () => {
afterEach(() => {
(copyToTheClipboard: any).mockClear();
showMenu.mockClear();
});
it("Should show the tree with nothing expanded", async () => {
const { component } = render();
expect(component).toMatchSnapshot();
});
it("Should show a 'No Sources' message if there are no sources", async () => {
const { component, defaultState } = render();
const sourceTree = defaultState.sourceTree;
sourceTree.contents = [];
component.setState({ sourceTree: sourceTree });
expect(component).toMatchSnapshot();
});
describe("When loading initial source", () => {
it("Shows the tree with one.js, two.js and three.js expanded", async () => {
const { component, props } = render();
await component.setProps({
...props,
expanded: ["one.js", "two.js", "three.js"]
});
expect(component).toMatchSnapshot();
});
});
describe("After changing expanded nodes", () => {
it("Shows the tree with four.js, five.js and six.js expanded", async () => {
const { component, props } = render();
await component.setProps({
...props,
expanded: ["four.js", "five.js", "six.js"]
});
expect(component).toMatchSnapshot();
});
});
describe("on receiving new props", () => {
describe("recreates tree", () => {
it("does not recreate tree if no new source is added", async () => {
const { component, props, defaultState } = render();
const sources = {
"server1.conn13.child1/41": createMockSource(
"server1.conn13.child1/41",
"http://mdn.com/three.js"
)
};
await component.setProps({ ...props, sources });
expect(component.state("uncollapsedTree")).toEqual(
defaultState.uncollapsedTree
);
});
it("updates tree with a new item", async () => {
const { component, props } = render();
const newSource = createMockSource(
"server1.conn13.child1/43",
"http://mdn.com/four.js",
true,
""
);
const newThreadSources = {
...props.sources.FakeThread,
"server1.conn13.child1/43": newSource
};
await component.setProps({
...props,
sources: newThreadSources
});
expect(
component.state("uncollapsedTree").contents[0].contents[0].contents
).toHaveLength(5);
});
it("updates sources if sources are emptied", async () => {
const { component, props, defaultState } = render();
expect(defaultState.uncollapsedTree.contents).toHaveLength(1);
await component.setProps({
...props,
sources: {},
sourceCount: 0
});
expect(component.state("uncollapsedTree").contents).toHaveLength(0);
});
it("recreates tree if projectRoot is changed", async () => {
const { component, props, defaultState } = render();
const sources = {
FakeThread: {
"server1.conn13.child1/41": createMockSource(
"server1.conn13.child1/41",
"http://mozilla.com/three.js"
)
}
};
expect(
defaultState.uncollapsedTree.contents[0].contents[0].contents
).toHaveLength(5);
await component.setProps({
...props,
sources,
projectRoot: "mozilla"
});
expect(
component.state("uncollapsedTree").contents[0].contents
).toHaveLength(1);
});
it("recreates tree if debuggeeUrl is changed", async () => {
const { component, props, defaultState } = render();
const sources = {
FakeThread: {
"server1.conn13.child1/41": createMockSource(
"server1.conn13.child1/41",
"http://mdn.com/three.js",
true,
""
)
}
};
expect(
defaultState.uncollapsedTree.contents[0].contents[0].contents
).toHaveLength(5);
await component.setProps({
...props,
debuggeeUrl: "mozilla",
sources
});
expect(
component.state("uncollapsedTree").contents[0].contents
).toHaveLength(1);
});
});
describe("updates threads", () => {
it("adds sources to the correct thread", async () => {
const { component, props } = render();
const newSource = createMockSource(
"server1.conn13.child1/43",
"http://mdn.com/four.js",
true,
""
);
const newThreadSources = {
FakeThread1: {
"server1.conn13.child1/43": newSource
}
};
expect(component.state("uncollapsedTree").contents[0].name).toEqual(
"FakeThread"
);
expect(
component.state("uncollapsedTree").contents[0].contents[0].contents
).toHaveLength(5);
expect(component.state("uncollapsedTree").contents[1]).toEqual(
undefined
);
await component.setProps({
...props,
sources: {
...props.sources,
...newThreadSources
}
});
expect(component.state("uncollapsedTree").contents[0].name).toEqual(
"FakeThread"
);
expect(
component.state("uncollapsedTree").contents[0].contents[0].contents
).toHaveLength(5);
expect(component.state("uncollapsedTree").contents[1].name).toEqual(
"FakeThread1"
);
expect(
component.state("uncollapsedTree").contents[1].contents[0].contents
).toHaveLength(1);
});
});
describe("updates highlighted items", () => {
it("updates highlightItems if selectedSource changes", async () => {
const { component, props } = render();
const mockSource = createMockSource(
"server1.conn13.child1/41",
"http://mdn.com/three.js",
false,
null
);
await component.setProps({
...props,
selectedSource: mockSource
});
expect(component).toMatchSnapshot();
});
});
});
describe("activateItem", () => {
it("select activated item", async () => {
const { instance, props } = render();
const item = createMockItem();
const spy = jest.spyOn(instance, "selectItem");
instance.onActivate(item);
expect(spy).toHaveBeenCalledWith(item);
expect(props.selectSource).toHaveBeenCalledWith(
mockcx,
"server1.conn13.child1/39"
);
});
});
describe("selectItem", () => {
it("should select item with no children", async () => {
const { instance, props } = render();
instance.selectItem(createMockItem());
expect(props.selectSource).toHaveBeenCalledWith(
mockcx,
"server1.conn13.child1/39"
);
});
it("should not select item with children", async () => {
const { props, instance } = render();
instance.selectItem(createMockDirectory());
expect(props.selectSource).not.toHaveBeenCalled();
});
});
describe("handles items", () => {
it("getChildren from directory", async () => {
const { component } = render();
const item = createMockDirectory("http://mdn.com/views", "views", [
"a",
"b"
]);
const children = component
.find("ManagedTree")
.props()
.getChildren(item);
expect(children).toEqual(["a", "b"]);
});
it("getChildren from non directory", async () => {
const { component } = render();
const children = component
.find("ManagedTree")
.props()
.getChildren(createMockItem());
expect(children).toEqual([]);
});
it("onExpand", async () => {
const { component, props } = render();
const expandedState = ["x", "y"];
await component
.find("ManagedTree")
.props()
.onExpand({}, expandedState);
expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
});
it("onCollapse", async () => {
const { component, props } = render();
const expandedState = ["y", "z"];
await component
.find("ManagedTree")
.props()
.onCollapse({}, expandedState);
expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
});
it("getParent", async () => {
const { component } = render();
const item = component.state("sourceTree").contents[0].contents[0];
const parent = component
.find("ManagedTree")
.props()
.getParent(item);
expect(parent.path).toEqual("FakeThread");
expect(parent.contents[0].contents).toHaveLength(5);
});
});
describe("getPath", () => {
it("should return path for item", async () => {
const { instance } = render();
const path = instance.getPath(createMockItem());
expect(path).toEqual("http://mdn.com/one.js/server1.conn13.child1/39/");
});
it("should return path for generated item", async () => {
const { instance } = render();
const pathOriginal = instance.getPath(
createMockItem("http://mdn.com/four.js", "four.js", {
id: "server1.conn13.child1/42/originalSource-sha"
})
);
expect(pathOriginal).toEqual(
"http://mdn.com/four.js/server1.conn13.child1/42/originalSource-sha/"
);
const pathGenerated = instance.getPath(
createMockItem("http://mdn.com/four.js", "four.js", {
id: "server1.conn13.child1/42"
})
);
expect(pathGenerated).toEqual(
"http://mdn.com/four.js/server1.conn13.child1/42/"
);
});
});
});
function generateDefaults(overrides: Object) {
const defaultSources = {
FakeThread: {
"server1.conn13.child1/39": createMockSource(
"server1.conn13.child1/39",
"http://mdn.com/one.js",
false,
null
),
"server1.conn13.child1/40": createMockSource(
"server1.conn13.child1/40",
"http://mdn.com/two.js",
false,
null
),
"server1.conn13.child1/41": createMockSource(
"server1.conn13.child1/41",
"http://mdn.com/three.js",
false,
null
),
"server1.conn13.child1/42/originalSource-sha": createMockSource(
"server1.conn13.child1/42/originalSource-sha",
"http://mdn.com/four.js",
false,
null
),
"server1.conn13.child1/42": createMockSource(
"server1.conn13.child1/42",
"http://mdn.com/four.js",
false,
"data:application/json?charset=utf?dsffewrsf"
)
}
};
return {
cx: mockcx,
autoExpandAll: true,
selectSource: jest.fn(),
setExpandedState: jest.fn(),
sources: defaultSources,
debuggeeUrl: "http://mdn.com",
clearProjectDirectoryRoot: jest.fn(),
setProjectDirectoryRoot: jest.fn(),
focusItem: jest.fn(),
projectRoot: "",
threads: [
{
name: "FakeThread",
actor: "FakeThread"
},
{
name: "FakeThread1",
actor: "FakeThread1"
}
],
...overrides
};
}
function render(overrides = {}) {
const props = generateDefaults(overrides);
// $FlowIgnore
const component = shallow();
const defaultState = component.state();
const instance = component.instance();
instance.shouldComponentUpdate = () => true;
return { component, props, defaultState, instance };
}
function createMockSource(id, url, isBlackBoxed = false, sourceMapURL = null) {
return {
...makeMockSource(url, id),
isBlackBoxed,
sourceMapURL
};
}
function createMockDirectory(path = "folder/", name = "folder", contents = []) {
return {
type: "directory",
name,
path,
contents
};
}
function createMockItem(
path = "http://mdn.com/one.js",
name = "one.js",
contents = { id: "server1.conn13.child1/39" }
) {
return {
type: "source",
name,
path,
contents
};
}