Bug 1463674 - Run all jsterm test with both the old and the new version; r=bgrins.
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Thu, 05 Jul 2018 16:49:53 +0200
changeset 819018 28d0ba8fa12c18915e50a8b71c38800d800f72a9
parent 819017 44bf63c34c0a8e8c23247683a97b5b468324d926
child 819019 f6df6a982ee9510ca32dd3afa52dfe9f8c3586a3
child 819186 d13a54a85e2fd7a3b81876fb0cac355eedaa9e33
push id116413
push userbgrinstead@mozilla.com
push dateMon, 16 Jul 2018 22:40:17 +0000
reviewersbgrins
bugs1463674
milestone63.0a1
Bug 1463674 - Run all jsterm test with both the old and the new version; r=bgrins. This will allow us to avoid regression for both versions. Also, the test were adapted to the codeMirror jsterm. MozReview-Commit-ID: eZBvLv7JBH
devtools/client/webconsole/test/mochitest/browser_jsterm_add_edited_input_to_history.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_array_no_index.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_cached_results.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_crossdomain_iframe.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_escape_key.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_extraneous_closing_brackets.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_helpers.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_chrome_tab.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_inside_text.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_native_getters.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_nav_and_tab_key.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key.js
devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key_no_selection.js
devtools/client/webconsole/test/mochitest/browser_jsterm_completion.js
devtools/client/webconsole/test/mochitest/browser_jsterm_copy_command.js
devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_a_select_all.js
devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_key_nav.js
devtools/client/webconsole/test/mochitest/browser_jsterm_document_no_xray.js
devtools/client/webconsole/test/mochitest/browser_jsterm_dollar.js
devtools/client/webconsole/test/mochitest/browser_jsterm_error_docs.js
devtools/client/webconsole/test/mochitest/browser_jsterm_error_outside_valid_range.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_clear.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_dollar.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_x.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_help.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_keys_values.js
devtools/client/webconsole/test/mochitest/browser_jsterm_helper_pprint.js
devtools/client/webconsole/test/mochitest/browser_jsterm_hide_when_devtools_chrome_enabled_false.js
devtools/client/webconsole/test/mochitest/browser_jsterm_history_arrow_keys.js
devtools/client/webconsole/test/mochitest/browser_jsterm_input_expansion.js
devtools/client/webconsole/test/mochitest/browser_jsterm_inspect.js
devtools/client/webconsole/test/mochitest/browser_jsterm_instance_of.js
devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
devtools/client/webconsole/test/mochitest/browser_jsterm_no_autocompletion_on_defined_variables.js
devtools/client/webconsole/test/mochitest/browser_jsterm_no_input_and_tab_key_pressed.js
devtools/client/webconsole/test/mochitest/browser_jsterm_null_undefined.js
devtools/client/webconsole/test/mochitest/browser_jsterm_popup_close_on_tab_switch.js
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_clipboard.js
devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_file.js
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_add_edited_input_to_history.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_add_edited_input_to_history.js
@@ -7,27 +7,28 @@
 // lost after navigating in history.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
 
 add_task(async function() {
-  const hud = await openNewTabAndConsole(TEST_URI);
-  await testEditedInputHistory(hud);
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
 });
 
-async function testEditedInputHistory(hud) {
-  const jsterm = hud.jsterm;
-  const inputNode = jsterm.inputNode;
+async function performTests() {
+  const {jsterm} = await openNewTabAndConsole(TEST_URI);
 
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
-  is(inputNode.selectionStart, 0);
-  is(inputNode.selectionEnd, 0);
+  checkJsTermCursor(jsterm, 0, "Cursor is at expected position");
 
   jsterm.setInputValue('"first item"');
   EventUtils.synthesizeKey("KEY_ArrowUp");
   is(jsterm.getInputValue(), '"first item"', "null test history up");
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(jsterm.getInputValue(), '"first item"', "null test history down");
 
   await jsterm.execute();
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
@@ -5,16 +5,24 @@
 
 "use strict";
 
 // Test that properties starting with underscores or dollars can be
 // autocompleted (bug 967468).
 const TEST_URI = "data:text/html;charset=utf8,test autocompletion with $ or _";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
 
   await jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
 
   // Should work with bug 967468.
   await testAutocomplete(jsterm, "Object.__d");
   await testAutocomplete(jsterm, "testObject.$$a");
 
@@ -23,17 +31,17 @@ add_task(async function() {
   await testAutocomplete(jsterm, "testObject.$$aa");
 
   // Should work with bug 1207868.
   await jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
   await testAutocomplete(jsterm, "foobar");
   await testAutocomplete(jsterm, "blargh");
   await testAutocomplete(jsterm, "foobar.a");
   await testAutocomplete(jsterm, "blargh.a");
-});
+}
 
 async function testAutocomplete(jsterm, inputString) {
   jsterm.setInputValue(inputString);
   await new Promise(resolve => jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve));
 
   const popup = jsterm.autocompletePopup;
   ok(popup.itemCount > 0, `There's ${popup.itemCount} suggestions for '${inputString}'`);
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_array_no_index.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_array_no_index.js
@@ -11,16 +11,24 @@ const TEST_URI = `data:text/html;charset
 <head>
   <script>
     window.foo = [1,2,3];
   </script>
 </head>
 <body>bug 585991 - Autocomplete popup on array</body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
 
   const {
     autocompletePopup: popup
   } = jsterm;
 
   const onPopUpOpen = popup.once("popup-opened");
 
@@ -33,9 +41,9 @@ add_task(async function() {
   const popupItems = popup.getItems().map(e => e.label);
   is(popupItems.includes("0"), false, "Completing on an array doesn't show numbers.");
 
   info("press Escape to close the popup");
   const onPopupClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Escape");
 
   await onPopupClose;
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_cached_results.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_cached_results.js
@@ -6,66 +6,91 @@
 // Tests that the cached autocomplete results are used when the new
 // user input is a subset of the existing completion results.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,<p>test cached autocompletion results";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
-  const {
-    autocompletePopup: popup,
-    completeNode,
-    inputNode: input,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
-  const jstermComplete = (value, offset) =>
-    jstermSetValueAndComplete(jsterm, value, offset);
+  const jstermComplete = (value, caretPosition) =>
+    jstermSetValueAndComplete(jsterm, value, caretPosition);
 
   // Test if 'doc' gives 'document'
   await jstermComplete("doc");
-  is(input.value, "doc", "'docu' completion (input.value)");
-  is(completeNode.value, "   ument", "'docu' completion (completeNode)");
+  is(jsterm.getInputValue(), "doc", "'docu' completion (input.value)");
+  checkJsTermCompletionValue(jsterm, "   ument", "'docu' completion (completeNode)");
 
-  // Test typing 'window.'.
+  // Test typing 'window.'.'
   await jstermComplete("window.");
   ok(popup.getItems().length > 0, "'window.' gave a list of suggestions");
 
-  await jsterm.execute("window.docfoobar = true");
+  info("Add a property on the window object");
+  await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
+    content.wrappedJSObject.window.docfoobar = true;
+  });
+
+  // Test typing d (i.e. input is now 'window.d').
+  let onUpdated = jsterm.once("autocomplete-updated");
+  EventUtils.synthesizeKey("d");
+  await onUpdated;
+  ok(!getPopupLabels(popup).includes("docfoobar"),
+    "autocomplete popup does not contain docfoobar. List has not been updated");
 
-  // Test typing 'window.doc'.
-  await jstermComplete("window.doc");
+  // Test typing o (i.e. input is now 'window.do').
+  jsterm.once("autocomplete-updated");
+  EventUtils.synthesizeKey("o");
+  await onUpdated;
+  ok(!getPopupLabels(popup).includes("docfoobar"),
+    "autocomplete popup does not contain docfoobar. List has not been updated");
+
+  // Test that backspace does not cause a request to the server
+  onUpdated = jsterm.once("autocomplete-updated");
+  EventUtils.synthesizeKey("KEY_Backspace");
+  await onUpdated;
   ok(!getPopupLabels(popup).includes("docfoobar"),
     "autocomplete cached results do not contain docfoobar. list has not been updated");
 
-  // Test that backspace does not cause a request to the server
-  await jstermComplete("window.do");
-  ok(!getPopupLabels(popup).includes("docfoobar"),
-    "autocomplete cached results do not contain docfoobar. list has not been updated");
-
-  await jsterm.execute("delete window.docfoobar");
+  await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
+    delete content.wrappedJSObject.window.docfoobar;
+  });
 
   // Test if 'window.getC' gives 'getComputedStyle'
   await jstermComplete("window.");
   await jstermComplete("window.getC");
   ok(getPopupLabels(popup).includes("getComputedStyle"),
     "autocomplete results do contain getComputedStyle");
 
   // Test if 'dump(d' gives non-zero results
   await jstermComplete("dump(d");
   ok(popup.getItems().length > 0, "'dump(d' gives non-zero results");
 
   // Test that 'dump(window.)' works.
   await jstermComplete("dump(window.)", -1);
   ok(popup.getItems().length > 0, "'dump(window.' gave a list of suggestions");
 
-  await jsterm.execute("window.docfoobar = true");
+  info("Add a property on the window object");
+  await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
+    content.wrappedJSObject.window.docfoobar = true;
+  });
 
-  // Make sure 'dump(window.doc)' does not contain 'docfoobar'.
-  await jstermComplete("dump(window.doc)", -1);
+  // Make sure 'dump(window.d)' does not contain 'docfoobar'.
+  onUpdated = jsterm.once("autocomplete-updated");
+  EventUtils.synthesizeKey("d");
+  await onUpdated;
   ok(!getPopupLabels(popup).includes("docfoobar"),
     "autocomplete cached results do not contain docfoobar. list has not been updated");
-});
+}
 
 function getPopupLabels(popup) {
   return popup.getItems().map(item => item.label);
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_crossdomain_iframe.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_crossdomain_iframe.js
@@ -3,17 +3,26 @@
 
 // Test that autocomplete doesn't break when trying to reach into objects from
 // a different domain. See Bug 989025.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test-iframe-parent.html";
+
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const { jsterm } = hud;
 
   const onParentTitle = waitForMessage(hud, "iframe parent");
   jsterm.execute("document.title");
   await onParentTitle;
   ok(true, "root document's title is accessible");
 
@@ -29,9 +38,9 @@ add_task(async function() {
   const permissionDenied = await onPermissionDeniedMessage;
   ok(permissionDenied.node.classList.contains("error"),
     "A message error is shown when trying to inspect window[0]");
 
   const onParentLocation = waitForMessage(hud, "test-iframe-parent.html");
   hud.jsterm.execute("window.location");
   await onParentLocation;
   ok(true, "root document's location is accessible");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_escape_key.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_escape_key.js
@@ -18,23 +18,28 @@ const TEST_URI = `data:text/html;charset
       item0: "value0",
       item1: "value1",
     });
   </script>
 </head>
 <body>bug 585991 - autocomplete popup escape key usage test</body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
   info("web console opened");
 
