Bug 717369 - Autocomplete CSS properties and values in the Style Editor - Part 3 - Style editor tests, r=harth, robcee
authorGirish Sharma <scrapmachines@gmail.com>
Tue, 28 Jan 2014 20:41:22 +0530
changeset 181584 59cc8ecd78d3204acd5c70eb745a2a2d62428d7e
parent 181583 025d4a4b4223575aaaf7442f416a064f000d6d79
child 181585 cbc8854278fbfeb9cf0f3f5603c9da38e384ffae
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersharth, robcee
bugs717369
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 717369 - Autocomplete CSS properties and values in the Style Editor - Part 3 - Style editor tests, r=harth, robcee
browser/devtools/sourceeditor/autocomplete.js
browser/devtools/styleeditor/test/autocomplete.html
browser/devtools/styleeditor/test/browser.ini
browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
--- a/browser/devtools/sourceeditor/autocomplete.js
+++ b/browser/devtools/sourceeditor/autocomplete.js
@@ -25,25 +25,25 @@ function setupAutoCompletion(ctx, walker
     position: "after_start",
     fixedWidth: true,
     theme: "auto",
     autoSelect: true
   });
 
   let keyMap = {
     "Tab": cm => {
-      if (popup && popup.isOpen) {
+      if (popup && (popup.isOpen || popup._panel.state == "showing")) {
         cycleSuggestions(ed);
         return;
       }
 
       return win.CodeMirror.Pass;
     },
     "Shift-Tab": cm => {
-      if (popup && popup.isOpen) {
+      if (popup && (popup.isOpen || popup._panel.state == "showing")) {
         cycleSuggestions(ed, true);
         return;
       }
 
       return win.CodeMirror.Pass;
     },
   };
   keyMap[Editor.accel("Space")] = cm => autoComplete(ctx);
@@ -78,27 +78,30 @@ function autoComplete({ ed, cm }) {
     return;
   }
   let cur = ed.getCursor();
   completer.complete(cm.getRange({line: 0, ch: 0}, cur), cur)
     .then(suggestions => {
     if (!suggestions || !suggestions.length || !suggestions[0].preLabel) {
       private.suggestionInsertedOnce = false;
       popup.hidePopup();
+      ed.emit("after-suggest");
       return;
     }
     // The cursor is at the end of the currently entered part of the token, like
     // "backgr|" but we need to open the popup at the beginning of the character
     // "b". Thus we need to calculate the width of the entered part of the token
     // ("backgr" here). 4 comes from the popup's left padding.
     let left = suggestions[0].preLabel.length * cm.defaultCharWidth() + 4;
     popup.hidePopup();
     popup.setItems(suggestions);
     popup.openPopup(cm.display.cursor, -1 * left, 0);
     private.suggestionInsertedOnce = false;
+    // This event is used in tests.
+    ed.emit("after-suggest");
   });
 }
 
 /**
  * Cycles through provided suggestions by the popup in a top to bottom manner
  * when `reverse` is not true. Opposite otherwise.
  */
 function cycleSuggestions(ed, reverse) {
@@ -114,53 +117,63 @@ function cycleSuggestions(ed, reverse) {
       popup.selectPreviousItem();
     } else {
       firstItem = popup.getItemAtIndex(0);
       if (firstItem.label == firstItem.preLabel && popup.itemCount > 1) {
         firstItem = popup.getItemAtIndex(1);
         popup.selectNextItem();
       }
     }
-    if (popup.itemCount == 1) {
+    if (popup.itemCount == 1)
       popup.hidePopup();
-    }
     ed.replaceText(firstItem.label.slice(firstItem.preLabel.length), cur, cur);
   } else {
     let fromCur = {
       line: cur.line,
       ch  : cur.ch - popup.selectedItem.label.length
     };
     if (reverse)
       popup.selectPreviousItem();
     else
       popup.selectNextItem();
     ed.replaceText(popup.selectedItem.label, fromCur, cur);
   }
+  // This event is used in tests.
+  ed.emit("suggestion-entered");
 }
 
 /**
  * onkeydown handler for the editor instance to prevent autocompleting on some
  * keypresses.
  */
 function onEditorKeypress(ed, event) {
   let private = privates.get(ed);
   switch (event.keyCode) {
     case event.DOM_VK_UP:
     case event.DOM_VK_DOWN:
     case event.DOM_VK_LEFT:
     case event.DOM_VK_RIGHT:
+    case event.DOM_VK_HOME:
+    case event.DOM_VK_END:
     case event.DOM_VK_BACK_SPACE:
     case event.DOM_VK_DELETE:
     case event.DOM_VK_ENTER:
     case event.DOM_VK_RETURN:
     case event.DOM_VK_ESCAPE:
       private.doNotAutocomplete = true;
       private.popup.hidePopup();
       break;
 
     default:
       private.doNotAutocomplete = false;
   }
 }
 
+/**
+ * Returns the private popup. This method is used by tests to test the feature.
+ */
+function getPopup({ ed }) {
+  return privates.get(ed).popup;
+}
 // Export functions
 
 module.exports.setupAutoCompletion = setupAutoCompletion;
+module.exports.getAutocompletionPopup = getPopup;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/autocomplete.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+<head>
+  <title>testcase for autocomplete testing</title>
+  <style type="text/css">
+  div {
+    font-size: 4em;
+  }
+
+  div > span {
+     text-decoration: underline;
+  }
+
+  div + button {
+    border: 2px dotted red;
+  }
+  </style>
+</head>
+<body>
+  <div>parent <span>child</span></div><button>sibling</button>
+</body>
+</html>
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
+  autocomplete.html
   browser_styleeditor_cmd_edit.html
   four.html
   head.js
   import.css
   import.html
   import2.css
   longload.html
   media-small.css
