diff --git a/browser/devtools/sourceeditor/autocomplete.js b/browser/devtools/sourceeditor/autocomplete.js
index 0260947b478c..9aa8c99a7032 100644
--- a/browser/devtools/sourceeditor/autocomplete.js
+++ b/browser/devtools/sourceeditor/autocomplete.js
@@ -30,7 +30,7 @@ function setupAutoCompletion(ctx, walker) {
let keyMap = {
"Tab": cm => {
- if (popup && popup.isOpen) {
+ if (popup && (popup.isOpen || popup._panel.state == "showing")) {
cycleSuggestions(ed);
return;
}
@@ -38,7 +38,7 @@ function setupAutoCompletion(ctx, walker) {
return win.CodeMirror.Pass;
},
"Shift-Tab": cm => {
- if (popup && popup.isOpen) {
+ if (popup && (popup.isOpen || popup._panel.state == "showing")) {
cycleSuggestions(ed, true);
return;
}
@@ -83,6 +83,7 @@ function autoComplete({ ed, cm }) {
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
@@ -94,6 +95,8 @@ function autoComplete({ ed, cm }) {
popup.setItems(suggestions);
popup.openPopup(cm.display.cursor, -1 * left, 0);
private.suggestionInsertedOnce = false;
+ // This event is used in tests.
+ ed.emit("after-suggest");
});
}
@@ -119,9 +122,8 @@ function cycleSuggestions(ed, reverse) {
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 = {
@@ -134,6 +136,8 @@ function cycleSuggestions(ed, reverse) {
popup.selectNextItem();
ed.replaceText(popup.selectedItem.label, fromCur, cur);
}
+ // This event is used in tests.
+ ed.emit("suggestion-entered");
}
/**
@@ -147,6 +151,8 @@ function onEditorKeypress(ed, event) {
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:
@@ -161,6 +167,13 @@ function onEditorKeypress(ed, event) {
}
}
+/**
+ * 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;
diff --git a/browser/devtools/styleeditor/test/autocomplete.html b/browser/devtools/styleeditor/test/autocomplete.html
new file mode 100644
index 000000000000..c4eb56bd5aa4
--- /dev/null
+++ b/browser/devtools/styleeditor/test/autocomplete.html
@@ -0,0 +1,22 @@
+
+
+
+ testcase for autocomplete testing
+
+
+
+ parent child
+
+
diff --git a/browser/devtools/styleeditor/test/browser.ini b/browser/devtools/styleeditor/test/browser.ini
index 68a1583ef012..cada4b38bf45 100644
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -1,5 +1,6 @@
[DEFAULT]
support-files =
+ autocomplete.html
browser_styleeditor_cmd_edit.html
four.html
head.js
@@ -26,6 +27,7 @@ support-files =
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]
diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js b/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
new file mode 100644
index 000000000000..781dbf5d296d
--- /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();
+}