Bug 1463674 - Add new test helpers for JsTerm; r=bgrins. draft
authorNicolas Chevobbe <nchevobbe@mozilla.com>
Thu, 05 Jul 2018 16:52:38 +0200
changeset 818822 5b916fe9ad953ce80c058be1ea2eb8894c625c8e
parent 818821 bf88130eb8e92b2bf29dac5024f0dc49f727e9c7
child 818823 77a333a7137b233f3280cddcfc81bc3008953530
push id116354
push userbmo:nchevobbe@mozilla.com
push dateMon, 16 Jul 2018 16:01:04 +0000
reviewersbgrins
bugs1463674
milestone63.0a1
Bug 1463674 - Add new test helpers for JsTerm; r=bgrins. Since we are dealing with 2 versions of this component, we introduce new helpers that abtracts how we get or assert some values. MozReview-Commit-ID: 1XNPcmwwsBj
devtools/client/sourceeditor/editor.js
devtools/client/webconsole/test/mochitest/head.js
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -23,16 +23,17 @@ const VALID_KEYMAPS = new Set(["emacs", 
 // Maximum allowed margin (in number of lines) from top or bottom of the editor
 // while shifting to a line which was initially out of view.
 const MAX_VERTICAL_OFFSET = 3;
 
 // Match @Scratchpad/N:LINE[:COLUMN] or (LINE[:COLUMN]) anywhere at an end of
 // line in text selection.
 const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
 const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
+const AUTOCOMPLETE_MARK_CLASSNAME = "cm-auto-complete-shadow-text";
 
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { PrefObserver } = require("devtools/client/shared/prefs");
 const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
@@ -1268,20 +1269,30 @@ Editor.prototype = {
 
     if (this.config.autocomplete && Services.prefs.getBoolPref(AUTOCOMPLETE)) {
       this.initializeAutoCompletion(this.config.autocompleteOpts);
     } else {
       this.destroyAutoCompletion();
     }
   },
 
+  getAutoCompletionText() {
+    const cm = editors.get(this);
+    const mark = cm.getAllMarks().find(m => m.className === AUTOCOMPLETE_MARK_CLASSNAME);
+    if (!mark) {
+      return "";
+    }
+
+    return mark.title || "";
+  },
+
   setAutoCompletionText: function(text) {
     const cursor = this.getCursor();
     const cm = editors.get(this);
-    const className = "cm-auto-complete-shadow-text";
+    const className = AUTOCOMPLETE_MARK_CLASSNAME;
 
     cm.getAllMarks().forEach(mark => {
       if (mark.className === className) {
         mark.clear();
       }
     });
 
     if (text) {
--- a/devtools/client/webconsole/test/mochitest/head.js
+++ b/devtools/client/webconsole/test/mochitest/head.js
@@ -346,33 +346,51 @@ function hasFocus(node) {
     && node.ownerDocument.hasFocus();
 }
 
 /**
  * Set the value of the JsTerm and its caret position, and fire a completion request.
  *
  * @param {JsTerm} jsterm
  * @param {String} value : The value to set the jsterm to.
- * @param {Integer} caretIndexOffset : A number that will be added to value.length
- *                  when setting the caret. A negative number will place the caret
- *                  in (end - offset) position. Default to 0 (caret set at the end)
+ * @param {Integer} caretPosition : The index where to place the cursor. A negative
+ *                  number will place the caret at (value.length - offset) position.
+ *                  Default to value.length (caret set at the end).
  * @param {Integer} completionType : One of the following jsterm property
  *                   - COMPLETE_FORWARD
  *                   - COMPLETE_BACKWARD
  *                   - COMPLETE_HINT_ONLY
  *                   - COMPLETE_PAGEUP
  *                   - COMPLETE_PAGEDOWN
  *                  Will default to COMPLETE_HINT_ONLY.
  * @returns {Promise} resolves when the jsterm is completed.
  */
-function jstermSetValueAndComplete(jsterm, value, caretIndexOffset = 0, completionType) {
-  const {inputNode} = jsterm;
-  inputNode.value = value;
-  const index = value.length + caretIndexOffset;
-  inputNode.setSelectionRange(index, index);
+function jstermSetValueAndComplete(
+  jsterm,
+  value,
+  caretPosition = value.length,
+  completionType
+) {
+  jsterm.setInputValue(value);
+
+  if (caretPosition < 0) {
+    caretPosition = value.length + caretPosition;
+  }
+
+  if (Number.isInteger(caretPosition)) {
+    if (jsterm.inputNode) {
+      const {inputNode} = jsterm;
+      inputNode.value = value;
+      inputNode.setSelectionRange(caretPosition, caretPosition);
+    }
+
+    if (jsterm.editor) {
+      jsterm.editor.setCursor(jsterm.editor.getPosition(caretPosition));
+    }
+  }
 
   return jstermComplete(jsterm, completionType);
 }
 
 /**
  * Fires a completion request on the jsterm with the specified completionType
  *
  * @param {JsTerm} jsterm
@@ -387,16 +405,130 @@ function jstermSetValueAndComplete(jster
  */
 function jstermComplete(jsterm, completionType = jsterm.COMPLETE_HINT_ONLY) {
   const updated = jsterm.once("autocomplete-updated");
   jsterm.complete(completionType);
   return updated;
 }
 
 /**
+ * Checks if the jsterm has the expected completion value.
+ *
+ * @param {JsTerm} jsterm
+ * @param {String} expectedValue
+ * @param {String} assertionInfo: Description of the assertion passed to `is`.
+ */
+function checkJsTermCompletionValue(jsterm, expectedValue, assertionInfo) {
+  const completionValue = getJsTermCompletionValue(jsterm);
+  if (completionValue === null) {
+    ok(false, "Couldn't retrieve the completion value");
+  }
+
+  info(`Expects "${expectedValue}", is "${completionValue}"`);
+
+  if (jsterm.completeNode) {
+    is(completionValue, expectedValue, assertionInfo);
+  } else {
+    // CodeMirror jsterm doesn't need to add prefix-spaces.
+    is(completionValue, expectedValue.trim(), assertionInfo);
+  }
+}
+
+/**
+ * Checks if the cursor on jsterm is at the expected position.
+ *
+ * @param {JsTerm} jsterm
+ * @param {Integer} expectedCursorIndex
+ * @param {String} assertionInfo: Description of the assertion passed to `is`.
+ */
+function checkJsTermCursor(jsterm, expectedCursorIndex, assertionInfo) {
+  if (jsterm.inputNode) {
+    const {selectionStart, selectionEnd} = jsterm.inputNode;
+    is(selectionStart, expectedCursorIndex, assertionInfo);
+    ok(selectionStart === selectionEnd);
+  } else {
+    is(jsterm.editor.getCursor().ch, expectedCursorIndex, assertionInfo);
+  }
+}
+
+/**
+ * Checks the jsterm value and the cursor position given an expected string containing
+ * a "|" to indicate the expected cursor position.
+ *
+ * @param {JsTerm} jsterm
+ * @param {String} expectedStringWithCursor:
+ *                  String with a "|" to indicate the expected cursor position.
+ *                  For example, this is how you assert an empty value with the focus "|",
+ *                  and this indicates the value should be "test" and the cursor at the
+ *                  end of the input: "test|".
+ * @param {String} assertionInfo: Description of the assertion passed to `is`.
+ */
+function checkJsTermValueAndCursor(jsterm, expectedStringWithCursor, assertionInfo) {
+  info(`Checking jsterm state: \n${expectedStringWithCursor}`);
+  if (!expectedStringWithCursor.includes("|")) {
+    ok(false,
+      `expectedStringWithCursor must contain a "|" char to indicate cursor position`);
+  }
+
+  const inputValue = expectedStringWithCursor.replace("|", "");
+  is(jsterm.getInputValue(), inputValue, "jsterm has expected value");
+  if (jsterm.inputNode) {
+    is(jsterm.inputNode.selectionStart, jsterm.inputNode.selectionEnd);
+    is(jsterm.inputNode.selectionStart, expectedStringWithCursor.indexOf("|"),
+      assertionInfo);
+  } else {
+    const lines = expectedStringWithCursor.split("\n");
+    const lineWithCursor = lines.findIndex(line => line.includes("|"));
+    const {ch, line} = jsterm.editor.getCursor();
+    is(line, lineWithCursor, assertionInfo + " - correct line");
+    is(ch, lines[lineWithCursor].indexOf("|"), assertionInfo + " - correct ch");
+  }
+}
+
+/**
+ * Returns the jsterm completion value, whether there's CodeMirror enabled or not.
+ *
+ * @param {JsTerm} jsterm
+ * @returns {String}
+ */
+function getJsTermCompletionValue(jsterm) {
+  if (jsterm.completeNode) {
+    return jsterm.completeNode.value;
+  }
+
+  if (jsterm.editor) {
+    return jsterm.editor.getAutoCompletionText();
+  }
+
+  return null;
+}
+
+/**
+ * Returns a boolean indicating if the jsterm is focused, whether there's CodeMirror
+ * enabled or not.
+ *
+ * @param {JsTerm} jsterm
+ * @returns {Boolean}
+ */
+function isJstermFocused(jsterm) {
+  const document = jsterm.outputNode.ownerDocument;
+  const documentIsFocused = document.hasFocus();
+
+  if (jsterm.inputNode) {
+    return document.activeElement == jsterm.inputNode && documentIsFocused;
+  }
+
+  if (jsterm.editor) {
+    return documentIsFocused && jsterm.editor.hasFocus();
+  }
+
+  return false;
+}
+
+/**
  * Open the JavaScript debugger.
  *
  * @param object options
  *        Options for opening the debugger:
  *        - tab: the tab you want to open the debugger for.
  * @return object
  *         A promise that is resolved once the debugger opens, or rejected if
  *         the open fails. The resolution callback is given one argument, an