-  const {
-    autocompletePopup: popup,
-    completeNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   const onPopUpOpen = popup.once("popup-opened");
 
   info("wait for completion: window.foo.");
   jsterm.setInputValue("window.foo");
   EventUtils.sendString(".");
 
   await onPopUpOpen;
@@ -45,10 +50,10 @@ add_task(async function() {
   info("press Escape to close the popup");
   const onPopupClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Escape");
 
   await onPopupClose;
 
   ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
   is(jsterm.getInputValue(), "window.foo.", "completion was cancelled");
-  ok(!completeNode.value, "completeNode is empty");
-});
+  ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_extraneous_closing_brackets.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_extraneous_closing_brackets.js
@@ -6,17 +6,25 @@
 // Tests that, when the user types an extraneous closing bracket, no error
 // appears. See Bug 592442.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,test for bug 592442";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
 
   try {
     await jstermSetValueAndComplete(jsterm, "document.getElementById)");
     ok(true, "no error was thrown when an extraneous bracket was inserted");
   } catch (ex) {
     ok(false, "an error was thrown when an extraneous bracket was inserted");
   }
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_helpers.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_helpers.js
@@ -6,22 +6,30 @@
 // Tests that the autocompletion results contain the names of JSTerm helpers.
 // See Bug 686937.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,<p>test JSTerm Helpers autocomplete";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {jsterm} = await openNewTabAndConsole(TEST_URI);
   await testInspectAutoCompletion(jsterm, "i", true);
   await testInspectAutoCompletion(jsterm, "window.", false);
   await testInspectAutoCompletion(jsterm, "dump(i", true);
   await testInspectAutoCompletion(jsterm, "window.dump(i", true);
-});
+}
 
 async function testInspectAutoCompletion(jsterm, inputValue, expectInspect) {
   jsterm.setInputValue(inputValue);
   await complete(jsterm);
   is(getPopupItemsLabel(jsterm.autocompletePopup).includes("inspect"), expectInspect,
     `autocomplete results${expectInspect ? "" : " does not"} contain helper 'inspect'`);
 }
 
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_chrome_tab.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_chrome_tab.js
@@ -3,20 +3,28 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that code completion works properly in chrome tabs, like about:config.
 
 "use strict";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole("about:config");
   ok(hud, "we have a console");
   ok(hud.iframeWindow, "we have the console UI window");
 
   const {jsterm} = hud;
   ok(jsterm, "we have a jsterm");
   ok(hud.outputNode, "we have an output node");
 
   // Test typing 'docu'.
   await jstermSetValueAndComplete(jsterm, "docu");
-  is(jsterm.completeNode.value, "    ment", "'docu' completion");
-});
+  checkJsTermCompletionValue(jsterm, "    ment", "'docu' completion");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_in_debugger_stackframe.js
@@ -7,16 +7,24 @@
 // stackframe from the js debugger.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test-autocomplete-in-stackframe.html";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   // Force the old debugger UI since it's directly used (see Bug 1301705)
   await pushPref("devtools.debugger.new-debugger-frontend", false);
 
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
   const {
     autocompletePopup: popup,
   } = jsterm;
 
@@ -88,17 +96,17 @@ add_task(async function() {
 
   // Test if 'foo2Obj.prop1.prop11.' gives suggestions for a string,i.e. 'length'
   await jstermComplete("foo2Obj.prop1.prop11.");
   ok(getPopupLabels(popup).includes("length"), `results do contain "length"`);
 
   // Test if 'foo2Obj[0].' throws no errors.
   await jstermComplete("foo2Obj[0].");
   is(getPopupLabels(popup).length, 0, "no items for foo2Obj[0]");
-});
+}
 
 function getPopupLabels(popup) {
   return popup.getItems().map(item => item.label);
 }
 
 function pauseDebugger(debuggerPanel) {
   const debuggerWin = debuggerPanel.panelWin;
   const debuggerController = debuggerWin.DebuggerController;
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_inside_text.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_inside_text.js
@@ -12,51 +12,52 @@ const TEST_URI = `data:text/html;charset
   <script>
     window.testBugA = "hello world";
     window.testBugB = "hello world 2";
   </script>
 </head>
 <body>bug 812618 - test completion inside text</body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
   info("web console opened");
 
-  const {
-    autocompletePopup: popup,
-    completeNode,
-    inputNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   const onPopUpOpen = popup.once("popup-opened");
 
   const dumpString = "dump(window.testBu)";
-  jsterm.setInputValue(dumpString);
-  inputNode.selectionStart = inputNode.selectionEnd = dumpString.indexOf(")");
-  EventUtils.sendString("g");
+  jstermSetValueAndComplete(jsterm, dumpString, -1);
 
   await onPopUpOpen;
 
   ok(popup.isOpen, "popup is open");
   is(popup.itemCount, 2, "popup.itemCount is correct");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
   is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
-  ok(!completeNode.value, "completeNode.value is empty");
+  ok(!getJsTermCompletionValue(jsterm), "completeNode.value is empty");
 
   const items = popup.getItems().map(e => e.label);
   const expectedItems = ["testBugB", "testBugA"];
   is(items.join("-"), expectedItems.join("-"), "getItems returns the items we expect");
 
   info("press Tab and wait for popup to hide");
   const onPopupClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Tab");
 
   await onPopupClose;
 
   // At this point the completion suggestion should be accepted.
   ok(!popup.isOpen, "popup is not open");
   const expectedInput = "dump(window.testBugB)";
   is(jsterm.getInputValue(), expectedInput, "completion was successful after VK_TAB");
-  is(inputNode.selectionStart, expectedInput.length - 1, "cursor location is correct");
-  is(inputNode.selectionStart, inputNode.selectionEnd, "cursor location (confirmed)");
-  ok(!completeNode.value, "completeNode is empty");
-});
+  checkJsTermCursor(jsterm, expectedInput.length - 1, "cursor location is correct");
+  ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_native_getters.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_native_getters.js
@@ -6,35 +6,40 @@
 // Tests that native getters (e.g. document.body) autocompletes in the web console.
 // See Bug 651501.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Test document.body autocompletion";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
 
-  const {
-    autocompletePopup: popup,
-    completeNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   ok(!popup.isOpen, "popup is not open");
   const onPopupOpen = popup.once("popup-opened");
 
   jsterm.setInputValue("document.body");
   EventUtils.sendString(".");
 
   await onPopupOpen;
 
   ok(popup.isOpen, "popup is open");
   is(popup.itemCount, jsterm._autocompleteCache.length, "popup.itemCount is correct");
   ok(jsterm._autocompleteCache.includes("addEventListener"),
-        "addEventListener is in the list of suggestions");
+    "addEventListener is in the list of suggestions");
   ok(jsterm._autocompleteCache.includes("bgColor"),
     "bgColor is in the list of suggestions");
   ok(jsterm._autocompleteCache.includes("ATTRIBUTE_NODE"),
     "ATTRIBUTE_NODE is in the list of suggestions");
 
   const onPopupClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Escape");
 
@@ -48,10 +53,10 @@ add_task(async function() {
 
   await onAutoCompleteUpdated;
 
   // Build the spaces that are placed in the input to place the autocompletion result at
   // the expected spot:
   // > document.bo        <-- input
   // > -----------dy      <-- autocomplete
   const spaces = " ".repeat(inputStr.length + 1);
-  is(completeNode.value, spaces + "dy", "autocomplete shows document.body");
-});
+  checkJsTermCompletionValue(jsterm, spaces + "dy", "autocomplete shows document.body");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_nav_and_tab_key.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_nav_and_tab_key.js
@@ -20,23 +20,28 @@ const TEST_URI = `data:text/html;charset
       item2: "value2",
       item3: "value3",
     });
   </script>
 </head>
 <body>bug 585991 - autocomplete popup navigation and tab key usage test</body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
   info("web console opened");
 
