diff options
Diffstat (limited to 'devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js')
-rw-r--r-- | devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js new file mode 100644 index 0000000000..bcbb4bef2a --- /dev/null +++ b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js @@ -0,0 +1,224 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Basic tests for exporting Network panel content into HAR format. + */ + +const EXPECTED_REQUEST_HEADER_COUNT = 9; +const EXPECTED_RESPONSE_HEADER_COUNT = 6; + +add_task(async function () { + // Disable tcp fast open, because it is setting a response header indicator + // (bug 1352274). TCP Fast Open is not present on all platforms therefore the + // number of response headers will vary depending on the platform. + await pushPref("network.tcp.tcp_fastopen_enable", false); + const { tab, monitor, toolbox } = await initNetMonitor(SIMPLE_URL, { + requestCount: 1, + }); + + info("Starting test... "); + + await testSimpleReload({ tab, monitor, toolbox }); + await testResponseBodyLimits({ tab, monitor, toolbox }); + await testManyReloads({ tab, monitor, toolbox }); + await testClearedRequests({ tab, monitor, toolbox }); + + // Do not use teardown(monitor) as testClearedRequests register broken requests + // which never complete and would block on waitForAllNetworkUpdateEvents + await closeTabAndToolbox(); +}); + +async function testSimpleReload({ tab, monitor, toolbox }) { + info("Test with a simple page reload"); + + const har = await reloadAndCopyAllAsHar({ tab, monitor, toolbox }); + + // Check out HAR log + isnot(har.log, null, "The HAR log must exist"); + is(har.log.creator.name, "Firefox", "The creator field must be set"); + is(har.log.browser.name, "Firefox", "The browser field must be set"); + is(har.log.pages.length, 1, "There must be one page"); + is(har.log.entries.length, 1, "There must be one request"); + + const page = har.log.pages[0]; + + is(page.title, SIMPLE_URL, "There must be some page title"); + ok("onContentLoad" in page.pageTimings, "There must be onContentLoad time"); + ok("onLoad" in page.pageTimings, "There must be onLoad time"); + + const entry = har.log.entries[0]; + assertNavigationRequestEntry(entry); + + info("We get the response content and timings when doing a simple reload"); + isnot(entry.response.content.text, undefined, "Check response body"); + is(entry.response.content.text.length, 465, "Response body is complete"); + isnot(entry.timings, undefined, "Check timings"); +} + +async function testResponseBodyLimits({ tab, monitor, toolbox }) { + info("Test response body limit (non zero)."); + await pushPref("devtools.netmonitor.responseBodyLimit", 10); + let har = await reloadAndCopyAllAsHar({ tab, monitor, toolbox }); + let entry = har.log.entries[0]; + is(entry.response.content.text.length, 10, "Response body must be truncated"); + + info("Test response body limit (zero)."); + await pushPref("devtools.netmonitor.responseBodyLimit", 0); + har = await reloadAndCopyAllAsHar({ tab, monitor, toolbox }); + entry = har.log.entries[0]; + is( + entry.response.content.text.length, + 465, + "Response body must not be truncated" + ); +} + +async function testManyReloads({ tab, monitor, toolbox }) { + const har = await reloadAndCopyAllAsHar({ + tab, + monitor, + toolbox, + reloadTwice: true, + }); + // In most cases, we will have two requests, but sometimes, + // the first one might be missing as we couldn't fetch any lazy data for it. + Assert.greaterOrEqual( + har.log.entries.length, + 1, + "There must be at least one request" + ); + info( + "Assert the first navigation request which has been cancelled by the second reload" + ); + // Requests may come out of order, so try to find the bogus cancelled request + let entry = har.log.entries.find(e => e.response.status == 0); + if (entry) { + ok(entry, "Found the cancelled request"); + is(entry.request.method, "GET", "Method is set"); + is(entry.request.url, SIMPLE_URL, "URL is set"); + // We always get the following headers: + // "Host", "User-agent", "Accept", "Accept-Language", "Accept-Encoding", "Connection" + // but are missing the three last headers: + // "Upgrade-Insecure-Requests", "Pragma", "Cache-Control" + is(entry.request.headers.length, 6, "But headers are partialy populated"); + is(entry.response.status, 0, "And status is set to 0"); + } + + entry = har.log.entries.find(e => e.response.status != 0); + assertNavigationRequestEntry(entry); +} + +async function testClearedRequests({ tab, monitor, toolbox }) { + info("Navigate to an empty page"); + const topDocumentURL = + "https://example.org/document-builder.sjs?html=empty-document"; + const iframeURL = + "https://example.org/document-builder.sjs?html=" + + encodeURIComponent( + `iframe<script>fetch("/document-builder.sjs?html=iframe-request")</script>` + ); + + await waitForAllNetworkUpdateEvents(); + await navigateTo(topDocumentURL); + + info("Create an iframe doing a request and remove the iframe."); + info( + "Doing this, should notify a network request that is destroyed on the server side" + ); + const onNetworkEvents = waitForNetworkEvents(monitor, 2); + await SpecialPowers.spawn( + tab.linkedBrowser, + [iframeURL], + async function (_iframeURL) { + const iframe = content.document.createElement("iframe"); + iframe.setAttribute("src", _iframeURL); + content.document.body.appendChild(iframe); + } + ); + // Wait for the two request to be processed (iframe doc + fetch requests) + // before removing the iframe so that the netmonitor is able to fetch + // all lazy data without throwing + await onNetworkEvents; + await waitForAllNetworkUpdateEvents(); + + info("Remove the iframe so that lazy request data are freed"); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + content.document.querySelector("iframe").remove(); + }); + + // HAR will try to re-fetch lazy data and may throw on the iframe fetch request. + // This subtest is meants to verify we aren't throwing here and HAR export + // works fine, even if some requests can't be fetched. + const har = await copyAllAsHARWithContextMenu(monitor); + is(har.log.entries.length, 2, "There must be two requests"); + is( + har.log.entries[0].request.url, + topDocumentURL, + "First request is for the top level document" + ); + is( + har.log.entries[1].request.url, + iframeURL, + "Second request is for the iframe" + ); + info( + "The fetch request doesn't appear in HAR export, because its lazy data is freed and we completely ignore the request." + ); +} + +function assertNavigationRequestEntry(entry) { + info("Assert that the entry relates to the navigation request"); + Assert.greater(entry.time, 0, "Check the total time"); + is(entry.request.method, "GET", "Check the method"); + is(entry.request.url, SIMPLE_URL, "Check the URL"); + is( + entry.request.headers.length, + EXPECTED_REQUEST_HEADER_COUNT, + "Check number of request headers" + ); + is(entry.response.status, 200, "Check response status"); + is(entry.response.statusText, "OK", "Check response status text"); + is( + entry.response.headers.length, + EXPECTED_RESPONSE_HEADER_COUNT, + "Check number of response headers" + ); + is( + entry.response.content.mimeType, + "text/html", + "Check response content type" + ); +} +/** + * Reload the page and copy all as HAR. + */ +async function reloadAndCopyAllAsHar({ + tab, + monitor, + toolbox, + reloadTwice = false, +}) { + const { store, windowRequire } = monitor.panelWin; + const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); + + store.dispatch(Actions.batchEnable(false)); + + const onNetworkEvent = waitForNetworkEvents(monitor, 1); + const { onDomCompleteResource } = + await waitForNextTopLevelDomCompleteResource(toolbox.commands); + + if (reloadTwice) { + reloadBrowser(); + } + await reloadBrowser(); + + info("Waiting for network events"); + await onNetworkEvent; + info("Waiting for DOCUMENT_EVENT dom-complete resource"); + await onDomCompleteResource; + + return copyAllAsHARWithContextMenu(monitor); +} |