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(); +}