Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: http3 OR http2
- Manifest: devtools/client/netmonitor/test/browser.toml
/* Any copyright is dedicated to the Public Domain.
"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,
24,
"There should be 24 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"
);
}
});