mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 07:48:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
80
devtools/client/debugger/new/test/mochitest/.eslintrc
Normal file
80
devtools/client/debugger/new/test/mochitest/.eslintrc
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"globals": {
|
||||
"add_task": false,
|
||||
"Assert": false,
|
||||
"BrowserTestUtils": false,
|
||||
"content": false,
|
||||
"ContentTask": false,
|
||||
"ContentTaskUtils": false,
|
||||
"EventUtils": false,
|
||||
"executeSoon": false,
|
||||
"expectUncaughtException": false,
|
||||
"export_assertions": false,
|
||||
"extractJarToTmp": false,
|
||||
"finish": false,
|
||||
"getJar": false,
|
||||
"getRootDirectory": false,
|
||||
"getTestFilePath": false,
|
||||
"gBrowser": false,
|
||||
"gTestPath": false,
|
||||
"info": false,
|
||||
"is": false,
|
||||
"isnot": false,
|
||||
"ok": false,
|
||||
"registerCleanupFunction": false,
|
||||
"requestLongerTimeout": false,
|
||||
"SimpleTest": false,
|
||||
"SpecialPowers": false,
|
||||
"TestUtils": false,
|
||||
"thisTestLeaksUncaughtRejectionsAndShouldBeFixed": false,
|
||||
"todo": false,
|
||||
"todo_is": false,
|
||||
"todo_isnot": false,
|
||||
"waitForClipboard": false,
|
||||
"waitForExplicitFinish": false,
|
||||
"waitForFocus": false,
|
||||
|
||||
// Globals introduced in debugger-specific head.js
|
||||
"promise": false,
|
||||
"BrowserToolboxProcess": false,
|
||||
"OS": false,
|
||||
"waitForNextDispatch": false,
|
||||
"waitForDispatch": false,
|
||||
"waitForThreadEvents": false,
|
||||
"waitForState": false,
|
||||
"waitForElement": false,
|
||||
"waitForPaused": false,
|
||||
"waitForSources": false,
|
||||
"isPaused": false,
|
||||
"assertPausedLocation": false,
|
||||
"assertHighlightLocation": false,
|
||||
"createDebuggerContext": false,
|
||||
"initDebugger": false,
|
||||
"invokeInTab": false,
|
||||
"findSource": false,
|
||||
"findElement": false,
|
||||
"findElementWithSelector": false,
|
||||
"findAllElements": false,
|
||||
"openNewTabAndToolbox": false,
|
||||
"selectSource": false,
|
||||
"stepOver": false,
|
||||
"stepIn": false,
|
||||
"stepOut": false,
|
||||
"resume": false,
|
||||
"reload": false,
|
||||
"navigate": false,
|
||||
"removeBreakpoint": false,
|
||||
"addBreakpoint": false,
|
||||
"toggleCallStack": false,
|
||||
"toggleScopes": false,
|
||||
"isVisibleWithin": false,
|
||||
"clickElement": false,
|
||||
"rightClickElement": false,
|
||||
"selectMenuItem": false,
|
||||
"togglePauseOnExceptions": false,
|
||||
"type": false,
|
||||
"pressKey": false,
|
||||
"EXAMPLE_URL": false,
|
||||
"waitUntil": false
|
||||
}
|
||||
}
|
||||
60
devtools/client/debugger/new/test/mochitest/browser.ini
Normal file
60
devtools/client/debugger/new/test/mochitest/browser.ini
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
skip-if = (os == 'linux' && debug && bits == 32)
|
||||
support-files =
|
||||
head.js
|
||||
!/devtools/client/commandline/test/helpers.js
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
examples/bundle.js
|
||||
examples/bundle.js.map
|
||||
examples/doc-scripts.html
|
||||
examples/doc-script-switching.html
|
||||
examples/doc-exceptions.html
|
||||
examples/doc-iframes.html
|
||||
examples/doc-frames.html
|
||||
examples/doc-debugger-statements.html
|
||||
examples/doc-minified.html
|
||||
examples/doc-sourcemaps.html
|
||||
examples/doc-sourcemap-bogus.html
|
||||
examples/doc-sources.html
|
||||
examples/bogus-map.js
|
||||
examples/entry.js
|
||||
examples/exceptions.js
|
||||
examples/long.js
|
||||
examples/math.min.js
|
||||
examples/nested/nested-source.js
|
||||
examples/opts.js
|
||||
examples/output.js
|
||||
examples/simple1.js
|
||||
examples/simple2.js
|
||||
examples/frames.js
|
||||
examples/script-switching-02.js
|
||||
examples/script-switching-01.js
|
||||
examples/times2.js
|
||||
|
||||
[browser_dbg-breaking.js]
|
||||
[browser_dbg-breaking-from-console.js]
|
||||
[browser_dbg-breakpoints.js]
|
||||
[browser_dbg-breakpoints-cond.js]
|
||||
[browser_dbg-call-stack.js]
|
||||
[browser_dbg-scopes.js]
|
||||
[browser_dbg-chrome-create.js]
|
||||
[browser_dbg-chrome-debugging.js]
|
||||
[browser_dbg-console.js]
|
||||
[browser_dbg-debugger-buttons.js]
|
||||
[browser_dbg-editor-gutter.js]
|
||||
[browser_dbg-editor-mode.js]
|
||||
[browser_dbg-editor-select.js]
|
||||
[browser_dbg-editor-highlight.js]
|
||||
[browser_dbg-iframes.js]
|
||||
[browser_dbg_keyboard-shortcuts.js]
|
||||
[browser_dbg-pause-exceptions.js]
|
||||
[browser_dbg-navigation.js]
|
||||
[browser_dbg-pretty-print.js]
|
||||
[browser_dbg-pretty-print-paused.js]
|
||||
[browser_dbg-searching.js]
|
||||
skip-if = true
|
||||
[browser_dbg-sourcemaps.js]
|
||||
[browser_dbg-sourcemaps-bogus.js]
|
||||
[browser_dbg-sources.js]
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that `debugger` statements are hit before the debugger even
|
||||
// initializes and it properly highlights the right location in the
|
||||
// debugger.
|
||||
|
||||
add_task(function* () {
|
||||
const url = EXAMPLE_URL + "doc-script-switching.html";
|
||||
const toolbox = yield openNewTabAndToolbox(url, "webconsole");
|
||||
|
||||
// Type "debugger" into console
|
||||
let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
|
||||
jsterm.execute("debugger");
|
||||
|
||||
// Wait for the debugger to be selected and make sure it's paused
|
||||
yield new Promise((resolve) => {
|
||||
toolbox.on("jsdebugger-selected", resolve);
|
||||
});
|
||||
is(toolbox.threadClient.state, "paused");
|
||||
|
||||
// Create a dbg context
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
const { selectors: { getSelectedSource }, getState } = dbg;
|
||||
|
||||
// Make sure the thread is paused in the right source and location
|
||||
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
||||
is(dbg.win.cm.getValue(), "debugger");
|
||||
const source = getSelectedSource(getState()).toJS();
|
||||
assertPausedLocation(dbg, source, 1);
|
||||
});
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests the breakpoints are hit in various situations.
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
const { selectors: { getSelectedSource }, getState } = dbg;
|
||||
|
||||
// Make sure we can set a top-level breakpoint and it will be hit on
|
||||
// reload.
|
||||
yield addBreakpoint(dbg, "scripts.html", 18);
|
||||
reload(dbg);
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "scripts.html", 18);
|
||||
yield resume(dbg);
|
||||
|
||||
const paused = waitForPaused(dbg);
|
||||
|
||||
// Create an eval script that pauses itself.
|
||||
invokeInTab("doEval");
|
||||
|
||||
yield paused;
|
||||
yield resume(dbg);
|
||||
const source = getSelectedSource(getState()).toJS();
|
||||
ok(!source.url, "It is an eval source");
|
||||
|
||||
yield addBreakpoint(dbg, source, 5);
|
||||
invokeInTab("evaledFunc");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, source, 5);
|
||||
});
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function findBreakpoint(dbg, url, line) {
|
||||
const { selectors: { getBreakpoint }, getState } = dbg;
|
||||
const source = findSource(dbg, url);
|
||||
return getBreakpoint(getState(), { sourceId: source.id, line });
|
||||
}
|
||||
|
||||
function setConditionalBreakpoint(dbg, index, condition) {
|
||||
return Task.spawn(function* () {
|
||||
rightClickElement(dbg, "gutter", index);
|
||||
selectMenuItem(dbg, 2);
|
||||
yield waitForElement(dbg, ".conditional-breakpoint-panel input");
|
||||
findElementWithSelector(dbg, ".conditional-breakpoint-panel input").focus();
|
||||
type(dbg, condition);
|
||||
pressKey(dbg, "Enter");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
yield selectSource(dbg, "simple2");
|
||||
|
||||
// Adding a conditional Breakpoint
|
||||
yield setConditionalBreakpoint(dbg, 5, "1");
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
let bp = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp.condition, "1", "breakpoint is created with the condition");
|
||||
|
||||
// Editing a conditional Breakpoint
|
||||
yield setConditionalBreakpoint(dbg, 5, "2");
|
||||
yield waitForDispatch(dbg, "SET_BREAKPOINT_CONDITION");
|
||||
bp = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp.condition, "21", "breakpoint is created with the condition");
|
||||
|
||||
// Removing a conditional breakpoint
|
||||
clickElement(dbg, "gutter", 5);
|
||||
yield waitForDispatch(dbg, "REMOVE_BREAKPOINT");
|
||||
bp = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp, null, "breakpoint was removed");
|
||||
|
||||
// Adding a condition to a breakpoint
|
||||
clickElement(dbg, "gutter", 5);
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
yield setConditionalBreakpoint(dbg, 5, "1");
|
||||
bp = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp.condition, "1", "breakpoint is created with the condition");
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function toggleBreakpoint(dbg, index) {
|
||||
const bp = findElement(dbg, "breakpointItem", index);
|
||||
const input = bp.querySelector("input");
|
||||
input.click();
|
||||
}
|
||||
|
||||
function removeBreakpoint(dbg, index) {
|
||||
return Task.spawn(function* () {
|
||||
const bp = findElement(dbg, "breakpointItem", index);
|
||||
bp.querySelector(".close-btn").click();
|
||||
yield waitForDispatch(dbg, "REMOVE_BREAKPOINT");
|
||||
});
|
||||
}
|
||||
|
||||
function disableBreakpoint(dbg, index) {
|
||||
return Task.spawn(function* () {
|
||||
toggleBreakpoint(dbg, index);
|
||||
yield waitForDispatch(dbg, "REMOVE_BREAKPOINT");
|
||||
});
|
||||
}
|
||||
|
||||
function enableBreakpoint(dbg, index) {
|
||||
return Task.spawn(function* () {
|
||||
toggleBreakpoint(dbg, index);
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
});
|
||||
}
|
||||
|
||||
function toggleBreakpoints(dbg) {
|
||||
return Task.spawn(function* () {
|
||||
const btn = findElement(dbg, "toggleBreakpoints");
|
||||
btn.click();
|
||||
yield waitForDispatch(dbg, "TOGGLE_BREAKPOINTS");
|
||||
});
|
||||
}
|
||||
|
||||
function findBreakpoint(dbg, url, line) {
|
||||
const { selectors: { getBreakpoint }, getState } = dbg;
|
||||
const source = findSource(dbg, url);
|
||||
return getBreakpoint(getState(), { sourceId: source.id, line });
|
||||
}
|
||||
|
||||
function findBreakpoints(dbg) {
|
||||
const { selectors: { getBreakpoints }, getState } = dbg;
|
||||
return getBreakpoints(getState());
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
|
||||
// Create two breakpoints
|
||||
yield selectSource(dbg, "simple2");
|
||||
yield addBreakpoint(dbg, "simple2", 3);
|
||||
yield addBreakpoint(dbg, "simple2", 5);
|
||||
|
||||
// Disable the first one
|
||||
yield disableBreakpoint(dbg, 1);
|
||||
let bp1 = findBreakpoint(dbg, "simple2", 3);
|
||||
let bp2 = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp1.disabled, true, "first breakpoint is disabled");
|
||||
is(bp2.disabled, false, "second breakpoint is enabled");
|
||||
|
||||
// Disable and Re-Enable the second one
|
||||
yield disableBreakpoint(dbg, 2);
|
||||
yield enableBreakpoint(dbg, 2);
|
||||
bp2 = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp2.disabled, false, "second breakpoint is enabled");
|
||||
});
|
||||
|
||||
// toggle all
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
|
||||
// Create two breakpoints
|
||||
yield selectSource(dbg, "simple2");
|
||||
yield addBreakpoint(dbg, "simple2", 3);
|
||||
yield addBreakpoint(dbg, "simple2", 5);
|
||||
|
||||
// Disable all of the breakpoints
|
||||
yield toggleBreakpoints(dbg);
|
||||
let bp1 = findBreakpoint(dbg, "simple2", 3);
|
||||
let bp2 = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp1.disabled, true, "first breakpoint is disabled");
|
||||
is(bp2.disabled, true, "second breakpoint is disabled");
|
||||
|
||||
// Enable all of the breakpoints
|
||||
yield toggleBreakpoints(dbg);
|
||||
bp1 = findBreakpoint(dbg, "simple2", 3);
|
||||
bp2 = findBreakpoint(dbg, "simple2", 5);
|
||||
is(bp1.disabled, false, "first breakpoint is enabled");
|
||||
is(bp2.disabled, false, "second breakpoint is enabled");
|
||||
|
||||
// Remove the breakpoints
|
||||
yield removeBreakpoint(dbg, 1);
|
||||
yield removeBreakpoint(dbg, 1);
|
||||
const bps = findBreakpoints(dbg);
|
||||
is(bps.size, 0, "breakpoints are removed");
|
||||
});
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// checks to see if the frame is selected and the title is correct
|
||||
function isFrameSelected(dbg, index, title) {
|
||||
const $frame = findElement(dbg, "frame", index);
|
||||
const frame = dbg.selectors.getSelectedFrame(dbg.getState());
|
||||
|
||||
const elSelected = $frame.classList.contains("selected");
|
||||
const titleSelected = frame.displayName == title;
|
||||
|
||||
return elSelected && titleSelected;
|
||||
}
|
||||
|
||||
function toggleButton(dbg) {
|
||||
const callStackBody = findElement(dbg, "callStackBody");
|
||||
return callStackBody.querySelector(".show-more");
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-script-switching.html");
|
||||
|
||||
toggleCallStack(dbg);
|
||||
|
||||
const notPaused = findElement(dbg, "callStackBody").innerText;
|
||||
is(notPaused, "Not Paused", "Not paused message is shown");
|
||||
|
||||
invokeInTab("firstCall");
|
||||
yield waitForPaused(dbg);
|
||||
|
||||
ok(isFrameSelected(dbg, 1, "secondCall"), "the first frame is selected");
|
||||
|
||||
clickElement(dbg, "frame", 2);
|
||||
ok(isFrameSelected(dbg, 2, "firstCall"), "the second frame is selected");
|
||||
|
||||
let button = toggleButton(dbg);
|
||||
ok(!button, "toggle button shouldn't be there");
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-frames.html");
|
||||
|
||||
toggleCallStack(dbg);
|
||||
|
||||
invokeInTab("startRecursion");
|
||||
yield waitForPaused(dbg);
|
||||
|
||||
ok(isFrameSelected(dbg, 1, "recurseA"), "the first frame is selected");
|
||||
|
||||
// check to make sure that the toggle button isn't there
|
||||
let button = toggleButton(dbg);
|
||||
let frames = findAllElements(dbg, "frames");
|
||||
is(button.innerText, "Expand Rows", "toggle button should be expand");
|
||||
is(frames.length, 7, "There should be at most seven frames");
|
||||
|
||||
button.click();
|
||||
|
||||
button = toggleButton(dbg);
|
||||
frames = findAllElements(dbg, "frames");
|
||||
is(button.innerText, "Collapse Rows", "toggle button should be collapse");
|
||||
is(frames.length, 22, "All of the frames should be shown");
|
||||
});
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that a chrome debugger can be created in a new process.
|
||||
*/
|
||||
|
||||
const { BrowserToolboxProcess } = Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", {});
|
||||
let gProcess = undefined;
|
||||
|
||||
function initChromeDebugger() {
|
||||
info("Initializing a chrome debugger process.");
|
||||
return new Promise(resolve => {
|
||||
BrowserToolboxProcess.init(onClose, (event, _process) => {
|
||||
info("Browser toolbox process started successfully.");
|
||||
resolve(_process);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
ok(!gProcess._dbgProcess.isRunning,
|
||||
"The remote debugger process isn't closed as it should be!");
|
||||
is(gProcess._dbgProcess.exitValue, (Services.appinfo.OS == "WINNT" ? 0 : 256),
|
||||
"The remote debugger process didn't die cleanly.");
|
||||
|
||||
info("process exit value: " + gProcess._dbgProcess.exitValue);
|
||||
|
||||
info("profile path: " + gProcess._dbgProfilePath);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("devtools.debugger.remote-enabled");
|
||||
gProcess = null;
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
// Windows XP and 8.1 test slaves are terribly slow at this test.
|
||||
requestLongerTimeout(5);
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
|
||||
|
||||
gProcess = yield initChromeDebugger();
|
||||
|
||||
ok(gProcess._dbgProcess,
|
||||
"The remote debugger process wasn't created properly!");
|
||||
ok(gProcess._dbgProcess.isRunning,
|
||||
"The remote debugger process isn't running!");
|
||||
is(typeof gProcess._dbgProcess.pid, "number",
|
||||
"The remote debugger process doesn't have a pid (?!)");
|
||||
|
||||
info("process location: " + gProcess._dbgProcess.location);
|
||||
info("process pid: " + gProcess._dbgProcess.pid);
|
||||
info("process name: " + gProcess._dbgProcess.processName);
|
||||
info("process sig: " + gProcess._dbgProcess.processSignature);
|
||||
|
||||
ok(gProcess._dbgProfilePath,
|
||||
"The remote debugger profile wasn't created properly!");
|
||||
|
||||
is(
|
||||
gProcess._dbgProfilePath,
|
||||
OS.Path.join(OS.Constants.Path.profileDir, "chrome_debugger_profile"),
|
||||
"The remote debugger profile isn't where we expect it!"
|
||||
);
|
||||
|
||||
info("profile path: " + gProcess._dbgProfilePath);
|
||||
|
||||
gProcess.close();
|
||||
});
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that chrome debugging works.
|
||||
*/
|
||||
|
||||
var gClient, gThreadClient;
|
||||
var gNewGlobal = promise.defer();
|
||||
var gNewChromeSource = promise.defer();
|
||||
|
||||
var { DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
var customLoader = new DevToolsLoader();
|
||||
customLoader.invisibleToDebugger = true;
|
||||
var { DebuggerServer } = customLoader.require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/main");
|
||||
|
||||
function initDebuggerClient() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
return new DebuggerClient(transport);
|
||||
}
|
||||
|
||||
function attachThread(client, actor) {
|
||||
return new Promise(resolve => {
|
||||
client.attachTab(actor, (response, tabClient) => {
|
||||
tabClient.attachThread(null, (r, threadClient) => {
|
||||
resolve(threadClient);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onNewGlobal() {
|
||||
ok(true, "Received a new chrome global.");
|
||||
gClient.removeListener("newGlobal", onNewGlobal);
|
||||
gNewGlobal.resolve();
|
||||
}
|
||||
|
||||
function onNewSource(event, packet) {
|
||||
if (packet.source.url.startsWith("chrome:")) {
|
||||
ok(true, "Received a new chrome source: " + packet.source.url);
|
||||
gThreadClient.removeListener("newSource", onNewSource);
|
||||
gNewChromeSource.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function resumeAndCloseConnection() {
|
||||
return new Promise(resolve => {
|
||||
gThreadClient.resume(() => resolve(gClient.close()));
|
||||
});
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
gThreadClient = null;
|
||||
gNewGlobal = null;
|
||||
gNewChromeSource = null;
|
||||
|
||||
customLoader = null;
|
||||
DebuggerServer = null;
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
gClient = initDebuggerClient();
|
||||
|
||||
const [type] = yield gClient.connect();
|
||||
is(type, "browser", "Root actor should identify itself as a browser.");
|
||||
|
||||
const response = yield gClient.getProcess();
|
||||
let actor = response.form.actor;
|
||||
gThreadClient = yield attachThread(gClient, actor);
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
|
||||
|
||||
// listen for a new source and global
|
||||
gThreadClient.addListener("newSource", onNewSource);
|
||||
gClient.addListener("newGlobal", onNewGlobal);
|
||||
yield promise.all([ gNewGlobal.promise, gNewChromeSource.promise ]);
|
||||
|
||||
yield resumeAndCloseConnection();
|
||||
});
|
||||
|
|
@ -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.");
|
||||
});
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function clickStepOver(dbg) {
|
||||
clickElement(dbg, "stepOver");
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
function clickStepIn(dbg) {
|
||||
clickElement(dbg, "stepIn");
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
function clickStepOut(dbg) {
|
||||
clickElement(dbg, "stepOut");
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test debugger buttons
|
||||
* 1. resume
|
||||
* 2. stepOver
|
||||
* 3. stepIn
|
||||
* 4. stepOver to the end of a function
|
||||
* 5. stepUp at the end of a function
|
||||
*/
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-debugger-statements.html");
|
||||
|
||||
yield reload(dbg);
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 8);
|
||||
|
||||
// resume
|
||||
clickElement(dbg, "resume");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 12);
|
||||
|
||||
// step over
|
||||
yield clickStepOver(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 13);
|
||||
|
||||
// step into
|
||||
yield clickStepIn(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 18);
|
||||
|
||||
// step over
|
||||
yield clickStepOver(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 20);
|
||||
|
||||
// step out
|
||||
yield clickStepOut(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 20);
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests the breakpoint gutter and making sure breakpoint icons exist
|
||||
// correctly
|
||||
|
||||
// Utilities for interacting with the editor
|
||||
function clickGutter(dbg, line) {
|
||||
clickElement(dbg, "gutter", line);
|
||||
}
|
||||
|
||||
function getLineEl(dbg, line) {
|
||||
const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
|
||||
return lines[line - 1];
|
||||
}
|
||||
|
||||
function assertEditorBreakpoint(dbg, line, shouldExist) {
|
||||
const exists = !!getLineEl(dbg, line).querySelector(".new-breakpoint");
|
||||
ok(exists === shouldExist,
|
||||
"Breakpoint " + (shouldExist ? "exists" : "does not exist") +
|
||||
" on line " + line);
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
const { selectors: { getBreakpoints, getBreakpoint }, getState } = dbg;
|
||||
const source = findSource(dbg, "simple1.js");
|
||||
|
||||
yield selectSource(dbg, source.url);
|
||||
|
||||
// Make sure that clicking the gutter creates a breakpoint icon.
|
||||
clickGutter(dbg, 4);
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
|
||||
assertEditorBreakpoint(dbg, 4, true);
|
||||
|
||||
// Make sure clicking at the same place removes the icon.
|
||||
clickGutter(dbg, 4);
|
||||
yield waitForDispatch(dbg, "REMOVE_BREAKPOINT");
|
||||
is(getBreakpoints(getState()).size, 0, "No breakpoints exist");
|
||||
assertEditorBreakpoint(dbg, 4, false);
|
||||
|
||||
// Test that a breakpoint icon slides down to the correct line.
|
||||
clickGutter(dbg, 2);
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
|
||||
ok(getBreakpoint(getState(), { sourceId: source.id, line: 4 }),
|
||||
"Breakpoint has correct line");
|
||||
assertEditorBreakpoint(dbg, 2, false);
|
||||
assertEditorBreakpoint(dbg, 4, true);
|
||||
|
||||
// Do the same sliding and make sure it works if there's already a
|
||||
// breakpoint.
|
||||
clickGutter(dbg, 2);
|
||||
yield waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
|
||||
assertEditorBreakpoint(dbg, 2, false);
|
||||
assertEditorBreakpoint(dbg, 4, true);
|
||||
|
||||
clickGutter(dbg, 4);
|
||||
yield waitForDispatch(dbg, "REMOVE_BREAKPOINT");
|
||||
is(getBreakpoints(getState()).size, 0, "No breakpoints exist");
|
||||
assertEditorBreakpoint(dbg, 4, false);
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* 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 + "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.
|
||||
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);
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the editor sets the correct mode for different file
|
||||
// types
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
|
||||
yield selectSource(dbg, "simple1.js");
|
||||
is(dbg.win.cm.getOption("mode").name, "javascript", "Mode is correct");
|
||||
|
||||
yield selectSource(dbg, "doc-scripts.html");
|
||||
is(dbg.win.cm.getOption("mode").name, "htmlmixed", "Mode is correct");
|
||||
});
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the editor highlights the correct location when the
|
||||
// debugger pauses
|
||||
|
||||
// checks to see if the first breakpoint is visible
|
||||
function isElementVisible(dbg, elementName) {
|
||||
const bpLine = findElement(dbg, elementName);
|
||||
const cm = findElement(dbg, "codeMirror");
|
||||
return bpLine && isVisibleWithin(cm, bpLine);
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
// This test runs too slowly on linux debug. I'd like to figure out
|
||||
// which is the slowest part of this and make it run faster, but to
|
||||
// fix a frequent failure allow a longer timeout.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const dbg = yield initDebugger("doc-scripts.html");
|
||||
const { selectors: { getSelectedSource }, getState } = dbg;
|
||||
const simple1 = findSource(dbg, "simple1.js");
|
||||
const simple2 = findSource(dbg, "simple2.js");
|
||||
|
||||
// Set the initial breakpoint.
|
||||
yield addBreakpoint(dbg, simple1, 4);
|
||||
ok(!getSelectedSource(getState()), "No selected source");
|
||||
|
||||
// Call the function that we set a breakpoint in.
|
||||
invokeInTab("main");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, simple1, 4);
|
||||
|
||||
// Step through to another file and make sure it's paused in the
|
||||
// right place.
|
||||
yield stepIn(dbg);
|
||||
assertPausedLocation(dbg, simple2, 2);
|
||||
|
||||
// Step back out to the initial file.
|
||||
yield stepOut(dbg);
|
||||
yield stepOut(dbg);
|
||||
assertPausedLocation(dbg, simple1, 5);
|
||||
yield resume(dbg);
|
||||
|
||||
// Make sure that we can set a breakpoint on a line out of the
|
||||
// viewport, and that pausing there scrolls the editor to it.
|
||||
let longSrc = findSource(dbg, "long.js");
|
||||
yield addBreakpoint(dbg, longSrc, 66);
|
||||
|
||||
invokeInTab("testModel");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, longSrc, 66);
|
||||
ok(isElementVisible(dbg, "breakpoint"), "Breakpoint is visible");
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test debugging a page with iframes
|
||||
* 1. pause in the main thread
|
||||
* 2. pause in the iframe
|
||||
*/
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-iframes.html");
|
||||
|
||||
// test pausing in the main thread
|
||||
yield reload(dbg);
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "iframes.html", 8);
|
||||
|
||||
// test pausing in the iframe
|
||||
yield resume(dbg);
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 8);
|
||||
|
||||
// test pausing in the iframe
|
||||
yield resume(dbg);
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "debugger-statements.html", 12);
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* 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");
|
||||
const { selectors: { getSelectedSource, getPause }, getState } = dbg;
|
||||
|
||||
invokeInTab("firstCall");
|
||||
yield waitForPaused(dbg);
|
||||
|
||||
yield navigate(dbg, "doc-scripts.html", "simple1.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.");
|
||||
ok(!getPause(getState()), "No pause state exists");
|
||||
|
||||
yield navigate(dbg,
|
||||
"doc-scripts.html",
|
||||
"simple1.js",
|
||||
"simple2.js",
|
||||
"long.js",
|
||||
"scripts.html"
|
||||
);
|
||||
|
||||
is(countSources(dbg), 4, "4 sources are loaded.");
|
||||
|
||||
// Test that the current select source persists across reloads
|
||||
yield selectSource(dbg, "long.js");
|
||||
yield reload(dbg, "long.js");
|
||||
ok(getSelectedSource(getState()).get("url").includes("long.js"),
|
||||
"Selected source is long.js");
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function uncaughtException() {
|
||||
return invokeInTab("uncaughtException").catch(() => {});
|
||||
}
|
||||
|
||||
function caughtException() {
|
||||
return invokeInTab("caughtException");
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Pausing on exception
|
||||
1. skip an uncaught exception
|
||||
2. pause on an uncaught exception
|
||||
3. pause on a caught error
|
||||
4. skip a caught error
|
||||
*/
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-exceptions.html");
|
||||
|
||||
// test skipping an uncaught exception
|
||||
yield togglePauseOnExceptions(dbg, false, false);
|
||||
yield uncaughtException();
|
||||
ok(!isPaused(dbg));
|
||||
|
||||
// Test pausing on an uncaught exception
|
||||
yield togglePauseOnExceptions(dbg, true, false);
|
||||
uncaughtException();
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "exceptions.js", 2);
|
||||
yield resume(dbg);
|
||||
|
||||
// Test pausing on a caught Error
|
||||
caughtException();
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "exceptions.js", 15);
|
||||
yield resume(dbg);
|
||||
|
||||
// Test skipping a caught error
|
||||
yield togglePauseOnExceptions(dbg, true, true);
|
||||
caughtException();
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "exceptions.js", 17);
|
||||
yield resume(dbg);
|
||||
});
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests pretty-printing a source that is currently paused.
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-minified.html");
|
||||
|
||||
yield selectSource(dbg, "math.min.js");
|
||||
yield addBreakpoint(dbg, "math.min.js", 2);
|
||||
|
||||
invokeInTab("arithmetic");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "math.min.js", 2);
|
||||
|
||||
clickElement(dbg, "prettyPrintButton");
|
||||
yield waitForDispatch(dbg, "TOGGLE_PRETTY_PRINT");
|
||||
|
||||
assertPausedLocation(dbg, "math.min.js:formatted", 18);
|
||||
|
||||
yield resume(dbg);
|
||||
});
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests basic pretty-printing functionality.
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-minified.html");
|
||||
|
||||
yield selectSource(dbg, "math.min.js");
|
||||
clickElement(dbg, "prettyPrintButton");
|
||||
yield waitForDispatch(dbg, "TOGGLE_PRETTY_PRINT");
|
||||
|
||||
const ppSrc = findSource(dbg, "math.min.js:formatted");
|
||||
ok(ppSrc, "Pretty-printed source exists");
|
||||
|
||||
yield addBreakpoint(dbg, ppSrc, 18);
|
||||
|
||||
invokeInTab("arithmetic");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, ppSrc, 18);
|
||||
yield stepOver(dbg);
|
||||
assertPausedLocation(dbg, ppSrc, 27);
|
||||
yield resume(dbg);
|
||||
|
||||
// The pretty-print button should go away in the pretty-printed
|
||||
// source.
|
||||
ok(!findElement(dbg, "sourceFooter"), "Footer is hidden");
|
||||
|
||||
yield selectSource(dbg, "math.min.js");
|
||||
ok(findElement(dbg, "sourceFooter"), "Footer is hidden");
|
||||
});
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function toggleNode(dbg, index) {
|
||||
clickElement(dbg, "scopeNode", index);
|
||||
}
|
||||
|
||||
function getLabel(dbg, index) {
|
||||
return findElement(dbg, "scopeNode", index).innerText;
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-script-switching.html");
|
||||
|
||||
toggleScopes(dbg);
|
||||
|
||||
invokeInTab("firstCall");
|
||||
yield waitForPaused(dbg);
|
||||
|
||||
toggleNode(dbg, 1);
|
||||
toggleNode(dbg, 2);
|
||||
|
||||
yield waitForDispatch(dbg, "LOAD_OBJECT_PROPERTIES");
|
||||
|
||||
is(getLabel(dbg, 1), "secondCall");
|
||||
is(getLabel(dbg, 2), "<this>");
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Testing source search
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-script-switching.html");
|
||||
|
||||
pressKey(dbg, "sourceSearch");
|
||||
yield waitForElement(dbg, "input");
|
||||
findElementWithSelector(dbg, "input").focus();
|
||||
type(dbg, "sw");
|
||||
pressKey(dbg, "Enter");
|
||||
|
||||
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
||||
let source = dbg.selectors.getSelectedSource(dbg.getState());
|
||||
ok(source.get("url").match(/switching-01/), "first source is selected");
|
||||
|
||||
// 2. arrow keys and check to see if source is selected
|
||||
pressKey(dbg, "sourceSearch");
|
||||
findElementWithSelector(dbg, "input").focus();
|
||||
type(dbg, "sw");
|
||||
pressKey(dbg, "Down");
|
||||
pressKey(dbg, "Enter");
|
||||
|
||||
yield waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
||||
source = dbg.selectors.getSelectedSource(dbg.getState());
|
||||
ok(source.get("url").match(/switching-02/), "second source is selected");
|
||||
});
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that an error while loading a sourcemap does not break
|
||||
// debugging.
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-sourcemap-bogus.html");
|
||||
const { selectors: { getSources }, getState } = dbg;
|
||||
|
||||
yield selectSource(dbg, "bogus-map.js");
|
||||
|
||||
// We should still be able to set breakpoints and pause in the
|
||||
// generated source.
|
||||
yield addBreakpoint(dbg, "bogus-map.js", 4);
|
||||
invokeInTab("runCode");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, "bogus-map.js", 4);
|
||||
|
||||
// Make sure that only the single generated source exists. The
|
||||
// sourcemap failed to download.
|
||||
is(getSources(getState()).size, 1, "Only 1 source exists");
|
||||
});
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests loading sourcemapped sources, setting breakpoints, and
|
||||
// stepping in them.
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-sourcemaps.html");
|
||||
const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
|
||||
|
||||
yield waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js");
|
||||
ok(true, "Original sources exist");
|
||||
const entrySrc = findSource(dbg, "entry.js");
|
||||
|
||||
yield selectSource(dbg, entrySrc);
|
||||
ok(dbg.win.cm.getValue().includes("window.keepMeAlive"),
|
||||
"Original source text loaded correctly");
|
||||
|
||||
// Test that breakpoint sliding is not attempted. The breakpoint
|
||||
// should not move anywhere.
|
||||
yield addBreakpoint(dbg, entrySrc, 13);
|
||||
is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
|
||||
ok(getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
|
||||
"Breakpoint has correct line");
|
||||
|
||||
// Test breaking on a breakpoint
|
||||
yield addBreakpoint(dbg, "entry.js", 15);
|
||||
is(getBreakpoints(getState()).size, 2, "Two breakpoints exist");
|
||||
ok(getBreakpoint(getState(), { sourceId: entrySrc.id, line: 15 }),
|
||||
"Breakpoint has correct line");
|
||||
|
||||
invokeInTab("keepMeAlive");
|
||||
yield waitForPaused(dbg);
|
||||
assertPausedLocation(dbg, entrySrc, 15);
|
||||
|
||||
yield stepIn(dbg);
|
||||
assertPausedLocation(dbg, "times2.js", 2);
|
||||
yield stepOver(dbg);
|
||||
assertPausedLocation(dbg, "times2.js", 3);
|
||||
|
||||
yield stepOut(dbg);
|
||||
yield stepOut(dbg);
|
||||
assertPausedLocation(dbg, "entry.js", 16);
|
||||
});
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the source tree works.
|
||||
|
||||
function* waitForSourceCount(dbg, i) {
|
||||
// We are forced to wait until the DOM nodes appear because the
|
||||
// source tree batches its rendering.
|
||||
yield waitUntil(() => {
|
||||
return findAllElements(dbg, "sourceNodes").length === i;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const dbg = yield initDebugger("doc-sources.html");
|
||||
const { selectors: { getSelectedSource }, getState } = dbg;
|
||||
|
||||
// Expand nodes and make sure more sources appear.
|
||||
is(findAllElements(dbg, "sourceNodes").length, 2);
|
||||
|
||||
clickElement(dbg, "sourceArrow", 2);
|
||||
is(findAllElements(dbg, "sourceNodes").length, 7);
|
||||
|
||||
clickElement(dbg, "sourceArrow", 3);
|
||||
is(findAllElements(dbg, "sourceNodes").length, 8);
|
||||
|
||||
// Select a source.
|
||||
ok(!findElementWithSelector(dbg, ".sources-list .focused"),
|
||||
"Source is not focused");
|
||||
const selected = waitForDispatch(dbg, "SELECT_SOURCE");
|
||||
clickElement(dbg, "sourceNode", 4);
|
||||
yield selected;
|
||||
ok(findElementWithSelector(dbg, ".sources-list .focused"),
|
||||
"Source is focused");
|
||||
ok(getSelectedSource(getState()).get("url").includes("nested-source.js"),
|
||||
"The right source is selected");
|
||||
|
||||
// Make sure new sources appear in the list.
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const script = content.document.createElement("script");
|
||||
script.src = "math.min.js";
|
||||
content.document.body.appendChild(script);
|
||||
});
|
||||
|
||||
yield waitForSourceCount(dbg, 9);
|
||||
is(findElement(dbg, "sourceNode", 7).querySelector("span").innerText,
|
||||
"math.min.js",
|
||||
"The dynamic script exists");
|
||||
|
||||
// Make sure named eval sources appear in the list.
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
content.eval("window.evaledFunc = function() {} //# sourceURL=evaled.js");
|
||||
});
|
||||
yield waitForSourceCount(dbg, 11);
|
||||
is(findElement(dbg, "sourceNode", 2).querySelector("span").innerText,
|
||||
"evaled.js",
|
||||
"The eval script exists");
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* 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");
|
||||
|
||||
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);
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
### Test Examples
|
||||
|
||||
##### Pages
|
||||
* **doc_script-switching-01** - includes two scripts that reference each other. The second function has a debugger.
|
||||
* **doc-scripts** - includes three sources, a long source and two sources that reference each other.
|
||||
* **doc-iframes** - includes an iframe with the debugger statements source.
|
||||
* **debugger-statements** - inline script with functions for testing stepping.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
function runCode(){
|
||||
var a=1;
|
||||
a=a*2;
|
||||
return a;
|
||||
}
|
||||
//# sourceMappingURL=bogus.map
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId])
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ exports: {},
|
||||
/******/ id: moduleId,
|
||||
/******/ loaded: false
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.loaded = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(0);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
const times2 = __webpack_require__(1);
|
||||
const { output } = __webpack_require__(2);
|
||||
const opts = __webpack_require__(3);
|
||||
|
||||
output(times2(1));
|
||||
output(times2(2));
|
||||
|
||||
if(opts.extra) {
|
||||
output(times2(3));
|
||||
}
|
||||
|
||||
window.keepMeAlive = function() {
|
||||
// This function exists to make sure this script is never garbage
|
||||
// collected. It is also callable from tests.
|
||||
return times2(4);
|
||||
}
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 1 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
module.exports = function(x) {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 2 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
function output(str) {
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
module.exports = { output };
|
||||
|
||||
|
||||
/***/ },
|
||||
/* 3 */
|
||||
/***/ function(module, exports) {
|
||||
|
||||
module.exports = {
|
||||
extra: true
|
||||
};
|
||||
|
||||
|
||||
/***/ }
|
||||
/******/ ]);
|
||||
//# sourceMappingURL=bundle.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///webpack/bootstrap 4ef8c7ec7c1df790781e","webpack:///./entry.js","webpack:///./times2.js","webpack:///./output.js","webpack:///./opts.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA","file":"bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 4ef8c7ec7c1df790781e","const times2 = require(\"./times2\");\nconst { output } = require(\"./output\");\nconst opts = require(\"./opts\");\n\noutput(times2(1));\noutput(times2(2));\n\nif(opts.extra) {\n output(times2(3));\n}\n\nwindow.keepMeAlive = function() {\n // This function exists to make sure this script is never garbage\n // collected. It is also callable from tests.\n return times2(4);\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./entry.js\n// module id = 0\n// module chunks = 0","module.exports = function(x) {\n return x * 2;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./times2.js\n// module id = 1\n// module chunks = 0","function output(str) {\n console.log(str);\n}\n\nmodule.exports = { output };\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./output.js\n// module id = 2\n// module chunks = 0","module.exports = {\n extra: true\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./opts.js\n// module id = 3\n// module chunks = 0"],"sourceRoot":""}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Debugger Statements</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
debugger;
|
||||
test();
|
||||
|
||||
function test() {
|
||||
debugger;
|
||||
stepIntoMe();
|
||||
}
|
||||
|
||||
function stepIntoMe() {
|
||||
// step in
|
||||
stepOverMe();
|
||||
// step out
|
||||
}
|
||||
|
||||
function stepOverMe() {
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Debugger test page</title>
|
||||
<script type="text/javascript" src="exceptions.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Frames</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
debugger;
|
||||
// This inline script allows this HTML page to show up as a
|
||||
// source. It also needs to introduce a new global variable so
|
||||
// it's not immediately garbage collected.
|
||||
function inline_script() { var x = 5; }
|
||||
</script>
|
||||
<script type="text/javascript" src="frames.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Iframe</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
debugger;
|
||||
// This inline script allows this HTML page to show up as a
|
||||
// source. It also needs to introduce a new global variable so
|
||||
// it's not immediately garbage collected.
|
||||
function inline_script() { var x = 5; }
|
||||
</script>
|
||||
<iframe src="doc-debugger-statements.html"></iframe>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="math.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<button onclick="firstCall()">Click me!</button>
|
||||
|
||||
<script type="text/javascript" src="script-switching-01.js"></script>
|
||||
<script type="text/javascript" src="script-switching-02.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="simple1.js"></script>
|
||||
<script src="simple2.js"></script>
|
||||
<script src="long.js"></script>
|
||||
<script>
|
||||
// This inline script allows this HTML page to show up as a
|
||||
// source. It also needs to introduce a new global variable so
|
||||
// it's not immediately garbage collected.
|
||||
function inline_script() { var x = 5; }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="bogus-map.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="simple1.js"></script>
|
||||
<script src="simple2.js"></script>
|
||||
<script src="long.js"></script>
|
||||
<script>
|
||||
// This inline script allows this HTML page to show up as a
|
||||
// source. It also needs to introduce a new global variable so
|
||||
// it's not immediately garbage collected.
|
||||
function inline_script() { var x = 5; }
|
||||
</script>
|
||||
<script src="nested/nested-source.js"></script>
|
||||
<script src="nested/deeper/deeper-source.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
const times2 = require("./times2");
|
||||
const { output } = require("./output");
|
||||
const opts = require("./opts");
|
||||
|
||||
output(times2(1));
|
||||
output(times2(2));
|
||||
|
||||
if(opts.extra) {
|
||||
output(times2(3));
|
||||
}
|
||||
|
||||
window.keepMeAlive = function() {
|
||||
// This function exists to make sure this script is never garbage
|
||||
// collected. It is also callable from tests.
|
||||
return times2(4);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
function uncaughtException() {
|
||||
throw "unreachable"
|
||||
}
|
||||
|
||||
function caughtError() {
|
||||
try {
|
||||
throw new Error("error");
|
||||
} catch (e) {
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
|
||||
function caughtException() {
|
||||
try {
|
||||
throw "reachable";
|
||||
} catch (e) {
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
function recurseA(i) {
|
||||
if (i == 20) {
|
||||
debugger;
|
||||
return;
|
||||
}
|
||||
|
||||
// down into the rabbit hole we go
|
||||
return (i % 2) ? recurseA(++i) : recurseB(++i);
|
||||
}
|
||||
|
||||
function recurseB(i) {
|
||||
if (i == 20) {
|
||||
debugger;
|
||||
return;
|
||||
}
|
||||
|
||||
// down into the rabbit hole we go
|
||||
return (i % 2) ? recurseA(++i) : recurseB(++i);
|
||||
}
|
||||
|
||||
|
||||
window.startRecursion = function() {
|
||||
return recurseA(0);
|
||||
}
|
||||
76
devtools/client/debugger/new/test/mochitest/examples/long.js
Normal file
76
devtools/client/debugger/new/test/mochitest/examples/long.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
var app = {};
|
||||
|
||||
// Generic "model" object. You can use whatever
|
||||
// framework you want. For this application it
|
||||
// may not even be worth separating this logic
|
||||
// out, but we do this to demonstrate one way to
|
||||
// separate out parts of your application.
|
||||
app.TodoModel = function (key) {
|
||||
this.key = key;
|
||||
this.todos = [];
|
||||
this.onChanges = [];
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.addTodo = function (title) {
|
||||
this.todos = this.todos.concat([{
|
||||
id: Utils.uuid(),
|
||||
title: title,
|
||||
completed: false
|
||||
}]);
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.inform = function() {
|
||||
// Something changed, but we do nothing
|
||||
return null;
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggleAll = function (checked) {
|
||||
// Note: it's usually better to use immutable data structures since they're
|
||||
// easier to reason about and React works very well with them. That's why
|
||||
// we use map() and filter() everywhere instead of mutating the array or
|
||||
// todo items themselves.
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return Object.assign({}, todo, {completed: checked});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.toggle = function (todoToToggle) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToToggle ?
|
||||
todo :
|
||||
Object.assign({}, todo, {completed: !todo.completed});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.destroy = function (todo) {
|
||||
this.todos = this.todos.filter(function (candidate) {
|
||||
return candidate !== todo;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.save = function (todoToSave, text) {
|
||||
this.todos = this.todos.map(function (todo) {
|
||||
return todo !== todoToSave ? todo : Object.assign({}, todo, {title: text});
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
app.TodoModel.prototype.clearCompleted = function () {
|
||||
this.todos = this.todos.filter(function (todo) {
|
||||
return !todo.completed;
|
||||
});
|
||||
|
||||
this.inform();
|
||||
};
|
||||
|
||||
function testModel() {
|
||||
const model = new app.TodoModel();
|
||||
model.clearCompleted();
|
||||
}
|
||||
3
devtools/client/debugger/new/test/mochitest/examples/math.min.js
vendored
Normal file
3
devtools/client/debugger/new/test/mochitest/examples/math.min.js
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
function add(a,b,k){var result=a+b;return k(result)}function sub(a,b,k){var result=a-b;return k(result)}function mul(a,b,k){var result=a*b;return k(result)}function div(a,b,k){var result=a/b;return k(result)}function arithmetic(){
|
||||
add(4,4,function(a){
|
||||
sub(a,2,function(b){mul(b,3,function(c){div(c,2,function(d){console.log(d)})})})})};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
function computeSomething() {
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
extra: true
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
function output(str) {
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
module.exports = { output };
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function firstCall() {
|
||||
secondCall();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function secondCall() {
|
||||
// This comment is useful: ☺
|
||||
debugger;
|
||||
function foo() {}
|
||||
if (x) {
|
||||
foo();
|
||||
}
|
||||
}
|
||||
|
||||
var x = true;
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
function main() {
|
||||
// A comment so we can test that breakpoint sliding works across
|
||||
// multiple lines
|
||||
const func = foo(1, 2);
|
||||
const result = func();
|
||||
return result;
|
||||
}
|
||||
|
||||
function doEval() {
|
||||
eval("(" + function() {
|
||||
debugger;
|
||||
|
||||
window.evaledFunc = function() {
|
||||
var foo = 1;
|
||||
var bar = 2;
|
||||
return foo + bar;
|
||||
};
|
||||
}.toString() + ")()");
|
||||
}
|
||||
|
||||
function doNamedEval() {
|
||||
eval("(" + function() {
|
||||
debugger;
|
||||
|
||||
window.evaledFunc = function() {
|
||||
var foo = 1;
|
||||
var bar = 2;
|
||||
return foo + bar;
|
||||
};
|
||||
}.toString() + ")();\n //# sourceURL=evaled.js");
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
function foo(x, y) {
|
||||
function bar() {
|
||||
return x + y;
|
||||
}
|
||||
return bar;
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = function(x) {
|
||||
return x * 2;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
module.exports = {
|
||||
entry: "./entry.js",
|
||||
output: {
|
||||
filename: "bundle.js"
|
||||
},
|
||||
devtool: "sourcemap"
|
||||
}
|
||||
684
devtools/client/debugger/new/test/mochitest/head.js
Normal file
684
devtools/client/debugger/new/test/mochitest/head.js
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* The Mochitest API documentation
|
||||
* @module mochitest
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mochitest API to wait for certain events.
|
||||
* @module mochitest/waits
|
||||
* @parent mochitest
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mochitest API predefined asserts.
|
||||
* @module mochitest/asserts
|
||||
* @parent mochitest
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mochitest API for interacting with the debugger.
|
||||
* @module mochitest/actions
|
||||
* @parent mochitest
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper methods for the mochitest API.
|
||||
* @module mochitest/helpers
|
||||
* @parent mochitest
|
||||
*/
|
||||
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
|
||||
|
||||
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
|
||||
delete window.resumeTest;
|
||||
});
|
||||
|
||||
// Wait until an action of `type` is dispatched. This is different
|
||||
// then `_afterDispatchDone` because it doesn't wait for async actions
|
||||
// to be done/errored. Use this if you want to listen for the "start"
|
||||
// action of an async operation (somewhat rare).
|
||||
function waitForNextDispatch(store, type) {
|
||||
return new Promise(resolve => {
|
||||
store.dispatch({
|
||||
// Normally we would use `services.WAIT_UNTIL`, but use the
|
||||
// internal name here so tests aren't forced to always pass it
|
||||
// in
|
||||
type: "@@service/waitUntil",
|
||||
predicate: action => action.type === type,
|
||||
run: (dispatch, getState, action) => {
|
||||
resolve(action);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Wait until an action of `type` is dispatched. If it's part of an
|
||||
// async operation, wait until the `status` field is "done" or "error"
|
||||
function _afterDispatchDone(store, type) {
|
||||
return new Promise(resolve => {
|
||||
store.dispatch({
|
||||
// Normally we would use `services.WAIT_UNTIL`, but use the
|
||||
// internal name here so tests aren't forced to always pass it
|
||||
// in
|
||||
type: "@@service/waitUntil",
|
||||
predicate: action => {
|
||||
if (action.type === type) {
|
||||
return action.status ?
|
||||
(action.status === "done" || action.status === "error") :
|
||||
true;
|
||||
}
|
||||
},
|
||||
run: (dispatch, getState, action) => {
|
||||
resolve(action);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a specific action type to be dispatch.
|
||||
* If an async action, will wait for it to be done.
|
||||
*
|
||||
* @memberof mochitest/waits
|
||||
* @param {Object} dbg
|
||||
* @param {String} type
|
||||
* @param {Number} eventRepeat
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function waitForDispatch(dbg, type, eventRepeat = 1) {
|
||||
let count = 0;
|
||||
|
||||
return Task.spawn(function* () {
|
||||
info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
|
||||
while (count < eventRepeat) {
|
||||
yield _afterDispatchDone(dbg.store, type);
|
||||
count++;
|
||||
info(type + " dispatched " + count + " time(s)");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for specific thread events.
|
||||
*
|
||||
* @memberof mochitest/waits
|
||||
* @param {Object} dbg
|
||||
* @param {String} eventName
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function waitForThreadEvents(dbg, eventName) {
|
||||
info("Waiting for thread event '" + eventName + "' to fire.");
|
||||
const thread = dbg.toolbox.threadClient;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
thread.addListener(eventName, function onEvent(eventName, ...args) {
|
||||
info("Thread event '" + eventName + "' fired.");
|
||||
thread.removeListener(eventName, onEvent);
|
||||
resolve.apply(resolve, args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for `predicate(state)` to be true. `state` is the redux app state.
|
||||
*
|
||||
* @memberof mochitest/waits
|
||||
* @param {Object} dbg
|
||||
* @param {Function} predicate
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function waitForState(dbg, predicate) {
|
||||
return new Promise(resolve => {
|
||||
const unsubscribe = dbg.store.subscribe(() => {
|
||||
if (predicate(dbg.store.getState())) {
|
||||
unsubscribe();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for sources to be loaded.
|
||||
*
|
||||
* @memberof mochitest/waits
|
||||
* @param {Object} dbg
|
||||
* @param {Array} sources
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
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 => {
|
||||
return s.get("url").includes(url);
|
||||
});
|
||||
}
|
||||
|
||||
if (!sourceExists(store.getState())) {
|
||||
return waitForState(dbg, sourceExists);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function waitForElement(dbg, selector) {
|
||||
return waitUntil(() => findElementWithSelector(dbg, selector))
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the debugger is paused at the correct location.
|
||||
*
|
||||
* @memberof mochitest/asserts
|
||||
* @param {Object} dbg
|
||||
* @param {String} source
|
||||
* @param {Number} line
|
||||
* @static
|
||||
*/
|
||||
function assertPausedLocation(dbg, source, line) {
|
||||
const { selectors: { getSelectedSource, getPause }, getState } = dbg;
|
||||
source = findSource(dbg, source);
|
||||
|
||||
// Check the selected source
|
||||
is(getSelectedSource(getState()).get("id"), source.id);
|
||||
|
||||
// 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
|
||||
ok(dbg.win.cm.lineInfo(line - 1).wrapClass.includes("debug-line"),
|
||||
"Line is highlighted as paused");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the debugger is highlighting the correct location.
|
||||
*
|
||||
* @memberof mochitest/asserts
|
||||
* @param {Object} dbg
|
||||
* @param {String} source
|
||||
* @param {Number} line
|
||||
* @static
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean for whether the debugger is paused.
|
||||
*
|
||||
* @memberof mochitest/asserts
|
||||
* @param {Object} dbg
|
||||
* @static
|
||||
*/
|
||||
function isPaused(dbg) {
|
||||
const { selectors: { getPause }, getState } = dbg;
|
||||
return !!getPause(getState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the debugger to be fully paused.
|
||||
*
|
||||
* @memberof mochitest/waits
|
||||
* @param {Object} dbg
|
||||
* @static
|
||||
*/
|
||||
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);
|
||||
// Make sure we have the paused state.
|
||||
if (!pause) {
|
||||
return false;
|
||||
}
|
||||
// Make sure the source text is completely loaded for the
|
||||
// source we are paused in.
|
||||
const sourceId = pause.getIn(["frame", "location", "sourceId"]);
|
||||
const sourceText = dbg.selectors.getSourceText(dbg.getState(), sourceId);
|
||||
return sourceText && !sourceText.get("loading");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createDebuggerContext(toolbox) {
|
||||
const win = toolbox.getPanel("jsdebugger").panelWin;
|
||||
const store = win.Debugger.store;
|
||||
|
||||
return {
|
||||
actions: win.Debugger.actions,
|
||||
selectors: win.Debugger.selectors,
|
||||
getState: store.getState,
|
||||
store: store,
|
||||
client: win.Debugger.client,
|
||||
toolbox: toolbox,
|
||||
win: win
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Intilializes the debugger.
|
||||
*
|
||||
* @memberof mochitest
|
||||
* @param {String} url
|
||||
* @param {Array} sources
|
||||
* @return {Promise} dbg
|
||||
* @static
|
||||
*/
|
||||
function initDebugger(url, ...sources) {
|
||||
return Task.spawn(function* () {
|
||||
const toolbox = yield openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
|
||||
return createDebuggerContext(toolbox);
|
||||
});
|
||||
}
|
||||
|
||||
window.resumeTest = undefined;
|
||||
/**
|
||||
* Pause the test and let you interact with the debugger.
|
||||
* The test can be resumed by invoking `resumeTest` in the console.
|
||||
*
|
||||
* @memberof mochitest
|
||||
* @static
|
||||
*/
|
||||
function pauseTest() {
|
||||
info("Test paused. Invoke resumeTest to continue.");
|
||||
return new Promise(resolve => resumeTest = resolve);
|
||||
}
|
||||
|
||||
// Actions
|
||||
/**
|
||||
* Returns a source that matches the URL.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {String} url
|
||||
* @return {Object} source
|
||||
* @static
|
||||
*/
|
||||
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));
|
||||
|
||||
if (!source) {
|
||||
throw new Error("Unable to find source: " + url);
|
||||
}
|
||||
|
||||
return source.toJS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the source.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {String} url
|
||||
* @param {Number} line
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function selectSource(dbg, url, line) {
|
||||
info("Selecting source: " + url);
|
||||
const source = findSource(dbg, url);
|
||||
const hasText = !!dbg.selectors.getSourceText(dbg.getState(), source.id);
|
||||
dbg.actions.selectSource(source.id, { line });
|
||||
|
||||
if (!hasText) {
|
||||
return waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps over.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function stepOver(dbg) {
|
||||
info("Stepping over");
|
||||
dbg.actions.stepOver();
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps in.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function stepIn(dbg) {
|
||||
info("Stepping in");
|
||||
dbg.actions.stepIn();
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps out.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function stepOut(dbg) {
|
||||
info("Stepping out");
|
||||
dbg.actions.stepOut();
|
||||
return waitForPaused(dbg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function resume(dbg) {
|
||||
info("Resuming");
|
||||
dbg.actions.resume();
|
||||
return waitForThreadEvents(dbg, "resumed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the debuggee.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {Array} sources
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function reload(dbg, ...sources) {
|
||||
return dbg.client.reload().then(() => waitForSources(...sources));
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates the debuggee to another url.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {String} url
|
||||
* @param {Array} sources
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function navigate(dbg, url, ...sources) {
|
||||
dbg.client.navigate(url);
|
||||
return waitForSources(dbg, ...sources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a breakpoint to a source at line/col.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {String} source
|
||||
* @param {Number} line
|
||||
* @param {Number} col
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function addBreakpoint(dbg, source, line, col) {
|
||||
source = findSource(dbg, source);
|
||||
const sourceId = source.id;
|
||||
dbg.actions.addBreakpoint({ sourceId, line, col });
|
||||
return waitForDispatch(dbg, "ADD_BREAKPOINT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a breakpoint from a source at line/col.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {String} source
|
||||
* @param {Number} line
|
||||
* @param {Number} col
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function removeBreakpoint(dbg, sourceId, line, col) {
|
||||
return dbg.actions.removeBreakpoint({ sourceId, line, col });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the Pause on exceptions feature in the debugger.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @param {Boolean} pauseOnExceptions
|
||||
* @param {Boolean} ignoreCaughtExceptions
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function togglePauseOnExceptions(dbg,
|
||||
pauseOnExceptions, ignoreCaughtExceptions) {
|
||||
const command = dbg.actions.pauseOnExceptions(
|
||||
pauseOnExceptions,
|
||||
ignoreCaughtExceptions
|
||||
);
|
||||
|
||||
if (!isPaused(dbg)) {
|
||||
return waitForThreadEvents(dbg, "resumed");
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
/**
|
||||
* Invokes a global function in the debuggee tab.
|
||||
*
|
||||
* @memberof mochitest/helpers
|
||||
* @param {String} fnc
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
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 cmdOrCtrl = isLinux ? { ctrlKey: true } : { metaKey: true };
|
||||
const keyMappings = {
|
||||
sourceSearch: { code: "p", modifiers: cmdOrCtrl},
|
||||
fileSearch: { code: "f", modifiers: cmdOrCtrl},
|
||||
"Enter": { code: "VK_RETURN" },
|
||||
"Up": { code: "VK_UP" },
|
||||
"Down": { code: "VK_DOWN" },
|
||||
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 }}
|
||||
};
|
||||
|
||||
/**
|
||||
* Simulates a key press in the debugger window.
|
||||
*
|
||||
* @memberof mochitest/helpers
|
||||
* @param {Object} dbg
|
||||
* @param {String} keyName
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function pressKey(dbg, keyName) {
|
||||
let keyEvent = keyMappings[keyName];
|
||||
|
||||
const { code, modifiers } = keyEvent;
|
||||
return EventUtils.synthesizeKey(
|
||||
code,
|
||||
modifiers || {},
|
||||
dbg.win
|
||||
);
|
||||
}
|
||||
|
||||
function type(dbg, string) {
|
||||
string.split("").forEach(char => {
|
||||
EventUtils.synthesizeKey(char, {}, dbg.win);
|
||||
});
|
||||
}
|
||||
|
||||
function isVisibleWithin(outerEl, innerEl) {
|
||||
const innerRect = innerEl.getBoundingClientRect();
|
||||
const outerRect = outerEl.getBoundingClientRect();
|
||||
return innerRect.top > outerRect.top &&
|
||||
innerRect.bottom < outerRect.bottom;
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
callStackHeader: ".call-stack-pane ._header",
|
||||
callStackBody: ".call-stack-pane .pane",
|
||||
scopesHeader: ".scopes-pane ._header",
|
||||
breakpointItem: i => `.breakpoints-list .breakpoint:nth-child(${i})`,
|
||||
scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
|
||||
frame: i => `.frames ul li:nth-child(${i})`,
|
||||
frames: ".frames ul li",
|
||||
gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
|
||||
menuitem: i => `menupopup menuitem:nth-child(${i})`,
|
||||
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",
|
||||
toggleBreakpoints: ".toggleBreakpoints",
|
||||
prettyPrintButton: ".prettyPrint",
|
||||
sourceFooter: ".source-footer",
|
||||
sourceNode: i => `.sources-list .tree-node:nth-child(${i})`,
|
||||
sourceNodes: ".sources-list .tree-node",
|
||||
sourceArrow: i => `.sources-list .tree-node:nth-child(${i}) .arrow`,
|
||||
};
|
||||
|
||||
function getSelector(elementName, ...args) {
|
||||
let selector = selectors[elementName];
|
||||
if (!selector) {
|
||||
throw new Error(`The selector ${elementName} is not defined`);
|
||||
}
|
||||
|
||||
if (typeof selector == "function") {
|
||||
selector = selector(...args);
|
||||
}
|
||||
|
||||
return selector;
|
||||
}
|
||||
|
||||
function findElement(dbg, elementName, ...args) {
|
||||
const selector = getSelector(elementName, ...args);
|
||||
return findElementWithSelector(dbg, selector);
|
||||
}
|
||||
|
||||
function findElementWithSelector(dbg, selector) {
|
||||
return dbg.win.document.querySelector(selector);
|
||||
}
|
||||
|
||||
function findAllElements(dbg, elementName, ...args) {
|
||||
const selector = getSelector(elementName, ...args);
|
||||
return dbg.win.document.querySelectorAll(selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a mouse click in the debugger DOM.
|
||||
*
|
||||
* @memberof mochitest/helpers
|
||||
* @param {Object} dbg
|
||||
* @param {String} elementName
|
||||
* @param {Array} args
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function clickElement(dbg, elementName, ...args) {
|
||||
const selector = getSelector(elementName, ...args);
|
||||
return EventUtils.synthesizeMouseAtCenter(
|
||||
findElementWithSelector(dbg, selector),
|
||||
{},
|
||||
dbg.win
|
||||
);
|
||||
}
|
||||
|
||||
function rightClickElement(dbg, elementName, ...args) {
|
||||
const selector = getSelector(elementName, ...args);
|
||||
const doc = dbg.win.document;
|
||||
return EventUtils.synthesizeMouseAtCenter(
|
||||
doc.querySelector(selector),
|
||||
{type: "contextmenu"},
|
||||
dbg.win
|
||||
);
|
||||
}
|
||||
|
||||
function selectMenuItem(dbg, index) {
|
||||
// the context menu is in the toolbox window
|
||||
const doc = dbg.toolbox.win.document;
|
||||
|
||||
// there are several context menus, we want the one with the menu-api
|
||||
const popup = doc.querySelector("menupopup[menu-api=\"true\"]");
|
||||
|
||||
const item = popup.querySelector(`menuitem:nth-child(${index})`);
|
||||
return EventUtils.synthesizeMouseAtCenter(item, {}, dbg.toolbox.win );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the debugger call stack accordian.
|
||||
*
|
||||
* @memberof mochitest/actions
|
||||
* @param {Object} dbg
|
||||
* @return {Promise}
|
||||
* @static
|
||||
*/
|
||||
function toggleCallStack(dbg) {
|
||||
return findElement(dbg, "callStackHeader").click();
|
||||
}
|
||||
|
||||
function toggleScopes(dbg) {
|
||||
return findElement(dbg, "scopesHeader").click();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue