/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ // This test covers all the blackboxing functionality relating to a selected // source open in the debugger editor. "use strict"; requestLongerTimeout(5); const contextMenuItems = { ignoreSource: { selector: "#node-menu-blackbox", label: "Ignore source" }, unignoreSource: { selector: "#node-menu-blackbox", label: "Unignore source" }, ignoreLines: { selector: "#node-menu-blackbox-lines", label: "Ignore lines" }, unignoreLines: { selector: "#node-menu-blackbox-lines", label: "Unignore lines", }, ignoreLine: { selector: "#node-menu-blackbox-line", label: "Ignore line" }, unignoreLine: { selector: "#node-menu-blackbox-line", label: "Unignore line", }, }; const SOURCE_IS_FULLY_IGNORED = "source"; const SOURCE_LINES_ARE_IGNORED = "line"; const SOURCE_IS_NOT_IGNORED = "none"; // Tests basic functionality for blackbox source and blackbox single and multiple lines add_task(async function testAllBlackBox() { // using the doc-command-click.html as it has a simple js file we can use // testing. const file = "simple4.js"; const dbg = await initDebugger("doc-command-click.html", file); const source = findSource(dbg, file); await selectSource(dbg, source); await addBreakpoint(dbg, file, 8); await testBlackBoxSource(dbg, source); await testBlackBoxMultipleLines(dbg, source); await testBlackBoxSingleLine(dbg, source); }); // Test that the blackboxed lines are persisted accross reloads and still work accordingly. add_task(async function testBlackBoxOnReload() { const file = "simple4.js"; const dbg = await initDebugger("doc-command-click.html", file); const source = findSource(dbg, file); await selectSource(dbg, source); // Adding 2 breakpoints in funcB() and funcC() which // would be hit in order. await addBreakpoint(dbg, file, 8); await addBreakpoint(dbg, file, 12); info("Reload without any blackboxing to make all necesary postions are hit"); const onReloaded = reload(dbg, file); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 2); await resume(dbg); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 8); await resume(dbg); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 12); await resumeAndWaitForPauseCounter(dbg); info("Wait for reload to complete after resume"); await onReloaded; assertNotPaused(dbg); info("Ignoring line 2 using the gutter context menu"); await openContextMenuInDebugger(dbg, "gutter", 2); await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); info("Ignoring line 7 to 9 using the editor context menu"); await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 9 }); await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); const onReloaded2 = reload(dbg, file); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 12); await resume(dbg); info("Wait for reload to complete after resume"); await onReloaded2; assertNotPaused(dbg); info( "Check that the expected blackbox context menu state is correct across reload" ); await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 2, nonBlackBoxedLine: 4, blackBoxedLines: [7, 9], nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 2, nonBlackBoxedLine: 4, blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); }); add_task(async function testBlackBoxOnToolboxRestart() { const dbg = await initDebugger("doc-command-click.html", "simple4.js"); const source = findSource(dbg, "simple4.js"); await selectSource(dbg, source); const onReloaded = reload(dbg, "simple4.js"); await waitForPaused(dbg); info("Assert it paused at the debugger statement"); assertPausedAtSourceAndLine(dbg, source.id, 2); await resume(dbg); await onReloaded; info("Ignoring line 2 using the gutter context menu"); await openContextMenuInDebugger(dbg, "gutter", 2); await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); await reloadBrowser(); // Wait a little bit incase of a pause await wait(1000); info("Assert that the debugger no longer pauses on the debugger statement"); assertNotPaused(dbg); info("Close the toolbox"); await dbg.toolbox.closeToolbox(); info("Reopen the toolbox on the debugger"); const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); const dbg2 = createDebuggerContext(toolbox); await waitForSelectedSource(dbg2, findSource(dbg2, "simple4.js")); await reloadBrowser(); // Wait a little incase of a pause await wait(1000); info("Assert that debbuger still does not pause on the debugger statement"); assertNotPaused(dbg2); }); async function testBlackBoxSource(dbg, source) { info("Start testing blackboxing the whole source"); info("Assert the blackbox context menu items before any blackboxing is done"); // When the source is not blackboxed there are no blackboxed lines await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackBoxedLines: null, nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: null, nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), }); info( "Blackbox the whole simple4.js source file using the editor context menu" ); await openContextMenuInDebugger(dbg, "CodeMirrorLines"); await selectBlackBoxContextMenuItem(dbg, "blackbox"); info("Assert that all lines in the source are styled correctly"); assertIgnoredStyleInSourceLines(dbg, { hasBlackboxedLinesClass: true }); info("Assert that the source tree for simple4.js has the ignored style"); const node = findSourceNodeWithText(dbg, "simple4.js"); ok( node.querySelector(".blackboxed"), "simple4.js node does not have the ignored style" ); invokeInTab("funcA"); info( "The debugger statement on line 2 and the breakpoint on line 8 should not be hit" ); assertNotPaused(dbg); info("Assert the blackbox context menu items after blackboxing is done"); // When the whole source is blackboxed there are no nonBlackboxed lines await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 2, nonBlackBoxedLine: null, blackBoxedLines: [3, 5], nonBlackBoxedLines: null, blackboxedSourceState: SOURCE_IS_FULLY_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 2, nonBlackBoxedLine: null, blackboxedSourceState: SOURCE_IS_FULLY_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); info("Unblackbox the whole source using the sourcetree context menu"); rightClickEl(dbg, findSourceNodeWithText(dbg, "simple4.js")); await waitForContextMenu(dbg); await selectBlackBoxContextMenuItem(dbg, "blackbox"); info("Assert that all lines in the source are un-styled correctly"); assertIgnoredStyleInSourceLines(dbg, { hasBlackboxedLinesClass: false }); info( "Assert that the source tree for simple4.js does not have the ignored style" ); const nodeAfterBlackbox = findSourceNodeWithText(dbg, "simple4.js"); ok( !nodeAfterBlackbox.querySelector(".blackboxed"), "simple4.js node still has the ignored style" ); invokeInTab("funcA"); info("assert the pause at the debugger statement on line 2"); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 2); await resume(dbg); info("assert the pause at the breakpoint set on line 8"); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 8); await resumeAndWaitForPauseCounter(dbg); assertNotPaused(dbg); // When the source is not blackboxed there are no blackboxed lines await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackBoxedLines: null, nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: null, nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); } async function testBlackBoxMultipleLines(dbg, source) { info("Blackbox lines 7 to 13 using the editor content menu items"); await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 13 }); await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 3, blackBoxedLines: [7, 9], nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 3, blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); info("Assert that the ignored lines are styled correctly"); assertIgnoredStyleInSourceLines(dbg, { lines: [7, 13], hasBlackboxedLinesClass: true, }); info("Assert that the source tree for simple4.js has the ignored style"); const node = findSourceNodeWithText(dbg, "simple4.js"); ok( node.querySelector(".blackboxed"), "simple4.js node does not have the ignored style" ); invokeInTab("funcA"); info("assert the pause at the debugger statement on line 2"); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 2); await resumeAndWaitForPauseCounter(dbg); info( "The breakpoint set on line 8 should not get hit as its within the blackboxed range" ); assertNotPaused(dbg); info("Unblackbox lines 7 to 13"); await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 13 }); await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackBoxedLines: null, nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackboxedSourceState: SOURCE_IS_NOT_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: null, nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); info("Assert that the un-ignored lines are no longer have the style"); assertIgnoredStyleInSourceLines(dbg, { lines: [7, 13], hasBlackboxedLinesClass: false, }); info( "Assert that the source tree for simple4.js does not have the ignored style" ); const nodeAfterBlackbox = findSourceNodeWithText(dbg, "simple4.js"); ok( !nodeAfterBlackbox.querySelector(".blackboxed"), "simple4.js still has the ignored style" ); invokeInTab("funcA"); // assert the pause at the debugger statement on line 2 await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 2); await resume(dbg); // assert the pause at the breakpoint set on line 8 await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 8); await resumeAndWaitForPauseCounter(dbg); assertNotPaused(dbg); } async function testBlackBoxSingleLine(dbg, source) { info("Black box line 2 of funcA() with the debugger statement"); await openContextMenuInDebugger(dbg, "gutter", 2); await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 2, nonBlackBoxedLine: 4, blackBoxedLines: null, nonBlackBoxedLines: [3, 4], blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: null, nonBlackBoxedLine: 4, blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); info("Assert that the ignored line 2 is styled correctly"); assertIgnoredStyleInSourceLines(dbg, { lines: [2], hasBlackboxedLinesClass: true, }); info("Black box line 4 of funcC() with the debugger statement"); await openContextMenuInDebugger(dbg, "gutter", 4); await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); info("Assert that the ignored line 4 is styled correctly"); assertIgnoredStyleInSourceLines(dbg, { lines: [4], hasBlackboxedLinesClass: true, }); invokeInTab("funcA"); // assert the pause at the breakpoint set on line 8 await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 8); await resumeAndWaitForPauseCounter(dbg); assertNotPaused(dbg); info("Un-blackbox line 2 of funcA()"); selectEditorLines(dbg, 2, 2); await openContextMenuInDebugger(dbg, "CodeMirrorLines"); await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); await assertEditorBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 4, nonBlackBoxedLine: 3, blackBoxedLines: null, nonBlackBoxedLines: [11, 12], blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertGutterBlackBoxBoxContextMenuItems(dbg, { blackboxedLine: 4, nonBlackBoxedLine: 3, blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, }); await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { blackBoxedSourceTreeNode: null, nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), }); info("Assert that the un-ignored line 2 is styled correctly"); assertIgnoredStyleInSourceLines(dbg, { lines: [2], hasBlackboxedLinesClass: false, }); invokeInTab("funcA"); // assert the pause at the debugger statement on line 2 await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 2); await resume(dbg); // assert the pause at the breakpoint set on line 8 await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, source.id, 8); await resume(dbg); assertNotPaused(dbg); } // Resume and wait for the thread context's `pauseCounter` to get // updated. async function resumeAndWaitForPauseCounter(dbg) { const prevThreadPauseCounter = getThreadContext(dbg).pauseCounter; await resume(dbg); return waitFor( () => getThreadContext(dbg).pauseCounter > prevThreadPauseCounter ); } async function assertContextMenuDisabled(dbg, selector, shouldBeDisabled) { const item = await waitFor(() => findContextMenu(dbg, selector)); is( item.disabled, shouldBeDisabled, `The the context menu item is ${ shouldBeDisabled ? "disabled" : "not disabled" }` ); } /** * Asserts that the gutter blackbox context menu items which are visible are correct * @params {Object} dbg * @params {Array} testFixtures * Details needed for the assertion. Any blackboxed/nonBlackboxed lines * and any blackboxed/nonBlackboxed sources */ async function assertGutterBlackBoxBoxContextMenuItems(dbg, testFixtures) { const { blackboxedLine, nonBlackBoxedLine, blackboxedSourceState } = testFixtures; if (blackboxedLine) { info( "Asserts that the gutter context menu items when clicking on the gutter of a blackboxed line" ); const popup = await openContextMenuInDebugger( dbg, "gutter", blackboxedLine ); // When the whole source is blackboxed the the gutter visually shows `ignore line` // but it is disabled indicating that individual lines cannot be nonBlackboxed. const item = blackboxedSourceState == SOURCE_IS_FULLY_IGNORED ? contextMenuItems.ignoreLine : contextMenuItems.unignoreLine; await assertContextMenuLabel(dbg, item.selector, item.label); await assertContextMenuDisabled( dbg, item.selector, blackboxedSourceState == SOURCE_IS_FULLY_IGNORED ); await closeContextMenu(dbg, popup); } if (nonBlackBoxedLine) { info( "Asserts that the gutter context menu items when clicking on the gutter of a nonBlackboxed line" ); const popup = await openContextMenuInDebugger( dbg, "gutter", nonBlackBoxedLine ); const item = contextMenuItems.ignoreLine; await assertContextMenuLabel(dbg, item.selector, item.label); await assertContextMenuDisabled(dbg, item.selector, false); await closeContextMenu(dbg, popup); } } /** * Asserts that the source tree blackbox context menu items which are visible are correct * @params {Object} dbg * @params {Array} testFixtures * Details needed for the assertion. Any blackboxed/nonBlackboxed sources */ async function assertSourceTreeBlackBoxBoxContextMenuItems(dbg, testFixtures) { const { blackBoxedSourceTreeNode, nonBlackBoxedSourceTreeNode } = testFixtures; if (blackBoxedSourceTreeNode) { info( "Asserts that the source tree blackbox context menu items when clicking on a blackboxed source tree node" ); rightClickEl(dbg, blackBoxedSourceTreeNode); const popup = await waitForContextMenu(dbg); const item = contextMenuItems.unignoreSource; await assertContextMenuLabel(dbg, item.selector, item.label); await closeContextMenu(dbg, popup); } if (nonBlackBoxedSourceTreeNode) { info( "Asserts that the source tree blackbox context menu items when clicking on an un-blackboxed sorce tree node" ); rightClickEl(dbg, nonBlackBoxedSourceTreeNode); const popup = await waitForContextMenu(dbg); const _item = contextMenuItems.ignoreSource; await assertContextMenuLabel(dbg, _item.selector, _item.label); await closeContextMenu(dbg, popup); } } /** * Asserts that the editor blackbox context menu items which are visible are correct * @params {Object} dbg * @params {Array} testFixtures * Details needed for the assertion. Any blackboxed/nonBlackboxed lines * and any blackboxed/nonBlackboxed sources */ async function assertEditorBlackBoxBoxContextMenuItems(dbg, testFixtures) { const { blackboxedLine, nonBlackBoxedLine, blackBoxedLines, nonBlackBoxedLines, blackboxedSourceState, } = testFixtures; if (blackboxedLine) { info( "Asserts the editor blackbox context menu items when right-clicking on a single blackboxed line" ); const popup = await selectEditorLinesAndOpenContextMenu(dbg, { startLine: blackboxedLine, }); const expectedContextMenuItems = [contextMenuItems.unignoreSource]; if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { expectedContextMenuItems.push(contextMenuItems.unignoreLine); } for (const expectedContextMenuItem of expectedContextMenuItems) { info( "Checking context menu item " + expectedContextMenuItem.selector + " with label " + expectedContextMenuItem.label ); await assertContextMenuLabel( dbg, expectedContextMenuItem.selector, expectedContextMenuItem.label ); } await closeContextMenu(dbg, popup); } if (nonBlackBoxedLine) { info( "Asserts the editor blackbox context menu items when right-clicking on a single non-blackboxed line" ); const popup = await selectEditorLinesAndOpenContextMenu(dbg, { startLine: nonBlackBoxedLine, }); const expectedContextMenuItems = [ blackboxedSourceState == SOURCE_IS_NOT_IGNORED ? contextMenuItems.ignoreSource : contextMenuItems.unignoreSource, contextMenuItems.ignoreLine, ]; for (const expectedContextMenuItem of expectedContextMenuItems) { info( "Checking context menu item " + expectedContextMenuItem.selector + " with label " + expectedContextMenuItem.label ); await assertContextMenuLabel( dbg, expectedContextMenuItem.selector, expectedContextMenuItem.label ); } await closeContextMenu(dbg, popup); } if (blackBoxedLines) { info( "Asserts the editor blackbox context menu items when right-clicking on multiple blackboxed lines" ); const popup = await selectEditorLinesAndOpenContextMenu(dbg, { startLine: blackBoxedLines[0], endLine: blackBoxedLines[1], }); const expectedContextMenuItems = [contextMenuItems.unignoreSource]; if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { expectedContextMenuItems.push(contextMenuItems.unignoreLines); } for (const expectedContextMenuItem of expectedContextMenuItems) { info( "Checking context menu item " + expectedContextMenuItem.selector + " with label " + expectedContextMenuItem.label ); await assertContextMenuLabel( dbg, expectedContextMenuItem.selector, expectedContextMenuItem.label ); } await closeContextMenu(dbg, popup); } if (nonBlackBoxedLines) { info( "Asserts the editor blackbox context menu items when right-clicking on multiple non-blackboxed lines" ); const popup = await selectEditorLinesAndOpenContextMenu(dbg, { startLine: nonBlackBoxedLines[0], endLine: nonBlackBoxedLines[1], }); const expectedContextMenuItems = [ blackboxedSourceState == SOURCE_IS_NOT_IGNORED ? contextMenuItems.ignoreSource : contextMenuItems.unignoreSource, ]; if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { expectedContextMenuItems.push(contextMenuItems.ignoreLines); } for (const expectedContextMenuItem of expectedContextMenuItems) { info( "Checking context menu item " + expectedContextMenuItem.selector + " with label " + expectedContextMenuItem.label ); await assertContextMenuLabel( dbg, expectedContextMenuItem.selector, expectedContextMenuItem.label ); } await closeContextMenu(dbg, popup); } } /** * Selects a range of lines * @param {Object} dbg * @param {Number} startLine * @param {Number} endLine */ function selectEditorLines(dbg, startLine, endLine) { getCM(dbg).setSelection( { line: startLine - 1, ch: 0 }, { line: endLine, ch: 0 } ); }