зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1460971 - Update Debugger Frontend v52. r=jdescottes
MozReview-Commit-ID: J3gIzebCWre
This commit is contained in:
Родитель
922e46b318
Коммит
0da2193389
|
@ -1,9 +1,9 @@
|
|||
This is the debugger.html project output.
|
||||
See https://github.com/devtools-html/debugger.html
|
||||
|
||||
Version 50
|
||||
Version 52
|
||||
|
||||
Comparison: https://github.com/devtools-html/debugger.html/compare/release-49...release-50
|
||||
Comparison: https://github.com/devtools-html/debugger.html/compare/release-51...release-52
|
||||
|
||||
Packages:
|
||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.2
|
||||
|
|
|
@ -973,16 +973,17 @@ img.close::before {
|
|||
|
||||
.search-field {
|
||||
position: relative;
|
||||
|
||||
height: 27px;
|
||||
width: calc(100% - 1px);
|
||||
background-color: var(--theme-toolbar-background);
|
||||
border-bottom: 1px solid var(--theme-splitter-color);
|
||||
padding: 5px 10px;
|
||||
padding-right: 10px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search-field.big {
|
||||
padding: 5px 10px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
|
@ -993,23 +994,27 @@ img.close::before {
|
|||
}
|
||||
|
||||
.search-field i svg {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.search-field.big i svg {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.search-field.big input {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.search-field input {
|
||||
position: relative;
|
||||
margin-left: 30px;
|
||||
border: none;
|
||||
line-height: 30px;
|
||||
background-color: var(--theme-toolbar-background);
|
||||
color: var(--theme-body-color-active);
|
||||
width: calc(100% - 38px);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-field.big input {
|
||||
position: relative;
|
||||
margin-left: 30px;
|
||||
font-size: 14px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.search-field input:focus {
|
||||
|
@ -1022,22 +1027,14 @@ img.close::before {
|
|||
|
||||
.search-field i.magnifying-glass,
|
||||
.search-field i.sad-face {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
padding: 2px 0;
|
||||
padding: 6px;
|
||||
width: 24px;
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
.search-field i.sad-face {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
.search-field.big i.magnifying-glass,
|
||||
.search-field.big i.sad-face {
|
||||
padding: 14px;
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.search-field .magnifying-glass path,
|
||||
|
@ -1053,16 +1050,16 @@ img.close::before {
|
|||
color: var(--theme-highlight-orange);
|
||||
}
|
||||
|
||||
.search-field.big .summary {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.search-field .summary {
|
||||
line-height: 27px;
|
||||
padding-right: 10px;
|
||||
color: var(--theme-body-color-inactive);
|
||||
}
|
||||
|
||||
.search-field.big .summary {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.search-field .search-nav-buttons {
|
||||
display: flex;
|
||||
user-select: none;
|
||||
|
@ -1869,7 +1866,7 @@ html .toggle-button.end.vertical svg {
|
|||
}
|
||||
|
||||
.search-field .close-btn {
|
||||
margin-top: 12px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.search-bottom-bar * {
|
||||
|
@ -2918,6 +2915,7 @@ debug-expression-error {
|
|||
color: var(--theme-content-color1);
|
||||
position: relative;
|
||||
transition: all 0.25s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.breakpoints-list .breakpoint-heading,
|
||||
|
@ -3087,7 +3085,7 @@ html[dir="rtl"] .breakpoints-list .breakpoint .breakpoint-line {
|
|||
}
|
||||
|
||||
.CodeMirror.cm-s-mozilla-breakpoint {
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.CodeMirror.cm-s-mozilla-breakpoint .CodeMirror-lines {
|
||||
|
|
|
@ -24745,6 +24745,10 @@ const inExpression = (parent, grandParent) => inStepExpression(parent) || t.isJS
|
|||
|
||||
const isExport = node => t.isExportNamedDeclaration(node) || t.isExportDefaultDeclaration(node);
|
||||
|
||||
function getStartLine(node) {
|
||||
return node.loc.start.line;
|
||||
}
|
||||
|
||||
function getPausePoints(sourceId) {
|
||||
const state = {};
|
||||
(0, _ast.traverseAst)(sourceId, { enter: onEnter }, state);
|
||||
|
@ -24781,10 +24785,12 @@ function onEnter(node, ancestors, state) {
|
|||
}
|
||||
|
||||
if (isReturn(node)) {
|
||||
// We do not want to pause at the return and the call e.g. return foo()
|
||||
if (isCall(node.argument)) {
|
||||
// We do not want to pause at the return if the
|
||||
// argument is a call on the same line e.g. return foo()
|
||||
if (isCall(node.argument) && getStartLine(node) == getStartLine(node.argument)) {
|
||||
return addEmptyPoint(state, startLocation);
|
||||
}
|
||||
|
||||
return addStopPoint(state, startLocation);
|
||||
}
|
||||
|
||||
|
|
|
@ -112,15 +112,16 @@ function setPausePoints(sourceId) {
|
|||
return;
|
||||
}
|
||||
|
||||
const pausePoints = await (0, _parser.getPausePoints)(source.id);
|
||||
const pausePoints = await (0, _parser.getPausePoints)(sourceId);
|
||||
|
||||
if ((0, _devtoolsSourceMap.isGeneratedId)(source.id)) {
|
||||
await client.setPausePoints(source.id, pausePoints);
|
||||
if ((0, _devtoolsSourceMap.isGeneratedId)(sourceId)) {
|
||||
await client.setPausePoints(sourceId, pausePoints);
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: "SET_PAUSE_POINTS",
|
||||
source: source.toJS(),
|
||||
sourceText: source.text,
|
||||
sourceId,
|
||||
pausePoints
|
||||
});
|
||||
};
|
||||
|
|
|
@ -18,14 +18,13 @@ exports.breakOnNext = breakOnNext;
|
|||
* @static
|
||||
*/
|
||||
function breakOnNext() {
|
||||
return ({
|
||||
return async ({
|
||||
dispatch,
|
||||
client
|
||||
}) => {
|
||||
client.breakOnNext();
|
||||
await client.breakOnNext();
|
||||
return dispatch({
|
||||
type: "BREAK_ON_NEXT",
|
||||
value: true
|
||||
type: "BREAK_ON_NEXT"
|
||||
});
|
||||
};
|
||||
}
|
|
@ -142,6 +142,15 @@ Object.defineProperty(exports, "pauseOnExceptions", {
|
|||
}
|
||||
});
|
||||
|
||||
var _selectComponent = require("./selectComponent");
|
||||
|
||||
Object.defineProperty(exports, "selectComponent", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _selectComponent.selectComponent;
|
||||
}
|
||||
});
|
||||
|
||||
var _selectFrame = require("./selectFrame");
|
||||
|
||||
Object.defineProperty(exports, "selectFrame", {
|
||||
|
|
|
@ -19,6 +19,7 @@ DevToolsModules(
|
|||
'paused.js',
|
||||
'pauseOnExceptions.js',
|
||||
'resumed.js',
|
||||
'selectComponent.js',
|
||||
'selectFrame.js',
|
||||
'setPopupObjectProperties.js',
|
||||
'skipPausing.js',
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.selectComponent = selectComponent;
|
||||
|
||||
/* 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/>. */
|
||||
function selectComponent(componentIndex) {
|
||||
return async ({
|
||||
dispatch
|
||||
}) => {
|
||||
dispatch({
|
||||
type: "SELECT_COMPONENT",
|
||||
componentIndex
|
||||
});
|
||||
};
|
||||
}
|
|
@ -118,7 +118,7 @@ function selectLocation(location) {
|
|||
}
|
||||
|
||||
const source = sourceRecord.toJS();
|
||||
dispatch((0, _tabs.addTab)(source, 0));
|
||||
dispatch((0, _tabs.addTab)(source.url, 0));
|
||||
dispatch({
|
||||
type: "SELECT_SOURCE",
|
||||
source,
|
||||
|
|
|
@ -22,10 +22,10 @@ var _selectors = require("../../selectors/index");
|
|||
* Redux actions for the sources state
|
||||
* @module actions/sources
|
||||
*/
|
||||
function addTab(source, tabIndex) {
|
||||
function addTab(url, tabIndex) {
|
||||
return {
|
||||
type: "ADD_TAB",
|
||||
source,
|
||||
url,
|
||||
tabIndex
|
||||
};
|
||||
}
|
||||
|
|
|
@ -436,7 +436,7 @@ class QuickOpenModal extends _react.Component {
|
|||
return _react2.default.createElement(_Modal2.default, {
|
||||
"in": enabled,
|
||||
handleClose: this.closeModal
|
||||
}, _react2.default.createElement(_SearchInput2.default, {
|
||||
}, _react2.default.createElement(_SearchInput2.default, _extends({
|
||||
query: query,
|
||||
hasPrefix: true,
|
||||
count: this.getResultCount(),
|
||||
|
@ -447,8 +447,11 @@ class QuickOpenModal extends _react.Component {
|
|||
onKeyDown: this.onKeyDown,
|
||||
handleClose: this.closeModal,
|
||||
expanded: expanded,
|
||||
showClose: false,
|
||||
selectedItemId: expanded && items[selectedIndex] ? items[selectedIndex].id : ""
|
||||
}), this.renderLoading(), newResults && _react2.default.createElement(_ResultList2.default, _extends({
|
||||
}, this.isSourceSearch() ? {
|
||||
size: "big"
|
||||
} : {})), this.renderLoading(), newResults && _react2.default.createElement(_ResultList2.default, _extends({
|
||||
key: "results",
|
||||
items: items,
|
||||
selected: selectedIndex,
|
||||
|
|
|
@ -14,6 +14,10 @@ var _actions = require("../../actions/index");
|
|||
|
||||
var _actions2 = _interopRequireDefault(_actions);
|
||||
|
||||
var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
|
||||
|
||||
var _classnames2 = _interopRequireDefault(_classnames);
|
||||
|
||||
var _selectors = require("../../selectors/index");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
@ -22,6 +26,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|||
* 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/>. */
|
||||
class ReactComponentStack extends _react.PureComponent {
|
||||
onMouseDown(e, componentIndex) {
|
||||
if (e.nativeEvent.which == 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.selectComponent(componentIndex);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
componentStack
|
||||
|
@ -29,14 +41,19 @@ class ReactComponentStack extends _react.PureComponent {
|
|||
return _react2.default.createElement("div", {
|
||||
className: "pane frames"
|
||||
}, _react2.default.createElement("ul", null, componentStack.slice().reverse().map((component, index) => _react2.default.createElement("li", {
|
||||
key: index
|
||||
className: (0, _classnames2.default)("frame", {
|
||||
selected: this.props.selectedComponentIndex === index
|
||||
}),
|
||||
key: index,
|
||||
onMouseDown: e => this.onMouseDown(e, index)
|
||||
}, component))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
extra: (0, _selectors.getExtra)(state)
|
||||
extra: (0, _selectors.getExtra)(state),
|
||||
selectedComponentIndex: (0, _selectors.getSelectedComponentIndex)(state)
|
||||
});
|
||||
|
||||
exports.default = (0, _reactRedux.connect)(mapStateToProps, _actions2.default)(ReactComponentStack);
|
|
@ -8,7 +8,6 @@ DIRS += [
|
|||
'PrimaryPanes',
|
||||
'SecondaryPanes',
|
||||
'shared',
|
||||
'test',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
|
|
|
@ -141,7 +141,8 @@ class SearchInput extends _react.Component {
|
|||
selectedItemId,
|
||||
showErrorEmoji,
|
||||
size,
|
||||
summaryMsg
|
||||
summaryMsg,
|
||||
showClose
|
||||
} = this.props;
|
||||
const inputProps = {
|
||||
className: (0, _classnames2.default)({
|
||||
|
@ -172,7 +173,7 @@ class SearchInput extends _react.Component {
|
|||
"aria-expanded": expanded
|
||||
}, this.renderSvg(), _react2.default.createElement("input", inputProps), summaryMsg && _react2.default.createElement("div", {
|
||||
className: "summary"
|
||||
}, summaryMsg), this.renderNav(), _react2.default.createElement(_Close2.default, {
|
||||
}, summaryMsg), this.renderNav(), showClose && _react2.default.createElement(_Close2.default, {
|
||||
handleClick: handleClose,
|
||||
buttonClass: size
|
||||
})));
|
||||
|
@ -184,6 +185,7 @@ SearchInput.defaultProps = {
|
|||
expanded: false,
|
||||
hasPrefix: false,
|
||||
selectedItemId: "",
|
||||
size: ""
|
||||
size: "",
|
||||
showClose: true
|
||||
};
|
||||
exports.default = SearchInput;
|
|
@ -1,316 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _Outline = require("../../components/PrimaryPanes/Outline");
|
||||
|
||||
var _Outline2 = _interopRequireDefault(_Outline);
|
||||
|
||||
var _testHead = require("../../utils/test-head");
|
||||
|
||||
var _devtoolsContextmenu = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-contextmenu"];
|
||||
|
||||
var _clipboard = require("../../utils/clipboard");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
jest.mock("devtools-contextmenu", () => ({
|
||||
showMenu: jest.fn()
|
||||
}));
|
||||
jest.mock("../../utils/clipboard", () => ({
|
||||
copyToTheClipboard: jest.fn()
|
||||
}));
|
||||
const sourceId = "id";
|
||||
const mockFunctionText = "mock function text";
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
return _objectSpread({
|
||||
selectLocation: jest.genMockFunction(),
|
||||
selectedSource: {
|
||||
get: () => sourceId
|
||||
},
|
||||
getFunctionText: jest.fn().mockReturnValue(mockFunctionText),
|
||||
flashLineRange: jest.fn(),
|
||||
isHidden: false,
|
||||
symbols: {},
|
||||
selectedLocation: {
|
||||
sourceId: sourceId
|
||||
},
|
||||
onAlphabetizeClick: jest.fn()
|
||||
}, overrides);
|
||||
}
|
||||
|
||||
function render(overrides = {}) {
|
||||
const props = generateDefaults(overrides);
|
||||
const component = (0, _enzyme.shallow)(_react2.default.createElement(_Outline2.default.WrappedComponent, props));
|
||||
const instance = component.instance();
|
||||
return {
|
||||
component,
|
||||
props,
|
||||
instance
|
||||
};
|
||||
}
|
||||
|
||||
describe("Outline", () => {
|
||||
afterEach(() => {
|
||||
_clipboard.copyToTheClipboard.mockClear();
|
||||
|
||||
_devtoolsContextmenu.showMenu.mockClear();
|
||||
});
|
||||
it("renders a list of functions when properties change", async () => {
|
||||
const symbols = {
|
||||
functions: [(0, _testHead.makeSymbolDeclaration)("my_example_function1", 21), (0, _testHead.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: [(0, _testHead.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({
|
||||
line: startLine,
|
||||
sourceId
|
||||
});
|
||||
});
|
||||
describe("renders outline", () => {
|
||||
describe("renders loading", () => {
|
||||
it("if symbols is not defined", () => {
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
symbols: null
|
||||
});
|
||||
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: [(0, _testHead.makeSymbolDeclaration)("my_example_function1", 21), (0, _testHead.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
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("`No functions` if all func are anonymous", async () => {
|
||||
const symbols = {
|
||||
functions: [(0, _testHead.makeSymbolDeclaration)("anonymous", 25), (0, _testHead.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: [(0, _testHead.makeSymbolDeclaration)("c_function", 25), (0, _testHead.makeSymbolDeclaration)("x_function", 30), (0, _testHead.makeSymbolDeclaration)("a_function", 70)]
|
||||
};
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
symbols: symbols,
|
||||
alphabetizeOutline: true
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("calls onAlphabetizeClick when sort button is clicked", async () => {
|
||||
const symbols = {
|
||||
functions: [(0, _testHead.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: [(0, _testHead.makeSymbolDeclaration)("x_function", 25, 26, "x_klass"), (0, _testHead.makeSymbolDeclaration)("a2_function", 30, 31, "a_klass"), (0, _testHead.makeSymbolDeclaration)("a1_function", 70, 71, "a_klass")],
|
||||
classes: [(0, _testHead.makeSymbolDeclaration)("x_klass", 24, 27), (0, _testHead.makeSymbolDeclaration)("a_klass", 29, 72)]
|
||||
};
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
symbols: symbols
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("renders functions by function class, alphabetically", async () => {
|
||||
const symbols = {
|
||||
functions: [(0, _testHead.makeSymbolDeclaration)("x_function", 25, 26, "x_klass"), (0, _testHead.makeSymbolDeclaration)("a2_function", 30, 31, "a_klass"), (0, _testHead.makeSymbolDeclaration)("a1_function", 70, 71, "a_klass")],
|
||||
classes: [(0, _testHead.makeSymbolDeclaration)("x_klass", 24, 27), (0, _testHead.makeSymbolDeclaration)("a_klass", 29, 72)]
|
||||
};
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
symbols: symbols,
|
||||
alphabetizeOutline: true
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("selects class on click on class headline", async () => {
|
||||
const symbols = {
|
||||
functions: [(0, _testHead.makeSymbolDeclaration)("x_function", 25, 26, "x_klass")],
|
||||
classes: [(0, _testHead.makeSymbolDeclaration)("x_klass", 24, 27)]
|
||||
};
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render({
|
||||
symbols: symbols
|
||||
});
|
||||
await component.find("h2").simulate("click", {});
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
line: 24,
|
||||
sourceId: sourceId
|
||||
});
|
||||
});
|
||||
it("does not select an item if selectedSource is not defined", async () => {
|
||||
const {
|
||||
instance,
|
||||
props
|
||||
} = render({
|
||||
selectedSource: null
|
||||
});
|
||||
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 = (0, _testHead.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
|
||||
});
|
||||
await instance.onContextMenu(mockEvent, {});
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
expect(_devtoolsContextmenu.showMenu).not.toHaveBeenCalled();
|
||||
});
|
||||
it("shows menu to copy func, copies to clipboard on click", async () => {
|
||||
const startLine = 12;
|
||||
const endLine = 21;
|
||||
const func = (0, _testHead.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(_devtoolsContextmenu.showMenu).toHaveBeenCalledWith(mockEvent, expectedMenuOptions);
|
||||
|
||||
_devtoolsContextmenu.showMenu.mock.calls[0][1][0].click();
|
||||
|
||||
expect(_clipboard.copyToTheClipboard).toHaveBeenCalledWith(mockFunctionText);
|
||||
expect(props.flashLineRange).toHaveBeenCalledWith({
|
||||
end: endLine,
|
||||
sourceId: sourceId,
|
||||
start: startLine
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,248 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _immutable = require("devtools/client/shared/vendor/immutable");
|
||||
|
||||
var _ProjectSearch = require("../ProjectSearch");
|
||||
|
||||
var _projectTextSearch = require("../../reducers/project-text-search");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
const hooks = {
|
||||
on: [],
|
||||
off: []
|
||||
};
|
||||
const shortcuts = {
|
||||
dispatch(eventName) {
|
||||
hooks.on.forEach(hook => {
|
||||
if (hook.event === eventName) {
|
||||
hook.cb();
|
||||
}
|
||||
});
|
||||
hooks.off.forEach(hook => {
|
||||
if (hook.event === eventName) {
|
||||
hook.cb();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
on: jest.fn((event, cb) => hooks.on.push({
|
||||
event,
|
||||
cb
|
||||
})),
|
||||
off: jest.fn((event, cb) => hooks.off.push({
|
||||
event,
|
||||
cb
|
||||
}))
|
||||
};
|
||||
const context = {
|
||||
shortcuts
|
||||
};
|
||||
const testResults = (0, _immutable.List)([{
|
||||
filepath: "testFilePath1",
|
||||
matches: [{
|
||||
match: "match1",
|
||||
value: "some thing match1",
|
||||
column: 30
|
||||
}, {
|
||||
match: "match2",
|
||||
value: "some thing match2",
|
||||
column: 60
|
||||
}, {
|
||||
match: "match3",
|
||||
value: "some thing match3",
|
||||
column: 90
|
||||
}]
|
||||
}, {
|
||||
filepath: "testFilePath2",
|
||||
matches: [{
|
||||
match: "match4",
|
||||
value: "some thing match4",
|
||||
column: 80
|
||||
}, {
|
||||
match: "match5",
|
||||
value: "some thing match5",
|
||||
column: 40
|
||||
}]
|
||||
}]);
|
||||
const testMatch = {
|
||||
match: "match1",
|
||||
value: "some thing match1",
|
||||
column: 30
|
||||
};
|
||||
|
||||
function render(overrides = {}, mounted = false) {
|
||||
const props = _objectSpread({
|
||||
status: "DONE",
|
||||
sources: {},
|
||||
results: (0, _immutable.List)([]),
|
||||
query: "foo",
|
||||
activeSearch: "project",
|
||||
closeProjectSearch: jest.fn(),
|
||||
searchSources: jest.fn(),
|
||||
clearSearch: jest.fn(),
|
||||
updateSearchStatus: jest.fn(),
|
||||
selectLocation: jest.fn(),
|
||||
doSearchForHighlight: jest.fn()
|
||||
}, overrides);
|
||||
|
||||
return mounted ? (0, _enzyme.mount)(_react2.default.createElement(_ProjectSearch.ProjectSearch, props), {
|
||||
context
|
||||
}) : (0, _enzyme.shallow)(_react2.default.createElement(_ProjectSearch.ProjectSearch, props), {
|
||||
context
|
||||
});
|
||||
}
|
||||
|
||||
describe("ProjectSearch", () => {
|
||||
beforeEach(() => {
|
||||
context.shortcuts.on.mockClear();
|
||||
context.shortcuts.off.mockClear();
|
||||
});
|
||||
it("renders nothing when disabled", () => {
|
||||
const component = render({
|
||||
activeSearch: null
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("where <Enter> has not been pressed", () => {
|
||||
const component = render({
|
||||
query: ""
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("found no search results", () => {
|
||||
const component = render();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("should display loading message while search is in progress", () => {
|
||||
const component = render({
|
||||
query: "match",
|
||||
status: _projectTextSearch.statusType.fetching
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("found search results", () => {
|
||||
const component = render({
|
||||
query: "match",
|
||||
results: testResults
|
||||
}, true);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("turns off shortcuts on unmount", () => {
|
||||
const component = render({
|
||||
query: ""
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
component.unmount();
|
||||
expect(context.shortcuts.off).toHaveBeenCalled();
|
||||
});
|
||||
it("calls inputOnChange", () => {
|
||||
const component = render({
|
||||
results: testResults
|
||||
}, true);
|
||||
component.find("SearchInput input").simulate("change", {
|
||||
target: {
|
||||
value: "bar"
|
||||
}
|
||||
});
|
||||
expect(component.state().inputValue).toEqual("bar");
|
||||
});
|
||||
it("onKeyDown Escape/Other", () => {
|
||||
const searchSources = jest.fn();
|
||||
const component = render({
|
||||
results: testResults,
|
||||
searchSources
|
||||
}, true);
|
||||
component.find("SearchInput input").simulate("keydown", {
|
||||
key: "Escape"
|
||||
});
|
||||
expect(searchSources).not.toHaveBeenCalled();
|
||||
searchSources.mockClear();
|
||||
component.find("SearchInput input").simulate("keydown", {
|
||||
key: "Other",
|
||||
stopPropagation: jest.fn()
|
||||
});
|
||||
expect(searchSources).not.toHaveBeenCalled();
|
||||
});
|
||||
it("onKeyDown Enter", () => {
|
||||
const searchSources = jest.fn();
|
||||
const component = render({
|
||||
results: testResults,
|
||||
searchSources
|
||||
}, true);
|
||||
component.find("SearchInput input").simulate("keydown", {
|
||||
key: "Enter",
|
||||
stopPropagation: jest.fn()
|
||||
});
|
||||
expect(searchSources).toHaveBeenCalledWith("foo");
|
||||
});
|
||||
it("onEnterPress shortcut no match or setExpanded", () => {
|
||||
const selectLocation = jest.fn();
|
||||
const component = render({
|
||||
results: testResults,
|
||||
selectLocation
|
||||
}, true);
|
||||
component.instance().focusedItem = {};
|
||||
shortcuts.dispatch("Enter");
|
||||
expect(selectLocation).not.toHaveBeenCalled();
|
||||
});
|
||||
it("onEnterPress shortcut match", () => {
|
||||
const selectLocation = jest.fn();
|
||||
const component = render({
|
||||
results: testResults,
|
||||
selectLocation
|
||||
}, true);
|
||||
component.instance().focusedItem = {
|
||||
match: testMatch
|
||||
};
|
||||
shortcuts.dispatch("Enter");
|
||||
expect(selectLocation).toHaveBeenCalledWith(testMatch);
|
||||
});
|
||||
it("onEnterPress shortcut setExpanded", () => {
|
||||
const selectLocation = jest.fn();
|
||||
const component = render({
|
||||
results: testResults,
|
||||
selectLocation
|
||||
}, true);
|
||||
const setExpanded = jest.fn();
|
||||
const testFile = {
|
||||
filepath: "testFilePath1",
|
||||
matches: [testMatch]
|
||||
};
|
||||
component.instance().focusedItem = {
|
||||
setExpanded,
|
||||
file: testFile,
|
||||
expanded: true
|
||||
};
|
||||
shortcuts.dispatch("Enter");
|
||||
expect(setExpanded).toHaveBeenCalledWith(testFile, false);
|
||||
});
|
||||
describe("showErrorEmoji", () => {
|
||||
it("false if not done & results", () => {
|
||||
const component = render({
|
||||
status: _projectTextSearch.statusType.fetching,
|
||||
results: testResults
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("false if not done & no results", () => {
|
||||
const component = render({
|
||||
status: _projectTextSearch.statusType.fetching
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
}); // "false if done & has results"
|
||||
// is the same test as "found search results"
|
||||
// "true if done & has no results"
|
||||
// is the same test as "found no search results"
|
||||
});
|
||||
});
|
|
@ -1,277 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _QuickOpenModal = require("../QuickOpenModal");
|
||||
|
||||
var _fuzzaldrinPlus = require("devtools/client/debugger/new/dist/vendors").vendored["fuzzaldrin-plus"];
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
jest.mock("fuzzaldrin-plus");
|
||||
|
||||
function generateModal(propOverrides, renderType = "shallow") {
|
||||
const props = _objectSpread({
|
||||
enabled: false,
|
||||
query: "",
|
||||
searchType: "sources",
|
||||
sources: [],
|
||||
tabs: [],
|
||||
selectLocation: jest.fn(),
|
||||
setQuickOpenQuery: jest.fn(),
|
||||
highlightLineRange: jest.fn(),
|
||||
clearHighlightLineRange: jest.fn(),
|
||||
closeQuickOpen: jest.fn()
|
||||
}, propOverrides);
|
||||
|
||||
return {
|
||||
wrapper: renderType === "shallow" ? (0, _enzyme.shallow)(_react2.default.createElement(_QuickOpenModal.QuickOpenModal, props)) : (0, _enzyme.mount)(_react2.default.createElement(_QuickOpenModal.QuickOpenModal, props)),
|
||||
props
|
||||
};
|
||||
}
|
||||
|
||||
describe("QuickOpenModal", () => {
|
||||
beforeEach(() => {
|
||||
_fuzzaldrinPlus.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("Ensure anonymous functions do not render in QuickOpenModal", () => {
|
||||
const {
|
||||
wrapper
|
||||
} = generateModal({
|
||||
enabled: true,
|
||||
query: "@",
|
||||
searchType: "functions",
|
||||
symbols: {
|
||||
functions: [{
|
||||
title: "anonymous"
|
||||
}, {
|
||||
title: "c"
|
||||
}, {
|
||||
title: "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(_fuzzaldrinPlus.filter).toHaveBeenCalledWith([], "somefil", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
});
|
||||
});
|
||||
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(_fuzzaldrinPlus.filter).toHaveBeenCalledWith([], "somefil", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
});
|
||||
});
|
||||
test("basic symbol seach", () => {
|
||||
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: {
|
||||
get: jest.fn(() => true)
|
||||
}
|
||||
}, "mount");
|
||||
wrapper.find("input").simulate("change", {
|
||||
target: {
|
||||
value: "@someFunc"
|
||||
}
|
||||
});
|
||||
expect(_fuzzaldrinPlus.filter).toHaveBeenCalledWith([], "someFunc", {
|
||||
key: "value",
|
||||
maxResults: 1000
|
||||
});
|
||||
});
|
||||
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("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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _ShortcutsModal = require("../ShortcutsModal");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
describe("ShortcutsModal", () => {
|
||||
it("renders when enabled", () => {
|
||||
const enabled = true;
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_ShortcutsModal.ShortcutsModal, {
|
||||
enabled: enabled
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
it("renders nothing when not enabled", () => {
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_ShortcutsModal.ShortcutsModal, null));
|
||||
expect(wrapper.text()).toBe("");
|
||||
});
|
||||
});
|
|
@ -1,639 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _SourcesTree = require("../../components/PrimaryPanes/SourcesTree");
|
||||
|
||||
var _SourcesTree2 = _interopRequireDefault(_SourcesTree);
|
||||
|
||||
var _immutable = require("devtools/client/shared/vendor/immutable");
|
||||
|
||||
var I = _interopRequireWildcard(_immutable);
|
||||
|
||||
var _devtoolsContextmenu = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-contextmenu"];
|
||||
|
||||
var _clipboard = require("../../utils/clipboard");
|
||||
|
||||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
jest.mock("devtools-contextmenu", () => ({
|
||||
showMenu: jest.fn()
|
||||
}));
|
||||
jest.mock("../../utils/clipboard", () => ({
|
||||
copyToTheClipboard: jest.fn()
|
||||
}));
|
||||
describe("SourcesTree", () => {
|
||||
afterEach(() => {
|
||||
_clipboard.copyToTheClipboard.mockClear();
|
||||
|
||||
_devtoolsContextmenu.showMenu.mockClear();
|
||||
});
|
||||
it("Should show the tree with nothing expanded", async () => {
|
||||
const {
|
||||
component
|
||||
} = render();
|
||||
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(_objectSpread({}, 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(_objectSpread({}, 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 mockSource = I.Map({
|
||||
"server1.conn13.child1/41": createMockSource("server1.conn13.child1/41", "http://mdn.com/three.js")
|
||||
});
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
sources: mockSource
|
||||
}));
|
||||
expect(component.state("uncollapsedTree")).toEqual(defaultState.uncollapsedTree);
|
||||
});
|
||||
it("updates tree with a new item", async () => {
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render();
|
||||
const sources = props.sources.merge({
|
||||
"server1.conn13.child1/42": createMockSource("server1.conn13.child1/42", "http://mdn.com/four.js")
|
||||
});
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
sources: sources
|
||||
}));
|
||||
expect(component.state("uncollapsedTree").contents[0].contents).toHaveLength(4);
|
||||
});
|
||||
it("updates sources if sources are emptied", async () => {
|
||||
const {
|
||||
component,
|
||||
props,
|
||||
defaultState
|
||||
} = render();
|
||||
expect(defaultState.uncollapsedTree.contents).toHaveLength(1);
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
sources: I.Map({})
|
||||
}));
|
||||
expect(component.state("uncollapsedTree").contents).toHaveLength(0);
|
||||
});
|
||||
it("recreates tree if projectRoot is changed", async () => {
|
||||
const {
|
||||
component,
|
||||
props,
|
||||
defaultState
|
||||
} = render();
|
||||
const sources = I.Map({
|
||||
"server1.conn13.child1/41": createMockSource("server1.conn13.child1/41", "http://mozilla.com/three.js")
|
||||
});
|
||||
expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(3);
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
sources: sources,
|
||||
projectRoot: "mozilla"
|
||||
}));
|
||||
expect(component.state("uncollapsedTree").contents[0].contents).toHaveLength(1);
|
||||
});
|
||||
it("recreates tree if debugeeUrl is changed", async () => {
|
||||
const {
|
||||
component,
|
||||
props,
|
||||
defaultState
|
||||
} = render();
|
||||
const mockSource = I.Map({
|
||||
"server1.conn13.child1/41": createMockSource("server1.conn13.child1/41", "http://mdn.com/three.js")
|
||||
});
|
||||
expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(3);
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
debuggeeUrl: "mozilla",
|
||||
sources: mockSource
|
||||
}));
|
||||
expect(component.state("uncollapsedTree").contents[0].contents).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
describe("updates list items", () => {
|
||||
it("updates list items if shownSource changes", async () => {
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render();
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
shownSource: "http://mdn.com/three.js"
|
||||
}));
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
sourceId: "server1.conn13.child1/41"
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("updates highlighted items", () => {
|
||||
it("updates highlightItems if selectedSource changes", async () => {
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render();
|
||||
const mockSource = I.Map({
|
||||
"server1.conn13.child1/41": createMockSource("server1.conn13.child1/41", "http://mdn.com/three.js")
|
||||
});
|
||||
await component.setProps(_objectSpread({}, props, {
|
||||
selectedSource: mockSource
|
||||
}));
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("focusItem", () => {
|
||||
it("update the focused item", async () => {
|
||||
const {
|
||||
component,
|
||||
instance,
|
||||
props
|
||||
} = render();
|
||||
const item = createMockItem();
|
||||
await instance.focusItem(item);
|
||||
await component.update();
|
||||
await component.find(".sources-list").simulate("keydown", {
|
||||
keyCode: 13
|
||||
});
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
sourceId: item.contents.get("id")
|
||||
});
|
||||
});
|
||||
});
|
||||
describe("with custom root", () => {
|
||||
it("renders custom root source list", async () => {
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
projectRoot: "mdn.com"
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("calls clearProjectDirectoryRoot on click", async () => {
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render({
|
||||
projectRoot: "mdn"
|
||||
});
|
||||
component.find(".sources-clear-root").simulate("click");
|
||||
expect(props.clearProjectDirectoryRoot).toHaveBeenCalled();
|
||||
});
|
||||
it("renders empty custom root source list", async () => {
|
||||
const {
|
||||
component
|
||||
} = render({
|
||||
projectRoot: "custom",
|
||||
sources: I.Map()
|
||||
});
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe("onContextMenu of the tree", () => {
|
||||
it("shows context menu on directory to set as root", async () => {
|
||||
const menuOptions = [{
|
||||
accesskey: "r",
|
||||
click: expect.any(Function),
|
||||
disabled: false,
|
||||
id: "node-set-directory-root",
|
||||
label: "Set directory root"
|
||||
}];
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
stopPropagation: jest.fn()
|
||||
};
|
||||
const {
|
||||
props,
|
||||
instance
|
||||
} = render({
|
||||
projectRoot: "root/"
|
||||
});
|
||||
await instance.onContextMenu(mockEvent, createMockDirectory());
|
||||
expect(_devtoolsContextmenu.showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
_devtoolsContextmenu.showMenu.mock.calls[0][1][0].click();
|
||||
|
||||
expect(props.setProjectDirectoryRoot).toHaveBeenCalled();
|
||||
expect(props.clearProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||
expect(_clipboard.copyToTheClipboard).not.toHaveBeenCalled();
|
||||
});
|
||||
it("shows context menu on file to copy source uri", async () => {
|
||||
const menuOptions = [{
|
||||
accesskey: "u",
|
||||
click: expect.any(Function),
|
||||
disabled: false,
|
||||
id: "node-menu-copy-source",
|
||||
label: "Copy source URI"
|
||||
}];
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
stopPropagation: jest.fn()
|
||||
};
|
||||
const {
|
||||
props,
|
||||
instance
|
||||
} = render({
|
||||
projectRoot: "root/"
|
||||
});
|
||||
await instance.onContextMenu(mockEvent, createMockItem());
|
||||
expect(_devtoolsContextmenu.showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
_devtoolsContextmenu.showMenu.mock.calls[0][1][0].click();
|
||||
|
||||
expect(props.setProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||
expect(props.clearProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||
expect(_clipboard.copyToTheClipboard).toHaveBeenCalled();
|
||||
});
|
||||
it("shows context menu on root to remove directory root", async () => {
|
||||
const menuOptions = [{
|
||||
click: expect.any(Function),
|
||||
disabled: false,
|
||||
id: "node-remove-directory-root",
|
||||
label: "Remove directory root"
|
||||
}];
|
||||
const {
|
||||
props,
|
||||
instance
|
||||
} = render({
|
||||
projectRoot: "root/"
|
||||
});
|
||||
const mockEvent = {
|
||||
preventDefault: jest.fn(),
|
||||
stopPropagation: jest.fn()
|
||||
};
|
||||
await instance.onContextMenu(mockEvent, createMockDirectory("root/", "root"));
|
||||
expect(_devtoolsContextmenu.showMenu).toHaveBeenCalledWith(mockEvent, menuOptions);
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||
|
||||
_devtoolsContextmenu.showMenu.mock.calls[0][1][0].click();
|
||||
|
||||
expect(props.setProjectDirectoryRoot).not.toHaveBeenCalled();
|
||||
expect(props.clearProjectDirectoryRoot).toHaveBeenCalled();
|
||||
expect(_clipboard.copyToTheClipboard).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe("renderItem", () => {
|
||||
it("should show icon for webpack item", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockDirectory("webpack://", "webpack://");
|
||||
const node = renderItem(instance, item);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show icon for angular item", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockDirectory("ng://", "ng://");
|
||||
const node = renderItem(instance, item);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show icon for moz-extension item", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockDirectory("moz-extension://", "moz-extension://");
|
||||
const node = renderItem(instance, item);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show icon for folder with arrow", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const node = renderItem(instance, createMockDirectory());
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show icon for folder with expanded arrow", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const node = renderItem(instance, createMockDirectory(), 1, false, true);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show focused item for folder with expanded arrow", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const node = renderItem(instance, createMockDirectory(), 1, true, true);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show source item with source icon", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const node = renderItem(instance, createMockItem());
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show source item with source icon with focus", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const node = renderItem(instance, createMockItem(), 1, true, false);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show domain item", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockItem("root", "root");
|
||||
const node = renderItem(instance, item, 0);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show domain item as debuggee", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockItem("root", "http://mdn.com");
|
||||
const node = renderItem(instance, item, 0);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show domain item as debuggee with focus and arrow", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockDirectory("root", "http://mdn.com");
|
||||
const node = renderItem(instance, item, 0, true);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should not show domain item when the projectRoot exists", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render({
|
||||
projectRoot: "root/"
|
||||
});
|
||||
const node = renderItem(instance, createMockItem(), 0);
|
||||
expect(node).toMatchSnapshot();
|
||||
});
|
||||
it("should show menu on contextmenu of an item", async () => {
|
||||
const {
|
||||
instance
|
||||
} = render();
|
||||
const item = createMockItem();
|
||||
instance.onContextMenu = jest.fn(() => {});
|
||||
const event = {
|
||||
event: "contextmenu"
|
||||
};
|
||||
const node = (0, _enzyme.shallow)(renderItem(instance, item, 1, true));
|
||||
node.simulate("contextmenu", event);
|
||||
expect(instance.onContextMenu).toHaveBeenCalledWith(event, item);
|
||||
});
|
||||
it("should focus on and select item on click", async () => {
|
||||
const {
|
||||
component,
|
||||
instance,
|
||||
props
|
||||
} = render();
|
||||
const item = createMockItem();
|
||||
const event = {
|
||||
event: "click"
|
||||
};
|
||||
const setExpanded = jest.fn();
|
||||
const node = (0, _enzyme.shallow)(renderItem(instance, item, 1, true, false, setExpanded));
|
||||
node.simulate("click", event);
|
||||
await component.find(".sources-list").simulate("keydown", {
|
||||
keyCode: 13
|
||||
});
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
sourceId: item.contents.get("id")
|
||||
});
|
||||
expect(setExpanded).not.toHaveBeenCalled();
|
||||
});
|
||||
it("should focus on and expand directory on click", async () => {
|
||||
const {
|
||||
component,
|
||||
instance,
|
||||
props
|
||||
} = render();
|
||||
const event = {
|
||||
event: "click"
|
||||
};
|
||||
const setExpanded = jest.fn();
|
||||
const mockDirectory = createMockDirectory();
|
||||
const node = (0, _enzyme.shallow)(renderItem(instance, mockDirectory, 1, true, false, setExpanded));
|
||||
node.simulate("click", event);
|
||||
expect(component.state("focusedItem")).toEqual(mockDirectory);
|
||||
expect(setExpanded).toHaveBeenCalled();
|
||||
expect(props.selectLocation).not.toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
describe("selectItem", () => {
|
||||
it("should select item with no children", async () => {
|
||||
const {
|
||||
instance,
|
||||
props
|
||||
} = render();
|
||||
instance.selectItem(createMockItem());
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
sourceId: "server1.conn13.child1/39"
|
||||
});
|
||||
});
|
||||
it("should not select item with children", async () => {
|
||||
const {
|
||||
props,
|
||||
instance
|
||||
} = render();
|
||||
instance.selectItem(createMockDirectory());
|
||||
expect(props.selectLocation).not.toHaveBeenCalled();
|
||||
});
|
||||
it("should select item on enter onKeyDown event", async () => {
|
||||
const {
|
||||
component,
|
||||
props,
|
||||
instance
|
||||
} = render();
|
||||
await instance.focusItem(createMockItem());
|
||||
await component.update();
|
||||
await component.find(".sources-list").simulate("keydown", {
|
||||
keyCode: 13
|
||||
});
|
||||
expect(props.selectLocation).toHaveBeenCalledWith({
|
||||
sourceId: "server1.conn13.child1/39"
|
||||
});
|
||||
});
|
||||
it("does not select if no item is focused on", async () => {
|
||||
const {
|
||||
component,
|
||||
props
|
||||
} = render();
|
||||
await component.find(".sources-list").simulate("keydown", {
|
||||
keyCode: 13
|
||||
});
|
||||
expect(props.selectLocation).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("mdn.com");
|
||||
expect(parent.contents).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
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/one.js/");
|
||||
});
|
||||
it("should return path for blackboxedboxed item", async () => {
|
||||
const item = createMockItem("http://mdn.com/blackboxed.js", "blackboxed.js", I.Map({
|
||||
id: "server1.conn13.child1/59"
|
||||
}));
|
||||
const source = I.Map({
|
||||
"server1.conn13.child1/59": createMockSource("server1.conn13.child1/59", "http://mdn.com/blackboxed.js", true)
|
||||
});
|
||||
const {
|
||||
instance
|
||||
} = render({
|
||||
sources: source
|
||||
});
|
||||
const path = instance.getPath(item);
|
||||
expect(path).toEqual("http://mdn.com/blackboxed.js/blackboxed.js/update");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function generateDefaults(overrides) {
|
||||
const defaultSources = I.Map({
|
||||
"server1.conn13.child1/39": createMockSource("server1.conn13.child1/39", "http://mdn.com/one.js"),
|
||||
"server1.conn13.child1/40": createMockSource("server1.conn13.child1/40", "http://mdn.com/two.js"),
|
||||
"server1.conn13.child1/41": createMockSource("server1.conn13.child1/41", "http://mdn.com/three.js")
|
||||
});
|
||||
return _objectSpread({
|
||||
autoExpandAll: true,
|
||||
selectLocation: jest.fn(),
|
||||
setExpandedState: jest.fn(),
|
||||
sources: defaultSources,
|
||||
debuggeeUrl: "http://mdn.com",
|
||||
clearProjectDirectoryRoot: jest.fn(),
|
||||
setProjectDirectoryRoot: jest.fn(),
|
||||
projectRoot: ""
|
||||
}, overrides);
|
||||
}
|
||||
|
||||
function renderItem(instance, item = createMockItem(), depth = 1, focused = false, expanded = false, setExpanded = jest.fn()) {
|
||||
return instance.renderItem(item, depth, focused, null, expanded, {
|
||||
setExpanded: setExpanded
|
||||
});
|
||||
}
|
||||
|
||||
function render(overrides = {}) {
|
||||
const props = generateDefaults(overrides);
|
||||
const component = (0, _enzyme.shallow)(_react2.default.createElement(_SourcesTree2.default.WrappedComponent, props));
|
||||
const defaultState = component.state();
|
||||
const instance = component.instance();
|
||||
|
||||
instance.shouldComponentUpdate = () => true;
|
||||
|
||||
return {
|
||||
component,
|
||||
props,
|
||||
defaultState,
|
||||
instance
|
||||
};
|
||||
}
|
||||
|
||||
function createMockSource(id, url, isBlackBoxed = false) {
|
||||
return I.Map({
|
||||
id: id,
|
||||
url: url,
|
||||
isPrettyPrinted: false,
|
||||
isWasm: false,
|
||||
sourceMapURL: null,
|
||||
isBlackBoxed: isBlackBoxed,
|
||||
loadedState: "unloaded"
|
||||
});
|
||||
}
|
||||
|
||||
function createMockDirectory(path = "folder/", name = "folder", contents = []) {
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
contents
|
||||
};
|
||||
}
|
||||
|
||||
function createMockItem(path = "http://mdn.com/one.js", name = "one.js", contents = I.Map({
|
||||
id: "server1.conn13.child1/39"
|
||||
})) {
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
contents
|
||||
};
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _react = require("devtools/client/shared/vendor/react");
|
||||
|
||||
var _react2 = _interopRequireDefault(_react);
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _WelcomeBox = require("../WelcomeBox");
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
describe("WelomeBox", () => {
|
||||
const setActiveSearch = () => null;
|
||||
|
||||
it("renders with default values", () => {
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_WelcomeBox.WelcomeBox, {
|
||||
setActiveSearch: setActiveSearch
|
||||
}));
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
it("doesn't render toggle button in horizontal mode", () => {
|
||||
const horizontal = true;
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_WelcomeBox.WelcomeBox, {
|
||||
horizontal: horizontal,
|
||||
setActiveSearch: setActiveSearch
|
||||
}));
|
||||
expect(wrapper.find("PaneToggleButton")).toHaveLength(0);
|
||||
});
|
||||
it("calls correct function on searchSources click", () => {
|
||||
const openQuickOpen = jest.fn();
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_WelcomeBox.WelcomeBox, {
|
||||
setActiveSearch: setActiveSearch,
|
||||
openQuickOpen: openQuickOpen
|
||||
}));
|
||||
wrapper.find(".welcomebox__searchSources").simulate("click");
|
||||
expect(openQuickOpen).toBeCalled();
|
||||
});
|
||||
it("calls correct function on searchProject click", () => {
|
||||
const setActiveSearchSpy = jest.fn();
|
||||
const wrapper = (0, _enzyme.shallow)(_react2.default.createElement(_WelcomeBox.WelcomeBox, {
|
||||
setActiveSearch: setActiveSearchSpy
|
||||
}));
|
||||
wrapper.find(".welcomebox__searchProject").simulate("click");
|
||||
expect(setActiveSearchSpy).toBeCalled();
|
||||
});
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _enzyme = require("enzyme/index");
|
||||
|
||||
var _WhyPaused = require("../SecondaryPanes/Frames/WhyPaused.js/index");
|
||||
|
||||
var _WhyPaused2 = _interopRequireDefault(_WhyPaused);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/* 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/>. */
|
||||
describe("WhyPaused", () => {
|
||||
it("should pause reason with message", () => {
|
||||
const why = {
|
||||
type: "breakpoint",
|
||||
message: "bla is hit"
|
||||
};
|
||||
const component = (0, _enzyme.shallow)((0, _WhyPaused2.default)(why));
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("should show pause reason with exception details", () => {
|
||||
const why = {
|
||||
type: "exception",
|
||||
exception: {
|
||||
class: "Error",
|
||||
preview: {
|
||||
name: "ReferenceError",
|
||||
message: "o is not defined"
|
||||
}
|
||||
}
|
||||
};
|
||||
const component = (0, _enzyme.shallow)((0, _WhyPaused2.default)(why));
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
it("should show pause reason with exception string", () => {
|
||||
const why = {
|
||||
type: "exception",
|
||||
exception: "Not Available"
|
||||
};
|
||||
const component = (0, _enzyme.shallow)((0, _WhyPaused2.default)(why));
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'Outline.spec.js',
|
||||
'ProjectSearch.spec.js',
|
||||
'QuickOpenModal.spec.js',
|
||||
'ShortcutsModal.spec.js',
|
||||
'SourcesTree.spec.js',
|
||||
'WelcomeBox.spec.js',
|
||||
'WhyPaused.spec.js',
|
||||
)
|
|
@ -70,11 +70,12 @@ function update(state = initialASTState(), action) {
|
|||
case "SET_PAUSE_POINTS":
|
||||
{
|
||||
const {
|
||||
source,
|
||||
sourceText,
|
||||
sourceId,
|
||||
pausePoints
|
||||
} = action;
|
||||
const emptyLines = (0, _ast.findEmptyLines)(source, pausePoints);
|
||||
return state.setIn(["pausePoints", source.id], pausePoints).setIn(["emptyLines", source.id], emptyLines);
|
||||
const emptyLines = (0, _ast.findEmptyLines)(sourceText, pausePoints);
|
||||
return state.setIn(["pausePoints", sourceId], pausePoints).setIn(["emptyLines", sourceId], emptyLines);
|
||||
}
|
||||
|
||||
case "OUT_OF_SCOPE_LOCATIONS":
|
||||
|
|
|
@ -24,6 +24,7 @@ exports.getFrameScope = getFrameScope;
|
|||
exports.getSelectedScope = getSelectedScope;
|
||||
exports.getSelectedScopeMappings = getSelectedScopeMappings;
|
||||
exports.getSelectedFrameId = getSelectedFrameId;
|
||||
exports.getSelectedComponentIndex = getSelectedComponentIndex;
|
||||
exports.getTopFrame = getTopFrame;
|
||||
exports.getDebuggeeUrl = getDebuggeeUrl;
|
||||
exports.getSkipPausing = getSkipPausing;
|
||||
|
@ -47,6 +48,7 @@ const createPauseState = exports.createPauseState = () => ({
|
|||
isWaitingOnBreak: false,
|
||||
frames: undefined,
|
||||
selectedFrameId: undefined,
|
||||
selectedComponentIndex: undefined,
|
||||
frameScopes: {
|
||||
generated: {},
|
||||
original: {},
|
||||
|
@ -177,6 +179,11 @@ function update(state = createPauseState(), action) {
|
|||
selectedFrameId: action.frame.id
|
||||
});
|
||||
|
||||
case "SELECT_COMPONENT":
|
||||
return _objectSpread({}, state, {
|
||||
selectedComponentIndex: action.componentIndex
|
||||
});
|
||||
|
||||
case "SET_POPUP_OBJECT_PROPERTIES":
|
||||
if (!action.properties) {
|
||||
return _objectSpread({}, state);
|
||||
|
@ -386,6 +393,10 @@ function getSelectedFrameId(state) {
|
|||
return state.pause.selectedFrameId;
|
||||
}
|
||||
|
||||
function getSelectedComponentIndex(state) {
|
||||
return state.pause.selectedComponentIndex;
|
||||
}
|
||||
|
||||
function getTopFrame(state) {
|
||||
const frames = getFrames(state);
|
||||
return frames && frames[0];
|
||||
|
|
|
@ -115,7 +115,7 @@ function update(state = initialSourcesState(), action) {
|
|||
return state.merge({
|
||||
tabs: updateTabList({
|
||||
sources: state
|
||||
}, action.source.url)
|
||||
}, action.url)
|
||||
});
|
||||
|
||||
case "MOVE_TAB":
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'test',
|
||||
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
var _getCallStackFrames = require("../getCallStackFrames");
|
||||
|
||||
var _immutable = require("devtools/client/shared/vendor/immutable");
|
||||
|
||||
var _lodash = require("devtools/client/shared/vendor/lodash");
|
||||
|
||||
/* 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/>. */
|
||||
describe("getCallStackFrames selector", () => {
|
||||
describe("library annotation", () => {
|
||||
it("annotates React frames", () => {
|
||||
const state = {
|
||||
frames: [{
|
||||
location: {
|
||||
sourceId: "source1"
|
||||
}
|
||||
}, {
|
||||
location: {
|
||||
sourceId: "source2"
|
||||
}
|
||||
}, {
|
||||
location: {
|
||||
sourceId: "source2"
|
||||
}
|
||||
}],
|
||||
sources: (0, _immutable.fromJS)({
|
||||
source1: {
|
||||
id: "source1",
|
||||
url: "webpack:///src/App.js"
|
||||
},
|
||||
source2: {
|
||||
id: "source2",
|
||||
url: "webpack:///~/react-dom/lib/ReactCompositeComponent.js"
|
||||
}
|
||||
}),
|
||||
selectedSource: (0, _immutable.fromJS)({
|
||||
id: "sourceId-originalSource"
|
||||
})
|
||||
};
|
||||
|
||||
const frames = _getCallStackFrames.getCallStackFrames.resultFunc(state.frames, state.sources, state.selectedSource, true);
|
||||
|
||||
expect(frames[0]).not.toHaveProperty("library");
|
||||
expect(frames[1]).toHaveProperty("library", "React");
|
||||
expect(frames[2]).toHaveProperty("library", "React");
|
||||
}); // Multiple Babel async frame groups occur when you have an async function
|
||||
// calling another async function (a common case).
|
||||
//
|
||||
// There are two possible frame groups that can occur depending on whether
|
||||
// one sets a breakpoint before or after an await
|
||||
|
||||
it("annotates frames related to Babel async transforms", () => {
|
||||
const preAwaitGroup = [{
|
||||
displayName: "asyncAppFunction",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "tryCatch",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "invoke",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "defineIteratorMethods/</prototype[method]",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "step",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "_asyncToGenerator/</<",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "Promise",
|
||||
location: {
|
||||
sourceId: "promise"
|
||||
}
|
||||
}, {
|
||||
displayName: "_asyncToGenerator/<",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "asyncAppFunction",
|
||||
location: {
|
||||
sourceId: "app"
|
||||
}
|
||||
}];
|
||||
const postAwaitGroup = [{
|
||||
displayName: "asyncAppFunction",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "tryCatch",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "invoke",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "defineIteratorMethods/</prototype[method]",
|
||||
location: {
|
||||
sourceId: "regenerator"
|
||||
}
|
||||
}, {
|
||||
displayName: "step",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "step/<",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "run",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "notify/<",
|
||||
location: {
|
||||
sourceId: "bundle"
|
||||
}
|
||||
}, {
|
||||
displayName: "flush",
|
||||
location: {
|
||||
sourceId: "microtask"
|
||||
}
|
||||
}];
|
||||
const state = {
|
||||
frames: [...preAwaitGroup, ...postAwaitGroup],
|
||||
sources: (0, _immutable.fromJS)({
|
||||
app: {
|
||||
id: "app",
|
||||
url: "webpack///app.js"
|
||||
},
|
||||
bundle: {
|
||||
id: "bundle",
|
||||
url: "https://foo.com/bundle.js"
|
||||
},
|
||||
regenerator: {
|
||||
id: "regenerator",
|
||||
url: "webpack:///foo/node_modules/regenerator-runtime/runtime.js"
|
||||
},
|
||||
microtask: {
|
||||
id: "microtask",
|
||||
url: "webpack:///foo/node_modules/core-js/modules/_microtask.js"
|
||||
},
|
||||
promise: {
|
||||
id: "promise",
|
||||
url: "webpack///foo/node_modules/core-js/modules/es6.promise.js"
|
||||
}
|
||||
}),
|
||||
selectedSource: (0, _immutable.fromJS)({
|
||||
id: "sourceId-originalSource"
|
||||
})
|
||||
};
|
||||
|
||||
const frames = _getCallStackFrames.getCallStackFrames.resultFunc(state.frames, state.sources, state.selectedSource);
|
||||
|
||||
const babelFrames = (0, _lodash.pullAt)(frames, [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17]);
|
||||
const otherFrames = frames;
|
||||
expect(babelFrames).toEqual(Array(babelFrames.length).fill(expect.objectContaining({
|
||||
library: "Babel"
|
||||
})));
|
||||
expect(otherFrames).not.toEqual(Array(babelFrames.length).fill(expect.objectContaining({
|
||||
library: "Babel"
|
||||
})));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'getCallStackFrames.spec.js',
|
||||
)
|
|
@ -44,8 +44,8 @@ function findBestMatchExpression(symbols, tokenPos) {
|
|||
}, null);
|
||||
}
|
||||
|
||||
function findEmptyLines(selectedSource, pausePoints) {
|
||||
if (!pausePoints || !selectedSource) {
|
||||
function findEmptyLines(sourceText, pausePoints) {
|
||||
if (!pausePoints || !sourceText) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -53,11 +53,11 @@ function findEmptyLines(selectedSource, pausePoints) {
|
|||
const breakpoints = pausePointsList.filter(point => point.types.break);
|
||||
const breakpointLines = breakpoints.map(point => point.location.line);
|
||||
|
||||
if (!selectedSource.text || breakpointLines.length == 0) {
|
||||
if (!sourceText || breakpointLines.length == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const lineCount = selectedSource.text.split("\n").length;
|
||||
const lineCount = sourceText.split("\n").length;
|
||||
const sourceLines = (0, _lodash.range)(1, lineCount + 1);
|
||||
return (0, _lodash.xor)(sourceLines, breakpointLines);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ DevToolsModules(
|
|||
'source.js',
|
||||
'tabs.js',
|
||||
'task.js',
|
||||
'test-head.js',
|
||||
'text.js',
|
||||
'timings.js',
|
||||
'ui.js',
|
||||
|
|
|
@ -40,6 +40,10 @@ const inExpression = (parent, grandParent) => inStepExpression(parent) || t.isJS
|
|||
|
||||
const isExport = node => t.isExportNamedDeclaration(node) || t.isExportDefaultDeclaration(node);
|
||||
|
||||
function getStartLine(node) {
|
||||
return node.loc.start.line;
|
||||
}
|
||||
|
||||
function getPausePoints(sourceId) {
|
||||
const state = {};
|
||||
(0, _ast.traverseAst)(sourceId, {
|
||||
|
@ -81,8 +85,9 @@ function onEnter(node, ancestors, state) {
|
|||
}
|
||||
|
||||
if (isReturn(node)) {
|
||||
// We do not want to pause at the return and the call e.g. return foo()
|
||||
if (isCall(node.argument)) {
|
||||
// We do not want to pause at the return if the
|
||||
// argument is a call on the same line e.g. return foo()
|
||||
if (isCall(node.argument) && getStartLine(node) == getStartLine(node.argument)) {
|
||||
return addEmptyPoint(state, startLocation);
|
||||
}
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@ skip-if = os == "linux" # bug 1351952
|
|||
[browser_dbg-outline.js]
|
||||
[browser_dbg-pause-exceptions.js]
|
||||
skip-if = !debug && (os == "win" && os_version == "6.1") # Bug 1456441
|
||||
[browser_dbg-pause-on-next.js]
|
||||
[browser_dbg-pause-ux.js]
|
||||
skip-if = os == "win"
|
||||
[browser_dbg-navigation.js]
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that when pause on next is selected, we pause on the next execution
|
||||
|
||||
add_task(async function() {
|
||||
const dbg = await initDebugger("doc-scripts.html");
|
||||
|
||||
clickElement(dbg, "pause");
|
||||
await waitForState(dbg, state => dbg.selectors.getIsWaitingOnBreak(state))
|
||||
invokeInTab("simple");
|
||||
|
||||
await waitForPaused(dbg, "simple3");
|
||||
assertPaused(dbg);
|
||||
});
|
|
@ -249,6 +249,15 @@ function assertNotPaused(dbg) {
|
|||
ok(!isPaused(dbg), "client is not paused");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the debugger is currently paused.
|
||||
* @memberof mochitest/asserts
|
||||
* @static
|
||||
*/
|
||||
function assertPaused(dbg) {
|
||||
ok(isPaused(dbg), "client is paused");
|
||||
}
|
||||
|
||||
function getVisibleSelectedFrameLine(dbg) {
|
||||
const { selectors: { getVisibleSelectedFrame }, getState } = dbg;
|
||||
const frame = getVisibleSelectedFrame(getState());
|
||||
|
@ -1014,6 +1023,7 @@ const selectors = {
|
|||
debugErrorLine: ".new-debug-line-error",
|
||||
codeMirror: ".CodeMirror",
|
||||
resume: ".resume.active",
|
||||
pause: ".pause.active",
|
||||
sourceTabs: ".source-tabs",
|
||||
stepOver: ".stepOver.active",
|
||||
stepOut: ".stepOut.active",
|
||||
|
|
Загрузка…
Ссылка в новой задаче