-  const {
-    autocompletePopup: popup,
-    completeNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   ok(!popup.isOpen, "popup is not open");
 
   const onPopUpOpen = popup.once("popup-opened");
   jsterm.setInputValue("window.foo");
 
   // Shows the popup
   EventUtils.sendString(".");
@@ -58,29 +63,29 @@ add_task(async function() {
   is(popup.selectedIndex, expectedPopupItems.length - 1,
       "Index of the first item from bottom is selected.");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
 
   const prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
   is(popup.selectedIndex, 0, "index 0 is selected");
   is(popup.selectedItem.label, "item3", "item3 is selected");
-  is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
+  checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
 
   is(popup.selectedIndex, 1, "index 1 is selected");
   is(popup.selectedItem.label, "item2", "item2 is selected");
-  is(completeNode.value, prefix + "item2", "completeNode.value holds item2");
+  checkJsTermCompletionValue(jsterm, prefix + "item2", "completeNode.value holds item2");
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
 
   is(popup.selectedIndex, 0, "index 0 is selected");
   is(popup.selectedItem.label, "item3", "item3 is selected");
-  is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
+  checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
 
   let currentSelectionIndex = popup.selectedIndex;
 
   EventUtils.synthesizeKey("KEY_PageDown");
 
   ok(popup.selectedIndex > currentSelectionIndex, "Index is greater after PGDN");
 
   currentSelectionIndex = popup.selectedIndex;
@@ -99,10 +104,10 @@ add_task(async function() {
   EventUtils.synthesizeKey("KEY_Tab");
 
   await onPopupClose;
 
   // At this point the completion suggestion should be accepted.
   ok(!popup.isOpen, "popup is not open");
   is(jsterm.getInputValue(), "window.foo.item3",
      "completion was successful after KEY_Tab");
-  ok(!completeNode.value, "completeNode is empty");
-});
+  ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_paste_undo.js
@@ -11,51 +11,60 @@ XPCOMUtils.defineLazyServiceGetter(
   this,
   "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1",
   "nsIClipboardHelper"
 );
 const stringToCopy = "foobazbarBug642615";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
   ui.clearOutput();
-  ok(!jsterm.completeNode.value, "no completeNode.value");
+  ok(!getJsTermCompletionValue(jsterm), "no completeNode.value");
 
   jsterm.setInputValue("doc");
 
   info("wait for completion value after typing 'docu'");
   let onAutocompleteUpdated = jsterm.once("autocomplete-updated");
   EventUtils.sendString("u");
   await onAutocompleteUpdated;
 
-  const completionValue = jsterm.completeNode.value;
+  const completionValue = getJsTermCompletionValue(jsterm);
 
   info(`Copy "${stringToCopy}" in clipboard`);
   await waitForClipboardPromise(() =>
     clipboardHelper.copyString(stringToCopy), stringToCopy);
 
   jsterm.setInputValue("docu");
   info("wait for completion update after clipboard paste");
   onAutocompleteUpdated = jsterm.once("autocomplete-updated");
-  goDoCommand("cmd_paste");
+  EventUtils.synthesizeKey("v", {accelKey: true});
 
   await onAutocompleteUpdated;
 
-  ok(!jsterm.completeNode.value, "no completion value after paste");
+  ok(!getJsTermCompletionValue(jsterm), "no completion value after paste");
 
   info("wait for completion update after undo");
   onAutocompleteUpdated = jsterm.once("autocomplete-updated");
 
-  goDoCommand("cmd_undo");
+  EventUtils.synthesizeKey("z", {accelKey: true});
 
   await onAutocompleteUpdated;
 
-  is(jsterm.completeNode.value, completionValue, "same completeNode.value after undo");
+  checkJsTermCompletionValue(jsterm, completionValue,
+    "same completeNode.value after undo");
 
   info("wait for completion update after clipboard paste (ctrl-v)");
   onAutocompleteUpdated = jsterm.once("autocomplete-updated");
 
   EventUtils.synthesizeKey("v", {accelKey: true});
 
   await onAutocompleteUpdated;
-  ok(!jsterm.completeNode.value, "no completion value after paste (ctrl-v)");
-});
+  ok(!getJsTermCompletionValue(jsterm), "no completion value after paste (ctrl-v)");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key.js
@@ -20,21 +20,26 @@ const TEST_URI = `data:text/html;charset
       item2: "value2",
       item3: "value3",
     });
   </script>
 </head>
 <body>bug 585991 - test pressing return with open popup</body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const { jsterm } = await openNewTabAndConsole(TEST_URI);
-  const {
-    autocompletePopup: popup,
-    completeNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   const onPopUpOpen = popup.once("popup-opened");
 
   info("wait for completion suggestions: window.foobar.");
 
   jsterm.setInputValue("window.fooba");
   EventUtils.sendString("r.");
 
@@ -52,21 +57,21 @@ add_task(async function() {
   is(popup.selectedIndex, expectedPopupItems.length - 1,
     "First index from bottom is selected");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
 
   is(popup.selectedIndex, 0, "index 0 is selected");
   is(popup.selectedItem.label, "item3", "item3 is selected");
   const prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
-  is(completeNode.value, prefix + "item3", "completeNode.value holds item3");
+  checkJsTermCompletionValue(jsterm, prefix + "item3", "completeNode.value holds item3");
 
   info("press Return to accept suggestion. wait for popup to hide");
   const onPopupClose = popup.once("popup-closed");
   EventUtils.synthesizeKey("KEY_Enter");
 
   await onPopupClose;
 
   ok(!popup.isOpen, "popup is not open after KEY_Enter");
   is(jsterm.getInputValue(), "window.foobar.item3",
     "completion was successful after KEY_Enter");
-  ok(!completeNode.value, "completeNode is empty");
-});
+  ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key_no_selection.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_autocomplete_return_key_no_selection.js
@@ -16,25 +16,30 @@ const TEST_URI = `data:text/html;charset
 </head>
 <body>bug 873250 - test pressing return with open popup, but no selection</body>`;
 
 const {
   getHistoryEntries,
 } = require("devtools/client/webconsole/selectors/history");
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {
     jsterm,
     ui,
   } = await openNewTabAndConsole(TEST_URI);
 
-  const {
-    autocompletePopup: popup,
-    completeNode,
-  } = jsterm;
+  const { autocompletePopup: popup } = jsterm;
 
   const onPopUpOpen = popup.once("popup-opened");
 
   info("wait for popup to show");
   jsterm.setInputValue("window.testBu");
   EventUtils.sendString("g");
 
   await onPopUpOpen;
@@ -45,15 +50,15 @@ add_task(async function() {
 
   info("press Return and wait for popup to hide");
   const onPopUpClose = popup.once("popup-closed");
   executeSoon(() => EventUtils.synthesizeKey("KEY_Enter"));
   await onPopUpClose;
 
   ok(!popup.isOpen, "popup is not open after KEY_Enter");
   is(jsterm.getInputValue(), "", "inputNode is empty after KEY_Enter");
-  is(completeNode.value, "", "completeNode is empty");
+  ok(!getJsTermCompletionValue(jsterm), "completeNode is empty");
 
   const state = ui.consoleOutput.getStore().getState();
   const entries = getHistoryEntries(state);
   is(entries[entries.length - 1], "window.testBug",
      "jsterm history is correct");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_completion.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_completion.js
@@ -5,69 +5,76 @@
 
 // Tests that code completion works properly.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,<p>test code completion";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
-  const input = jsterm.inputNode;
 
   // Test typing 'docu'.
   await jstermSetValueAndComplete(jsterm, "docu");
-  is(input.value, "docu", "'docu' completion (input.value)");
-  is(jsterm.completeNode.value, "    ment", "'docu' completion (completeNode)");
+  is(jsterm.getInputValue(), "docu", "'docu' completion (input.value)");
+  checkJsTermCompletionValue(jsterm, "    ment", "'docu' completion (completeNode)");
 
   // Test typing 'docu' and press tab.
   await jstermSetValueAndComplete(jsterm, "docu", undefined, jsterm.COMPLETE_FORWARD);
-  is(input.value, "document", "'docu' tab completion");
-  is(input.selectionStart, 8, "start selection is alright");
-  is(input.selectionEnd, 8, "end selection is alright");
-  is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed");
+  is(jsterm.getInputValue(), "document", "'docu' tab completion");
+
+  checkJsTermCursor(jsterm, "document".length, "cursor is at the end of 'document'");
+  is(getJsTermCompletionValue(jsterm).replace(/ /g, ""), "", "'docu' completed");
 
   // Test typing 'window.Ob' and press tab.  Just 'window.O' is
   // ambiguous: could be window.Object, window.Option, etc.
   await jstermSetValueAndComplete(jsterm, "window.Ob", undefined,
                                   jsterm.COMPLETE_FORWARD);
-  is(input.value, "window.Object", "'window.Ob' tab completion");
+  is(jsterm.getInputValue(), "window.Object", "'window.Ob' tab completion");
 
   // Test typing 'document.getElem'.
   await jstermSetValueAndComplete(
     jsterm, "document.getElem", undefined, jsterm.COMPLETE_FORWARD);
-  is(input.value, "document.getElem", "'document.getElem' completion");
-  is(jsterm.completeNode.value, "                entsByTagNameNS",
+  is(jsterm.getInputValue(), "document.getElem", "'document.getElem' completion");
+  checkJsTermCompletionValue(jsterm, "                entsByTagNameNS",
      "'document.getElem' completion");
 
   // Test pressing tab another time.
   await jsterm.complete(jsterm.COMPLETE_FORWARD);
-  is(input.value, "document.getElem", "'document.getElem' completion");
-  is(jsterm.completeNode.value, "                entsByTagName",
+  is(jsterm.getInputValue(), "document.getElem", "'document.getElem' completion");
+  checkJsTermCompletionValue(jsterm, "                entsByTagName",
      "'document.getElem' another tab completion");
 
   // Test pressing shift_tab.
   await jstermComplete(jsterm, jsterm.COMPLETE_BACKWARD);
-  is(input.value, "document.getElem", "'document.getElem' untab completion");
-  is(jsterm.completeNode.value, "                entsByTagNameNS",
+  is(jsterm.getInputValue(), "document.getElem", "'document.getElem' untab completion");
+  checkJsTermCompletionValue(jsterm, "                entsByTagNameNS",
      "'document.getElem' completion");
 
   ui.clearOutput();
 
   await jstermSetValueAndComplete(jsterm, "docu");
-  is(jsterm.completeNode.value, "    ment", "'docu' completion");
+  checkJsTermCompletionValue(jsterm, "    ment", "'docu' completion");
 
   await jsterm.execute();
-  is(jsterm.completeNode.value, "", "clear completion on execute()");
+  checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
 
   // Test multi-line completion works
   await jstermSetValueAndComplete(jsterm, "console.log('one');\nconsol");
-  is(jsterm.completeNode.value, "                   \n      e",
+  checkJsTermCompletionValue(jsterm, "                   \n      e",
      "multi-line completion");
 
   // Test non-object autocompletion.
   await jstermSetValueAndComplete(jsterm, "Object.name.sl");
-  is(jsterm.completeNode.value, "              ice", "non-object completion");
+  checkJsTermCompletionValue(jsterm, "              ice", "non-object completion");
 
   // Test string literal autocompletion.
   await jstermSetValueAndComplete(jsterm, "'Asimov'.sl");
-  is(jsterm.completeNode.value, "           ice", "string literal completion");
-});
+  checkJsTermCompletionValue(jsterm, "           ice", "string literal completion");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_copy_command.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_copy_command.js
@@ -22,32 +22,40 @@ const TEST_URI = `data:text/html;charset
     <h1>Testing copy command</h1>
     <p>This is some example text</p>
     <p id="${id}">${text}</p>
   </div>
   <div><p></p></div>
 </body>`;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {jsterm} = await openNewTabAndConsole(TEST_URI);
   const random = Math.random();
   const string = "Text: " + random;
   const obj = {a: 1, b: "foo", c: random};
 
   await testCopy(jsterm, random, random.toString());
   await testCopy(jsterm, JSON.stringify(string), string);
   await testCopy(jsterm, obj.toSource(), JSON.stringify(obj, null, "  "));
 
   const outerHTML = await ContentTask.spawn(gBrowser.selectedBrowser, id,
     function(elementId) {
       return content.document.getElementById(elementId).outerHTML;
     }
   );
   await testCopy(jsterm, `$("#${id}")`, outerHTML);
-});
+}
 
 function testCopy(jsterm, stringToCopy, expectedResult) {
   return waitForClipboardPromise(() => {
     info(`Attempting to copy: "${stringToCopy}"`);
     const command = `copy(${stringToCopy})`;
     info(`Executing command: "${command}"`);
     jsterm.execute(command);
   }, expectedResult);
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_a_select_all.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_a_select_all.js
@@ -4,32 +4,69 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Map Control + A to Select All, In the web console input
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Test console select all";
 
-add_task(async function testCtrlA() {
-  const hud = await openNewTabAndConsole(TEST_URI);
+add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
 
-  const jsterm = hud.jsterm;
+async function performTests() {
+  const {jsterm} = await openNewTabAndConsole(TEST_URI);
+
   jsterm.setInputValue("Ignore These Four Words");
-  const inputNode = jsterm.inputNode;
 
   // Test select all with (cmd|control) + a.
   EventUtils.synthesizeKey("a", { accelKey: true });
 
-  const inputLength = inputNode.selectionEnd - inputNode.selectionStart;
+  const inputLength = getSelectionTextLength(jsterm);
   is(inputLength, jsterm.getInputValue().length, "Select all of input");
 
-  // (cmd|control) + e cannot be disabled on Linux so skip this section on that
-  // OS.
+  // (cmd|control) + e cannot be disabled on Linux so skip this section on that OS.
   if (Services.appinfo.OS !== "Linux") {
    // Test do nothing on Control + E.
     jsterm.setInputValue("Ignore These Four Words");
-    inputNode.selectionStart = 0;
+    setCursorAtStart(jsterm);
     EventUtils.synthesizeKey("e", { accelKey: true });
-    is(inputNode.selectionStart, 0,
-      "control|cmd + e does not move to end of input");
+    checkSelectionStart(jsterm, 0, "control|cmd + e does not move to end of input");
+  }
+}
+
+function getSelectionTextLength(jsterm) {
+  if (jsterm.inputNode) {
+    return jsterm.inputNode.selectionEnd - jsterm.inputNode.selectionStart;
+  }
+
+  if (jsterm.editor) {
+    return jsterm.editor.getSelection().length;
   }
-});
+
+  return null;
+}
+
+function setCursorAtStart(jsterm) {
+  if (jsterm.inputNode) {
+    jsterm.inputNode.selectionStart = 0;
+  }
+
+  if (jsterm.editor) {
+    jsterm.editor.setCursor({line: 0, ch: 0});
+  }
+}
+
+function checkSelectionStart(jsterm, expectedCursorIndex, assertionInfo) {
+  if (jsterm.inputNode) {
+    const { selectionStart } = jsterm.inputNode;
+    is(selectionStart, expectedCursorIndex, assertionInfo);
+  } else {
+    const [ selection ] = jsterm.editor.codeMirror.listSelections();
+    const { head} = selection;
+    is(head.ch, expectedCursorIndex, assertionInfo);
+  }
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_key_nav.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_ctrl_key_nav.js
@@ -10,224 +10,277 @@
 // - ctrl-e does not work on windows,
 // - ctrl-a, ctrl-p and ctrl-n only work on OSX
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
                  "bug 804845 and bug 619598";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const {jsterm} = await openNewTabAndConsole(TEST_URI);
 
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
-  is(jsterm.inputNode.selectionStart, 0);
-  is(jsterm.inputNode.selectionEnd, 0);
+  checkJsTermCursor(jsterm, 0, "Cursor is at the start of the input");
 
   testSingleLineInputNavNoHistory(jsterm);
   testMultiLineInputNavNoHistory(jsterm);
   await testNavWithHistory(jsterm);
-});
+}
 
 function testSingleLineInputNavNoHistory(jsterm) {
-  const inputNode = jsterm.inputNode;
+  const checkInput = (expected, assertionInfo) =>
+    checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
+
   // Single char input
   EventUtils.sendString("1");
-  is(inputNode.selectionStart, 1, "caret location after single char input");
+  checkInput("1|", "caret location after single char input");
 
   // nav to start/end with ctrl-a and ctrl-e;
   synthesizeLineStartKey();
-  is(inputNode.selectionStart, 0,
-     "caret location after single char input and ctrl-a");
+  checkInput("|1", "caret location after single char input and ctrl-a");
 
   synthesizeLineEndKey();
-  is(inputNode.selectionStart, 1,
-     "caret location after single char input and ctrl-e");
+  checkInput("1|", "caret location after single char input and ctrl-e");
 
   // Second char input
   EventUtils.sendString("2");
+  checkInput("12|", "caret location after second char input");
+
   // nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n
   EventUtils.synthesizeKey("KEY_ArrowUp");
-  is(inputNode.selectionStart, 0,
-     "caret location after two char input and KEY_ArrowUp");
+  checkInput("|12", "caret location after two char input and KEY_ArrowUp");
+
   EventUtils.synthesizeKey("KEY_ArrowDown");
-  is(inputNode.selectionStart, 2,
-     "caret location after two char input and KEY_ArrowDown");
+  checkInput("12|", "caret location after two char input and KEY_ArrowDown");
+
+  synthesizeLineStartKey();
+  checkInput("|12", "move caret to beginning of 2 char input with ctrl-a");
 
   synthesizeLineStartKey();
-  is(inputNode.selectionStart, 0,
-     "move caret to beginning of 2 char input with ctrl-a");
-  synthesizeLineStartKey();
-  is(inputNode.selectionStart, 0,
-     "no change of caret location on repeat ctrl-a");
+  checkInput("|12", "no change of caret location on repeat ctrl-a");
+
   synthesizeLineUpKey();
-  is(inputNode.selectionStart, 0,
-     "no change of caret location on ctrl-p from beginning of line");
+  checkInput("|12", "no change of caret location on ctrl-p from beginning of line");
+
+  synthesizeLineEndKey();
+  checkInput("12|", "move caret to end of 2 char input with ctrl-e");
 
   synthesizeLineEndKey();
-  is(inputNode.selectionStart, 2,
-     "move caret to end of 2 char input with ctrl-e");
-  synthesizeLineEndKey();
-  is(inputNode.selectionStart, 2,
-     "no change of caret location on repeat ctrl-e");
+  checkInput("12|", "no change of caret location on repeat ctrl-e");
+
   synthesizeLineDownKey();
-  is(inputNode.selectionStart, 2,
-     "no change of caret location on ctrl-n from end of line");
+  checkInput("12|", "no change of caret location on ctrl-n from end of line");
 
   synthesizeLineUpKey();
-  is(inputNode.selectionStart, 0, "ctrl-p moves to start of line");
+  checkInput("|12", "ctrl-p moves to start of line");
 
   synthesizeLineDownKey();
-  is(inputNode.selectionStart, 2, "ctrl-n moves to end of line");
+  checkInput("12|", "ctrl-n moves to end of line");
 }
 
 function testMultiLineInputNavNoHistory(jsterm) {
-  const inputNode = jsterm.inputNode;
+  const checkInput = (expected, assertionInfo) =>
+    checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
+
   const lineValues = ["one", "2", "something longer", "", "", "three!"];
   jsterm.setInputValue("");
   // simulate shift-return
-  for (let i = 0; i < lineValues.length; i++) {
-    jsterm.setInputValue(jsterm.getInputValue() + lineValues[i]);
+  for (const lineValue of lineValues) {
+    jsterm.setInputValue(jsterm.getInputValue() + lineValue);
     EventUtils.synthesizeKey("KEY_Enter", {shiftKey: true});
   }
-  const inputValue = jsterm.getInputValue();
-  is(inputNode.selectionStart, inputNode.selectionEnd);
-  is(inputNode.selectionStart, inputValue.length,
-     "caret at end of multiline input");
 
-  // possibility newline is represented by one ('\r', '\n') or two
-  // ('\r\n') chars
-  const newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0];
+  checkInput(
+`one
+2
+something longer
+
+
+three!
+|`, "caret at end of multiline input");
 
   // Ok, test navigating within the multi-line string!
   EventUtils.synthesizeKey("KEY_ArrowUp");
-  let expectedStringAfterCarat = lineValues[5] + newlineString;
-  is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
-     "up arrow from end of multiline");
+  checkInput(
+`one
+2
+something longer
+
+
+|three!
+`, "up arrow from end of multiline");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-  is(jsterm.getInputValue().slice(inputNode.selectionStart), "",
-     "down arrow from within multiline");
+  checkInput(
+`one
+2
+something longer
+
+
+three!
+|`, "down arrow from within multiline");
 
   // navigate up through input lines
   synthesizeLineUpKey();
-  is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
-     "ctrl-p from end of multiline");
+  checkInput(
+`one
+2
+something longer
 
-  for (let i = 4; i >= 0; i--) {
+
+|three!
+`, "ctrl-p from end of multiline");
+
+  for (let i = 0; i < 5; i++) {
     synthesizeLineUpKey();
-    expectedStringAfterCarat = lineValues[i] + newlineString +
-      expectedStringAfterCarat;
-    is(jsterm.getInputValue().slice(inputNode.selectionStart),
-      expectedStringAfterCarat, "ctrl-p from within line " + i +
-      " of multiline input");
   }
+
+  checkInput(
+`|one
+2
+something longer
+
+
+three!
+`, "reached start of input");
+
   synthesizeLineUpKey();
-  is(inputNode.selectionStart, 0, "reached start of input");
-  is(jsterm.getInputValue(), inputValue,
-     "no change to multiline input on ctrl-p from beginning of multiline");
+  checkInput(
+`|one
+2
+something longer
+
+
+three!
+`, "no change to multiline input on ctrl-p from beginning of multiline");
 
   // navigate to end of first line
   synthesizeLineEndKey();
-  let caretPos = inputNode.selectionStart;
-  let expectedStringBeforeCarat = lineValues[0];
-  is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
-     "ctrl-e into multiline input");
+  checkInput(
+`one|
+2
+something longer
+
+
+three!
+`, "ctrl-e into multiline input");
+
   synthesizeLineEndKey();
-  is(inputNode.selectionStart, caretPos,
-     "repeat ctrl-e doesn't change caret position in multiline input");
+  checkInput(
+`one|
+2
+something longer
+
+
+three!
+`, "repeat ctrl-e doesn't change caret position in multiline input");
 
-  // navigate down one line; ctrl-a to the beginning; ctrl-e to end
-  for (let i = 1; i < lineValues.length; i++) {
-    synthesizeLineDownKey();
-    synthesizeLineStartKey();
-    caretPos = inputNode.selectionStart;
-    expectedStringBeforeCarat += newlineString;
-    is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
-       "ctrl-a to beginning of line " + (i + 1) + " in multiline input");
+  synthesizeLineDownKey();
+  synthesizeLineStartKey();
+  checkInput(
+`one
+|2
+something longer
+
+
+three!
+`);
 
-    synthesizeLineEndKey();
-    caretPos = inputNode.selectionStart;
-    expectedStringBeforeCarat += lineValues[i];
-    is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
-       "ctrl-e to end of line " + (i + 1) + "in multiline input");
-  }
+  synthesizeLineEndKey();
+  synthesizeLineDownKey();
+  synthesizeLineStartKey();
+  checkInput(
+`one
+2
+|something longer
+
+
+three!
+`);
 }
 
 async function testNavWithHistory(jsterm) {
-  const inputNode = jsterm.inputNode;
+  const checkInput = (expected, assertionInfo) =>
+    checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
 
   // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
   // caret placed _within_ single line input
   const values = [
-    '"single line input"',
-    '"a longer single-line input to check caret repositioning"',
-    '"multi-line"\n"input"\n"here!"',
+    "single line input",
+    "a longer single-line input to check caret repositioning",
+    "multi-line\ninput\nhere",
   ];
 
   // submit to history
-  for (let i = 0; i < values.length; i++) {
-    jsterm.setInputValue(values[i]);
+  for (const value of values) {
+    jsterm.setInputValue(value);
     await jsterm.execute();
   }
-  is(inputNode.selectionStart, 0, "caret location at start of empty line");
+
+  checkInput("|", "caret location at start of empty line");
+
+  synthesizeLineUpKey();
+  checkInput("multi-line\ninput\nhere|", "caret location at end of last history input");
+
+  synthesizeLineStartKey();
+  checkInput("multi-line\ninput\n|here",
+    "caret location at beginning of last line of last history input");
+
+  synthesizeLineUpKey();
+  checkInput("multi-line\n|input\nhere",
+    "caret location at beginning of second line of last history input");
+
+  synthesizeLineUpKey();
+  checkInput("|multi-line\ninput\nhere",
+    "caret location at beginning of first line of last history input");
 
   synthesizeLineUpKey();
-  is(inputNode.selectionStart, values[values.length - 1].length,
-     "caret location correct at end of last history input");
+  checkInput("a longer single-line input to check caret repositioning|",
+    "caret location at the end of second history input");
+
+  synthesizeLineUpKey();
+  checkInput("single line input|", "caret location at the end of first history input");
+
+  synthesizeLineUpKey();
+  checkInput("|single line input",
+    "ctrl-p at beginning of history moves caret location to beginning of line");
 
-  // Navigate backwards history with ctrl-p
-  for (let i = values.length - 1; i > 0; i--) {
-    const match = values[i].match(/(\n)/g);
-    if (match) {
-      // multi-line inputs won't update from history unless caret at beginning
-      synthesizeLineStartKey();
-      for (let j = 0; j < match.length; j++) {
-        synthesizeLineUpKey();
-      }
-      synthesizeLineUpKey();
-    } else {
-      // single-line inputs will update from history from end of line
-      synthesizeLineUpKey();
-    }
-    is(jsterm.getInputValue(), values[i - 1],
-       "ctrl-p updates inputNode from backwards history values[" + i - 1 + "]");
-  }
+  synthesizeLineDownKey();
+  checkInput("a longer single-line input to check caret repositioning|",
+    "caret location at the end of second history input");
 
-  let inputValue = jsterm.getInputValue();
-  synthesizeLineUpKey();
-  is(inputNode.selectionStart, 0,
-     "ctrl-p at beginning of history moves caret location to beginning " +
-     "of line");
-  is(jsterm.getInputValue(), inputValue,
-     "no change to input value on ctrl-p from beginning of line");
+  synthesizeLineDownKey();
+  checkInput("multi-line\ninput\nhere|", "caret location at end of last history input");
 
-  // Navigate forwards history with ctrl-n
-  for (let i = 1; i < values.length; i++) {
-    synthesizeLineDownKey();
-    is(jsterm.getInputValue(), values[i],
-       "ctrl-n updates inputNode from forwards history values[" + i + "]");
-    is(inputNode.selectionStart, values[i].length,
-       "caret location correct at end of history input for values[" + i + "]");
-  }
   synthesizeLineDownKey();
-  ok(!jsterm.getInputValue(), "ctrl-n at end of history updates to empty input");
+  checkInput("|", "ctrl-n at end of history updates to empty input");
 
   // Simulate editing multi-line
-  inputValue = "one\nlinebreak";
+  const inputValue = "one\nlinebreak";
   jsterm.setInputValue(inputValue);
+  checkInput("one\nlinebreak|");
 
   // Attempt nav within input
   synthesizeLineUpKey();
-  is(jsterm.getInputValue(), inputValue,
-     "ctrl-p from end of multi-line does not trigger history");
+  checkInput("one|\nlinebreak", "ctrl-p from end of multi-line does not trigger history");
 
   synthesizeLineStartKey();
+  checkInput("|one\nlinebreak");
+
   synthesizeLineUpKey();
-  is(jsterm.getInputValue(), values[values.length - 1],
-     "ctrl-p from start of multi-line triggers history");
+  checkInput("multi-line\ninput\nhere|",
+    "ctrl-p from start of multi-line triggers history");
 }
 
 function synthesizeLineStartKey() {
   EventUtils.synthesizeKey("a", {ctrlKey: true});
 }
 
 function synthesizeLineEndKey() {
   EventUtils.synthesizeKey("e", {ctrlKey: true});
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_document_no_xray.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_document_no_xray.js
@@ -1,17 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html,Test evaluating document";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const {jsterm} = hud;
 
   // check for occurrences of Object XRayWrapper, bug 604430
   const onMessage = waitForMessage(hud, "HTMLDocument");
   jsterm.execute("document");
   const {node} = await onMessage;
   is(node.textContent.includes("xray"), false, "document - no XrayWrapper");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_dollar.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_dollar.js
@@ -7,20 +7,28 @@
 
 // Test that using `$` and `$$` in jsterm call the global content functions
 // if they are defined. See Bug 621644.
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test-jsterm-dollar.html";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   await test$(hud);
   await test$$(hud);
-});
+}
 
 async function test$(hud) {
   hud.ui.clearOutput();
   const msg = await hud.jsterm.execute("$(document.body)");
   ok(msg.textContent.includes("<p>"), "jsterm output is correct for $()");
 }
 
 async function test$$(hud) {
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_error_docs.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_error_docs.js
@@ -1,16 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html,Test error documentation";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const {jsterm} = hud;
 
   // Check that errors with entries in errordocs.js display links next to their messages.
   const ErrorDocs = require("devtools/server/actors/errordocs");
 
   const ErrorDocStatements = {
     "JSMSG_BAD_RADIX": "(42).toString(0);",
@@ -25,9 +33,9 @@ add_task(async function() {
     hud.ui.clearOutput();
     const onMessage = waitForMessage(hud, "RangeError:");
     jsterm.execute(expression);
     const {node} = await onMessage;
     const learnMoreLink = node.querySelector(".learn-more-link");
     ok(learnMoreLink, `There is a [Learn More] link for "${errorMessageName}" error`);
     is(learnMoreLink.title, title, `The link has the expected "${title}" title`);
   }
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_error_outside_valid_range.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_error_outside_valid_range.js
@@ -4,18 +4,26 @@
 "use strict";
 
 // Ensure that dom errors, with error numbers outside of the range
 // of valid js.msg errors, don't cause crashes (See Bug 1270721).
 
 const TEST_URI = "data:text/html,Test error documentation";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const {jsterm} = hud;
 
   const text = "TypeError: 'redirect' member of RequestInit 'foo' is not a valid value " +
                "for enumeration RequestRedirect";
   const onErrorMessage =  waitForMessage(hud, text, ".message.error");
   jsterm.execute("new Request('',{redirect:'foo'})");
   await onErrorMessage;
   ok(true, "Error message displayed as expected, without crashing the console.");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_clear.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_clear.js
@@ -1,22 +1,30 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html,Test <code>clear()</code> jsterm helper";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const onMessage = waitForMessage(hud, "message");
   ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     content.wrappedJSObject.console.log("message");
   });
   await onMessage;
 
   const onCleared = waitFor(() =>
     hud.jsterm.outputNode.querySelector(".message") === null);
   hud.jsterm.execute("clear()");
   await onCleared;
   ok(true, "Console was cleared");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar.js
@@ -11,16 +11,24 @@ const TEST_URI = `data:text/html,
     <li>First</li>
     <li>Second</li>
   </ul>
   <aside>Sidebar</aside>
 </main>
 `;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   let onMessage = waitForMessage(hud, "<main>");
   jsterm.execute("$('main')");
   let message = await onMessage;
   ok(message, "`$('main')` worked");
 
@@ -33,9 +41,9 @@ add_task(async function() {
   jsterm.execute("$('main > ul > li').tagName");
   message = await onMessage;
   ok(message, "`$` result can be used right away");
 
   onMessage = waitForMessage(hud, "null");
   jsterm.execute("$('div')");
   message = await onMessage;
   ok(message, "`$('div')` does return null");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_dollar.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_dollar.js
@@ -11,16 +11,24 @@ const TEST_URI = `data:text/html,
     <li>First</li>
     <li>Second</li>
   </ul>
   <aside>Sidebar</aside>
 </main>
 `;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   let onMessage = waitForMessage(hud, "Array [ main ]");
   jsterm.execute("$$('main')");
   let message = await onMessage;
   ok(message, "`$$('main')` worked");
 
@@ -33,9 +41,9 @@ add_task(async function() {
   jsterm.execute("$$('main > ul > li').map(el => el.tagName).join(' - ')");
   message = await onMessage;
   ok(message, "`$$` result can be used right away");
 
   onMessage = waitForMessage(hud, "Array []");
   jsterm.execute("$$('div')");
   message = await onMessage;
   ok(message, "`$$('div')` returns an empty array");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_x.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_dollar_x.js
@@ -11,21 +11,29 @@ const TEST_URI = `data:text/html,
     <li>First</li>
     <li>Second</li>
   </ul>
   <aside>Sidebar</aside>
 </main>
 `;
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   let onMessage = waitForMessage(hud, "Array [ li, li ]");
   jsterm.execute("$x('.//li')");
   let message = await onMessage;
   ok(message, "`$x` worked");
 
   onMessage = waitForMessage(hud, "<li>");
   jsterm.execute("$x('.//li', document.body)[0]");
   message = await onMessage;
   ok(message, "`$x()` result can be used right away");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_help.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_help.js
@@ -2,16 +2,24 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html,Test <code>help()</code> jsterm helper";
 const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   let openedLinks = 0;
   const oldOpenLink = hud.openLink;
   hud.openLink = (url) => {
     if (url == HELP_URL) {
       openedLinks++;
@@ -23,9 +31,9 @@ add_task(async function() {
   await jsterm.execute("help");
   await jsterm.execute("?");
 
   const messages = Array.from(jsterm.outputNode.querySelectorAll(".message"));
   ok(messages.every(msg => msg.classList.contains("command")),
     "There is no results shown for the help commands");
   is(openedLinks, 3, "correct number of pages opened by the help calls");
   hud.openLink = oldOpenLink;
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_keys_values.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_keys_values.js
@@ -2,16 +2,24 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI =
   "data:text/html,Test <code>keys()</code> & <code>values()</code> jsterm helper";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   let onMessage = waitForMessage(hud, `Array [ "a", "b" ]`);
   jsterm.execute("keys({a: 2, b:1})");
   let message = await onMessage;
   ok(message, "`keys()` worked");
 
@@ -19,9 +27,9 @@ add_task(async function() {
   jsterm.execute("values({a: 2, b:1})");
   message = await onMessage;
   ok(message, "`values()` worked");
 
   onMessage = waitForMessage(hud, "Array");
   jsterm.execute("keys(window)");
   message = await onMessage;
   ok(message, "`keys(window)` worked");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_pprint.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_helper_pprint.js
@@ -1,16 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI = "data:text/html,Test <code>pprint()</code> jsterm helper";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const {jsterm} = hud;
 
   let onMessage = waitForMessage(hud, `"  b: 2\n  a: 1"`);
   jsterm.execute("pprint({b:2, a:1})");
   let message = await onMessage;
   ok(message, "`pprint()` worked");
 
@@ -26,9 +34,9 @@ add_task(async function() {
   message = await onMessage;
   ok(message, "`pprint('hi')` worked");
 
   // check that pprint(function) shows function source (See Bug 618344).
   onMessage = waitForMessage(hud, `"function() { var someCanaryValue = 42; }`);
   jsterm.execute("pprint(function() { var someCanaryValue = 42; })");
   message = await onMessage;
   ok(message, "`pprint(function)` shows function source");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_hide_when_devtools_chrome_enabled_false.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_hide_when_devtools_chrome_enabled_false.js
@@ -18,16 +18,24 @@
  *     set them but there is no reason not to do so as the input is disabled).
  *   - webconsole jsterm should be enabled
  *   - webconsole object inspector properties should be set.
  */
 
 "use strict";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   let browserConsole, webConsole, objInspector;
 
   // We don't use `pushPref()` because we need to revert the same pref later
   // in the test.
   Services.prefs.setBoolPref("devtools.chrome.enabled", true);
 
   browserConsole = await HUDService.toggleBrowserConsole();
   objInspector = await getObjectInspector(browserConsole);
@@ -47,18 +55,21 @@ add_task(async function() {
   browserConsole = await HUDService.toggleBrowserConsole();
   objInspector = await getObjectInspector(browserConsole);
   testJSTermIsNotVisible(browserConsole);
 
   webConsole = await openConsole(browserTab);
   objInspector = await getObjectInspector(webConsole);
   testJSTermIsVisible(webConsole);
   await testObjectInspectorPropertiesAreSet(objInspector);
+
+  info("Close webconsole and browser console");
   await closeConsole(browserTab);
-});
+  await HUDService.toggleBrowserConsole();
+}
 
 /**
  * Returns either the Variables View or Object Inspector depending on which is
  * currently in use.
  */
 async function getObjectInspector(hud) {
   const { ui, jsterm } = hud;
 
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_history_arrow_keys.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_history_arrow_keys.js
@@ -25,113 +25,89 @@ add_task(async function() {
   await pushPref("devtools.webconsole.jsterm.codeMirror", true);
   await performTests();
 });
 
 async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const { jsterm } = hud;
 
+  const checkInput = (expected, assertionInfo) =>
+    checkJsTermValueAndCursor(jsterm, expected, assertionInfo);
+
   jsterm.focus();
-  ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
+  checkInput("|", "input is empty");
 
   info("Execute each test value in the console");
   for (const value of TEST_VALUES) {
     jsterm.setInputValue(value);
     await jsterm.execute();
   }
 
-  const values = TEST_VALUES;
-
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[4], "VK_UP: jsterm.getInputValue() #4 is correct");
-  is(getCaretPosition(jsterm), values[4].length, "caret location is correct");
+  checkInput("document.location|", "↑: input #4 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[3], "VK_UP: jsterm.getInputValue() #3 is correct");
-  is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
+  checkInput("document;\nwindow;\ndocument.body|", "↑: input #3 is correct");
   ok(inputHasNoSelection(jsterm));
 
-  info("Select text and ensure hitting arrow up twice won't navigate the history");
-  setCursorAtPosition(jsterm, values[3].length - 2);
+  info("Move cursor and ensure hitting arrow up twice won't navigate the history");
+  EventUtils.synthesizeKey("KEY_ArrowLeft");
+  EventUtils.synthesizeKey("KEY_ArrowLeft");
+  checkInput("document;\nwindow;\ndocument.bo|dy");
+
   EventUtils.synthesizeKey("KEY_ArrowUp");
   EventUtils.synthesizeKey("KEY_ArrowUp");
 
-  is(jsterm.getInputValue(), values[3],
-    "VK_UP two times: jsterm.getInputValue() #3 is correct");
-  is(getCaretPosition(jsterm), jsterm.getInputValue().indexOf("\n"),
-    "caret location is correct");
+  checkInput("document;|\nwindow;\ndocument.body", "↑↑: input #3 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[3],
-     "VK_UP again: jsterm.getInputValue() #3 is correct");
-  is(getCaretPosition(jsterm), 0, "caret location is correct");
+  checkInput("|document;\nwindow;\ndocument.body", "↑ again: input #3 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[2], "VK_UP: jsterm.getInputValue() #2 is correct");
+  checkInput("document.body|", "↑: input #2 is correct");
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[1], "VK_UP: jsterm.getInputValue() #1 is correct");
+  checkInput("window|", "↑: input #1 is correct");
 
   EventUtils.synthesizeKey("KEY_ArrowUp");
-
-  is(jsterm.getInputValue(), values[0], "VK_UP: jsterm.getInputValue() #0 is correct");
-  is(getCaretPosition(jsterm), values[0].length, "caret location is correct");
+  checkInput("document|", "↑: input #0 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[1], "VK_DOWN: jsterm.getInputValue() #1 is correct");
-  is(getCaretPosition(jsterm), values[1].length, "caret location is correct");
+  checkInput("window|", "↓: input #1 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[2], "VK_DOWN: jsterm.getInputValue() #2 is correct");
+  checkInput("document.body|", "↓: input #2 is correct");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[3], "VK_DOWN: jsterm.getInputValue() #3 is correct");
-  is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
+  checkInput("document;\nwindow;\ndocument.body|", "↓: input #3 is correct");
   ok(inputHasNoSelection(jsterm));
 
   setCursorAtPosition(jsterm, 2);
+  checkInput("do|cument;\nwindow;\ndocument.body");
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
   EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[3],
-    "VK_DOWN two times: jsterm.getInputValue() #3 is correct");
-
-  ok(getCaretPosition(jsterm) > jsterm.getInputValue().lastIndexOf("\n")
-    && inputHasNoSelection(jsterm),
-    "caret location is correct");
-
-  EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[3],
-     "VK_DOWN again: jsterm.getInputValue() #3 is correct");
-  is(getCaretPosition(jsterm), values[3].length, "caret location is correct");
+  checkInput("document;\nwindow;\ndo|cument.body", "↓↓: input #3 is correct");
   ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
