browser_rules_variables_autocomplete.js (5687B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test for autocomplete of CSS variables in the Rules view. 7 8 const IFRAME_URL = `https://example.org/document-builder.sjs?html=${encodeURIComponent(` 9 <style> 10 @property --iframe { 11 syntax: "*"; 12 inherits: true; 13 } 14 body { 15 --iframe-not-registered: turquoise; 16 } 17 18 h1 { 19 color: tomato; 20 } 21 </style> 22 <h1>iframe</h1> 23 `)}`; 24 25 const TEST_URI = `https://example.org/document-builder.sjs?html= 26 <script> 27 CSS.registerProperty({ 28 name: "--js", 29 syntax: "<color>", 30 inherits: false, 31 initialValue: "gold" 32 }); 33 </script> 34 <style> 35 @property --css { 36 syntax: "<color>"; 37 inherits: false; 38 initial-value: tomato; 39 } 40 41 h1 { 42 --css: red; 43 --not-registered: blue; 44 --nested: var(--js); 45 --nested-with-function: color-mix(in srgb, var(--css) 50%, var(--not-registered)); 46 color: gold; 47 } 48 </style> 49 <h1>Hello world</h1> 50 <iframe src="${encodeURIComponent(IFRAME_URL)}"></iframe>`; 51 52 add_task(async function () { 53 await pushPref("layout.css.properties-and-values.enabled", true); 54 55 await addTab(TEST_URI); 56 const { inspector, view } = await openRuleView(); 57 await selectNode("h1", inspector); 58 59 info("Wait for @property panel to be displayed"); 60 await waitFor(() => 61 view.styleDocument.querySelector("#registered-properties-container") 62 ); 63 64 const topLevelVariables = [ 65 { label: "--css", postLabel: "rgb(255, 0, 0)", hasColorSwatch: true }, 66 { label: "--js", postLabel: "gold", hasColorSwatch: true }, 67 { label: "--nested", postLabel: "rgb(255, 215, 0)", hasColorSwatch: true }, 68 { 69 label: "--nested-with-function", 70 postLabel: "color-mix(in srgb, rgb(255, 0, 0) 50%, blue)", 71 hasColorSwatch: true, 72 }, 73 { label: "--not-registered", postLabel: "blue", hasColorSwatch: true }, 74 ]; 75 await checkNewPropertyCssVariableAutocomplete(view, topLevelVariables); 76 77 await checkCssVariableAutocomplete( 78 view, 79 getTextProperty(view, 1, { color: "gold" }).editor.valueSpan, 80 topLevelVariables 81 ); 82 83 info( 84 "Check that the list is correct when selecting a node from another document" 85 ); 86 await selectNodeInFrames(["iframe", "h1"], inspector); 87 88 const iframeVariables = [ 89 { label: "--iframe" }, 90 { 91 label: "--iframe-not-registered", 92 postLabel: "turquoise", 93 hasColorSwatch: true, 94 }, 95 ]; 96 await checkNewPropertyCssVariableAutocomplete(view, iframeVariables); 97 98 await checkCssVariableAutocomplete( 99 view, 100 getTextProperty(view, 1, { color: "tomato" }).editor.valueSpan, 101 iframeVariables 102 ); 103 }); 104 105 async function checkNewPropertyCssVariableAutocomplete( 106 view, 107 expectedPopupItems 108 ) { 109 const ruleEditor = getRuleViewRuleEditor(view, 1); 110 const editor = await focusNewRuleViewProperty(ruleEditor); 111 const onPopupOpen = editor.popup.once("popup-opened"); 112 EventUtils.sendString("--"); 113 await onPopupOpen; 114 115 assertEditorPopupItems( 116 editor, 117 // we don't display postLabel for the new property 118 expectedPopupItems.map(item => ({ label: item.label })) 119 ); 120 121 info("Close the popup"); 122 const onPopupClosed = once(editor.popup, "popup-closed"); 123 EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); 124 await onPopupClosed; 125 126 info("Close the editor"); 127 EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); 128 } 129 130 async function checkCssVariableAutocomplete( 131 view, 132 inplaceEditorEl, 133 expectedPopupItems 134 ) { 135 const editor = await focusEditableField(view, inplaceEditorEl); 136 await wait(500); 137 138 const onCloseParenthesisAppended = editor.once("after-suggest"); 139 EventUtils.sendString("var("); 140 await onCloseParenthesisAppended; 141 142 let onRuleViewChanged = view.once("ruleview-changed"); 143 EventUtils.sendString("--"); 144 const onPopupOpen = editor.popup.once("popup-opened"); 145 view.debounce.flush(); 146 await onPopupOpen; 147 assertEditorPopupItems(editor, expectedPopupItems); 148 await onRuleViewChanged; 149 150 info("Close the popup"); 151 const onPopupClosed = once(editor.popup, "popup-closed"); 152 EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); 153 await onPopupClosed; 154 155 info("Cancel"); 156 onRuleViewChanged = view.once("ruleview-changed"); 157 EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow); 158 await onRuleViewChanged; 159 160 view.debounce.flush(); 161 } 162 163 /** 164 * Check that the popup items are the expected ones. 165 * 166 * @param {InplaceEditor} editor 167 * @param {Array{Object}} expectedPopupItems 168 */ 169 function assertEditorPopupItems(editor, expectedPopupItems) { 170 const popupListItems = Array.from(editor.popup.list.querySelectorAll("li")); 171 is( 172 popupListItems.length, 173 expectedPopupItems.length, 174 "Popup has expected number of items" 175 ); 176 popupListItems.forEach((li, i) => { 177 const expected = expectedPopupItems[i]; 178 const value = 179 (li.querySelector(".initial-value")?.textContent ?? "") + 180 li.querySelector(".autocomplete-value").textContent; 181 is(value, expected.label, `Popup item #${i} as expected label`); 182 183 // Don't pollute test logs if we don't have the expected variable 184 if (value !== expected.label) { 185 return; 186 } 187 188 const postLabelEl = li.querySelector(".autocomplete-postlabel"); 189 is( 190 li.querySelector(".autocomplete-postlabel")?.textContent, 191 expected.postLabel, 192 `${expected.label} has expected post label` 193 ); 194 is( 195 !!postLabelEl?.querySelector(".autocomplete-swatch"), 196 !!expected.hasColorSwatch, 197 `${expected.label} ${ 198 expected.hasColorSwatch ? "has" : "does not have" 199 } a post label color swatch` 200 ); 201 }); 202 }