browser_changes_nested_rules.js (5090B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test that the Changes panel works with nested rules. 7 8 // Declare rule individually so we can use them for the assertions as well 9 // In the end, we should have nested rule looking like: 10 // - @media screen and (height > 5px) { 11 // -- @layer myLayer { 12 // --- @container myContainer (width > 10px) { 13 // ----- div { 14 // ------- & > span { … } 15 // ------- & .mySpan { 16 // --------- &:not(:focus) { 17 18 const spanNotFocusedRule = `&:not(:focus) { 19 text-decoration: underline; 20 }`; 21 22 const spanClassRule = `.mySpan { 23 font-weight: bold; 24 ${spanNotFocusedRule} 25 }`; 26 27 const spanRule = `& > span { 28 outline: 1px solid gold; 29 }`; 30 31 const divRule = `div { 32 color: tomato; 33 ${spanRule} 34 ${spanClassRule} 35 }`; 36 37 const containerRule = `@container myContainer (width > 10px) { 38 /* in container */ 39 ${divRule} 40 }`; 41 const layerRule = `@layer myLayer { 42 /* in layer */ 43 ${containerRule} 44 }`; 45 const mediaRule = `@media screen and (height > 5px) { 46 /* in media */ 47 ${layerRule} 48 }`; 49 50 const TEST_URI = ` 51 <style> 52 body { 53 container: myContainer / inline-size 54 } 55 ${mediaRule} 56 </style> 57 <div>hello <span class="mySpan">world</span></div> 58 `; 59 60 const applyModificationAfterDivPropertyChange = ruleText => 61 ruleText.replace("tomato", "cyan"); 62 63 const EXPECTED_AFTER_DIV_PROP_CHANGE = [ 64 { 65 text: "@media screen and (height > 5px) {", 66 copyRuleClipboard: applyModificationAfterDivPropertyChange(mediaRule), 67 }, 68 { 69 text: "@layer myLayer {", 70 copyRuleClipboard: applyModificationAfterDivPropertyChange(layerRule), 71 }, 72 { 73 text: "@container myContainer (width > 10px) {", 74 copyRuleClipboard: applyModificationAfterDivPropertyChange(containerRule), 75 }, 76 { 77 text: "div {", 78 copyRuleClipboard: applyModificationAfterDivPropertyChange(divRule), 79 }, 80 ]; 81 82 const applyModificationAfterSpanPropertiesChange = ruleText => 83 ruleText 84 .replace("1px solid gold", "4px solid gold") 85 .replace("bold", "bolder") 86 .replace("underline", "underline dotted"); 87 88 const EXPECTED_AFTER_SPAN_PROP_CHANGES = EXPECTED_AFTER_DIV_PROP_CHANGE.map( 89 expected => ({ 90 ...expected, 91 copyRuleClipboard: applyModificationAfterSpanPropertiesChange( 92 expected.copyRuleClipboard 93 ), 94 }) 95 ).concat([ 96 { 97 text: "& .mySpan {", 98 copyRuleClipboard: 99 applyModificationAfterSpanPropertiesChange(spanClassRule), 100 }, 101 { 102 text: "&:not(:focus) {", 103 copyRuleClipboard: 104 applyModificationAfterSpanPropertiesChange(spanNotFocusedRule), 105 }, 106 { 107 text: "& > span {", 108 copyRuleClipboard: applyModificationAfterSpanPropertiesChange(spanRule), 109 }, 110 ]); 111 112 add_task(async function () { 113 await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); 114 const { inspector, view: ruleView } = await openRuleView(); 115 const changesView = selectChangesView(inspector); 116 const { document: panelDoc, store } = changesView; 117 const panel = panelDoc.querySelector("#sidebar-panel-changes"); 118 119 await selectNode("div", inspector); 120 let onTrackChange = waitForDispatch(store, "TRACK_CHANGE"); 121 await updateDeclaration(ruleView, 1, { color: "tomato" }, { color: "cyan" }); 122 await onTrackChange; 123 124 await assertSelectors(panel, EXPECTED_AFTER_DIV_PROP_CHANGE); 125 126 await selectNode(".mySpan", inspector); 127 onTrackChange = waitForDispatch(store, "TRACK_CHANGE"); 128 await updateDeclaration( 129 ruleView, 130 1, 131 { "text-decoration": "underline" }, 132 { "text-decoration": "underline dotted" } 133 ); 134 await onTrackChange; 135 136 onTrackChange = waitForDispatch(store, "TRACK_CHANGE"); 137 await updateDeclaration( 138 ruleView, 139 2, 140 { "font-weight": "bold" }, 141 { "font-weight": "bolder" } 142 ); 143 await onTrackChange; 144 145 onTrackChange = waitForDispatch(store, "TRACK_CHANGE"); 146 await updateDeclaration( 147 ruleView, 148 3, 149 { outline: "1px solid gold" }, 150 { outline: "4px solid gold" } 151 ); 152 await onTrackChange; 153 154 await assertSelectors(panel, EXPECTED_AFTER_SPAN_PROP_CHANGES); 155 }); 156 157 async function assertSelectors(panel, expected) { 158 await waitFor( 159 () => getSelectors(panel).length === expected.length, 160 "Wait for the expected number of selectors item" 161 ); 162 163 const selectorsEl = getSelectors(panel); 164 is( 165 selectorsEl.length, 166 expected.length, 167 "Got the expected number of selectors item" 168 ); 169 170 for (let i = 0; i < expected.length; i++) { 171 const selectorEl = selectorsEl[i]; 172 const expectedItem = expected[i]; 173 174 is( 175 selectorEl.innerText, 176 expectedItem.text, 177 `Got expected selector text at index ${i}` 178 ); 179 info(`Click the Copy Rule button for the "${expectedItem.text}" rule`); 180 const button = selectorEl 181 .closest(".changes__rule") 182 .querySelector(".changes__copy-rule-button"); 183 await waitForClipboardPromise( 184 () => button.click(), 185 () => checkClipboardData(expectedItem.copyRuleClipboard) 186 ); 187 } 188 } 189 190 function checkClipboardData(expected) { 191 const actual = SpecialPowers.getClipboardData("text/plain"); 192 return actual.trim() === expected.trim(); 193 }