browser_net_resend.js (12369B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /** 7 * Tests if resending a request works. 8 */ 9 10 add_task(async function () { 11 if ( 12 Services.prefs.getBoolPref( 13 "devtools.netmonitor.features.newEditAndResend", 14 true 15 ) 16 ) { 17 await testResendRequest(); 18 } else { 19 await testOldEditAndResendPanel(); 20 } 21 }); 22 23 // This tests resending a request without editing using 24 // the resend context menu item. This particularly covering 25 // the new resend functionality. 26 async function testResendRequest() { 27 const { tab, monitor } = await initNetMonitor(POST_DATA_URL, { 28 requestCount: 1, 29 }); 30 info("Starting test... "); 31 32 const { document, store, windowRequire } = monitor.panelWin; 33 34 // Action should be processed synchronously in tests. 35 const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); 36 store.dispatch(Actions.batchEnable(false)); 37 38 await performRequests(monitor, tab, 2); 39 40 is( 41 document.querySelectorAll(".request-list-item").length, 42 2, 43 "There are currently two requests" 44 ); 45 46 const firstResend = await resendRequestAndWaitForNewRequest( 47 monitor, 48 document.querySelectorAll(".request-list-item")[0] 49 ); 50 51 Assert.notStrictEqual( 52 firstResend.originalResource.resourceId, 53 firstResend.newResource.resourceId, 54 "The resent request is different resource from the first request" 55 ); 56 57 is( 58 firstResend.originalResource.url, 59 firstResend.newResource.url, 60 "The resent request has the same url and query parameters and the first request" 61 ); 62 63 // The priority header only appears when the urgency and incremental values 64 // are not both default values (u=3 and i=false). In this case the original 65 // request has no priority header and the resent request does, hence we subtract one. 66 is( 67 firstResend.originalResource.requestHeaders.headers.length, 68 firstResend.newResource.requestHeaders.headers.length - 1, 69 "The no of headers are the same" 70 ); 71 72 // Because a resent request has a different purpose and principal it will 73 // also have a different CoS flag (meaning a different priority header). 74 // So we can't compare the original and resent request's priority and skip it. 75 firstResend.originalResource.requestHeaders.headers.forEach( 76 ({ name, value }) => { 77 if (name === "Priority") { 78 return; 79 } 80 const foundHeader = firstResend.newResource.requestHeaders.headers.find( 81 header => header.name == name 82 ); 83 is( 84 value, 85 foundHeader.value, 86 `The '${name}' header for the request and the resent request match` 87 ); 88 } 89 ); 90 91 info("Check that the custom headers and form data are resent correctly"); 92 const secondResend = await resendRequestAndWaitForNewRequest( 93 monitor, 94 document.querySelectorAll(".request-list-item")[1] 95 ); 96 97 Assert.notStrictEqual( 98 secondResend.originalResource.resourceId, 99 secondResend.newResource.resourceId, 100 "The resent request is different resource from the second request" 101 ); 102 103 const customHeader = 104 secondResend.originalResource.requestHeaders.headers.find( 105 header => header.name == "custom-header-xxx" 106 ); 107 108 const customHeaderInResentRequest = 109 secondResend.newResource.requestHeaders.headers.find( 110 header => header.name == "custom-header-xxx" 111 ); 112 113 is( 114 customHeader.value, 115 customHeaderInResentRequest.value, 116 "The custom header in the resent request is the same as the second request" 117 ); 118 119 is( 120 customHeaderInResentRequest.value, 121 "custom-value-xxx", 122 "The custom header in the resent request is correct" 123 ); 124 125 is( 126 secondResend.originalResource.requestPostData.postData.text, 127 secondResend.newResource.requestPostData.postData.text, 128 "The form data in the resent is the same as the second request" 129 ); 130 } 131 132 async function resendRequestAndWaitForNewRequest(monitor, originalRequestItem) { 133 const { document, store, windowRequire, connector } = monitor.panelWin; 134 const { getSelectedRequest, getDisplayedRequests } = windowRequire( 135 "devtools/client/netmonitor/src/selectors/index" 136 ); 137 138 info("Select the request to resend"); 139 const expectedNoOfRequestsAfterResend = 140 getDisplayedRequests(store.getState()).length + 1; 141 142 const waitForHeaders = waitUntil(() => 143 document.querySelector(".headers-overview") 144 ); 145 EventUtils.sendMouseEvent({ type: "mousedown" }, originalRequestItem); 146 await waitForHeaders; 147 148 const originalResourceId = getSelectedRequest(store.getState()).id; 149 150 const waitForNewRequest = waitUntil( 151 () => 152 getDisplayedRequests(store.getState()).length == 153 expectedNoOfRequestsAfterResend && 154 getSelectedRequest(store.getState()).id !== originalResourceId 155 ); 156 157 info("Open the context menu and select the resend for the request"); 158 EventUtils.sendMouseEvent({ type: "contextmenu" }, originalRequestItem); 159 const wait = waitForNetworkEvents(monitor, 1); 160 await selectContextMenuItem(monitor, "request-list-context-resend-only"); 161 await waitForNewRequest; 162 await wait; 163 164 const newResourceId = getSelectedRequest(store.getState()).id; 165 166 // Make sure we fetch the request headers and post data for the 167 // new request so we can assert them. 168 await connector.requestData(newResourceId, "requestHeaders"); 169 await connector.requestData(newResourceId, "requestPostData"); 170 171 return { 172 originalResource: getRequestById(store.getState(), originalResourceId), 173 newResource: getRequestById(store.getState(), newResourceId), 174 }; 175 } 176 177 // This is a basic test for the old edit and resend panel 178 // This should be removed soon in Bug 1745416 when we remove 179 // the old panel functionality. 180 async function testOldEditAndResendPanel() { 181 const ADD_QUERY = "t1=t2"; 182 const ADD_HEADER = "Test-header: true"; 183 const ADD_UA_HEADER = "User-Agent: Custom-Agent"; 184 const ADD_POSTDATA = "&t3=t4"; 185 186 const { tab, monitor } = await initNetMonitor(POST_DATA_URL, { 187 requestCount: 1, 188 }); 189 info("Starting test... "); 190 191 const { document, store, windowRequire } = monitor.panelWin; 192 const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); 193 const { getSelectedRequest, getSortedRequests } = windowRequire( 194 "devtools/client/netmonitor/src/selectors/index" 195 ); 196 197 store.dispatch(Actions.batchEnable(false)); 198 199 // Execute requests. 200 await performRequests(monitor, tab, 2); 201 202 const origItemId = getSortedRequests(store.getState())[0].id; 203 204 store.dispatch(Actions.selectRequest(origItemId)); 205 await waitForRequestData( 206 store, 207 ["requestHeaders", "requestPostData"], 208 origItemId 209 ); 210 211 let origItem = getSortedRequests(store.getState())[0]; 212 213 // add a new custom request cloned from selected request 214 215 store.dispatch(Actions.cloneSelectedRequest()); 216 await testCustomForm(origItem); 217 218 let customItem = getSelectedRequest(store.getState()); 219 testCustomItem(customItem, origItem); 220 221 // edit the custom request 222 await editCustomForm(); 223 224 // FIXME: reread the customItem, it's been replaced by a new object (immutable!) 225 customItem = getSelectedRequest(store.getState()); 226 testCustomItemChanged(customItem, origItem); 227 228 // send the new request 229 const wait = waitForNetworkEvents(monitor, 1); 230 store.dispatch(Actions.sendCustomRequest()); 231 await wait; 232 233 let sentItem; 234 // Testing sent request will require updated requestHeaders and requestPostData, 235 // we must wait for both properties get updated before starting test. 236 await waitUntil(() => { 237 sentItem = getSelectedRequest(store.getState()); 238 origItem = getSortedRequests(store.getState())[0]; 239 return ( 240 sentItem && 241 sentItem.requestHeaders && 242 sentItem.requestPostData && 243 origItem && 244 origItem.requestHeaders && 245 origItem.requestPostData 246 ); 247 }); 248 249 await testSentRequest(sentItem, origItem); 250 251 // Ensure the UI shows the new request, selected, and that the detail panel was closed. 252 is( 253 getSortedRequests(store.getState()).length, 254 3, 255 "There are 3 requests shown" 256 ); 257 is( 258 document 259 .querySelector(".request-list-item.selected") 260 .getAttribute("data-id"), 261 sentItem.id, 262 "The sent request is selected" 263 ); 264 is( 265 document.querySelector(".network-details-bar"), 266 null, 267 "The detail panel is hidden" 268 ); 269 270 await teardown(monitor); 271 272 function testCustomItem(item, orig) { 273 is( 274 item.method, 275 orig.method, 276 "item is showing the same method as original request" 277 ); 278 is(item.url, orig.url, "item is showing the same URL as original request"); 279 } 280 281 function testCustomItemChanged(item, orig) { 282 const { url } = item; 283 const expectedUrl = orig.url + "&" + ADD_QUERY; 284 285 is(url, expectedUrl, "menu item is updated to reflect url entered in form"); 286 } 287 288 /* 289 * Test that the New Request form was populated correctly 290 */ 291 async function testCustomForm(data) { 292 await waitUntil(() => document.querySelector(".custom-request-panel")); 293 is( 294 document.getElementById("custom-method-value").value, 295 data.method, 296 "new request form showing correct method" 297 ); 298 299 is( 300 document.getElementById("custom-url-value").value, 301 data.url, 302 "new request form showing correct url" 303 ); 304 305 const query = document.getElementById("custom-query-value"); 306 is( 307 query.value, 308 "foo=bar\nbaz=42\ntype=urlencoded", 309 "new request form showing correct query string" 310 ); 311 312 const headers = document 313 .getElementById("custom-headers-value") 314 .value.split("\n"); 315 for (const { name, value } of data.requestHeaders.headers) { 316 ok( 317 headers.includes(name + ": " + value), 318 "form contains header from request" 319 ); 320 } 321 322 const postData = document.getElementById("custom-postdata-value"); 323 is( 324 postData.value, 325 data.requestPostData.postData.text, 326 "new request form showing correct post data" 327 ); 328 } 329 330 /* 331 * Add some params and headers to the request form 332 */ 333 async function editCustomForm() { 334 monitor.panelWin.focus(); 335 336 const query = document.getElementById("custom-query-value"); 337 const queryFocus = once(query, "focus", false); 338 // Bug 1195825: Due to some unexplained dark-matter with promise, 339 // focus only works if delayed by one tick. 340 query.setSelectionRange(query.value.length, query.value.length); 341 executeSoon(() => query.focus()); 342 await queryFocus; 343 344 // add params to url query string field 345 typeInNetmonitor(["VK_RETURN"], monitor); 346 typeInNetmonitor(ADD_QUERY, monitor); 347 348 const headers = document.getElementById("custom-headers-value"); 349 const headersFocus = once(headers, "focus", false); 350 headers.setSelectionRange(headers.value.length, headers.value.length); 351 headers.focus(); 352 await headersFocus; 353 354 // add a header 355 typeInNetmonitor(["VK_RETURN"], monitor); 356 typeInNetmonitor(ADD_HEADER, monitor); 357 358 // add a User-Agent header, to check if default headers can be modified 359 // (there will be two of them, first gets overwritten by the second) 360 typeInNetmonitor(["VK_RETURN"], monitor); 361 typeInNetmonitor(ADD_UA_HEADER, monitor); 362 363 const postData = document.getElementById("custom-postdata-value"); 364 const postFocus = once(postData, "focus", false); 365 postData.setSelectionRange(postData.value.length, postData.value.length); 366 postData.focus(); 367 await postFocus; 368 369 // add to POST data once textarea has updated 370 await waitUntil(() => postData.textContent !== ""); 371 typeInNetmonitor(ADD_POSTDATA, monitor); 372 } 373 374 /* 375 * Make sure newly created event matches expected request 376 */ 377 async function testSentRequest(data, origData) { 378 is(data.method, origData.method, "correct method in sent request"); 379 is(data.url, origData.url + "&" + ADD_QUERY, "correct url in sent request"); 380 381 const { headers } = data.requestHeaders; 382 const hasHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_HEADER); 383 ok(hasHeader, "new header added to sent request"); 384 385 const hasUAHeader = headers.some( 386 h => `${h.name}: ${h.value}` == ADD_UA_HEADER 387 ); 388 ok(hasUAHeader, "User-Agent header added to sent request"); 389 390 is( 391 data.requestPostData.postData.text, 392 origData.requestPostData.postData.text + ADD_POSTDATA, 393 "post data added to sent request" 394 ); 395 } 396 }