network-overrides-test-helpers.js (9642B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /* import-globals-from head.js */ 7 8 /* exported ORIGINAL_SCRIPT */ 9 const ORIGINAL_SCRIPT = ` 10 const div = document.createElement("div"); 11 div.id = "created-by-original"; 12 document.body.appendChild(div); 13 `; 14 15 /* exported OVERRIDDEN_SCRIPT */ 16 const OVERRIDDEN_SCRIPT = ` 17 const div = document.createElement("div"); 18 div.id = "created-by-override"; 19 document.body.appendChild(div); 20 `; 21 22 /* exported ORIGINAL_STYLESHEET */ 23 const ORIGINAL_STYLESHEET = ` 24 body { 25 color: red; 26 } 27 `; 28 29 /* exported OVERRIDDEN_STYLESHEET */ 30 const OVERRIDDEN_STYLESHEET = ` 31 body { 32 color: blue; 33 } 34 `; 35 36 /* exported ORIGINAL_HTML */ 37 const ORIGINAL_HTML = `<!DOCTYPE html> 38 <html> 39 <head> 40 <meta charset="utf-8"/> 41 <title>Original title</title> 42 </head> 43 <body> 44 Original content 45 <script type="text/javascript" src="/script.js"></script> 46 <style>@import "/style.css"</style> 47 </body> 48 </html>`; 49 50 /* exported OVERRIDDEN_HTML */ 51 const OVERRIDDEN_HTML = `<!DOCTYPE html> 52 <html> 53 <head> 54 <meta charset="utf-8"/> 55 <title>Overridden title</title> 56 </head> 57 <body> 58 Overridden content 59 </body> 60 </html> 61 `; 62 63 function startOverridesHTTPServer() { 64 const httpServer = createTestHTTPServer(); 65 const baseURL = `http://localhost:${httpServer.identity.primaryPort}/`; 66 67 httpServer.registerContentType("html", "text/html"); 68 httpServer.registerContentType("js", "application/javascript"); 69 70 httpServer.registerPathHandler("/index.html", (request, response) => { 71 response.setStatusLine(request.httpVersion, 200, "OK"); 72 response.setHeader("Cache-Control", "max-age=60000"); 73 response.write(ORIGINAL_HTML); 74 }); 75 76 httpServer.registerPathHandler("/script.js", (request, response) => { 77 response.setHeader("Content-Type", "application/javascript"); 78 response.setHeader("Cache-Control", "max-age=60000"); 79 response.write(ORIGINAL_SCRIPT); 80 }); 81 82 httpServer.registerPathHandler("/style.css", (request, response) => { 83 response.setHeader("Content-Type", "text/css; charset=utf-8"); 84 response.setHeader("Cache-Control", "max-age=60000"); 85 response.write(ORIGINAL_STYLESHEET); 86 }); 87 88 return baseURL; 89 } 90 91 /* exported assertOverrideColumnStatus */ 92 /** 93 * Check if the override column is currently visible or not. 94 * 95 * @param {object} monitor 96 * The netmonitor monitor instance. 97 * @param {object} options 98 * @param {boolean} options.visible 99 * Whether the column is expected to be visible or not. 100 */ 101 async function assertOverrideColumnStatus(monitor, { visible }) { 102 const doc = monitor.panelWin.document; 103 104 // Assert override column is hidden 105 is( 106 !!doc.querySelector(`#requests-list-override-button`), 107 visible, 108 `Column Override should be ${visible ? "visible" : "hidden"}` 109 ); 110 111 // Assert override column context menu item status. 112 const overrideColumnToggle = await openContextMenuForItem( 113 monitor, 114 // Trigger the column context menu on the status column which should usually 115 // be available. 116 doc.querySelector("#requests-list-status-button"), 117 "request-list-header-override-toggle" 118 ); 119 120 if (visible) { 121 is( 122 overrideColumnToggle.getAttribute("checked"), 123 "true", 124 "The Override column menu item is checked" 125 ); 126 } else { 127 ok( 128 !overrideColumnToggle.getAttribute("checked"), 129 "The Override column menu item is unchecked" 130 ); 131 } 132 133 // Assert that override column is always disabled 134 is( 135 overrideColumnToggle.disabled, 136 true, 137 "The Override column menu item is disabled" 138 ); 139 140 await hideContextMenu(overrideColumnToggle.parentNode); 141 } 142 143 /* exported assertOverrideCellStatus */ 144 /** 145 * Check if the provided cell is displayed as overridden or not. 146 * 147 * @param {object} request 148 * The request to assert. 149 * @param {object} options 150 * @param {boolean} options.overridden 151 * Whether the request is expected to be overridden or not. 152 */ 153 function assertOverrideCellStatus(request, { overridden }) { 154 is( 155 request 156 .querySelector(".requests-list-override") 157 .classList.contains("request-override-enabled"), 158 overridden, 159 `The request is ${overridden ? " " : "not "}shown as overridden` 160 ); 161 } 162 163 /** 164 * Open the netmonitor context menu on the provided element 165 * 166 * @param {object} monitor 167 * The network monitor object 168 * @param {Element} el 169 * The element on which the menu should be opened 170 * @param {string} id 171 * The id of the context menu item 172 */ 173 async function openContextMenuForItem(monitor, el, id) { 174 EventUtils.sendMouseEvent({ type: "contextmenu" }, el); 175 const menuItem = getContextMenuItem(monitor, id); 176 const popup = menuItem.parentNode; 177 178 if (popup.state != "open") { 179 await BrowserTestUtils.waitForEvent(popup, "popupshown"); 180 } 181 182 return menuItem; 183 } 184 185 /* exported setNetworkOverride */ 186 /** 187 * Setup a network override for the provided monitor, request, override file 188 * name and content. 189 * 190 * @param {object} monitor 191 * The netmonitor monitor instance. 192 * @param {object} request 193 * The request to override. 194 * @param {string} overrideFileName 195 * The file name to use for the override. 196 * @param {string} overrideContent 197 * The content to use for the override. 198 * @returns {string} 199 * The path to the overridden file. 200 * @param {boolean} isEmpty 201 * True if the saved file is supposed to be empty. 202 * This is a workaround for the case where the response is not available. 203 */ 204 async function setNetworkOverride( 205 monitor, 206 request, 207 overrideFileName, 208 overrideContent, 209 isEmpty = false 210 ) { 211 const overridePath = prepareFilePicker( 212 overrideFileName, 213 monitor.toolbox.topWindow 214 ); 215 216 info("Select the request to update"); 217 EventUtils.sendMouseEvent({ type: "mousedown" }, request); 218 219 info("Use set override from the context menu"); 220 EventUtils.sendMouseEvent({ type: "contextmenu" }, request); 221 const waitForSetOverride = waitForDispatch( 222 monitor.toolbox.store, 223 "SET_NETWORK_OVERRIDE" 224 ); 225 await selectContextMenuItem(monitor, "request-list-context-set-override"); 226 await waitForSetOverride; 227 228 info(`Wait for ${overrideFileName} to be saved to disk and re-write it`); 229 await writeTextContentToPath(overrideContent, overridePath, isEmpty); 230 231 return overridePath; 232 } 233 234 /* exported removeNetworkOverride */ 235 /** 236 * Remove a network override for the provided monitor and request. 237 * 238 * @param {object} monitor 239 * The netmonitor monitor instance. 240 * @param {object} request 241 * The request for which the override should be removed. 242 */ 243 async function removeNetworkOverride(monitor, request) { 244 info("Select the request to update"); 245 EventUtils.sendMouseEvent({ type: "mousedown" }, request); 246 247 info("Use remove override from the context menu"); 248 EventUtils.sendMouseEvent({ type: "contextmenu" }, request); 249 const waitForSetOverride = waitForDispatch( 250 monitor.toolbox.store, 251 "REMOVE_NETWORK_OVERRIDE" 252 ); 253 await selectContextMenuItem(monitor, "request-list-context-remove-override"); 254 await waitForSetOverride; 255 } 256 257 /* exported prepareFilePicker */ 258 /** 259 * Mock the file picker. 260 * 261 * @param {string} filename 262 * The name of the file to create. 263 * @param {XULWindow} chromeWindow 264 * The browser window. 265 * @returns {string} 266 * The path of the mocked file. 267 */ 268 function prepareFilePicker(filename, chromeWindow) { 269 const MockFilePicker = SpecialPowers.MockFilePicker; 270 MockFilePicker.init(chromeWindow.browsingContext); 271 const nsiFile = new FileUtils.File( 272 PathUtils.join(PathUtils.tempDir, filename) 273 ); 274 MockFilePicker.setFiles([nsiFile]); 275 return nsiFile.path; 276 } 277 278 /* exported writeTextContentToPath */ 279 /** 280 * Update the text content of the file at the provided path. 281 * 282 * @param {string} textContent 283 * The text content to set. 284 * @param {string} path 285 * The path of the file to update. 286 * @param {boolean} isEmpty 287 * True if the saved file is supposed to be empty. 288 */ 289 async function writeTextContentToPath(textContent, path, isEmpty = false) { 290 await BrowserTestUtils.waitForCondition(() => IOUtils.exists(path)); 291 await BrowserTestUtils.waitForCondition(async () => { 292 const { size } = await IOUtils.stat(path); 293 if (isEmpty) { 294 return size === 0; 295 } 296 return size > 0; 297 }); 298 299 // Bug 1946642: need to cleanup MockFilePicker before potentially creating 300 // another one. Can't use registerCleanupFunction because it will only run 301 // at the end of a full test, at this point init() might already have been 302 // called more than once. Here the MockFilePicker has done its job and can 303 // be cleaned up. 304 SpecialPowers.MockFilePicker.cleanup(); 305 306 await IOUtils.write(path, new TextEncoder().encode(textContent)); 307 } 308 309 /* exported setupNetworkOverridesTest */ 310 /** 311 * Sets up a basic server for network overrides tests, adds a tab loading this 312 * server and starts the netmonitor. 313 * 314 * @returns {object} 315 * An object with monitor, tab and document properties. 316 */ 317 async function setupNetworkOverridesTest({ enableCache = false } = {}) { 318 const baseURL = startOverridesHTTPServer(); 319 const TEST_URL = baseURL + "index.html"; 320 321 const { monitor, tab } = await initNetMonitor(TEST_URL, { 322 requestCount: 3, 323 enableCache, 324 }); 325 326 const { document, store, windowRequire } = monitor.panelWin; 327 const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); 328 store.dispatch(Actions.batchEnable(false)); 329 330 // Reload to have 3 requests in the list 331 const waitForEvents = waitForNetworkEvents(monitor, 3); 332 await navigateTo(TEST_URL); 333 await waitForEvents; 334 335 return { monitor, tab, document }; 336 }