diff options
Diffstat (limited to '')
-rw-r--r-- | devtools/client/netmonitor/test/browser_net_simple-request-details.js | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/test/browser_net_simple-request-details.js b/devtools/client/netmonitor/test/browser_net_simple-request-details.js new file mode 100644 index 0000000000..e52a0b101a --- /dev/null +++ b/devtools/client/netmonitor/test/browser_net_simple-request-details.js @@ -0,0 +1,388 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests if requests render correct information in the details UI. + */ + +add_task(async function () { + const { + L10N, + } = require("resource://devtools/client/netmonitor/src/utils/l10n.js"); + + const { monitor } = await initNetMonitor(SIMPLE_SJS, { + requestCount: 1, + }); + info("Starting test... "); + + const { document, store, windowRequire } = monitor.panelWin; + const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); + const { PANELS } = windowRequire("devtools/client/netmonitor/src/constants"); + const { getSelectedRequest, getSortedRequests } = windowRequire( + "devtools/client/netmonitor/src/selectors/index" + ); + + store.dispatch(Actions.batchEnable(false)); + + const wait = waitForNetworkEvents(monitor, 1); + await reloadBrowser(); + await wait; + + is( + getSelectedRequest(store.getState()), + undefined, + "There shouldn't be any selected item in the requests menu." + ); + is( + store.getState().requests.requests.length, + 1, + "The requests menu should not be empty after the first request." + ); + is( + !!document.querySelector(".network-details-bar"), + false, + "The network details panel should still be hidden after first request." + ); + + const waitForHeaders = waitForDOM(document, ".headers-overview"); + + store.dispatch(Actions.toggleNetworkDetails()); + + isnot( + getSelectedRequest(store.getState()), + undefined, + "There should be a selected item in the requests menu." + ); + is( + getSelectedIndex(store.getState()), + 0, + "The first item should be selected in the requests menu." + ); + is( + !!document.querySelector(".network-details-bar"), + true, + "The network details panel should not be hidden after toggle button was pressed." + ); + + await waitForHeaders; + + await testHeadersTab(); + await testCookiesTab(); + await testParamsTab(); + await testResponseTab(); + await testTimingsTab(); + await closePanelOnEsc(); + return teardown(monitor); + + function getSelectedIndex(state) { + if (!state.requests.selectedId) { + return -1; + } + return getSortedRequests(state).findIndex( + r => r.id === state.requests.selectedId + ); + } + + async function testHeadersTab() { + const tabEl = document.querySelectorAll( + ".network-details-bar .tabs-menu a" + )[0]; + const tabpanel = document.querySelector("#headers-panel"); + + is( + tabEl.getAttribute("aria-selected"), + "true", + "The headers tab in the network details pane should be selected." + ); + // Request URL + is( + tabpanel.querySelector(".url-preview .url").innerText, + SIMPLE_SJS, + "The url summary value is incorrect." + ); + + // Request method + is( + tabpanel.querySelectorAll(".treeLabel")[0].innerText, + "GET", + "The method summary value is incorrect." + ); + // Status code + is( + tabpanel.querySelector(".requests-list-status-code").innerText, + "200", + "The status summary code is incorrect." + ); + is( + tabpanel.querySelector(".status").childNodes[1].textContent, + "Och Aye", + "The status summary value is incorrect." + ); + // Version + is( + tabpanel.querySelectorAll(".tabpanel-summary-value")[1].innerText, + "HTTP/1.1", + "The HTTP version is incorrect." + ); + + await waitForRequestData(store, ["requestHeaders", "responseHeaders"]); + + is( + tabpanel.querySelectorAll(".accordion-item").length, + 2, + "There should be 2 header scopes displayed in this tabpanel." + ); + + is( + tabpanel.querySelectorAll(".accordion .treeLabelCell").length, + 23, + "There should be 23 header values displayed in this tabpanel." + ); + + const headersTable = tabpanel.querySelector(".accordion"); + const responseScope = headersTable.querySelectorAll( + "tr[id^='/Response Headers']" + ); + const requestScope = headersTable.querySelectorAll( + "tr[id^='/Request Headers']" + ); + + const headerLabels = headersTable.querySelectorAll( + ".accordion-item .accordion-header-label" + ); + + ok( + headerLabels[0].innerHTML.match( + new RegExp(L10N.getStr("responseHeaders") + " \\([0-9]+ .+\\)") + ), + "The response headers scope doesn't have the correct title." + ); + + ok( + headerLabels[1].innerHTML.includes(L10N.getStr("requestHeaders") + " ("), + "The request headers scope doesn't have the correct title." + ); + + const responseHeaders = [ + { + name: "cache-control", + value: "no-cache, no-store, must-revalidate", + pos: "first", + index: 1, + }, + { + name: "connection", + value: "close", + pos: "second", + index: 2, + }, + { + name: "content-length", + value: "12", + pos: "third", + index: 3, + }, + { + name: "content-type", + value: "text/plain; charset=utf-8", + pos: "fourth", + index: 4, + }, + { + name: "foo-bar", + value: "baz", + pos: "seventh", + index: 7, + }, + ]; + responseHeaders.forEach(header => { + is( + responseScope[header.index - 1].querySelector(".treeLabel").innerHTML, + header.name, + `The ${header.pos} response header name was incorrect.` + ); + is( + responseScope[header.index - 1].querySelector(".objectBox").innerHTML, + `${header.value}`, + `The ${header.pos} response header value was incorrect.` + ); + }); + + const requestHeaders = [ + { + name: "Cache-Control", + value: "no-cache", + pos: "fourth", + index: 4, + }, + { + name: "Connection", + value: "keep-alive", + pos: "fifth", + index: 5, + }, + { + name: "Host", + value: "example.com", + pos: "seventh", + index: 7, + }, + { + name: "Pragma", + value: "no-cache", + pos: "eighth", + index: 8, + }, + ]; + requestHeaders.forEach(header => { + is( + requestScope[header.index - 1].querySelector(".treeLabel").innerHTML, + header.name, + `The ${header.pos} request header name was incorrect.` + ); + is( + requestScope[header.index - 1].querySelector(".objectBox").innerHTML, + `${header.value}`, + `The ${header.pos} request header value was incorrect.` + ); + }); + } + + async function testCookiesTab() { + const tabpanel = await selectTab(PANELS.COOKIES, 1); + + const cookieAccordion = tabpanel.querySelector(".accordion"); + + is( + cookieAccordion.querySelectorAll(".accordion-item").length, + 2, + "There should be 2 cookie scopes displayed in this tabpanel." + ); + // 2 Cookies in response - 1 httpOnly and 1 value for each cookie - total 6 + + const resCookiesTable = cookieAccordion.querySelector( + "li[id='responseCookies'] .accordion-content .treeTable" + ); + is( + resCookiesTable.querySelectorAll("tr.treeRow").length, + 6, + "There should be 6 rows displayed in response cookies table" + ); + + const reqCookiesTable = cookieAccordion.querySelector( + "li[id='requestCookies'] .accordion-content .treeTable" + ); + is( + reqCookiesTable.querySelectorAll("tr.treeRow").length, + 2, + "There should be 2 cookie values displayed in request cookies table." + ); + } + + async function testParamsTab() { + const tabpanel = await selectTab(PANELS.REQUEST, 2); + + is( + tabpanel.querySelectorAll(".panel-container").length, + 0, + "There should be no param scopes displayed in this tabpanel." + ); + is( + tabpanel.querySelectorAll(".empty-notice").length, + 1, + "The empty notice should be displayed in this tabpanel." + ); + } + + async function testResponseTab() { + const tabpanel = await selectTab(PANELS.RESPONSE, 3); + await waitForDOM(document, "#response-panel .source-editor-mount"); + + is( + tabpanel.querySelectorAll( + "#response-panel .raw-data-toggle-input .devtools-checkbox-toggle" + ).length, + 0, + "The raw data toggle should not be shown in this tabpanel." + ); + is( + tabpanel.querySelectorAll(".source-editor-mount").length, + 1, + "The response payload should be shown initially." + ); + } + + async function testTimingsTab() { + const tabpanel = await selectTab(PANELS.TIMINGS, 4); + + const displayFormat = new RegExp(/[0-9]+ ms$/); + const propsToVerify = [ + "blocked", + "dns", + "connect", + "ssl", + "send", + "wait", + "receive", + ]; + + // To ensure that test case for a new property is written, otherwise this + // test will fail + is( + tabpanel.querySelectorAll(".tabpanel-summary-container").length, + propsToVerify.length, + `There should be exactly ${propsToVerify.length} values + displayed in this tabpanel` + ); + + propsToVerify.forEach(propName => { + ok( + tabpanel + .querySelector( + `#timings-summary-${propName} + .requests-list-timings-total` + ) + .innerHTML.match(displayFormat), + `The ${propName} timing info does not appear to be correct.` + ); + }); + } + + async function selectTab(tabName, pos) { + const tabEl = document.querySelectorAll( + ".network-details-bar .tabs-menu a" + )[pos]; + + const onPanelOpen = waitForDOM(document, `#${tabName}-panel`); + clickOnSidebarTab( + document, + tabEl.id.substring(0, tabEl.id.indexOf("-tab")) + ); + await onPanelOpen; + + is( + tabEl.getAttribute("aria-selected"), + "true", + `The ${tabName} tab in the network details pane should be selected.` + ); + + return document.querySelector(".network-details-bar .tab-panel"); + } + + // This test will timeout on failure + async function closePanelOnEsc() { + EventUtils.sendKey("ESCAPE", window); + + await waitUntil(() => { + return document.querySelector(".network-details-bar") == null; + }); + + is( + document.querySelectorAll(".network-details-bar").length, + 0, + "Network details panel should close on ESC key" + ); + } +}); |