browser_inplace-editor_autocomplete_02.js (9670B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 /* import-globals-from helper_inplace_editor.js */ 4 5 "use strict"; 6 7 const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js"); 8 const { 9 InplaceEditor, 10 } = require("resource://devtools/client/shared/inplace-editor.js"); 11 loadHelperScript("helper_inplace_editor.js"); 12 13 // Test the inplace-editor autocomplete popup for CSS values suggestions. 14 // Using a mocked list of CSS properties to avoid test failures linked to 15 // engine changes (new property, removed property, ...). 16 17 const mockValues = { 18 "background-image": [ 19 "linear-gradient", 20 "radial-gradient", 21 "repeating-radial-gradient", 22 ], 23 color: ["blue", "red", "rgb"], 24 display: ["block", "flex", "inline", "inline-block", "none"], 25 "grid-template-areas": ["unset", "inherits"], 26 }; 27 28 add_task(async function () { 29 await addTab( 30 "data:text/html;charset=utf-8," + "inplace editor CSS value autocomplete" 31 ); 32 const { host, win, doc } = await createHost(); 33 34 info("Test for css property completion"); 35 await createEditorAndRunCompletionTest(doc, win, "display", [ 36 ["b", "block", -1, []], 37 ["VK_BACK_SPACE", "b", -1, []], 38 ["VK_BACK_SPACE", "", -1, []], 39 ["i", "inline", 0, ["inline", "inline-block"]], 40 ["VK_DOWN", "inline-block", 1, ["inline", "inline-block"]], 41 ["VK_DOWN", "inline", 0, ["inline", "inline-block"]], 42 ["VK_LEFT", "inline", -1, []], 43 // Shift + navigation key shouldn't trigger autocomplete (Bug 1184538) 44 [{ key: "VK_UP", shiftKey: true }, "inline", -1, [], null, false, true], 45 [{ key: "VK_DOWN", shiftKey: true }, "inline", -1, [], null, false, true], 46 [{ key: "VK_LEFT", shiftKey: true }, "inline", -1, [], null, false, true], 47 [{ key: "VK_RIGHT", shiftKey: true }, "inline", -1, [], null, false, true], 48 [{ key: "VK_HOME", shiftKey: true }, "inline", -1, [], null, false, true], 49 [{ key: "VK_END", shiftKey: true }, "inline", -1, [], null, false, true], 50 // "Select All" keyboard shortcut shouldn't trigger autocomplete 51 [ 52 { 53 key: "a", 54 [AppConstants.platform == "macosx" ? "metaKey" : "ctrlKey"]: true, 55 }, 56 "inline", 57 -1, 58 [], 59 null, 60 false, 61 true, 62 ], 63 ]); 64 65 info("Test for css property completion after css comment"); 66 await createEditorAndRunCompletionTest(doc, win, "display", [ 67 ["/", "/", -1, []], 68 ["*", "/*", -1, []], 69 // "/*/" is still an unclosed comment 70 ["/", "/*/", -1, []], 71 [" ", "/*/ ", -1, []], 72 ["h", "/*/ h", -1, []], 73 ["i", "/*/ hi", -1, []], 74 [" ", "/*/ hi ", -1, []], 75 ["*", "/*/ hi *", -1, []], 76 // note that !important is not displayed here 77 ["/", "/*/ hi */block", 0, mockValues.display], 78 ["b", "/*/ hi */block", -1, []], 79 ["VK_BACK_SPACE", "/*/ hi */b", -1, []], 80 ["VK_BACK_SPACE", "/*/ hi */", -1, []], 81 ["i", "/*/ hi */inline", 0, ["inline", "inline-block"]], 82 ["VK_DOWN", "/*/ hi */inline-block", 1, ["inline", "inline-block"]], 83 ["VK_DOWN", "/*/ hi */inline", 0, ["inline", "inline-block"]], 84 ["VK_LEFT", "/*/ hi */inline", -1, []], 85 ]); 86 87 info("Test that !important is only added when we want"); 88 await createEditorAndRunCompletionTest(doc, win, "display", [ 89 // !important doesn't get displayed if there's only whitespace before the cursor 90 [" ", " block", 0, mockValues.display], 91 // !important doesn't get displayed if there's no meaningful char before 92 ["!", " !", -1, []], 93 ["i", " !i", -1, []], 94 ["m", " !im", -1, []], 95 // deleting `!im` 96 ["VK_BACK_SPACE", " !i", -1, []], 97 ["VK_BACK_SPACE", " !", -1, []], 98 ["VK_BACK_SPACE", " ", -1, []], 99 ["b", " block", -1, []], 100 ["VK_RIGHT", " block", -1, []], 101 // !important is displayed after a space 102 [" ", " block block", 0, [...mockValues.display, "!important"]], 103 [" ", " block block", 0, [...mockValues.display, "!important"]], 104 ["!", " block !important", -1, []], 105 ["VK_BACK_SPACE", " block !", -1, []], 106 ["VK_BACK_SPACE", " block ", -1, []], 107 ["VK_LEFT", " block ", -1, []], 108 // !important is displayed even if there is a space after the cursor 109 [" ", " block block ", 0, [...mockValues.display, "!important"]], 110 // Add a char that doesn't match anything, and place the cursor before it 111 ["x", " block x ", -1, []], 112 ["VK_LEFT", " block x ", -1, []], 113 // cursor is now between block and x: block | x (where | is the cursor) 114 ["VK_LEFT", " block x ", -1, []], 115 // trigger the autocomplete, and check that !important is not in it 116 [" ", " block block x ", 0, mockValues.display], 117 // cancel autocomplete 118 ["VK_BACK_SPACE", " block x ", -1, []], 119 // Move to the end 120 ...Array.from({ length: 3 }).map(() => ["VK_RIGHT", " block x ", -1, []]), 121 // Autocomplete !important 122 ["!", " block x !important", -1, []], 123 // Accept autocomplete 124 ["VK_RIGHT", " block x !important", -1, []], 125 // Check that no autocomplete appears when adding a space after !important 126 [" ", " block x !important ", -1, []], 127 // And that we don't try to autocomplete another !important keyword 128 ["!", " block x !important !", -1, []], 129 ]); 130 131 info("Test for css property completion in gradient function"); 132 await createEditorAndRunCompletionTest(doc, win, "background-image", [ 133 [ 134 `r`, 135 `radial-gradient`, 136 0, 137 ["radial-gradient", "repeating-radial-gradient"], 138 ], 139 // accept the completion 140 [`VK_RIGHT`, `radial-gradient`, -1, []], 141 // entering the opening bracket opens the autocomplete poup 142 [`(`, `radial-gradient(blue)`, 0, ["blue", "red", "rgb"]], 143 // the list gets properly filtered 144 [`r`, `radial-gradient(red)`, 0, ["red", "rgb"]], 145 // accept selected value 146 [`VK_RIGHT`, `radial-gradient(red)`, -1, []], 147 // entering a comma does trigger the autocomplete 148 [`,`, `radial-gradient(red,blue)`, 0, ["blue", "red", "rgb"]], 149 // entering a numerical value makes the autocomplete go away 150 [`1`, `radial-gradient(red,1)`, -1, []], 151 [`0`, `radial-gradient(red,10)`, -1, []], 152 [`%`, `radial-gradient(red,10%)`, -1, []], 153 // entering a space after the numerical value does trigger the autocomplete 154 [` `, `radial-gradient(red,10% blue)`, 0, ["blue", "red", "rgb"]], 155 [`r`, `radial-gradient(red,10% red)`, 0, ["red", "rgb"]], 156 [`g`, `radial-gradient(red,10% rgb)`, -1, []], 157 [`VK_RIGHT`, `radial-gradient(red,10% rgb)`, -1, []], 158 // there is no autocomplete for "rgb()" 159 [`(`, `radial-gradient(red,10% rgb())`, -1, []], 160 [`0`, `radial-gradient(red,10% rgb(0))`, -1, []], 161 [`,`, `radial-gradient(red,10% rgb(0,))`, -1, []], 162 [`1`, `radial-gradient(red,10% rgb(0,1))`, -1, []], 163 [`,`, `radial-gradient(red,10% rgb(0,1,))`, -1, []], 164 [`2`, `radial-gradient(red,10% rgb(0,1,2))`, -1, []], 165 // entering the closing parenthesis for rgb does not add a new one, 166 // but reuse the one that was automatically inserted 167 [`)`, `radial-gradient(red,10% rgb(0,1,2))`, -1, []], 168 // entering a comma does trigger the autocomplete again after rgb() is closed 169 [ 170 `,`, 171 `radial-gradient(red,10% rgb(0,1,2),blue)`, 172 0, 173 ["blue", "red", "rgb"], 174 ], 175 // cancel adding a new argument in radial-gradient 176 ["VK_BACK_SPACE", "radial-gradient(red,10% rgb(0,1,2),)", -1, []], 177 ["VK_BACK_SPACE", "radial-gradient(red,10% rgb(0,1,2))", -1, []], 178 // entering the closing parenthesis for radial-gradient does not add a new one, 179 // but reuse the one that was automatically inserted 180 [")", "radial-gradient(red,10% rgb(0,1,2))", -1, []], 181 // entering a comma does trigger the autocomplete for the property again 182 [ 183 ",", 184 "radial-gradient(red,10% rgb(0,1,2)),linear-gradient", 185 0, 186 // we don't show !important here 187 ["linear-gradient", "radial-gradient", "repeating-radial-gradient"], 188 ], 189 // don't go further, we covered everything 190 ]); 191 192 info("Test for no completion in string value"); 193 await createEditorAndRunCompletionTest(doc, win, "grid-template-areas", [ 194 [`"`, `"`, -1, 0], 195 [`a`, `"a`, -1, 0], 196 [` `, `"a `, -1, 0], 197 [`.`, `"a .`, -1, 0], 198 [`"`, `"a ."`, -1, 0], 199 ]); 200 201 host.destroy(); 202 gBrowser.removeCurrentTab(); 203 }); 204 205 /** 206 * 207 * @param {Document} doc 208 * @param {Window} win 209 * @param {string} property - The property name 210 * @param {Array} testData - The data passed to helper_inplace_editor.js `testComplation`. 211 * format : 212 * [ 213 * what key to press, 214 * expected input box value after keypress, 215 * selected suggestion index (-1 if popup is hidden), 216 * number of suggestions in the popup (0 if popup is hidden), or the array of items label 217 * ] 218 */ 219 async function createEditorAndRunCompletionTest(doc, win, property, testData) { 220 const xulDocument = win.top.document; 221 const popup = new AutocompletePopup(xulDocument, { autoSelect: true }); 222 223 await new Promise(resolve => { 224 createInplaceEditorAndClick( 225 { 226 start: async editor => { 227 for (const data of testData) { 228 await testCompletion(data, editor); 229 } 230 231 EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView); 232 }, 233 contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE, 234 property: { 235 name: property, 236 }, 237 cssProperties: { 238 getNames: () => Object.keys(mockValues), 239 getValues: propertyName => mockValues[propertyName] || [], 240 }, 241 done: resolve, 242 popup, 243 }, 244 doc 245 ); 246 }); 247 popup.destroy(); 248 }