-
-  is(jsterm.getInputValue(), values[4], "VK_DOWN: jsterm.getInputValue() #4 is correct");
+  checkInput("document;\nwindow;\ndocument.body|", "↓ again: input #3 is correct");
+  ok(inputHasNoSelection(jsterm));
 
   EventUtils.synthesizeKey("KEY_ArrowDown");
+  checkInput("document.location|", "↓: input #4 is correct");
 
-  ok(!jsterm.getInputValue(), "VK_DOWN: jsterm.getInputValue() is empty");
+  EventUtils.synthesizeKey("KEY_ArrowDown");
+  checkInput("|", "↓: input is empty");
 }
 
 function setCursorAtPosition(jsterm, pos) {
   const {inputNode, editor} = jsterm;
 
   if (editor) {
     let line = 0;
     let ch = 0;
@@ -146,25 +122,15 @@ function setCursorAtPosition(jsterm, pos
       return false;
     });
     return editor.setCursor({line, ch });
   }
 
   return inputNode.setSelectionRange(pos, pos);
 }
 
-function getCaretPosition(jsterm) {
-  const {inputNode, editor} = jsterm;
-
-  if (editor) {
-    return editor.getDoc().getRange({line: 0, ch: 0}, editor.getCursor()).length;
-  }
-
-  return inputNode.selectionStart;
-}
-
 function inputHasNoSelection(jsterm) {
   if (jsterm.editor) {
     return !jsterm.editor.getDoc().getSelection();
   }
 
   return jsterm.inputNode.selectionStart === jsterm.inputNode.selectionEnd;
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_input_expansion.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_input_expansion.js
@@ -5,34 +5,29 @@
 
 "use strict";
 
 // Check that the jsterm input supports multiline values. See Bug 588967.
 
 const TEST_URI = "data:text/html;charset=utf-8,Test for jsterm multine input";
 
 add_task(async function() {
-  const hud = await openNewTabAndConsole(TEST_URI);
-
-  const input = hud.jsterm.inputNode;
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
 
-  info("Focus the jsterm input");
-  input.focus();
+async function performTests() {
+  const {jsterm, ui} = await openNewTabAndConsole(TEST_URI);
+  const inputContainer = ui.window.document.querySelector(".jsterm-input-container");
 
-  const ordinaryHeight = input.clientHeight;
+  const ordinaryHeight = inputContainer.clientHeight;
 
   // Set a multiline value
-  input.value = "hello\nworld\n";
-
-  // Set the caret at the end of input
-  const length = input.value.length;
-  input.selectionEnd = length;
-  input.selectionStart = length;
-
-  info("Type 'd' in jsterm to trigger height change for the input");
-  EventUtils.sendString("d");
-  ok(input.clientHeight > ordinaryHeight, "the input expanded");
+  jsterm.setInputValue("hello\nworld\n");
+  ok(inputContainer.clientHeight > ordinaryHeight, "the input expanded");
 
   info("Erase the value and test if the inputNode shrinks again");
-  input.value = "";
-  EventUtils.sendString("d");
-  is(input.clientHeight, ordinaryHeight, "the input's height is normal again");
-});
+  jsterm.setInputValue("");
+  is(inputContainer.clientHeight, ordinaryHeight, "the input's height is normal again");
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_inspect.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_inspect.js
@@ -5,16 +5,24 @@
 
 // Check that the inspect() jsterm helper function works.
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf8,<p>test inspect() command";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
   const hud = toolbox.getCurrentPanel().hud;
 
   const jsterm = hud.jsterm;
 
   info("Test `inspect(window)`");
   // Add a global value so we can check it later.
   await jsterm.execute("testProp = 'testValue'");
@@ -50,13 +58,13 @@ add_task(async function() {
 
   /* Check that a primitive value can be inspected, too */
   info("Test `inspect(1)`");
   await jsterm.execute("inspect(1)");
 
   const inspectPrimitiveNode = await waitFor(() =>
     findInspectResultMessage(hud.ui.outputNode, 2));
   is(inspectPrimitiveNode.textContent, 1, "The primitive is displayed as expected");
-});
+}
 
 function findInspectResultMessage(node, index) {
   return node.querySelectorAll(".message.result")[index];
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_instance_of.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_instance_of.js
@@ -2,16 +2,24 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Check instanceof correctness. See Bug 599940.
 const TEST_URI = "data:text/html,Test <code>instanceof</code> evaluation";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const {jsterm} = hud;
 
   let onMessage = waitForMessage(hud, "true");
   jsterm.execute("[] instanceof Array");
   let message = await onMessage;
   ok(message, "`instanceof Array` is correct");
 
@@ -19,9 +27,9 @@ add_task(async function() {
   jsterm.execute("({}) instanceof Object");
   message = await onMessage;
   ok(message, "`instanceof Object` is correct");
 
   onMessage = waitForMessage(hud, "false");
   jsterm.execute("({}) instanceof Array");
   message = await onMessage;
   ok(message, "`instanceof Array` has expected result");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_multiline.js
@@ -71,54 +71,55 @@ const DATA = [
     value: null,
     extra: {
       lines: "3"
     }
   }
 ];
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   // Let's reset the counts.
   Services.telemetry.clearEvents();
 
   // Ensure no events have been logged
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   ok(!snapshot.parent, "No events have been logged for the main process");
 
-  const hud = await openNewTabAndConsole(TEST_URI);
-  const { inputNode } = hud.jsterm;
+  const {jsterm} = await openNewTabAndConsole(TEST_URI);
 
   for (const {input, shiftKey} of SHOULD_ENTER_MULTILINE) {
-    hud.jsterm.setInputValue(input);
+    jsterm.setInputValue(input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
 
-    const inputValue = hud.jsterm.getInputValue();
-    is(inputNode.selectionStart, inputNode.selectionEnd, "selection is collapsed");
-    is(inputNode.selectionStart, inputValue.length, "caret at end of multiline input");
-
-    const inputWithNewline = input + "\n";
-    is(inputValue, inputWithNewline, "Input value is correct");
+    // We need to remove the spaces at the end of the input since code mirror do some
+    // automatic indent in some case.
+    const newValue = jsterm.getInputValue().replace(/ +$/g, "");
+    is(newValue, input + "\n", "A new line was added");
   }
 
   for (const {input, shiftKey} of SHOULD_EXECUTE) {
-    hud.jsterm.setInputValue(input);
+    jsterm.setInputValue(input);
     EventUtils.synthesizeKey("VK_RETURN", { shiftKey });
 
-    await waitFor(() => !hud.jsterm.getInputValue());
-
-    const inputValue = hud.jsterm.getInputValue();
-    is(inputNode.selectionStart, 0, "selection starts/ends at 0");
-    is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
-    is(inputValue, "", "Input value is cleared");
+    await waitFor(() => !jsterm.getInputValue());
+    is(jsterm.getInputValue(), "", "Input is cleared");
   }
 
-  await hud.jsterm.execute("document.\nlocation.\nhref");
+  await jsterm.execute("document.\nlocation.\nhref");
 
   checkEventTelemetry();
-});
+}
 
 function checkEventTelemetry() {
   const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
   const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
                                                   event[2] === "execute_js" &&
                                                   event[3] === "webconsole" &&
                                                   event[4] === null
   );
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_no_autocompletion_on_defined_variables.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_no_autocompletion_on_defined_variables.js
@@ -6,32 +6,35 @@
 // Tests for bug 704295
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test-console.html";
 
 add_task(async function() {
-  const hud = await openNewTabAndConsole(TEST_URI);
-  testCompletion(hud);
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
 });
 
-function testCompletion(hud) {
+async function performTests() {
+  const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
-  const input = jsterm.inputNode;
 
   // Test typing 'var d = 5;' and press RETURN
   jsterm.setInputValue("var d = ");
   EventUtils.sendString("5;");
-  is(input.value, "var d = 5;", "var d = 5;");
-  is(jsterm.completeNode.value, "", "no completion");
+  is(jsterm.getInputValue(), "var d = 5;", "var d = 5;");
+  checkJsTermCompletionValue(jsterm, "", "no completion");
   EventUtils.synthesizeKey("KEY_Enter");
-  is(jsterm.completeNode.value, "", "clear completion on execute()");
+  checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
 
   // Test typing 'var a = d' and press RETURN
   jsterm.setInputValue("var a = ");
   EventUtils.sendString("d");
-  is(input.value, "var a = d", "var a = d");
-  is(jsterm.completeNode.value, "", "no completion");
+  is(jsterm.getInputValue(), "var a = d", "var a = d");
+  checkJsTermCompletionValue(jsterm, "", "no completion");
   EventUtils.synthesizeKey("KEY_Enter");
-  is(jsterm.completeNode.value, "", "clear completion on execute()");
+  checkJsTermCompletionValue(jsterm, "", "clear completion on execute()");
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_no_input_and_tab_key_pressed.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_no_input_and_tab_key_pressed.js
@@ -5,30 +5,33 @@
 
 "use strict";
 
 // See Bug 583816.
 
 const TEST_URI = "data:text/html,Testing jsterm with no input";
 
 add_task(async function() {
-  const hud = await openNewTabAndConsole(TEST_URI);
-  testCompletion(hud);
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
 });
 
-function testCompletion(hud) {
+async function performTests() {
+  const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
-  const input = jsterm.inputNode;
 
   // With empty input, tab through
   jsterm.setInputValue("");
   EventUtils.synthesizeKey("KEY_Tab");
   is(jsterm.getInputValue(), "", "inputnode is empty - matched");
-  ok(!hasFocus(input), "input isn't focused anymore");
+  ok(!isJstermFocused(jsterm), "input isn't focused anymore");
   jsterm.focus();
 
   // With non-empty input, insert a tab
   jsterm.setInputValue("window.Bug583816");
   EventUtils.synthesizeKey("KEY_Tab");
   is(jsterm.getInputValue(), "window.Bug583816\t",
      "input content - matched");
-  ok(hasFocus(input), "input is still focused");
+  ok(isJstermFocused(jsterm), "input is still focused");
 }
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_null_undefined.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_null_undefined.js
@@ -2,22 +2,30 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI =
   "data:text/html,Test evaluating null and undefined";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const jsterm = hud.jsterm;
 
   // Check that an evaluated null produces "null". See Bug 650780.
   let onMessage = waitForMessage(hud, `null`);
   jsterm.execute("null");
   let message = await onMessage;
   ok(message, "`null` returned the expected value");
 
   onMessage = waitForMessage(hud, "undefined");
   jsterm.execute("undefined");
   message = await onMessage;
   ok(message, "`undefined` returned the expected value");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_popup_close_on_tab_switch.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_popup_close_on_tab_switch.js
@@ -7,21 +7,29 @@
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete " +
                  "popup closes on tab switch";
 const TEST_URI_NAVIGATE = "data:text/html;charset=utf-8,<p>testing autocomplete closes";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   const popup = hud.jsterm.autocompletePopup;
   const popupShown = once(popup, "popup-opened");
 
   hud.jsterm.setInputValue("sc");
   EventUtils.sendString("r");
 
   await popupShown;
 
   await addTab(TEST_URI_NAVIGATE);
 
   ok(!popup.isOpen, "Popup closes on tab switch");
-});
+}
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_clipboard.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_clipboard.js
@@ -8,27 +8,35 @@
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/mochitest/test_jsterm_screenshot_command.html";
 
 // on some machines, such as macOS, dpr is set to 2. This is expected behavior, however
 // to keep tests consistant across OSs we are setting the dpr to 1
 const dpr = "--dpr 1";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   const hud = await openNewTabAndConsole(TEST_URI);
   ok(hud, "web console opened");
 
   await testClipboard(hud);
   await testFullpageClipboard(hud);
   await testSelectorClipboard(hud);
 
   // overflow
   await createScrollbarOverflow();
   await testFullpageClipboardScrollbar(hud);
-});
+}
 
 async function testClipboard(hud) {
   const command = `:screenshot --clipboard ${dpr}`;
   const onMessage = waitForMessage(hud, "Screenshot copied to clipboard.");
   hud.jsterm.execute(command);
   await onMessage;
   const contentSize = await getContentSize();
   const imgSize = await getImageSizeFromClipboard();
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_file.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_screenshot_command_file.js
@@ -9,23 +9,31 @@ const TEST_URI = "http://example.com/bro
                  "test/mochitest/test_jsterm_screenshot_command.html";
 
 const FileUtils = (ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})).FileUtils;
 // on some machines, such as macOS, dpr is set to 2. This is expected behavior, however
 // to keep tests consistant across OSs we are setting the dpr to 1
 const dpr = "--dpr 1";
 
 add_task(async function() {
+  // Run test with legacy JsTerm
+  await performTests();
+  // And then run it with the CodeMirror-powered one.
+  await pushPref("devtools.webconsole.jsterm.codeMirror", true);
+  await performTests();
+});
+
+async function performTests() {
   await addTab(TEST_URI);
 
   const hud = await openConsole();
   ok(hud, "web console opened");
 
   await testFile(hud);
-});
+}
 
 async function testFile(hud) {
   // Test capture to file
   const file = FileUtils.getFile("TmpD", [ "TestScreenshotFile.png" ]);
   const command = `:screenshot ${file.path} ${dpr}`;
   const onMessage = waitForMessage(hud, `Saved to ${file.path}`);
   hud.jsterm.execute(command);
   await onMessage;