@@ -21,16 +22,17 @@ support-files =
   simple.html
   sourcemaps.css
   sourcemaps.css.map
   sourcemaps.scss
   sourcemaps.html
   test_private.css
   test_private.html
 
+[browser_styleeditor_autocomplete.js]
 [browser_styleeditor_bug_740541_iframes.js]
 [browser_styleeditor_bug_851132_middle_click.js]
 [browser_styleeditor_bug_870339.js]
 [browser_styleeditor_cmd_edit.js]
 [browser_styleeditor_enabled.js]
 [browser_styleeditor_filesave.js]
 [browser_styleeditor_import.js]
 [browser_styleeditor_import_rule.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
@@ -0,0 +1,179 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TESTCASE_URI = TEST_BASE + "autocomplete.html";
+const MAX_SUGGESTIONS = 15;
+
+// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
+const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
+
+// Test cases to test that autocompletion works correctly when enabled.
+// Format:
+// [
+//   -1 for pressing Ctrl + Space or the particular key to press,
+//   Number of suggestions in the popup (-1 if popup is closed),
+//   Index of selected suggestion,
+//   1 to check whether the selected suggestion is inserted into the editor or not
+// ]
+let TEST_CASES = [
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  [-1, 1, 0],
+  ['VK_DOWN', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  [-1, MAX_SUGGESTIONS, 0],
+  ['VK_END', -1],
+  ['VK_RETURN', -1],
+  ['b', MAX_SUGGESTIONS, 0],
+  ['a', 11, 0],
+  ['VK_TAB', 11, 0, 1],
+  ['VK_TAB', 11, 1, 1],
+  [':', -1],
+  ['b', 9, 0],
+  ['l', 4, 0],
+  ['VK_TAB', 4, 0, 1],
+  ['VK_TAB', 4, 1, 1],
+  ['VK_TAB', 4, 2, 1],
+  ['VK_DOWN', -1],
+  ['VK_RETURN', -1],
+  ['b', 2, 0],
+  ['u', 1, 0],
+  ['VK_TAB', -1],
+  ['{', -1],
+  ['VK_HOME', -1],
+  ['VK_DOWN', -1],
+  ['VK_DOWN', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  ['VK_RIGHT', -1],
+  [-1, 1, 0],
+];
+
+let gEditor;
+let gPopup;
+let index = 0;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  addTabAndOpenStyleEditor(function(panel) {
+    panel.UI.on("editor-added", testEditorAdded);
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function testEditorAdded(aEvent, aEditor) {
+  info("Editor added, getting the source editor and starting tests");
+  aEditor.getSourceEditor().then(editor => {
+    info("source editor found, starting tests.");
+    gEditor = editor.sourceEditor;
+    gPopup = gEditor.getAutocompletionPopup();
+    waitForFocus(testState, gPanelWindow);
+  });
+}
+
+function testState() {
+  if (index == TEST_CASES.length) {
+    testAutocompletionDisabled();
+    return;
+  }
+
+  let [key] = TEST_CASES[index];
+  let mods = {};
+
+  if (key == -1) {
+    info("pressing Ctrl + Space to get result: [" + TEST_CASES[index] +
+         "] for index " + index);
+    gEditor.once("after-suggest", checkState);
+    key = " ";
+    mods.accelKey = true;
+  }
+  else if (/(down|left|right|return|home|end)/ig.test(key)) {
+    info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
+         "] for index " + index);
+    gEditor.once("cursorActivity", checkState);
+  }
+  else if (key == "VK_TAB") {
+    info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
+         "] for index " + index);
+    gEditor.once("suggestion-entered", checkState);
+  }
+  else {
+    info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
+         "] for index " + index);
+    gEditor.once("after-suggest", checkState);
+  }
+  EventUtils.synthesizeKey(key, mods, gPanelWindow);
+}
+
+function checkState() {
+  executeSoon(() => {
+    info("After keypress for index " + index);
+    let [key, total, current, inserted] = TEST_CASES[index];
+    if (total != -1) {
+      ok(gPopup._panel.state == "open" || gPopup._panel.state == "showing",
+         "Popup is open for index " + index);
+      is(total, gPopup.itemCount,
+         "Correct total suggestions for index " + index);
+      is(current, gPopup.selectedIndex,
+         "Correct index is selected for index " + index);
+      if (inserted) {
+        let { preLabel, label } = gPopup.getItemAtIndex(current);
+        let { line, ch } = gEditor.getCursor();
+        let lineText = gEditor.getText(line);
+        is(lineText.substring(ch - label.length, ch), label,
+           "Current suggestion from the popup is inserted into the editor.");
+      }
+    }
+    else {
+      ok(gPopup._panel.state != "open" && gPopup._panel.state != "showing",
+         "Popup is closed for index " + index);
+    }
+    index++;
+    testState();
+  });
+}
+
+function testAutocompletionDisabled() {
+  gBrowser.removeCurrentTab();
+
+  index = 0;
+  info("Starting test to check if autocompletion is disabled correctly.")
+  Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
+
+  addTabAndOpenStyleEditor(function(panel) {
+    panel.UI.on("editor-added", testEditorAddedDisabled);
+  });
+
+  content.location = TESTCASE_URI;
+}
+
+function testEditorAddedDisabled(aEvent, aEditor) {
+  info("Editor added, getting the source editor and starting tests");
+  aEditor.getSourceEditor().then(editor => {
+    ok(!editor.sourceEditor.getAutocompletionPopup,
+       "Autocompletion popup does not exist");
+    cleanup();
+  });
+}
+
+function cleanup() {
+  Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
+  gEditor = null;
+  gPopup = null;
+  finish();
+}