/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /** * Tests if resending a request works. */ add_task(async function () { if ( Services.prefs.getBoolPref( "devtools.netmonitor.features.newEditAndResend", true ) ) { await testResendRequest(); } else { await testOldEditAndResendPanel(); } }); // This tests resending a request without editing using // the resend context menu item. This particularly covering // the new resend functionality. async function testResendRequest() { const { tab, monitor } = await initNetMonitor(POST_DATA_URL, { requestCount: 1, }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; // Action should be processed synchronously in tests. const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); store.dispatch(Actions.batchEnable(false)); await performRequests(monitor, tab, 2); is( document.querySelectorAll(".request-list-item").length, 2, "There are currently two requests" ); const firstResend = await resendRequestAndWaitForNewRequest( monitor, document.querySelectorAll(".request-list-item")[0] ); ok( firstResend.originalResource.resourceId !== firstResend.newResource.resourceId, "The resent request is different resource from the first request" ); is( firstResend.originalResource.url, firstResend.newResource.url, "The resent request has the same url and query parameters and the first request" ); is( firstResend.originalResource.requestHeaders.headers.length, firstResend.newResource.requestHeaders.headers.length, "The no of headers are the same" ); firstResend.originalResource.requestHeaders.headers.forEach( ({ name, value }) => { const foundHeader = firstResend.newResource.requestHeaders.headers.find( header => header.name == name ); is( value, foundHeader.value, `The '${name}' header for the request and the resent request match` ); } ); info("Check that the custom headers and form data are resent correctly"); const secondResend = await resendRequestAndWaitForNewRequest( monitor, document.querySelectorAll(".request-list-item")[1] ); ok( secondResend.originalResource.resourceId !== secondResend.newResource.resourceId, "The resent request is different resource from the second request" ); const customHeader = secondResend.originalResource.requestHeaders.headers.find( header => header.name == "custom-header-xxx" ); const customHeaderInResentRequest = secondResend.newResource.requestHeaders.headers.find( header => header.name == "custom-header-xxx" ); is( customHeader.value, customHeaderInResentRequest.value, "The custom header in the resent request is the same as the second request" ); is( customHeaderInResentRequest.value, "custom-value-xxx", "The custom header in the resent request is correct" ); is( secondResend.originalResource.requestPostData.postData.text, secondResend.newResource.requestPostData.postData.text, "The form data in the resent is the same as the second request" ); } async function resendRequestAndWaitForNewRequest(monitor, originalRequestItem) { const { document, store, windowRequire, connector } = monitor.panelWin; const { getSelectedRequest, getDisplayedRequests } = windowRequire( "devtools/client/netmonitor/src/selectors/index" ); info("Select the request to resend"); const expectedNoOfRequestsAfterResend = getDisplayedRequests(store.getState()).length + 1; const waitForHeaders = waitUntil(() => document.querySelector(".headers-overview") ); EventUtils.sendMouseEvent({ type: "mousedown" }, originalRequestItem); await waitForHeaders; const originalResourceId = getSelectedRequest(store.getState()).id; const waitForNewRequest = waitUntil( () => getDisplayedRequests(store.getState()).length == expectedNoOfRequestsAfterResend && getSelectedRequest(store.getState()).id !== originalResourceId ); info("Open the context menu and select the resend for the request"); EventUtils.sendMouseEvent({ type: "contextmenu" }, originalRequestItem); await selectContextMenuItem(monitor, "request-list-context-resend-only"); await waitForNewRequest; const newResourceId = getSelectedRequest(store.getState()).id; // Make sure we fetch the request headers and post data for the // new request so we can assert them. await connector.requestData(newResourceId, "requestHeaders"); await connector.requestData(newResourceId, "requestPostData"); return { originalResource: getRequestById(store.getState(), originalResourceId), newResource: getRequestById(store.getState(), newResourceId), }; } // This is a basic test for the old edit and resend panel // This should be removed soon in Bug 1745416 when we remove // the old panel functionality. async function testOldEditAndResendPanel() { const ADD_QUERY = "t1=t2"; const ADD_HEADER = "Test-header: true"; const ADD_UA_HEADER = "User-Agent: Custom-Agent"; const ADD_POSTDATA = "&t3=t4"; const { tab, monitor } = await initNetMonitor(POST_DATA_URL, { requestCount: 1, }); info("Starting test... "); const { document, store, windowRequire } = monitor.panelWin; const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); const { getSelectedRequest, getSortedRequests } = windowRequire( "devtools/client/netmonitor/src/selectors/index" ); store.dispatch(Actions.batchEnable(false)); // Execute requests. await performRequests(monitor, tab, 2); const origItemId = getSortedRequests(store.getState())[0].id; store.dispatch(Actions.selectRequest(origItemId)); await waitForRequestData( store, ["requestHeaders", "requestPostData"], origItemId ); let origItem = getSortedRequests(store.getState())[0]; // add a new custom request cloned from selected request store.dispatch(Actions.cloneSelectedRequest()); await testCustomForm(origItem); let customItem = getSelectedRequest(store.getState()); testCustomItem(customItem, origItem); // edit the custom request await editCustomForm(); // FIXME: reread the customItem, it's been replaced by a new object (immutable!) customItem = getSelectedRequest(store.getState()); testCustomItemChanged(customItem, origItem); // send the new request const wait = waitForNetworkEvents(monitor, 1); store.dispatch(Actions.sendCustomRequest()); await wait; let sentItem; // Testing sent request will require updated requestHeaders and requestPostData, // we must wait for both properties get updated before starting test. await waitUntil(() => { sentItem = getSelectedRequest(store.getState()); origItem = getSortedRequests(store.getState())[0]; return ( sentItem && sentItem.requestHeaders && sentItem.requestPostData && origItem && origItem.requestHeaders && origItem.requestPostData ); }); await testSentRequest(sentItem, origItem); // Ensure the UI shows the new request, selected, and that the detail panel was closed. is( getSortedRequests(store.getState()).length, 3, "There are 3 requests shown" ); is( document .querySelector(".request-list-item.selected") .getAttribute("data-id"), sentItem.id, "The sent request is selected" ); is( document.querySelector(".network-details-bar"), null, "The detail panel is hidden" ); await teardown(monitor); function testCustomItem(item, orig) { is( item.method, orig.method, "item is showing the same method as original request" ); is(item.url, orig.url, "item is showing the same URL as original request"); } function testCustomItemChanged(item, orig) { const { url } = item; const expectedUrl = orig.url + "&" + ADD_QUERY; is(url, expectedUrl, "menu item is updated to reflect url entered in form"); } /* * Test that the New Request form was populated correctly */ async function testCustomForm(data) { await waitUntil(() => document.querySelector(".custom-request-panel")); is( document.getElementById("custom-method-value").value, data.method, "new request form showing correct method" ); is( document.getElementById("custom-url-value").value, data.url, "new request form showing correct url" ); const query = document.getElementById("custom-query-value"); is( query.value, "foo=bar\nbaz=42\ntype=urlencoded", "new request form showing correct query string" ); const headers = document .getElementById("custom-headers-value") .value.split("\n"); for (const { name, value } of data.requestHeaders.headers) { ok( headers.includes(name + ": " + value), "form contains header from request" ); } const postData = document.getElementById("custom-postdata-value"); is( postData.value, data.requestPostData.postData.text, "new request form showing correct post data" ); } /* * Add some params and headers to the request form */ async function editCustomForm() { monitor.panelWin.focus(); const query = document.getElementById("custom-query-value"); const queryFocus = once(query, "focus", false); // Bug 1195825: Due to some unexplained dark-matter with promise, // focus only works if delayed by one tick. query.setSelectionRange(query.value.length, query.value.length); executeSoon(() => query.focus()); await queryFocus; // add params to url query string field typeInNetmonitor(["VK_RETURN"], monitor); typeInNetmonitor(ADD_QUERY, monitor); const headers = document.getElementById("custom-headers-value"); const headersFocus = once(headers, "focus", false); headers.setSelectionRange(headers.value.length, headers.value.length); headers.focus(); await headersFocus; // add a header typeInNetmonitor(["VK_RETURN"], monitor); typeInNetmonitor(ADD_HEADER, monitor); // add a User-Agent header, to check if default headers can be modified // (there will be two of them, first gets overwritten by the second) typeInNetmonitor(["VK_RETURN"], monitor); typeInNetmonitor(ADD_UA_HEADER, monitor); const postData = document.getElementById("custom-postdata-value"); const postFocus = once(postData, "focus", false); postData.setSelectionRange(postData.value.length, postData.value.length); postData.focus(); await postFocus; // add to POST data once textarea has updated await waitUntil(() => postData.textContent !== ""); typeInNetmonitor(ADD_POSTDATA, monitor); } /* * Make sure newly created event matches expected request */ async function testSentRequest(data, origData) { is(data.method, origData.method, "correct method in sent request"); is(data.url, origData.url + "&" + ADD_QUERY, "correct url in sent request"); const { headers } = data.requestHeaders; const hasHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_HEADER); ok(hasHeader, "new header added to sent request"); const hasUAHeader = headers.some( h => `${h.name}: ${h.value}` == ADD_UA_HEADER ); ok(hasUAHeader, "User-Agent header added to sent request"); is( data.requestPostData.postData.text, origData.requestPostData.postData.text + ADD_POSTDATA, "post data added to sent request" ); } }