Bug 1302872 - update debugger bundle r=me UPDATE_BUNDLE

--HG--
rename : devtools/client/debugger/new/test/mochitest/examples/doc-script-switching-01.html => devtools/client/debugger/new/test/mochitest/examples/doc-script-switching.html
This commit is contained in:
James Long 2016-09-15 10:40:47 -04:00
parent 474601f18a
commit 9232f9818d
13 changed files with 499 additions and 189 deletions

View File

@ -1,4 +1,4 @@
// Generated from: 02b44328ac19e43705f51209237b20fb264f93f4 Merge pull request #711 from devtools-html/cleanup
// Generated from: 30002d3cfc4341840af847af9eb2c31cab18abb5 Move some of editor-select.js test into editor-highlight.js to make tests more focused (and avoid timeouts on linux debug) (#746)
var Debugger =
/******/ (function(modules) { // webpackBootstrap
@ -21282,8 +21282,8 @@ var Debugger =
var State = makeRecord({
sources: I.Map(),
selectedSource: undefined,
pendingSelectedSourceURL: undefined,
selectedLocation: undefined,
pendingSelectedLocation: undefined,
sourcesText: I.Map(),
sourceMaps: I.Map(),
tabs: I.List([])
@ -21312,19 +21312,22 @@ var Debugger =
break;
case "SELECT_SOURCE":
return state.merge({
selectedSource: action.source,
pendingSelectedSourceURL: null,
tabs: updateTabList(state, fromJS(action.source), action.options)
return state.set("selectedLocation", {
sourceId: action.source.id,
line: action.line
}).set("pendingSelectedLocation", null).merge({
tabs: updateTabList(state, fromJS(action.source), action.tabIndex)
});
case "SELECT_SOURCE_URL":
return state.merge({ pendingSelectedSourceURL: action.url });
return state.set("pendingSelectedLocation", {
url: action.url,
line: action.line
});
case "CLOSE_TAB":
return state.merge({
selectedSource: getNewSelectedSource(state, action.id),
tabs: removeSourceFromTabList(state, action.id)
return state.merge({ tabs: removeSourceFromTabList(state, action.id) }).set("selectedLocation", {
sourceId: getNewSelectedSourceId(state, action.id)
});
case "LOAD_SOURCE_TEXT":
@ -21359,9 +21362,9 @@ var Debugger =
return _updateText(state, action, [action.originalSource]);
case "NAVIGATE":
var source = state.selectedSource;
var sourceUrl = source && source.get("url");
return State().set("pendingSelectedSourceURL", sourceUrl);
var source = getSelectedSource({ sources: state });
var _url = source && source.get("url");
return State().set("pendingSelectedLocation", { url: _url });
}
return state;
@ -21402,16 +21405,16 @@ var Debugger =
/*
* Adds the new source to the tab list if it is not already there
*/
function updateTabList(state, source, options) {
function updateTabList(state, source, tabIndex) {
var tabs = state.get("tabs");
var selectedSource = state.get("selectedSource");
var selectedSource = getSelectedSource({ sources: state });
var selectedSourceIndex = tabs.indexOf(selectedSource);
var sourceIndex = tabs.indexOf(source);
var includesSource = !!tabs.find(t => t.get("id") == source.get("id"));
if (includesSource) {
if (options.position != undefined) {
return tabs.delete(sourceIndex).insert(options.position, source);
if (tabIndex != undefined) {
return tabs.delete(sourceIndex).insert(tabIndex, source);
}
return tabs;
@ -21423,13 +21426,13 @@ var Debugger =
/**
* Gets the next tab to select when a tab closes.
*/
function getNewSelectedSource(state, id) {
function getNewSelectedSourceId(state, id) {
var tabs = state.get("tabs");
var selectedSource = state.get("selectedSource");
var selectedSource = getSelectedSource({ sources: state });
// if we're not closing the selected tab return the selected tab
if (selectedSource.get("id") != id) {
return selectedSource;
return selectedSource.get("id");
}
var tabIndex = tabs.findIndex(tab => tab.get("id") == id);
@ -21441,11 +21444,11 @@ var Debugger =
// if we're closing the last tab, select the penultimate tab
if (tabIndex + 1 == numTabs) {
return tabs.get(tabIndex - 1);
return tabs.get(tabIndex - 1).get("id");
}
// return the next tab
return tabs.get(tabIndex + 1);
return tabs.get(tabIndex + 1).get("id");
}
// Selectors
@ -21484,11 +21487,15 @@ var Debugger =
}
function getSelectedSource(state) {
return state.sources.selectedSource;
return state.sources.selectedLocation && getSource(state, state.sources.selectedLocation.sourceId);
}
function getPendingSelectedSourceURL(state) {
return state.sources.pendingSelectedSourceURL;
function getSelectedLocation(state) {
return state.sources.selectedLocation;
}
function getPendingSelectedLocation(state) {
return state.sources.pendingSelectedLocation;
}
function getSourceMap(state, sourceId) {
@ -21514,7 +21521,8 @@ var Debugger =
getSourceText,
getSourceTabs,
getSelectedSource,
getPendingSelectedSourceURL,
getSelectedLocation,
getPendingSelectedLocation,
getSourceMap,
getPrettySource
};
@ -26884,11 +26892,10 @@ var Debugger =
switch (action.type) {
case constants.PAUSED:
if (action.status == "done") {
var _action$value = action.value;
var selectedFrameId = _action$value.selectedFrameId;
var frames = _action$value.frames;
var pauseInfo = _action$value.pauseInfo;
{
var selectedFrameId = action.selectedFrameId;
var frames = action.frames;
var pauseInfo = action.pauseInfo;
pauseInfo.isInterrupted = pauseInfo.why.type === "interrupted";
@ -26900,7 +26907,6 @@ var Debugger =
});
}
break;
case constants.RESUME:
return state.merge({
pause: null,
@ -27071,7 +27077,8 @@ var Debugger =
getSourceText: sources.getSourceText,
getSourceTabs: sources.getSourceTabs,
getSelectedSource: sources.getSelectedSource,
getPendingSelectedSourceURL: sources.getPendingSelectedSourceURL,
getSelectedLocation: sources.getSelectedLocation,
getPendingSelectedLocation: sources.getPendingSelectedLocation,
getSourceMap: sources.getSourceMap,
getPrettySource: sources.getPrettySource,
@ -28766,7 +28773,7 @@ var Debugger =
},
onKeyDown(e) {
if (e.key === "Escape") {
if (this.state.searchOn && e.key === "Escape") {
this.setState({ searchOn: false });
e.preventDefault();
}
@ -30166,7 +30173,7 @@ var Debugger =
var getSource = _require9.getSource;
var getSourceByURL = _require9.getSourceByURL;
var getSourceText = _require9.getSourceText;
var getPendingSelectedSourceURL = _require9.getPendingSelectedSourceURL;
var getPendingSelectedLocation = _require9.getPendingSelectedLocation;
var getSourceMap = _require9.getSourceMap;
var getSourceMapURL = _require9.getSourceMapURL;
var getFrames = _require9.getFrames;
@ -30196,9 +30203,9 @@ var Debugger =
// If a request has been made to show this source, go ahead and
// select it.
var pendingURL = getPendingSelectedSourceURL(getState());
if (pendingURL === source.url) {
dispatch(selectSource(source.id));
var pendingLocation = getPendingSelectedLocation(getState());
if (pendingLocation && pendingLocation.url === source.url) {
dispatch(selectSource(source.id, { line: pendingLocation.line }));
}
};
}
@ -30246,17 +30253,21 @@ var Debugger =
* @static
*/
function selectSourceURL(url) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
return _ref7 => {
var dispatch = _ref7.dispatch;
var getState = _ref7.getState;
var source = getSourceByURL(getState(), url);
if (source) {
dispatch(selectSource(source.get("id")));
dispatch(selectSource(source.get("id"), options));
} else {
dispatch({
type: constants.SELECT_SOURCE_URL,
url: url
url: url,
tabIndex: options.tabIndex,
line: options.line
});
}
};
@ -30288,7 +30299,8 @@ var Debugger =
dispatch({
type: constants.SELECT_SOURCE,
source: source,
options
tabIndex: options.tabIndex,
line: options.line
});
};
}
@ -30767,31 +30779,31 @@ var Debugger =
* @static
*/
function paused(pauseInfo) {
return _ref2 => {
var dispatch = _ref2.dispatch;
var getState = _ref2.getState;
var client = _ref2.client;
var frame = pauseInfo.frame;
var frames = pauseInfo.frames;
var why = pauseInfo.why;
return (() => {
var _ref2 = _asyncToGenerator(function* (_ref3) {
var dispatch = _ref3.dispatch;
var getState = _ref3.getState;
var client = _ref3.client;
var frame = pauseInfo.frame;
var frames = pauseInfo.frames;
var why = pauseInfo.why;
frames = yield updateFrameLocations(getState(), frames);
dispatch(evaluateExpressions());
return dispatch({
type: constants.PAUSED,
[PROMISE]: _asyncToGenerator(function* () {
frames = yield updateFrameLocations(getState(), frames);
dispatch(selectSource(frame.location.sourceId));
return {
pauseInfo: { why, frame },
frames: frames,
selectedFrameId: frame.id
};
})()
dispatch(evaluateExpressions());
dispatch({
type: constants.PAUSED,
pauseInfo: { why, frame },
frames: frames,
selectedFrameId: frame.id
});
dispatch(selectSource(frame.location.sourceId, { line: frame.location.line }));
});
};
return function (_x) {
return _ref2.apply(this, arguments);
};
})();
}
/**
@ -32783,7 +32795,7 @@ var Debugger =
var getSourceText = _require4.getSourceText;
var getBreakpointsForSource = _require4.getBreakpointsForSource;
var getSelectedSource = _require4.getSelectedSource;
var getSelectedLocation = _require4.getSelectedLocation;
var getSelectedFrame = _require4.getSelectedFrame;
var _require5 = __webpack_require__(194);
@ -32798,8 +32810,8 @@ var Debugger =
__webpack_require__(264);
function isSourceForFrame(source, frame) {
return source && frame && frame.location.sourceId === source.get("id");
function isTextForSource(sourceText) {
return !sourceText.get("loading") && !sourceText.get("error");
}
/**
@ -32818,7 +32830,7 @@ var Debugger =
var Editor = React.createClass({
propTypes: {
breakpoints: ImPropTypes.map.isRequired,
selectedSource: ImPropTypes.map,
selectedLocation: PropTypes.object,
sourceText: PropTypes.object,
addBreakpoint: PropTypes.func,
removeBreakpoint: PropTypes.func,
@ -32838,11 +32850,11 @@ var Debugger =
if (bp) {
this.props.removeBreakpoint({
sourceId: this.props.selectedSource.get("id"),
sourceId: this.props.selectedLocation.sourceId,
line: line + 1
});
} else {
this.props.addBreakpoint({ sourceId: this.props.selectedSource.get("id"),
this.props.addBreakpoint({ sourceId: this.props.selectedLocation.sourceId,
line: line + 1 },
// Pass in a function to get line text because the breakpoint
// may slide and it needs to compute the value at the new
@ -32851,45 +32863,52 @@ var Debugger =
}
},
clearDebugLine(line) {
this.editor.codeMirror.removeLineClass(line - 1, "line", "debug-line");
updateDebugLine(prevProps, nextProps) {
if (prevProps.selectedFrame) {
var line = prevProps.selectedFrame.location.line;
this.editor.codeMirror.removeLineClass(line - 1, "line", "debug-line");
}
if (nextProps.selectedFrame) {
var _line = nextProps.selectedFrame.location.line;
this.editor.codeMirror.addLineClass(_line - 1, "line", "debug-line");
}
},
setDebugLine(line) {
this.editor.codeMirror.addLineClass(line - 1, "line", "debug-line");
highlightLine() {
if (!this.pendingJumpLine) {
return;
}
// If the location has changed and a specific line is requested,
// move to that line and flash it.
var codeMirror = this.editor.codeMirror;
// Make sure to clean up after ourselves. Not only does this
// cancel any existing animation, but it avoids it from
// happening ever again (in case CodeMirror re-applies the
// class, etc).
if (this.lastJumpLine) {
codeMirror.removeLineClass(this.lastJumpLine - 1, "line", "highlight-line");
}
var line = this.pendingJumpLine;
this.editor.alignLine(line);
},
setSourceText(newSourceText, oldSourceText) {
if (newSourceText.get("loading")) {
this.setText("Loading...");
return;
// We only want to do the flashing animation if it's not a debug
// line, which has it's own styling.
if (!this.props.selectedFrame || this.props.selectedFrame.location.line !== line) {
this.editor.codeMirror.addLineClass(line - 1, "line", "highlight-line");
}
if (newSourceText.get("error")) {
this.setText("Error");
console.error(newSourceText.get("error"));
return;
}
this.setText(newSourceText.get("text"));
this.setMode(newSourceText);
resizeBreakpointGutter(this.editor.codeMirror);
this.lastJumpLine = line;
this.pendingJumpLine = null;
},
// Only reset the editor text if the source has changed.
// * Resetting the text will remove the breakpoints.
// * Comparing the source text is probably inneficient.
setText(text) {
if (!text || !this.editor) {
return;
}
if (text == this.editor.getText()) {
return;
}
this.editor.setText(text);
},
@ -32940,24 +32959,52 @@ var Debugger =
},
componentWillReceiveProps(nextProps) {
// Clear the currently highlighted line
if (isSourceForFrame(this.props.selectedSource, this.props.selectedFrame)) {
this.clearDebugLine(this.props.selectedFrame.location.line);
// This lifecycle method is responsible for updating the editor
// text.
var sourceText = nextProps.sourceText;
if (!sourceText) {
this.setText("");
this.editor.setMode({ name: "text" });
} else if (!isTextForSource(sourceText)) {
// There are only 2 possible states: errored or loading. Do
// nothing except put a message in the editor.
this.setText(sourceText.get("error") || "Loading...");
this.editor.setMode({ name: "text" });
} else if (this.props.sourceText !== sourceText) {
// Only update it if the `sourceText` object has actually changed.
// It is immutable so it will always change when updated.
this.setText(sourceText.get("text"));
this.setMode(sourceText);
resizeBreakpointGutter(this.editor.codeMirror);
}
},
componentDidUpdate(prevProps) {
// This is in `componentDidUpdate` so helper functions can expect
// `this.props` to be the current props. This lifecycle method is
// responsible for updating the editor annotations.
var selectedLocation = this.props.selectedLocation;
// If the location is different and a new line is requested,
// update the pending jump line. Note that if jumping to a line in
// a source where the text hasn't been loaded yet, we will set the
// line here but not jump until rendering the actual source.
if (prevProps.selectedLocation !== selectedLocation) {
if (selectedLocation && selectedLocation.line != undefined) {
this.pendingJumpLine = selectedLocation.line;
} else {
this.pendingJumpLine = null;
}
}
// Set the source text. The source text may not have been loaded
// yet. On startup, the source text may not exist yet.
if (nextProps.sourceText) {
this.setSourceText(nextProps.sourceText, this.props.sourceText);
}
if (this.props.selectedSource && !nextProps.selectedSource) {
this.editor.setText("");
}
// Highlight the paused line if necessary
if (isSourceForFrame(nextProps.selectedSource, nextProps.selectedFrame)) {
this.setDebugLine(nextProps.selectedFrame.location.line);
// Only update and jump around in real source texts. This will
// keep the jump state around until the real source text is
// loaded.
if (this.props.sourceText && isTextForSource(this.props.sourceText)) {
this.updateDebugLine(prevProps, this.props);
this.highlightLine();
}
},
@ -32979,13 +33026,13 @@ var Debugger =
});
module.exports = connect((state, props) => {
var selectedSource = getSelectedSource(state);
var selectedId = selectedSource && selectedSource.get("id");
var selectedLocation = getSelectedLocation(state);
var sourceId = selectedLocation && selectedLocation.sourceId;
return {
selectedSource,
sourceText: getSourceText(state, selectedId),
breakpoints: getBreakpointsForSource(state, selectedId),
selectedLocation,
sourceText: getSourceText(state, sourceId),
breakpoints: getBreakpointsForSource(state, sourceId),
selectedFrame: getSelectedFrame(state)
};
}, dispatch => bindActionCreators(actions, dispatch))(Editor);
@ -33231,6 +33278,13 @@ var Debugger =
var Svg = __webpack_require__(234);
var ImPropTypes = __webpack_require__(227);
var _require5 = __webpack_require__(212);
var Services = _require5.Services;
var shiftKey = Services.appinfo.OS === "Darwin" ? "\u21E7" : "Shift+";
var ctrlKey = Services.appinfo.OS === "Linux" ? "Ctrl+" : "";
var actions = __webpack_require__(213);
var Breakpoints = React.createFactory(__webpack_require__(271));
var Expressions = React.createFactory(__webpack_require__(274));
@ -33306,8 +33360,8 @@ var Debugger =
this.keyShortcutsEnabled = true;
keyShortcuts.on("F8", this.resume);
keyShortcuts.on("F10", this.stepOver);
keyShortcuts.on("F11", this.stepIn);
keyShortcuts.on("F12", this.stepOut);
keyShortcuts.on(`${ ctrlKey }F11`, this.stepIn);
keyShortcuts.on(`${ ctrlKey }Shift+F11`, this.stepOut);
},
componentWillUnmount() {
@ -33315,8 +33369,8 @@ var Debugger =
keyShortcuts.off("F8", this.resume);
keyShortcuts.off("F10", this.stepOver);
keyShortcuts.off("F11", this.stepIn);
keyShortcuts.off("F12", this.stepOut);
keyShortcuts.off(`${ ctrlKey }F11`, this.stepIn);
keyShortcuts.off(`${ ctrlKey }Shift+F11`, this.stepOut);
},
componentDidUpdate() {
@ -33325,7 +33379,7 @@ var Debugger =
renderStepButtons() {
var className = this.props.pause ? "active" : "disabled";
return [debugBtn(this.stepOver, "stepOver", className, "Step Over (F10)"), debugBtn(this.stepIn, "stepIn", className, "Step In (F11)"), debugBtn(this.stepOut, "stepOut", className, "Step Out \u21E7 (F12)")];
return [debugBtn(this.stepOver, "stepOver", className, "Step Over (F10)"), debugBtn(this.stepIn, "stepIn", className, `Step In (${ ctrlKey }F11)`), debugBtn(this.stepOut, "stepOut", className, `Step Out (${ ctrlKey }${ shiftKey }F11)`)];
},
renderPauseButton() {
@ -37722,7 +37776,7 @@ var Debugger =
key: source.get("id"),
onClick: () => {
var tabIndex = getLastVisibleTabIndex(sourceTabs, sourceTabEls);
selectSource(source.get("id"), { position: tabIndex });
selectSource(source.get("id"), { tabIndex });
this.toggleSourcesDropdown();
}
}, filename);
@ -37905,7 +37959,7 @@ var Debugger =
},
render() {
if (!this.props.selectedSource) {
if (!this.props.selectedSource || !isEnabled("prettyPrint") && !isEnabled("blackBox")) {
return null;
}
@ -37946,6 +38000,8 @@ var Debugger =
__webpack_require__(362);
var Svg = __webpack_require__(234);
var INITIAL_SELECTED_INDEX = 0;
var Autocomplete = React.createClass({
propTypes: {
selectItem: PropTypes.func,
@ -37957,7 +38013,7 @@ var Debugger =
getInitialState() {
return {
inputValue: "",
selectedIndex: -1
selectedIndex: INITIAL_SELECTED_INDEX
};
},
@ -38000,7 +38056,7 @@ var Debugger =
ref: "searchInput",
onChange: e => this.setState({
inputValue: e.target.value,
selectedIndex: -1
selectedIndex: INITIAL_SELECTED_INDEX
}),
onFocus: e => this.setState({ focused: true }),
onBlur: e => this.setState({ focused: false }),

View File

@ -151,7 +151,7 @@ var Debugger =
var consumer = _getConsumer(generatedSource.id);
// if there is not a consumer, then its a generated source without a map
// if there is not a consumer, then it's a generated source without a map
if (!consumer) {
return {
url: generatedSource.url,

View File

@ -381,6 +381,15 @@ ul.sources-list {
display: none;
}
.highlight-line .CodeMirror-line {
animation: fade-highlight-out 1.5s normal forwards;
}
@keyframes fade-highlight-out {
0% { background-color: var(--theme-highlight-gray); }
100% { background-color: transparent; }
}
.welcomebox {
width: 100%;

View File

@ -43,22 +43,29 @@
"waitForThreadEvents": false,
"waitForState": false,
"waitForPaused": false,
"waitForSources": false,
"isPaused": false,
"assertPausedLocation": false,
"assertHighlightLocation": false,
"initDebugger": false,
"invokeInTab": false,
"findSource": false,
"findElement": false,
"findAllElements": false,
"selectSource": false,
"stepOver": false,
"stepIn": false,
"stepOut": false,
"resume": false,
"reload": false,
"navigate": false,
"removeBreakpoint": false,
"addBreakpoint": false,
"toggleCallStack": false,
"isVisibleWithin": false,
"clickElement": false,
"togglePauseOnExceptions": false
"togglePauseOnExceptions": false,
"pressKey": false,
"EXAMPLE_URL": false
}
}

View File

@ -6,7 +6,7 @@ support-files =
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
examples/doc-scripts.html
examples/doc-script-switching-01.html
examples/doc-script-switching.html
examples/doc-exceptions.html
examples/doc-iframes.html
examples/doc-debugger-statements.html
@ -20,9 +20,13 @@ support-files =
[browser_dbg-editor-gutter.js]
[browser_dbg-editor-mode.js]
[browser_dbg-editor-select.js]
[browser_dbg-editor-highlight.js]
[browser_dbg-call-stack.js]
[browser_dbg-pause-exceptions.js]
[browser_dbg-chrome-create.js]
[browser_dbg-chrome-debugging.js]
[browser_dbg-iframes.js]
[browser_dbg-debugger-buttons.js]
[browser_dbg_keyboard-shortcuts.js]
[browser_dbg-navigation.js]
[browser_dbg-console.js]

View File

@ -17,7 +17,7 @@ function isFrameSelected(dbg, index, title) {
add_task(function* () {
const dbg = yield initDebugger(
"doc-script-switching-01.html",
"doc-script-switching.html",
"script-switching-01.js"
);

View File

@ -0,0 +1,34 @@
// Return a promise with a reference to jsterm, opening the split
// console if necessary. This cleans up the split console pref so
// it won't pollute other tests.
function getSplitConsole(dbg) {
const { toolbox, win } = dbg;
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
});
if (!win) {
win = toolbox.win;
}
if (!toolbox.splitConsole) {
EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
}
return new Promise(resolve => {
toolbox.getPanelWhenReady("webconsole").then(() => {
ok(toolbox.splitConsole, "Split console is shown.");
let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
resolve(jsterm);
});
});
}
add_task(function* () {
Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
const dbg = yield initDebugger("doc-script-switching.html");
yield getSplitConsole(dbg);
ok(dbg.toolbox.splitConsole, "Split console is shown.");
});

View File

@ -0,0 +1,47 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the editor will always highight the right line, no
// matter if the source text doesn't exist yet or even if the source
// doesn't exist.
add_task(function* () {
const dbg = yield initDebugger("doc-scripts.html");
const { selectors: { getSourceText }, getState } = dbg;
const sourceUrl = EXAMPLE_URL + "code-long.js";
// The source itself doesn't even exist yet, and using
// `selectSourceURL` will set a pending request to load this source
// and highlight a specific line.
dbg.actions.selectSourceURL(sourceUrl, { line: 66 });
// Wait for the source text to load and make sure we're in the right
// place.
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
assertHighlightLocation(dbg, "long.js", 66);
// Jump to line 16 and make sure the editor scrolled.
yield selectSource(dbg, "long.js", 16);
assertHighlightLocation(dbg, "long.js", 16);
// Make sure only one line is ever highlighted and the flash
// animation is cancelled on old lines.
yield selectSource(dbg, "long.js", 17);
yield selectSource(dbg, "long.js", 18);
assertHighlightLocation(dbg, "long.js", 18);
is(findAllElements(dbg, "highlightLine").length, 1,
"Only 1 line is highlighted");
// Test jumping to a line in a source that exists but hasn't been
// loaded yet.
yield waitForSources(dbg, "simple1.js");
selectSource(dbg, "simple1.js", 6);
// Make sure the source is in the loading state, wait for it to be
// fully loaded, and check the highlighted line.
const simple1 = findSource(dbg, "simple1.js");
ok(getSourceText(getState(), simple1.id).get("loading"));
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
ok(getSourceText(getState(), simple1.id).get("text"));
assertHighlightLocation(dbg, "simple1.js", 6);
});

View File

@ -5,10 +5,10 @@
// debugger pauses
// checks to see if the first breakpoint is visible
function isBreakpointVisible(dbg) {
const bpLine = findElement(dbg, "breakpoint");
function isElementVisible(dbg, elementName) {
const bpLine = findElement(dbg, elementName);
const cm = findElement(dbg, "codeMirror");
ok(isVisibleWithin(cm, bpLine), "CodeMirror is scrolled to line");
return bpLine && isVisibleWithin(cm, bpLine);
}
add_task(function* () {
@ -21,7 +21,7 @@ add_task(function* () {
const simple2 = findSource(dbg, "simple2.js");
// Set the initial breakpoint.
yield addBreakpoint(dbg, simple1.id, 4);
yield addBreakpoint(dbg, simple1, 4);
ok(!getSelectedSource(getState()), "No selected source");
// Call the function that we set a breakpoint in.
@ -42,11 +42,11 @@ add_task(function* () {
// Make sure that we can set a breakpoint on a line out of the
// viewport, and that pausing there scrolls the editor to it.
const longSrc = findSource(dbg, "long.js");
yield addBreakpoint(dbg, longSrc.id, 66);
let longSrc = findSource(dbg, "long.js");
yield addBreakpoint(dbg, longSrc, 66);
invokeInTab("testModel");
yield waitForPaused(dbg);
assertPausedLocation(dbg, longSrc, 66);
isBreakpointVisible(dbg);
ok(isElementVisible(dbg, "breakpoint"), "Breakpoint is visible");
});

View File

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function countSources(dbg) {
const sources = dbg.selectors.getSources(dbg.getState());
return sources.size;
}
/**
* Test navigating
* navigating while paused will reset the pause state and sources
*/
add_task(function* () {
const dbg = yield initDebugger(
"doc-script-switching.html",
"script-switching-01.js"
);
invokeInTab("firstCall");
yield waitForPaused(dbg);
yield navigate(dbg, "doc-scripts.html", "simple1.js", "long.js");
yield addBreakpoint(dbg, "simple1.js", 4);
invokeInTab("main");
yield waitForPaused(dbg);
assertPausedLocation(dbg, "simple1.js", 4);
is(countSources(dbg), 4, "4 sources are loaded.");
yield navigate(dbg, "about:blank");
yield waitForDispatch(dbg, "NAVIGATE");
is(countSources(dbg), 0, "0 sources are loaded.");
yield navigate(dbg,
"doc-scripts.html",
"simple1.js",
"simple2.js",
"long.js",
"scripts.html"
);
is(countSources(dbg), 4, "4 sources are loaded.");
});

View File

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test keyboard shortcuts.
*/
function pressResume(dbg) {
pressKey(dbg, "resumeKey");
return waitForPaused(dbg);
}
function pressStepOver(dbg) {
pressKey(dbg, "stepOverKey");
return waitForPaused(dbg);
}
function pressStepIn(dbg) {
pressKey(dbg, "stepInKey");
return waitForPaused(dbg);
}
function pressStepOut(dbg) {
pressKey(dbg, "stepOutKey");
return waitForPaused(dbg);
}
add_task(function*() {
const dbg = yield initDebugger(
"doc-debugger-statements.html",
"debugger-statements.html"
);
yield reload(dbg);
yield waitForPaused(dbg);
assertPausedLocation(dbg, "debugger-statements.html", 8);
yield pressResume(dbg);
assertPausedLocation(dbg, "debugger-statements.html", 12);
yield pressStepIn(dbg);
assertPausedLocation(dbg, "debugger-statements.html", 13);
yield pressStepOut(dbg);
assertPausedLocation(dbg, "debugger-statements.html", 14);
yield pressStepOver(dbg);
assertPausedLocation(dbg, "debugger-statements.html", 9);
});

View File

@ -95,28 +95,54 @@ function waitForState(dbg, predicate) {
});
}
function waitForMs(time) {
return new Promise(resolve => setTimeout(resolve, time));
function waitForSources(dbg, ...sources) {
if(sources.length === 0) {
return Promise.resolve();
}
info("Waiting on sources: " + sources.join(", "));
const {selectors: {getSources}, store} = dbg;
return Promise.all(sources.map(url => {
function sourceExists(state) {
return getSources(state).some(s => s.get("url").includes(url));
}
if(!sourceExists(store.getState())) {
return waitForState(dbg, sourceExists);
}
}));
}
function assertPausedLocation(dbg, source, line) {
const { selectors: { getSelectedSource, getPause }, getState } = dbg;
source = findSource(dbg, source);
// support passing in a partial url and fetching the source
if (typeof source == "string") {
source = findSource(dbg, source);
}
// check the selected source
// Check the selected source
is(getSelectedSource(getState()).get("url"), source.url);
// check the pause location
// Check the pause location
const location = getPause(getState()).getIn(["frame", "location"]);
is(location.get("sourceId"), source.id);
is(location.get("line"), line);
// check the debug line
// Check the debug line
ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
"Line is highlighted as paused");
}
function assertHighlightLocation(dbg, source, line) {
const { selectors: { getSelectedSource, getPause }, getState } = dbg;
source = findSource(dbg, source);
// Check the selected source
is(getSelectedSource(getState()).get("url"), source.url);
// Check the highlight line
const lineEl = findElement(dbg, "highlightLine");
ok(lineEl, "Line is highlighted");
ok(isVisibleWithin(findElement(dbg, "codeMirror"), lineEl),
"Highlighted line is visible");
ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("highlight-line"),
"Line is highlighted");
}
@ -125,11 +151,11 @@ function isPaused(dbg) {
return !!getPause(getState());
}
const waitForPaused = Task.async(function* (dbg) {
// We want to make sure that we get both a real paused event and
// that the state is fully populated. The client may do some more
// work (call other client methods) before populating the state.
return Promise.all([
function waitForPaused(dbg) {
return Task.spawn(function* () {
// We want to make sure that we get both a real paused event and
// that the state is fully populated. The client may do some more
// work (call other client methods) before populating the state.
yield waitForThreadEvents(dbg, "paused"),
yield waitForState(dbg, state => {
const pause = dbg.selectors.getPause(state);
@ -142,42 +168,32 @@ const waitForPaused = Task.async(function* (dbg) {
const sourceId = pause.getIn(["frame", "location", "sourceId"]);
const sourceText = dbg.selectors.getSourceText(dbg.getState(), sourceId);
return sourceText && !sourceText.get("loading");
})
]);
});
});
});
};
const initDebugger = Task.async(function* (url, ...sources) {
const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
const win = toolbox.getPanel("jsdebugger").panelWin;
const store = win.Debugger.store;
const { getSources } = win.Debugger.selectors;
function initDebugger(url, ...sources) {
return Task.spawn(function* () {
const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
const win = toolbox.getPanel("jsdebugger").panelWin;
const store = win.Debugger.store;
const { getSources } = win.Debugger.selectors;
const dbg = {
actions: win.Debugger.actions,
selectors: win.Debugger.selectors,
getState: store.getState,
store: store,
client: win.Debugger.client,
toolbox: toolbox,
win: win
};
const dbg = {
actions: win.Debugger.actions,
selectors: win.Debugger.selectors,
getState: store.getState,
store: store,
client: win.Debugger.client,
toolbox: toolbox,
win: win
};
if(sources.length) {
// TODO: Extract this out to a utility function
info("Waiting on sources: " + sources.join(", "));
yield Promise.all(sources.map(url => {
function sourceExists(state) {
return getSources(state).some(s => s.get("url").includes(url));
}
yield waitForSources(dbg, ...sources);
if(!sourceExists(store.getState())) {
return waitForState(dbg, sourceExists);
}
}));
}
return dbg;
});
return dbg;
});
};
window.resumeTest = undefined;
function pauseTest() {
@ -188,6 +204,13 @@ function pauseTest() {
// Actions
function findSource(dbg, url) {
if(typeof url !== "string") {
// Support passing in a source object itelf all APIs that use this
// function support both styles
const source = url;
return source;
}
const sources = dbg.selectors.getSources(dbg.getState());
const source = sources.find(s => s.get("url").includes(url));
@ -198,12 +221,15 @@ function findSource(dbg, url) {
return source.toJS();
}
function selectSource(dbg, url) {
function selectSource(dbg, url, line) {
info("Selecting source: " + url);
const source = findSource(dbg, url);
dbg.actions.selectSource(source.id);
const hasText = !!dbg.selectors.getSourceText(dbg.getState(), source.id);
dbg.actions.selectSource(source.id, { line });
return waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
if(!hasText) {
return waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
}
}
function stepOver(dbg) {
@ -234,10 +260,21 @@ function reload(dbg) {
return dbg.client.reload();
}
function addBreakpoint(dbg, sourceId, line, col) {
function navigate(dbg, url, ...sources) {
dbg.client.navigate(url);
return waitForSources(dbg, ...sources)
}
function addBreakpoint(dbg, source, line, col) {
source = findSource(dbg, source);
const sourceId = source.id;
return dbg.actions.addBreakpoint({ sourceId, line, col });
}
function removeBreakpoint(dbg, sourceId, line, col) {
return dbg.actions.removeBreakpoint({ sourceId, line, col });
}
function togglePauseOnExceptions(dbg,
pauseOnExceptions, ignoreCaughtExceptions) {
@ -256,11 +293,30 @@ function togglePauseOnExceptions(dbg,
// Helpers
// invoke a global function in the debugged tab
function invokeInTab(fnc) {
info(`Invoking function ${fnc} in tab`);
return ContentTask.spawn(gBrowser.selectedBrowser, fnc, function* (fnc) {
content.wrappedJSObject[fnc](); // eslint-disable-line mozilla/no-cpows-in-tests, max-len
});
}
const isLinux = Services.appinfo.OS === "Linux";
const keyMappings = {
pauseKey: { code: "VK_F8" },
resumeKey: { code: "VK_F8" },
stepOverKey: { code: "VK_F10" },
stepInKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux } },
stepOutKey: { code: "VK_F11", modifiers: { ctrlKey: isLinux, shiftKey: true } }
};
function pressKey(dbg, keyName) {
let keyEvent = keyMappings[keyName];
const { code, modifiers } = keyEvent;
return EventUtils.synthesizeKey(
code,
modifiers || {},
dbg.win
);
}
function isVisibleWithin(outerEl, innerEl) {
const innerRect = innerEl.getBoundingClientRect();
@ -275,12 +331,13 @@ const selectors = {
gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
pauseOnExceptions: ".pause-exceptions",
breakpoint: ".CodeMirror-code > .new-breakpoint",
highlightLine: ".CodeMirror-code > .highlight-line",
codeMirror: ".CodeMirror",
resume: ".resume.active",
stepOver: ".stepOver.active",
stepOut: ".stepOut.active",
stepIn: ".stepIn.active"
}
};
function getSelector(elementName, ...args) {
let selector = selectors[elementName];
@ -300,6 +357,11 @@ function findElement(dbg, elementName, ...args) {
return dbg.win.document.querySelector(selector);
}
function findAllElements(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args);
return dbg.win.document.querySelectorAll(selector);
}
// click an element in the debugger
function clickElement(dbg, elementName, ...args) {
const selector = getSelector(elementName, ...args);