Bug 1897755 - [devtools] Update DAMP tests to support Codemirror 6 r=perftest-reviewers,devtools-reviewers,nchevobbe,sparky

This patch adds support for running debugger DAMP tests with CM6.
Currently switch off by default. Set the `devtools.debugger.features.codemirror-next` pref in
the custom.js to `true` to run.

Differential Revision: https://phabricator.services.mozilla.com/D210909
This commit is contained in:
Hubert Boma Manilla 2024-07-19 18:42:02 +00:00
parent 7e58206c82
commit c41b562cdd
3 changed files with 84 additions and 41 deletions

View File

@ -625,7 +625,13 @@ class Editor extends EventEmitter {
codemirrorView: { EditorView, lineNumbers },
codemirrorState: { EditorState, Compartment },
codemirrorSearch: { highlightSelectionMatches },
codemirrorLanguage,
codemirrorLanguage: {
syntaxTreeAvailable,
indentUnit,
codeFolding,
syntaxHighlighting,
bracketMatching,
},
codemirrorLangJavascript,
lezerHighlight,
} = this.#CodeMirror6;
@ -663,21 +669,30 @@ class Editor extends EventEmitter {
);
const extensions = [
codemirrorLanguage.bracketMatching(),
indentCompartment.of(codemirrorLanguage.indentUnit.of(indentStr)),
bracketMatching(),
indentCompartment.of(indentUnit.of(indentStr)),
tabSizeCompartment.of(EditorState.tabSize.of(this.config.tabSize)),
lineWrapCompartment.of(
this.config.lineWrapping ? EditorView.lineWrapping : []
),
EditorState.readOnly.of(this.config.readOnly),
lineNumberCompartment.of(this.config.lineNumbers ? lineNumbers() : []),
codemirrorLanguage.codeFolding({
codeFolding({
placeholderText: "↔",
}),
foldGutterCompartment.of([]),
codemirrorLanguage.syntaxHighlighting(lezerHighlight.classHighlighter),
syntaxHighlighting(lezerHighlight.classHighlighter),
EditorView.updateListener.of(v => {
if (!cm.isDocumentLoadComplete) {
// Check that the full syntax tree is available the current viewport
if (syntaxTreeAvailable(v.state, v.view.viewState.viewport.to)) {
cm.isDocumentLoadComplete = true;
}
}
if (v.viewportChanged || v.docChanged) {
if (v.docChanged) {
cm.isDocumentLoadComplete = false;
}
// reset line gutter markers for the new visible ranges
// when the viewport changes(e.g when the page is scrolled).
if (this.#lineGutterMarkers.size > 0) {
@ -708,6 +723,8 @@ class Editor extends EventEmitter {
extensions,
});
cm.isDocumentLoadComplete = false;
this.#ownerDoc.sourceEditor = { editor: this, cm };
editors.set(this, cm);
}

View File

@ -49,9 +49,13 @@ const TEST_URL = PAGES_BASE_URL + "custom/debugger/app-build/index.html";
const MINIFIED_URL = `${IFRAME_BASE_URL}custom/debugger/app-build/static/js/minified.js`;
module.exports = async function () {
const isCm6Enabled = Services.prefs.getBoolPref(
"devtools.debugger.features.codemirror-next"
);
const tab = await testSetup(TEST_URL, { disableCache: true });
const toolbox = await openDebuggerAndLog("custom", EXPECTED);
const toolbox = await openDebuggerAndLog("custom", EXPECTED, isCm6Enabled);
dump("Waiting for debugger panel\n");
const panel = await toolbox.getPanelWhenReady("jsdebugger");
@ -62,16 +66,16 @@ module.exports = async function () {
// Reselect App.js as that's the source expected to be selected after page reload
await selectSource(dbg, EXPECTED.file);
await reloadDebuggerAndLog("custom", toolbox, EXPECTED);
await reloadDebuggerAndLog("custom", toolbox, EXPECTED, isCm6Enabled);
// these tests are only run on custom.jsdebugger
await pauseDebuggerAndLog(dbg, tab, EXPECTED_FUNCTION);
await stepDebuggerAndLog(dbg, tab, EXPECTED_FUNCTION);
await testProjectSearch(dbg, tab);
await testPreview(dbg, tab, EXPECTED_FUNCTION);
await testOpeningLargeMinifiedFile(dbg, tab);
await testPrettyPrint(dbg, toolbox);
await testPreview(dbg, tab, EXPECTED_FUNCTION, isCm6Enabled);
await testOpeningLargeMinifiedFile(dbg, isCm6Enabled);
await testPrettyPrint(dbg, toolbox, isCm6Enabled);
await closeToolboxAndLog("custom.jsdebugger", toolbox);
@ -198,12 +202,13 @@ async function testProjectSearch(dbg) {
await garbageCollect();
}
async function testPreview(dbg, tab, testFunction) {
async function testPreview(dbg, tab, testFunction, isCm6Enabled) {
dump("Executing preview test ...\n");
const pauseLocation = { line: 22, file: "App.js" };
let test = runTest("custom.jsdebugger.preview.DAMP");
await pauseDebugger(dbg, tab, testFunction, pauseLocation);
await hoverOnToken(dbg, "window.hitBreakpoint", "window");
await hoverOnToken(dbg, "window.hitBreakpoint", "window", isCm6Enabled);
test.done();
await removeBreakpoints(dbg);
@ -211,7 +216,8 @@ async function testPreview(dbg, tab, testFunction) {
await garbageCollect();
}
async function testOpeningLargeMinifiedFile(dbg) {
async function testOpeningLargeMinifiedFile(dbg, isCm6Enabled) {
dump("Executing opening large minified test ...\n");
const fileFirstMinifiedChars = `(()=>{var e,t,n,r,o={82603`;
dump("Open minified.js (large minified file)\n");
@ -220,7 +226,7 @@ async function testOpeningLargeMinifiedFile(dbg) {
);
const test = runTest("custom.jsdebugger.open-large-minified-file.DAMP");
const onSelected = selectSource(dbg, MINIFIED_URL);
await waitForText(dbg, fileFirstMinifiedChars);
await waitForText(dbg, fileFirstMinifiedChars, isCm6Enabled);
test.done();
await onSelected;
fullTest.done();
@ -233,7 +239,7 @@ async function testOpeningLargeMinifiedFile(dbg) {
await garbageCollect();
}
async function testPrettyPrint(dbg, toolbox) {
async function testPrettyPrint(dbg, toolbox, isCm6Enabled) {
const formattedFileUrl = `${MINIFIED_URL}:formatted`;
const filePrettyChars = "82603: (e, t, n) => {\n";
@ -241,12 +247,16 @@ async function testPrettyPrint(dbg, toolbox) {
await selectSource(dbg, MINIFIED_URL);
dump("Wait until CodeMirror highlighting is done\n");
const cm = getCM(dbg);
// highlightFrontier is not documented but is an internal variable indicating the current
// line that was just highlighted. This document has only 2 lines, so wait until both
// are highlighted. Since there was an other document opened before, we need to do an
// exact check to properly wait.
await waitUntil(() => cm.doc.highlightFrontier === 2);
const cm = getCM(dbg, isCm6Enabled);
await waitUntil(() => {
return isCm6Enabled
? cm.isDocumentLoadComplete
: // For CM5 highlightFrontier is not documented but is an internal variable indicating the current
// line that was just highlighted. This document has only 2 lines, so wait until both
// are highlighted. Since there was an other document opened before, we need to do an
// exact check to properly wait.
cm.doc.highlightFrontier === 2;
});
const prettyPrintButton = await waitUntil(() => {
return dbg.win.document.querySelector(".source-footer .prettyPrint.active");
@ -256,7 +266,7 @@ async function testPrettyPrint(dbg, toolbox) {
const test = runTest("custom.jsdebugger.pretty-print.DAMP");
prettyPrintButton.click();
await waitForSource(dbg, formattedFileUrl);
await waitForText(dbg, filePrettyChars);
await waitForText(dbg, filePrettyChars, isCm6Enabled);
test.done();
await addBreakpoint(dbg, 776, formattedFileUrl);
@ -265,12 +275,17 @@ async function testPrettyPrint(dbg, toolbox) {
const reloadAndPauseInPrettyPrintedFileTest = runTest(
"custom.jsdebugger.pretty-print.reload-and-pause.DAMP"
);
await reloadDebuggerAndLog("custom.pretty-print", toolbox, {
sources: 1105,
sourceURL: formattedFileUrl,
text: filePrettyChars,
threadsCount: EXPECTED.threadsCount,
});
await reloadDebuggerAndLog(
"custom.pretty-print",
toolbox,
{
sources: 1105,
sourceURL: formattedFileUrl,
text: filePrettyChars,
threadsCount: EXPECTED.threadsCount,
},
isCm6Enabled
);
await onPaused;
// When reloading, the `togglePrettyPrint` action is called to pretty print the minified source.

View File

@ -102,21 +102,31 @@ function findSource(dbg, url) {
}
exports.findSource = findSource;
function getCM(dbg) {
function getCM(dbg, isCm6Enabled) {
if (isCm6Enabled) {
return dbg.win.document.sourceEditor.cm;
}
const el = dbg.win.document.querySelector(".CodeMirror");
return el.CodeMirror;
}
exports.getCM = getCM;
function waitForText(dbg, text) {
function waitForText(dbg, text, isCm6Enabled = false) {
return waitUntil(() => {
// the welcome box is removed once text is displayed
const welcomebox = dbg.win.document.querySelector(".welcomebox");
if (welcomebox) {
return false;
}
const cm = getCM(dbg);
const editorText = cm.doc.getValue();
const cm = getCM(dbg, isCm6Enabled);
let editorText = "";
if (isCm6Enabled) {
if (cm) {
editorText = cm.state.doc.toString();
}
} else {
editorText = cm.doc.getValue();
}
return editorText.includes(text);
}, "text is visible");
}
@ -286,13 +296,13 @@ function evalInFrame(tab, testFunction) {
}
exports.evalInFrame = evalInFrame;
async function openDebuggerAndLog(label, expected) {
async function openDebuggerAndLog(label, expected, isCm6Enabled) {
const onLoad = async (toolbox, panel) => {
const dbg = await createContext(panel);
await waitForThreadCount(dbg, expected.threadsCount);
await waitForSource(dbg, expected.sourceURL);
await selectSource(dbg, expected.file);
await waitForText(dbg, expected.text);
await waitForText(dbg, expected.text, isCm6Enabled);
await waitForSymbols(dbg);
};
@ -305,7 +315,7 @@ async function openDebuggerAndLog(label, expected) {
}
exports.openDebuggerAndLog = openDebuggerAndLog;
async function reloadDebuggerAndLog(label, toolbox, expected) {
async function reloadDebuggerAndLog(label, toolbox, expected, isCm6Enabled) {
const onReload = async () => {
const panel = await toolbox.getPanelWhenReady("jsdebugger");
const dbg = await createContext(panel);
@ -318,7 +328,7 @@ async function reloadDebuggerAndLog(label, toolbox, expected) {
await waitForSources(dbg, expected.sources);
await waitForSource(dbg, expected.sourceURL);
await waitForText(dbg, expected.text);
await waitForText(dbg, expected.text, isCm6Enabled);
await waitForSymbols(dbg);
};
await reloadPageAndLog(`${label}.jsdebugger`, toolbox, onReload);
@ -392,11 +402,12 @@ async function step(dbg, stepType) {
}
exports.step = step;
async function hoverOnToken(dbg, textToWaitFor, textToHover) {
await waitForText(dbg, textToWaitFor);
const tokenElement = [
...dbg.win.document.querySelectorAll(".CodeMirror span"),
].find(el => el.textContent === textToHover);
async function hoverOnToken(dbg, textToWaitFor, textToHover, isCm6Enabled) {
await waitForText(dbg, textToWaitFor, isCm6Enabled);
const selector = isCm6Enabled ? ".cm-editor span" : ".CodeMirror span";
const tokenElement = [...dbg.win.document.querySelectorAll(selector)].find(
el => el.textContent === textToHover
);
const mouseOverEvent = new dbg.win.MouseEvent("mouseover", {
bubbles: true,